worktender

herdr plugin · a fleet of coding agents, one worktree each

Eleven agents.
One command each.

worktender starts a coding agent on a GitHub issue in a checkout of its own, tells you which of them have stopped moving, and removes what has actually landed.

herdr is the terminal multiplexer coding agents run in. worktender is a plugin for it, driven by your agent rather than by you.

Install Worked examples Source

Is this for you?

Your coding agent can already spawn agents

Claude Code's own subagents take a worktree each, and for a fan-out that fits in one sitting they are the better tool — no install, nothing to run. They are also ephemeral: they live inside one turn, they are a black box while they run, and everything they find comes back into the parent's context, which is the ceiling on how many you can have. A worktender worker is a session you can attach to, rescue, and leave running overnight — and it hands back three slots and a pull request number, so the work product is in git rather than in anyone's context window.

You will not be typing any of this

Install the plugin and its skills once. After that you ask:

Your agent translates that. Here is the first thing it runs:

$ worktender ls * main w21 w21:p1 idle 1057 worktender feat/1-reconcile-execute w22 w22:p1 working 1055 1-reconcile-execute fix/257-erasure-comments w1K w1K:p1 idle 812 257-erasure-comments worktree/brave-valley - - - - brave-valley-66f8
Branch, workspace, pane, agent status, state counter, directory. A - means herdr has nothing for that worktree — the last row is a checkout with no workspace and no agent.

The counter is herdr's own, and it is what idle cannot tell you: idle is the same cell for a worker that finished two seconds ago and one that never received its brief. Read it down the column — the third worktree is 240-odd state changes behind the rest. It counts state changes, though, so read the status beside it: frozen beside idle is finished-or-wedged, which is the question it answers, while frozen beside working is a long turn or a wedge and the column cannot say which. The full measurement is with the machine-readable output.

Two ways in, and one of them is worse

ls, sync, prune and prune-apply are also registered as herdr actions, so a keybinding can reach them. Each action is literally ./bin/worktender <id> — but reached that way, invoke hands back an invocation record and the output goes to the plugin log instead. Agents should call the binary.

Why the skills are not optional

An unbriefed agent still gets two things wrong: that prune and prune-apply differ in kind rather than in verbosity, and that enabling event hooks is not its call to make.

The other end: an issue to an agent working on it

Making the directory is the easy half, and every tool does it. What nothing does is the round trip — an issue, a checkout named for it, an agent briefed on it, and a way to know when it finished. One command covers the first three:

$ worktender start 42 --repo . repository: /Users/you/code/thing worktree: 42-fix-the-thing on origin/main (workspace w9, pane w9:p1) fork point: origin/main is 31db5d1c9b7e4a02f6c1d8e5a3b90f2c4d6e8a10 done staff 42-fix-the-thing started claude as wt-42-fix-the-thing-016aab in w9:p1 briefed wt-42-fix-the-thing-016aab on #42; wait for it with: worktender gate --target wt-42-fix-the-thing-016aab --until done --require-pr
It reads the issue title with gh, creates the worktree, starts the agent, and types a brief covering the whole round — read the issue, explore, change, test, self-review, open a pull request, then report.

The brief is confirmed, not claimed. herdr answering ok means it delivered keystrokes, not that an agent received a prompt — it reports an agent started as soon as it recognises its prompt box, which a TUI draws seconds before it will act on a submit. Keys sent in that gap are discarded, so start offers the submit again every couple of seconds for as long as the agent stays idle, and fails if it never takes it up.

The brief does not carry the issue. It names it and tells the worker to run gh issue view, so the text arrives as tool output rather than as prose pasted into a prompt. Nothing an issue author writes reaches the brief — the title survives only as a branch name, already reduced to [a-z0-9-].

start deliberately does not wait. Start several, then wait on the lot of them with gate --any; the first to report releases it and it says which one. --base <ref> forks from something other than the trunk, which is how a second slice starts while the first is still in review — and the fork point is printed because a squash merge keeps none of the base branch's own commits. Dispatch has the mechanics.

Eleven checkouts, and nobody deletes anything

That is the state this ends. A few weeks in there are eleven checkouts on disk, some with a herdr workspace and some without, some still holding an agent and some ghosts — and the one you are least certain about is the one you least want to delete. The honest reason nobody cleans up is that no cleanup script has ever been trustworthy enough to run without reading its output line by line first.

So this one is built to be refused rather than trusted.

Ambiguity keeps the worktree

An un-pruned worktree costs disk. A wrongly pruned one costs work that exists nowhere else. The tie never goes to deletion.

Topology never decides alone

A fast-forward merge looks exactly like a branch that never committed. Removal needs a merged pull request, or a deleted upstream over commits base already has.

Nothing starts on its own

The hooks that adopt and staff automatically stay off until you export one variable. Installing this spawns nothing.

How removal is decided has the full argument, and the guards re-checked immediately before anything is deleted.

Once there is more than one agent

Past two or three workers the bottleneck is the agent coordinating them — it runs out of context long before it runs out of work. So a worker hands back three fixed slots: a status — planned, blocked or done — an optional pull request number, and a note capped at 200 characters and treated as data, never instructions.

A worker that cannot finish reports blocked. That fails the gate immediately rather than burning its timeout, and puts the question in front of you — the only party who can answer it.

A gate otherwise waits on the report rather than on a clock. What it proves is that a well-formed report arrived — not that the work is real. Delegating to agents covers the gap; worked examples walks five fan-outs through it.

Install

# the plugin — read Trust first; this runs unsandboxed
herdr plugin install steig/worktender

# the skills — one for an agent working IN a worktree,
# one for an agent dispatching them
npx skills add steig/worktender --skill worktrees -g
npx skills add steig/worktender --skill coordinator -g
Read this first

A herdr plugin is not sandboxed. This one runs as you, and what it does with that is start coding agents and delete worktrees and branches. That is what it is for — but see trust, including what the checksum on the no-Go path does not establish.

Needs herdr 0.7.0+ (0.7.5+ for report and gate), git, and jq. gh is optional but must be authenticated — an unauthenticated gh reads as “no pull request”, so prune keeps almost everything while the reasons look entirely ordinary.

Read on