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.
Is this for you?
- You want several agents working at once, on real issues, without them colliding.
- You need to know which of them has stopped — and a status that says
idlecannot tell you. - You have checkouts you are not confident enough to delete.
- You use herdr, or are willing to.
- One agent in one checkout? You do not need this yet.
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:
- What worktrees do I have, and what is running in them?
- Clean up anything that has landed.
- Group the unresolved Sentry issues by root cause and put a worker on each.
Your agent translates that. Here is the first thing it runs:
- 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.
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.
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:
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
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
- Delegating to agentsWhat the coordinator skill encodes.
- Worked examplesFive fan-outs end to end, including when not to.
- DispatchThe mechanics: dispatch, report, gate, and the permission-mode problem.
- Machine-readable outputThe
--jsonevery command takes, and what each null means. - PruningWhat authorises a removal, and the guards.
- EventsThe hooks that adopt and staff automatically.
- TrustWhat running unsandboxed means here.
- ReferenceExit codes, errors, keybindings.