Skip to content

Install and remove ​

A Run works unattended, so anything missing has to be caught before it starts, not halfway through a Ticket. init sets a Target (the repository you start it in) up, run refuses one that is not, and remove takes out both what init wrote and what Runs left, so trying the pipeline on a repository of your own is safe.

StepCommandWhat it does
Installnpm install -g ticket-runnerPuts the ticket-runner command on the machine
Set a Target upticket-runner initWrites the pipeline's files, sets up GitHub, reports the rest
Start workticket-runner runRefuses a Target that is not set up; see Running
Take it outticket-runner removeRemoves what init wrote and what Runs left
Uninstallnpm uninstall -g ticket-runnerRemoves the command from the machine

Requirements ​

RequirementWhyChecked by
Node 22 or newerThe package's engines fieldnpm, at install
gitEvery Ticket gets its own worktree and branch–
gh, authenticated for the TargetEvery GitHub call goes through gh apiinit reports it; run refuses a Host without gh
claude, with the mattpocock-skills plugin, version 1.2.3 or laterEach Stage is a claude -p session, and the implement Stage drives the plugin's skills (ADR-0002)init reports both, the plugin by name only
claude logged in with your Claude planA Stage runs on that login and never sees the ANTHROPIC_API_KEY your shell exports, unless the Run is started with --api-key–
A GitHub repository with Tickets planned for itThe board is where Tickets come from; see Planning–
/setup-matt-pocock-skills, run once in the repository with GitHub Issues as the trackerThe planning skills file Specs and Tickets as GitHub issues because of it. It asks before it writes docs/agents/issue-tracker.md and an ## Agent skills block in CLAUDE.md, which show it ran–
A CI workflow in .github/workflowsA pull request with no checks is never merged, unless gates.ci is offinit reports it
A Check the pipeline can run itselftest and typecheck scripts in package.json, or commands in checksinit reports it; run refuses without one
The Target's own dependencies, installed in the main checkoutA Ticket's worktree finds them there, and nothing installs them for it. Without them every Check fails, at the merge-base too, and the Run ends on a failing base branch–

A merge only pulls the base branch into the main checkout, so after a Ticket that adds a dependency, install again there before the next Run.

The plugin comes from Anthropic's official marketplace, on a workstation as an Operator installs it on a cloud Host. claude plugin list shows the version installed:

bash
claude plugin marketplace add anthropics/claude-plugins-official
claude plugin install mattpocock-skills@claude-plugins-official

Install ​

bash
npm install -g ticket-runner

The same line upgrades it. Then read After upgrading in the Release of each version newer than the one you had: it says what your Targets need, such as running init again.

Installing straight from GitHub's version tags works too. The range asks for the highest tag rather than main, so the copy you get is always a version it can name (ADR-0007):

bash
npm install -g "github:jjongs2/ticket-runner#semver:*"

init and run both look up the newest version published as a GitHub Release. When it is above the number this copy reports, they print one line, and refuse nothing:

text
A newer version is out: 0.5.0, and this is 0.4.0 — upgrade with `npm install -g ticket-runner`.

Nothing is printed when this copy is the latest or ahead of it, or when GitHub could not be asked at all (no network, no gh).

Try it without installing ​

npx runs the package from npm's cache, so nothing is installed globally. Start it in the Target, and type npx ticket-runner wherever these pages, or the pipeline's own messages, say ticket-runner:

bash
cd ~/code/acme
npx ticket-runner init
git add .gitignore CLAUDE.md ticket-runner.json docs/agents .claude/skills/ticket-runner
git commit -m "chore: set up ticket-runner" && git push
npx ticket-runner run
npx ticket-runner remove   # when you are done with it
ToType
Take the newest version rather than one npm cached earliernpx ticket-runner@latest …
Keep one version between commandsnpx ticket-runner@<version> …
Clear what npx left behinddelete ~/.npm/_npx

Set a Target up with init ​

bash
cd ~/code/acme
ticket-runner init

It commits nothing. Commit what it wrote and push it to the base branch before the first Run: a Ticket's worktree is branched from the remote, so a Stage reads the conventions document only once the base branch carries it, and a cloud Host sees only what is pushed. It takes no Run lock, because it claims no Ticket.

What it writes ​

ItemWhereOn a later init
Two ignore lines.gitignore: .worktrees/, .ticket-runner/Added unless already ignored, in any spelling
An empty config fileticket-runner.json, containing {}Never touched once it exists
The conventions document, stamped with its versiondocs/agents/pipeline-conventions.mdRewritten when its text differs
A section pointing at that documentCLAUDE.md, created if missingAdded unless CLAUDE.md already names the document
The Operator's skill.claude/skills/ticket-runner/SKILL.mdRewritten when its text differs

The conventions document and the skill are the pipeline's own text, so an edit there does not survive the next init, with one exception. The document's first line carries a hidden <!-- ticket-runner:version <number> --> mark. When that mark names a newer version than the one running, init leaves the document and the skill exactly as they are and says to upgrade instead, because rewriting them would take the Target backwards.

What it does on GitHub ​

SettingWhat init does
The six triage labels (needs-triage, needs-info, ready-for-agent, ready-for-human, wontfix, in-progress)Creates the missing ones, named as labels says; leaves the rest
Squash mergingTurns it on; no other merge method is touched
Deleting a pull request's branch when it mergesSwitches it on, since a cloud Host cannot delete a branch itself (ADR-0008)

When gh is not authenticated, nothing on GitHub is done and the report says so. Changing these settings takes a gh user allowed to change the repository's settings. GitHub tells only such a user whether branches are deleted on merge, so a Run started by anyone else is refused as if that setting were off.

What it only reports ​

text
ticket-runner 0.5.2 init · /home/me/code/acme

Wrote:
  nothing to write

GitHub:
  labels: every triage label is already there
  merges: squash merging is on, and no other merge method was touched
  branches: a pull request's branch is already deleted when it merges

Checked:
  ✓ `gh` is authenticated
  ✓ `claude` runs
  ✓ the `mattpocock-skills` plugin is installed
  ✗ no CI workflow in `.github/workflows` — a pull request with no checks is never merged
  ✓ a Check is configured or inferable: npm test, npm run typecheck

Not ready: 1 item is yours to put right.

Any .yml or .yaml file in .github/workflows counts as a workflow.

Exit codeMeaning
0Every reported item passed
1At least one reported item is yours to fix
2Refused: the shell belongs to a Stage, or ticket-runner.json is invalid

So ticket-runner init && ticket-runner run stops before a Run that could not work.

Target readiness: what run refuses ​

run repairs nothing. It asks for each item init would have put there, in this order, and refuses on the first one missing, with exit code 2. It asks the same of every Host, so a Target a workstation's Run accepts is one an Operator can run from the Claude app too.

OrderRefused when
1.gitignore does not ignore .worktrees/ or .ticket-runner/
2docs/agents/pipeline-conventions.md is missing, or carries no version mark
3CLAUDE.md does not mention that document's path
4The Operator's skill is missing
5gh is not installed; the refusal names the install before init
6A triage label is missing, as labels spells it
7The repository keeps a pull request's branch after it merges
8The remote's base branch, such as origin/main, misses item 2, 3 or 4. Every worktree is made from it, so the refusal says to commit and push what init wrote, not to run init
text
$ ticket-runner run
This Target is not set up: `.gitignore` does not ignore `.worktrees/`. Run `ticket-runner init` here and start again; a Run puts nothing in place itself.

Readiness asks for presence and the version mark, never content. A conventions document another version wrote is only a warning, and the Run goes on:

text
warning: This Target's `docs/agents/pipeline-conventions.md` was left by 0.4.0, and this Run is 0.5.2 — run `ticket-runner init` here to bring it up to date.

One refusal comes before readiness: --api-key on a cloud Host, or with no ANTHROPIC_API_KEY exported.

Two more come after it:

  • State files in the checkout. An early pipeline kept a Ticket's resume state under .ticket-runner/state/; it now lives on the Target's remote, and nothing migrates it. A Run refuses while that directory holds state files and names their Tickets. Finish those Tickets with the version that wrote them, or hand them to a human, then delete the directory.
  • No Check. With the Checks gate on and no Check command configured or inferable, a Run refuses rather than merge unchecked code.

Take it out with remove ​

bash
cd ~/code/acme
ticket-runner remove        # lists what it will remove, then asks
ticket-runner remove --yes  # the same, without asking

remove takes out only what it can prove the pipeline put there. It commits nothing, so you review the changes with git status and commit them yourself. It takes the Run lock while it works, so no Run starts on a Target being taken apart.

Every refusal names what to do next. The lock is refused whoever holds it, even a Run on this Host whose process has gone: remove takes over no lock, so start ticket-runner run to finish what it held, or release it by hand (Hand-offs, stopping and resuming). A cloud Host is refused because it cannot delete a branch on the remote; run remove from a workstation.

What goes and what stays ​

Removed:

  • docs/agents/pipeline-conventions.md, the Operator's skill, and any directory that leaves empty.
  • The CLAUDE.md section init wrote, or the whole file when that section is all it holds.
  • .gitignore lines still under the comment init wrote, or the whole file when they are all it holds.
  • .ticket-runner/: Run logs and transcripts.
  • Every .worktrees/ticket-<n> worktree and its branch, other local agent/ branches, and .worktrees/ once empty.
  • The in-progress label, from every issue that wore it.
  • The ticket-runner/state branch. The report names every Ticket that can no longer be resumed.
  • ticket-runner.json, last, and only if every removal above worked.
  • The ticket-runner/lock branch, last. It is released rather than deleted if a removal failed.

Left:

  • The five triage labels other than in-progress: Planning uses them too.
  • A CLAUDE.md section you reworded: it may be yours now.
  • An ignore line under a comment of your own.
  • .worktrees/ when it holds anything else, and its ignore line with it.
  • Squash merging and branch deletion on merge: the repository may have had them before init.
  • agent/ branches on GitHub, and any pull requests open on them.
  • The standing Notes issue, and every comment a Run wrote: they are addressed to humans.

A removal that fails is reported and the rest carry on. Running remove again is a first run over what is left.

Exit codeMeaning
0Everything is gone, or there was nothing to remove
1At least one removal failed
2Refused before anything changed

Uninstall ​

bash
npm uninstall -g ticket-runner

Run ticket-runner remove in each Target first; the uninstalled command cannot do it afterwards.

References ​