shuttle-run
Run one claimed ticket on its own VM, or in its own git worktree, through an open pull request.
Skill by smlsr
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/smlsr-shuttle-run
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: productivity, agent-building
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/shuttle-run
What's inside
- Skill name
shuttle-run- Description
- Run one claimed ticket on its own VM, or in its own git worktree, through an open pull request. Bugbot runs on that pull request, not in this turn. Use when a Shuttle is started with the subagent prompt. Does not merge, and does not call Jira or Slack.
- License
- MIT
- Instructions
- 77 lines in SKILL.md · view on GitHub
Agent Skills spec issues
- files: referenced but missing: scripts/mcp_tools.py
Files (1)
- SKILL.md
SKILL.md (first lines)
# Shuttle run
You were started as one subagent, for one step of one ticket. The prompt contains the claim: ticket id, Jira key, locks, acceptance, branch `warp/<id>-<jira>` (`warp/<name>/<id>-<jira>` on a named run), and your agent id on the line `agent: <agent>`. A `resume` prompt is this same Shuttle, not a second one.
## The contract
You do this step and return one line. You never start another agent of any kind. You never subscribe to anything, set a timer, loop, sleep, or wait on the pull request, CI, Bugbot, Slack, or an approval. The parent does all the waiting: it requests Bugbot, watches CI, and sends you a fix step if one is needed. If this conversation is woken later by anything that is not a new step from the parent (a CI result, a pull-request comment, a timer), run the reap check and return. Do not act on it.
## The reap check
Run it first, before any other step. The exact command is in your prompt.
```bash
python3 <plugin>/scripts/agents.py reap --beam <parent>/.warp/beam.json --id <agent> --ticket <id>
python3 <plugin>/scripts/agents.py reap --remote --id <agent> --ticket <id>
```
The first form is for a worktree. The second is for a dedicated VM: it reads the beam from origin's base branch and writes nothing. `reap: continue` means carry on. `reap: exit <reason>` means the run was paused or stopped, the ticket is merged or parked, or the parent replaced you. Stop now. Do not commit, push, or open anything more. Return `result: <id> stopped <reason> agent=<agent>`. `<agent>` is your registry id or your cloud id. Do not start another step.
`ticket_state.py append` prints the same `reap:` line after every state you write. Read it every time. That is how a pause reaches you in the middle of a step.
When the prompt says "Run in your own cloud environment on a dedicated VM with its own clone and branch, not a git worktree on this machine", do that. Fetch the latest main, clone it, and create the branch yourself. First run `hostname` and `free -g`. Report that output with your branch name and working directory. Write `.warp/tickets/<id>/` on your branch and push it. The parent disk is not shared. Cloud MCP servers are the ones at cursor.com/agents, not the parent's session.
When the prompt names a `worktree:` path, work only inside that absolute path. Write `.warp/tickets/<id>/state.json` and `log.jsonl` at the parent absolute path (`--root` is that parent). Do not pass `--push`.
Either way: commit, push, and open or update the pull request. Never merge. Never touch the parent checkout. Never call Jira or Slack. Run `checkCommand` from config (often `make ci`) on this branch. Check every acceptance criterion and record each one as pass or fail on `pr.acResults`. Return one line: `result: <id> ok pr=<url> check=<green|red> ac=<name>:pass,<name>:fail hostname=<hostname> branch=<branch> cwd=<cwd> memory=<available-gib>` or `result: <id> failed <why> hostname=<hostname> cwd=<cwd>`. Opening the pull request is not done. The parent requests Bugbot, reads the findings, and may start this same Shuttle again on this branch for a fix. The orchestrator is the only merger. Do not implement from chat memory. Edit only this ticket's locks and `appendOnlyPaths` (add your own lines at the end). Do not force a `feature/` prefix. If you need work that is not on the base branch, record a blocker on the plan. Do not build another ticket. Do not ask. Before the pull request, `orchestrator.py record --plan --result` using the parent beam path when you are on a worktree. On a VM, commit that record into the ticket branch. Return the result line. Do not push to the base branch, and do not start a second ticket.
Optional `launch: agent` starts one new Agent from an IMPLEMENT prompt. Clone main so `.cursor` rules load, then create the branch. A beam file does not have to exist before that Agent starts. That mode is for a future Cloud Agents API. This plugin does not call that API. Jira and Slack calls below are for that mode only. The default subagent skips them. The parent does that work.
## Load the repo before editing
1. Read `.cursor/rules/`, including every rule with `alwaysApply: true`. Read rules whose globs match the lock paths.
2. Read `AGENTS.md` and `CLAUDE.md` at the repo root and under the ticket lock path, if they exist.
3. Read the preamble the plan names.
4. Read `.warp/config.yaml` and the claim in the IMPLEMENT prompt. Status must be `claimed` or `recovering`, and `agent` must be you. Otherwise stop. If status is `recovering`, you are the replacement for a dead Shuttle. Keep a branch that has work beyond the beam commit, the pull request, `jira.startedAt`, and the locks. Do not open a second pull request. A branch that is only the beam commit is not that work: start from latest main. After the heartbeat below, set status back to `recoveryPriorStatus` and continue that work.
5. Fetch the Jira issue if a key is set. Otherwise read the ticket in `CURSOR_PLAN.md`. Read every acceptance criterion.
6. Heartbeat. Write status into the parent checkout by absolute path. Do not push that directory. Repeat at each status change and at least every 5 minutes while you are working, always inside `staleMinutes` (default 15). This is not a timer: write it between steps of work you are already doing, and read the `reap:` line it prints. Do not commit `.warp/beam.json`. A failed turn returns `result: <id> failed <why>` so the parent can raise `worker-died`. The heartbeat watchdog still covers a parent that dies mid-turn.
```bash
python3 <plugin>/scripts/ticket_state.py append --id <id> --state started --agent <agent> --root <parent>
python3 <plugin>/scripts/ticket_state.py append --id <id> --state heartbeat --agent <agent> --root <parent>
```
## Then
1. Jira status and the claim comment. The plan id is not the issue key. If the claim prints `jira: PROJECT`, `jiraProject` is still empty. Call `getAccessibleAtlassianResources` and `getVisibleJiraProjects`, save the transcript, and run `jira_sync.py project --apply` before any transition. If several projects are listed, do not pick one and do not call `transitionJiraIssue`. If the claim prints `jira: RESOLVE`, search Jira before any transition. For each ticket, query the external-id field first (`jql`, then the other `queries` whose kind is `external`), then `labels = "warp:<id>"`, then `getJiraIssueRemoteIssueLinks` if you have a candidate issue. Stop when one issue matches. Two matches are ambiguous: do not fall through and do not pick one. Before any transition, record the hit: `python3 <plugin>/scripts/jira_sync.py resolve --ticket <id> --key <WAR-1> --issue-id <issue id> --cloud-id <cloudId> --status "<current status>"`. That writes `jiraKey`, `jira.id`, and `jira.cloudId` on the beam and in `.warp/jira-map.json`. A plan id is refused and nothing is stored. Do not call `transitionJiraIssue` until that command prints `jira: MUST DO` with the stored key. A saved transcript still works: `resolve --apply results.json` with `{"searches":[{"jql":"...","issues":[{"key":"WAR-1"}]}]}`. If the tool errors or Jira is not connected, record `unavailable` and carry on. That does not stop the run. If the search ran and returned no issue, apply `{"WV-01": []}` so the miss is recorded. Do the transition and comment with `jiraKey` from the todo only: `getAccessibleAtlassianResources` for `cloudId` (or `jiraSite` if set) on the server named `jiraMcp`, `getJiraIssue`, `getTransitionsForJiraIssue`, `jira_sync.py pick`, `transitionJiraIssue`, then `addOrEditJiraIssueComment` with the comment body. Record with `jira_sync.py record` and `record-comment`. If `getJiraIssue` says the issue was not found, record `--result not-found` (the error text can say not found). If `--apply` says `needs mapping` or `ambiguous`, do not call Jira and do not pass the plan id to `transitionJiraIssue`. When no ticket in this run has `jira.startedAt`, either of those results releases the claim and stops the run: the script prints `jira: STOP` and one sentence, `Run stopped because Jira issues are not linked (<id> / <key>)`. Post only that Herald payload. Do not implement, do not open a pull request, and do not also say the claim still stands. A later miss, after one ticket was moved to In Progress, is a per-ticket alarm: the claim stands and the run continues. `jiraTransition: false` does not stop the run. `/warp-jira-map` is only for a leftover after that later miss. If `jira.startedAt` is already set, the transition is not asked for again. No connector, no matching transition, or another failed call is not an error: record `unavailable`, `no-transition`, or `failed`. That writes `.warp/outbox.md` and a Herald message. Continue.
2. Set `planning` by appending it to your ticket directory and pushing that directory. Plan the diff inside the lock paths only. An escape is `alarm` / `lock-escape`. Name every file or directory outside the lock with `--escaped`, one flag per path, and push that too. Do not widen the lock yourself. Do not commit `.warp/beam.json`.
```bash
python3 <plugin>/scripts/ticket_state.py append --id <id> --state planning --root <parent>
python3 <plugin>/scripts/ticket_state.py append --id <id> --state lock-escape --escaped <path> --root <parent>
``` If `alarmRepair.state` is `naming`, you are only naming those paths: run `alarm_repair.py paths --id <id> --path <path>` and stop. Do not edit. A repair whose state is `working` already has the widened locks. Finish inside them. Clear the alarm only when the diff stays inside those locks and you set a normal status. If you still need a path outside them, set `alarm` / `lock-escape` with the new `--escaped` paths. If you error out, run `alarm_repair.py next --returned <id> --error "<why>"` and leave the alarm set.
3. Set `coding`. Branch `warp/<id>-<jiraKey>` (`warp/<name>/<id>-<jiraKey>` when the claim names a run) off `baseBranch` from `python3 <plugin>/scripts/provider.py resolve`. The parent started you with the model on the prompt's `model:` line: `model<Size>` for this ticket's size, else `config.model` (default `claude-sonnet-5-5-high`), or `fixModel` on a later fix round. Report that slug in your log; the slug must match the Cursor model picker.
4. Implement. Prove each acceptance criterion. Checkpoint spend with `scripts/beam.py spend`. If the Cursor run reports token usage, also record it. Pass the totals for this ticket, which replace the previous report:
```bash
python3 <plugin>/scripts/beam.py usage --beam .warp/beam.json --id <id> \
--tokens-in N --tokens-out N --tokens-cached N --cost USD
```
The same flags work on `beam.py set`. If the run does not report usage, do not invent numbers. The completion report shows n/a.
5. If resolve says connected, push and open or update the pull request with the first method in `methods` (the named MCP server, or `gh` for GitHub). If it says local, or every method fails, do not push and do not error: `python3 <plugin>/scripts/provider.py note --id <id> --reason "<why>"` and hand the branch name back. When the push worked and only opening the pull request failed (a 403, a read-only `gh`, or `mcp_allow.py` printing `ask` for `create_pull_request`), the branch is not local-only: go on with the check, the review, and the acceptance results, and write `done` without `--pr`. The parent opens the pull request on its next pass. `provider.py note` sees the branch on origin and says it was pushed. Set `review` and, in connected mode, `--pr <url>`. That records the pull request as opened. The slot stays occupied until the check rollup is green. Opening the pull request is not done. Run `checkCommand` and record `pr.check` as `green` or `red`, with the log on `pr.checkLog`. Check every acceptance criterion your prompt lists (`acceptance: AC-1: ... | AC-2: ...`, and one line each) against your branch, running what it says to run, and record a result for each on your last line: `--ac AC-1=pass --ac AC-2=fail ...` on the `--state done` command in your prompt. Pass only what you saw pass. The parent reads those results from your ticket folder; `pr.acResults` is where it stores them. A `VERIFY ONLY` prompt (`step=verify`) changes no code: check each criterion, record, and return `result: <id> ok acceptance=<passed>/<n>`. Do not run Bugbot in this VM unless your prompt has the `bugbotTrigger: shuttle` lines. Otherwise Bugbot runs on the pull request after the push, and the parent requests it. With those lines, you run the review after the check is green: diff your branch against the base the way Bugbot does (real bugs, crashes, security, data loss, unmet acceptance criteria; not style), fix what you can inside your locks, push, and record `review-pass` or `review-fail` for the head commit with the command in your prompt. A `REVIEW ONLY` prompt (`step=review`) changes no code: review, record, and return `result: <id> ok review=pass|fail findings=<n>`. Never record a review you did not do. Do the Jira comment and the pull-request comment, then `record-comment`. Local mode comments on Jira only. Return the result line with the pull request url, the check, and the acceptance results. A later `step=fix` run updates the same pull request, pushes, and returns again.
6. If Reed returns fixes and attempts remain, set `fix` and push only in connected mode. Past `maxFixAttempts`, the ticket is `alarm`. Manual tickets use this same loop before `awaiting_approval`.
If status is already `coding`, `fix`, or `recovering` and the branch exists, continue that branch. Do not open a second pull request. Heartbeat after every `beam.py set`.
Every acceptance-criterion comment names the id and the command that passed.
If Cursor asks you to press Allow or Run on a Jira or Slack tool, stop editing permission files. Tell the user to run `/warp-allow-notify --check`. `/warp-init` writes the project allowlist. A tool name that is not in `scripts/mcp_tools.py` belongs in `notifyAllow`.
Before an MCP tool that this skill does not name, run `python3 <plugin>/scripts/mcp_allow.py --server SERVER --tool TOOL --root .`. If it prints `ask`, do not call the tool. A missing hook is not permission to call it.
Never run `git stash`: every worktree of the repo shares one stash list, so another Shuttle's pop takes your edits and yours takes theirs. Commit work in progress on your branch, or discard it with `git checkout -- <path>`. A `step: fix` prompt lists what to fix under `FIX`. Fix those, add a test that fails without the fix where you can, rebuild generated files the change affects, and push a new commit. Never record `review-pass` on the commit the findings were made on.
When this Shuttle finishes, write `done` to your ticket folder as the last state: `python3 <plugin>/scripts/ticket_state.py append --id <id> --state done --agent <agent> --root <parent>` in a worktree, or with `--push` on your own VM. That line is how the parent knows you returned, for every step (implement, fix, restart, repair, review). On a cloud runner nothing else tells it, and without it your slot stays held until your heartbeat goes stale and you are counted dead. Then run `python3 <plugin>/scripts/session_note.py --type subagent-stop --beam .warp/beam.json`. That records the stop. The parent reconciles the ticket from the beam on the next tick. Then return the result line and end. Do not wait for Bugbot, CI, a review, or a merge. Do not leave anything running: no background process, no timer, no subscription.From smlsr/Warp, MIT.
More from smlsr/Warp
- 0.565score
warp-jira-map
smlsr
Map plan ticket ids to real Jira issue keys.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-sync
smlsr
Read Jira statuses into the beam.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-jira-check
smlsr
Explain, per ticket, which Jira transition and which Jira or pull-request comment should have happened, what the beam recorded, and what is missing.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-cleanup
smlsr
List registered Warp agents and clear idle or leftover cloud agents for this repo.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-status-post
smlsr
Post Warp status to Slack and Teams, including the status files.
SkillAFreshness: AClaude Code +9 - 0.561score
warp-scan
smlsr
Scan any repo, or one folder of it, for CURSOR_PLAN.md, schedule.json, a build map, or a Jira ticket export and build the Warp plan.
SkillAFreshness: AClaude Code +9 - 0.561score
warp-jira-external-id
smlsr
Write the plan id into Jira's External ID field for tickets that are already mapped.
SkillAFreshness: AClaude Code +9 - 0.561score
warp-jira-match
smlsr
Link unmapped Warp tickets to Jira issues by summary.
SkillAFreshness: AClaude Code +9 - 0.561score
warp-alarm
smlsr
Raise, record, and clear Warp alarms when a PR cannot pass Bugbot, a lock is escaped, or a ticket is stuck.
SkillAFreshness: AClaude Code +9 - 0.560score
warp-allow-notify
smlsr
Write the Cursor allowlist so Slack and Jira calls Warp makes do not stop for Allow or Run.
SkillAFreshness: AClaude Code +9 - 0.560score
warp-from-jira
smlsr
Build the plan files /warp-scan reads from the Jira issues that carry one or more labels, for example /warp-from-jira version:1.5.5,ui.
SkillAFreshness: AClaude Code +9 - 0.560score
warp-status
smlsr
Write and report the Warp status file: done, working, and left.
SkillAFreshness: AClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Oct 14, 2026 (16 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/smlsr/Warp
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from smlsr
- 0.565score
warp-status-post
smlsr
Post Warp status to Slack and Teams, including the status files.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-sync
smlsr
Read Jira statuses into the beam.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-jira-check
smlsr
Explain, per ticket, which Jira transition and which Jira or pull-request comment should have happened, what the beam recorded, and what is missing.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-cleanup
smlsr
List registered Warp agents and clear idle or leftover cloud agents for this repo.
SkillAFreshness: AClaude Code +9 - 0.565score
warp-jira-map
smlsr
Map plan ticket ids to real Jira issue keys.
SkillAFreshness: AClaude Code +9 - 0.561score
warp-jira-match
smlsr
Link unmapped Warp tickets to Jira issues by summary.
SkillAFreshness: AClaude Code +9
Cite this page
shuttle-run skill. Agent Mart. https://agentmart.dev/skills/smlsr-shuttle-run (last updated 2026-10-24)