A skill for coding agents, and a web app to watch them
Run a whole team of coding agents from one conversation.
You talk to one agent: the orchestrator, or an adjutant that relays your briefs and answers to it. The
orchestrator runs the rest of the team inside
herdr: implementers write the code, reviewers
check it, and an integrator combines the work when it ships as one branch. Fixes and reviews go round until
each task passes.
orchestrator
› /herdsman run
"tasks: issues 12 and 14;
implementers: <harness> <model>;
reviewer: <harness> <model>"
Two parts work together. The skill runs the team. The
herdsman command serves a browser app that shows the team and lets you steer it. You can use
the skill alone.
The skill
Install
herdr
and the coding-agent harnesses you want for each role.
Install the skill for the orchestrator's harness. Claude Code:
gh skill install arafathusayn/herdsman herdsman --agent claude-code --scope user
Codex:
gh skill install arafathusayn/herdsman herdsman --agent codex --scope user
Start the orchestrator in a herdr pane and give it the route.
/herdsman run "tasks: issues 12 and 14; implementers: <harness> <model>; reviewer: <harness> <model>"
The web app
Install the command with Homebrew.
brew tap arafathusayn/tap
brew trust arafathusayn/tap
brew install --cask arafathusayn/tap/herdsman
Start it on the machine that runs herdr.
herdsman web --open
Type the 6-digit code that the terminal shows. The browser is now paired with this host.
Install
The skill
Install it with the
GitHub CLI
for the harness that runs the orchestrator.
Claude Code
gh skill install arafathusayn/herdsman herdsman --agent claude-code --scope user
Then run it as /herdsman.
Codex
gh skill install arafathusayn/herdsman herdsman --agent codex --scope user
Then run it as $herdsman.
For another harness:
gh skill install arafathusayn/herdsman herdsman --agent <agent> --scope user
<agent> is the harness's name as gh skill install --help lists it. Add
--pin <tag or commit> to pin a version. Run gh skill update to update later.
A skill from main needs a herdsman built from the same clone (make build), because main can be ahead of the latest release.
Start the orchestrator inside a herdr pane. Outside herdr (HERDR_ENV is not 1),
the skill asks whether the agents run in herdr panes or as background subagents.
The herdsman command
One Go binary with the web app inside. Nothing else runs on your machine.
Homebrew
brew tap arafathusayn/tap
brew trust arafathusayn/tap
brew install --cask arafathusayn/tap/herdsman
Homebrew installs from a tap outside its own only after you trust it. brew trust does that
once. To get a new release later:
brew upgrade --cask herdsman
Release archive
Download the archive for macOS or Linux from
Releases, then put
herdsman on your PATH.
From source
Build from a clone with Go, Bun and a C compiler (cgo, for the SQLite driver). This writes
bin/herdsman.
git clone --recurse-submodules https://github.com/arafathusayn/herdsman.git
cd herdsman
make build
Check it, then check that this machine can run a route:
herdsman version
herdsman doctor
The team
Every agent runs in its own herdr pane. Each one can use a different harness and a different model: any
coding agent that herdr can start and recognize in a pane can take any role.
Adjutant
optional
You talk to it instead of the orchestrator. It asks you every question of the route, gos included, and
relays briefs and answers through a private message channel. Your answer there is your go.
Never writes code, runs tests, commits or pushes.
Orchestrator
Runs the route. You talk to it directly, or through the adjutant. It runs the interview, writes the
contracts, launches and watches the agents, forwards review findings and records the results.
Never writes application code and never pushes.
Implementer
one per task
Writes the code and the tests for its task on its own branch, optionally in its own git worktree.
Reviewer
Reviews each task branch and, before a push, the combined result. It reports only P0 and P1 findings.
Changes nothing.
Integrator
when combined
Combines the accepted task branches into one branch, fixes what breaks between them, and is the only
agent that pushes.
Checkpointer
helper pane
Asks the orchestrator to save its memory every 15 minutes, and to compact its context past a token
limit.
How a route runs
A route is one run of the team over a set of tasks. The orchestrator sleeps between events, so each wake
does real work.
1InterviewTasks, models and rules. It shows a dispatch plan and waits for your go.
2ContractsA route folder: shared rules and one contract per task.
3Launchherdr tabs and panes, and a full brief for each implementer.
4WaitA background waiter exits on the first event.
5ReviewEach finished task goes to a reviewer: fix or pass.
6CloseResults recorded, lessons saved to memory.
Fix round. A fix verdict sends the task back to its implementer, and its new report
goes back to Wait. A task gets at most three fix rounds; after that, you decide.
Combined tasks. The integrator merges the accepted branches in phases. Only a passing final
review of the combined head lets it push.
Pairing and security
A link alone controls nothing. A browser must also prove that it can see the host's terminal.
The linkThe terminal prints a link with a one-run token after its #. Browsers leave that part out
of requests, so proxies and logs never see it.
The codeThe same terminal shows a 6-digit code. You type it in the browser.
PairedThe browser keeps a device secret for this host until the server stops.
A code lasts 2 minutes and allows 5 tries. At most 8 pairings wait at one time.
Guessing is bounded. Each link allows 20 wrong codes in all. Then it stops working, open pairings
end, and the terminal prints a new link.
Strong secrets. The link token is 192 bits and the device secret 256 bits, both from the system's
random source.
Loopback by default. The server listens on 127.0.0.1 unless --host names
one other address. A LAN address serves plain HTTP, and the server warns about it.
No cookies. A request without the header carries no authority, so another site cannot ride on a
paired browser.
Sandboxed artifacts. HTML pages that agents wrote open in a sandboxed frame, with no access to the
app or your pairing.
Skill commands
In the orchestrator session, inside herdr:
/herdsman [subcommand] [options]
Subcommand
What it does
run <route request> or no subcommand
Runs the full route. The request names the tasks, harnesses, models and rules.
adjutant
Attaches this session as the adjutant, your side of the message channel. Options:
--name, --orchestrator <pane>, --inbox <root>,
--launch.
orchestrator
Orchestrates as run does. With
--adjutant <name> --name <own-name> --inbox <root>, it enrolls in the
message channel first.
message
Messages between the adjutant and the orchestrator:
status (default), wait, read, release,
ack, send, hold, unhold.
checkpoint
Manages the checkpointer: start (default), now, probe,
status, stop.
status
Shows the health of every agent in the current route. Changes nothing.
help
Shows the subcommands, options and examples.
Examples
# Two issues, one pull request each
/herdsman run "tasks: issues 12 and 14; implementers: <harness> <model>; reviewer: <harness> <model>"
# Four parts of one change, combined into one pull request
/herdsman run "tasks: four parts of one change, one pull request; implementers: <harness> <model>; integrator: the first implementer that is free; reviewers: two <harness> <model>"
# Check on the team
/herdsman status
Checkpointer
A pane next to the orchestrator that keeps its memory saved and its context small.
/herdsman checkpoint start --every 10 --limit 300k
/herdsman checkpoint now
Option
Default
Meaning
--pane <id>
this session's pane
The orchestrator pane to watch
--every <minutes>
15
Time between checkpoints (start only)
--limit <tokens>
250k
Compact at or over this context size
--prompt "<text>"
the script's default
The memory checkpoint prompt
--compact "<command>"
/compact
The harness's compact command
--side right|down
right
Where the new pane goes (start only)
CLI commands
Besides web, the herdsman command holds the tools the skill runs. Each one reads
strict settings, stops with exit code 2 and the setting's name when one is wrong, and prints its usage and
its exit codes with --help. Most print JSON with --json.
herdsman help
herdsman <command> --help
Command
What it does
wait
Waits for the next event of the loop: a report, a review, a blocked or stalled agent, a health flag.
Prints TICK when none comes before the deadline.
watch
Watches the implementers, the reviewer or pull requests:
implementers|reviewer|prs. prs returns on the first pull request change,
or with a TICK.
status
A health snapshot of every agent in HERDSMAN_STATUS_SPECS and of the machine.
route
route check prints an ok or MISSING line per item of a route
folder at a stage (plan, launch or close);
route log appends a dated run-state line.
checkpointer
The checkpointer loop (TARGET=<pane>), one checkpoint (--once) or a
probe of the context size (--probe).
checkpointer guard <name> <pane> checks, in adjutant mode, that the
approved registry still names that pane as the orchestrator.
message
The adjutant message channel: status, wait, read,
release, ack, send, hold and
unhold, and its services register, courier,
card-watch, resume-goal, authority and
leak-gate. For a route the daemon owns (--route, or
HERDSMAN_ROUTE), the same actions go through the daemon, plus enroll(next release).
daemon next release
The per-user message channel daemon: run, install, uninstall,
start, stop, restart, status,
route, setup, pair and credential. See
Message channel daemon.
approval-gate
Binds a relayed go to one effect before it runs (consume), and records its outcome
(complete).
transcript-audit
Audits a Claude or Codex transcript for writes outside the allowed roots: GAP,
FLAG and AUDIT lines.
run-check
Runs one heavy check at a time on this machine, under a shared lock, behind a load gate, at a lower
priority. --publish <hours> writes the machine's load reading for the gate.
doctor
Checks that this machine can run a route: herdr, gh, the binary, the inbox, the sandbox, the load.
models
Probes each installed harness for its version, models, efforts and default effort as it reports
them. models last and models save keep your last pick for each role.
afk
How long you have been away: the machine's idle time or your newest message, whichever is newer.
review
review start --worktree <path> --head <commit>
prepares a clean, detached worktree for a reviewer, with its own build caches.
The commands marked next release are on main; build from source to use them now.
Message channel daemon
Without the daemon, each route with an adjutant runs its own courier and watcher as
herdsman message services. herdsman daemon is one process per user that can own a
route's channel instead: it serves the message commands over a local socket, imports the route into a SQLite
database with a journal and crash replay, and file-mode writers refuse an inbox it owns.
The daemon is on main and comes with the next release. The skill's routes still start the
file-mode services, so moving a route to the daemon is a step you run by hand.
# Install the LaunchAgent (macOS) or the systemd user unit (Linux), then check it
herdsman daemon install
herdsman daemon status
# A new route, or an existing inbox moved to the daemon
herdsman daemon route create --repo <repo> --route <route>
herdsman daemon route adopt --inbox <path> --route <route> --confirm <peers.approved sha256>
# The setup proof and the pair, recorded by you at a terminal
herdsman daemon setup <route> --rule <policy file>...
herdsman daemon pair approve <route> <sha256>
Private home. Its files live where no sandboxed agent can reach: --private-home, else
HERDSMAN_PRIVATE_HOME, else ~/.herdsman-private. The route databases are SQLite
files under daemon/routes/ there, and its socket is run/daemon.sock.
Routes.route create makes a new route. route adopt moves an existing
inbox to the daemon as one resumable transaction: it takes the inbox's locks, imports the inbox into a
staging database, records the adoption in state/adopting.json, moves file mode's setup proof,
approval and credential records to state/legacy/, drains the file-mode processes, then
publishes the route. A refusal before state/adopting.json exists (for example a bad inbox
path, a held lock, a write in flight, a pair that does not match --confirm, or a failed
import) releases the locks and leaves the route unadopted and the inbox's records unchanged. Once that
file exists, an error such as adopt_busy means the adoption is pending: the inbox has
changed, and after adopt_busy the locks stay held, so do not restart the file-mode services.
Resolve the reported problem and run route adopt again with the same route and
--confirm; the daemon also retries a pending adoption when it starts and on its scheduler.
route remove, route reset-events and route list do the rest. Route
names are unique across the daemon and compared without case.
Setup and pairing.setup records a route's setup proof and
pair approve approves a proposed pair. Both need an attended terminal (pair approve
takes --interaction <ask reference> instead), because only the human records them.
pair retire and credential revoke take a party or a credential back.
Supervisor.start, stop and restart go through the
LaunchAgent or unit; a stopped daemon stays stopped until start. uninstall stops
it and removes the unit. Only one daemon runs per account.
What changes for a route. Once the daemon owns a route's inbox, herdsman message takes
the daemon path for that route, file-mode writers refuse the inbox, and message status and
the web app's Messages tab show the daemon's liveness instead of the courier and watcher heartbeats.
Exit codes.0 done; 1 the daemon is down or degraded
(status), or no answer within 10 s (start, stop,
restart, uninstall); 2 usage, a refusal, another daemon of this
account running, or no attended terminal; 3 the human boundary; 75 retryable:
busy, the route is unowned, herdr is unreachable, or the daemon is not answering.
Web app
herdsman web serves a browser app that shows and steers the team. Start it on the machine that
runs herdr. It prints a link, a pairing code when a browser opens the link, and a QR code for a phone.
herdsman web --open
TeamA board of every agent in the route, by role or by status.
AgentEach agent's live terminal, its keys and a prompt box. Terminal mode types straight into the
pane.
ChecklistThe route's checklist lines.
Run stateThe orchestrator's run log, newest first. Older lines load a page at a time.
DocsContracts, reports and notes in the route folder.
ChangesThe code each task changed against its base branch, inline or side by side.
ArtifactsHTML pages agents wrote in the route's workspace: reports, reviews, plans.
MessagesThe channel between the adjutant and the orchestrator, and the daemon's health when it owns the inbox.
Read only.
Press ⌘K or Ctrl+K anywhere in the app to open the command palette.
Create a route
New route beside the Workspaces heading in the sidebar, or Route: New route… in the
palette, opens a form. It first offers the recommended setup, or a custom one. Each role then offers your
previous choice of harness, model and effort; Choose again reads the harness lists afresh. Pick a
folder, type the request, confirm the GitHub account that can read the repository, and press
Start route. The app opens a herdr tab named <project>-orchestrator, starts the
harness, model and effort you chose through herdr, sends /herdsman run "<request>" ($herdsman
for Codex), and splits a checkpointer pane beside it.
Models and efforts come from each installed harness, as it reports them, newest first; the same probe runs
as
herdsman models. The picks are saved when the route starts. The Adjutant role starts an
adjutant and its orchestrator in two tabs with one private inbox per route, and shows the channel setup as a
checklist: creating the inbox, the setup proof, attaching the adjutant and approving the pair stay your
steps. If a step fails, the form shows which steps ran; nothing is closed.
The hosted app
A copy of the app built from main is served at
herdsman.arafat.ai (also herdsman.arafat.dev, and the
short names h. and herd. on both domains). The server prints a "Hosted app" link
that opens it with this host filled in.
The first time the hosted app calls this computer or a private address, the browser asks to access devices
on your local network. Allow it, then choose Try again. The link's pairing token waits until the host
answers.
Each address is its own browser origin. Hosts and pairings saved on one address do not show on
another.
Phones and other hosts
The app can reach any number of hosts: this computer, a LAN or Tailscale address, or a domain in front of a
server. Open the Hosts dialog from the server button in the top bar, or paste a host's printed link into it.
Each host pairs on its own.
A page served over HTTPS can reach plain-HTTP hosts only on this computer. A phone's
127.0.0.1 is the phone itself. So pick the row that fits where the browser is:
The browser is on
Start the server with
Then open
This computer
herdsman web --open
The printed link, or the "Hosted app" link
A phone on your tailnet recommended for phones
tailscale serve --bg 7878 herdsman web --public-url https://<machine>.<tailnet>.ts.net
Scan the QR code. It opens the hosted app with this server filled in.
A device on the same LAN
herdsman web --host 192.168.1.20
Scan the QR code. It opens the server's own app over plain HTTP.
Anywhere, behind your proxy
herdsman web --allow-host box.example.com
https://box.example.com, or add it as a host in the hosted app
Without --public-url, the QR code opens the server's own app, and a line under it says how to
get the hosted app.
Listen on one other address, such as a LAN or Tailscale IP
--allow-host <name>
none
Also answer to this host name, such as the domain of a proxy or tunnel (repeatable)
--public-url <https url>
none
The HTTPS address in front of this server. It is also an allowed host name, and the QR code then
opens the hosted app
--client <url>
HERDSMAN_CLIENT, else
https://herdsman.arafat.ai
A hosted copy of the app; none turns the hosted link off
--inbox <folder>
HERDSMAN_INBOX
The adjutant's private message folder, for the Messages tab (read only)
--route <folder>
found from the panes' folders
A route folder to show first (repeatable)
--session <name>
HERDR_SESSION
The herdr session
--open, --no-qr
off
Open the browser; skip the QR code
Troubleshooting
The hosted app says "Cannot reach This computer"
The browser has not allowed the hosted app to reach devices on your local network yet. Allow its prompt,
then choose
Try again. If you blocked it before, allow local network access again in the browser's settings
for the site.
A host shows as blocked in the hosted app
It is a plain-HTTP address on another machine, which an HTTPS page may not call. Put it behind HTTPS,
for example with
tailscale serve and --public-url, or open the server's own app at its printed
link.
The pairing code does not work
A code lasts 2 minutes and allows 5 tries. Open the printed link again to get a new code. After 20 wrong
codes for one link, that link stops working: open the new link that the terminal printed.
A paired browser asks for a code again
Device secrets last until the server stops. After a restart, open the new link and pair again.
Hosts I added do not show on another address
Each address, such as herdsman.arafat.ai and h.arafat.dev, is its own browser
origin with its own saved hosts. Add the host again there.
Homebrew says the tap is not trusted
Trust it once, then install or upgrade again:
brew trust arafathusayn/tap
The skill asks where agents run
The orchestrator is outside herdr (HERDR_ENV is not 1). Answer whether each
role's agents run in herdr panes or as background subagents, or start the orchestrator inside a herdr
pane.
A message or daemon command exits 75
It is retryable: the daemon is busy or not answering, the route is unowned, or herdr is unreachable. Run
herdsman daemon status, then run the command again.
On PATH, for macOS or Linux (the Homebrew cask or a release archive). The skill runs
every command through it; no jq or nc needed. A browser for the web app.
Go, Bun and a C compiler
Only to build from source: make build builds with cgo, because the daemon's store links
the SQLite driver (clang from the Xcode command line tools on macOS; gcc or clang on Linux), and the
web build reads the Reactre submodule. The Homebrew cask and the release archives need none of this.
/bin/bash 3.2+
Runs the route folder's one-line run-check.sh wrapper (exec herdsman run-check "$@"), which every check goes through. The macOS default shell works.
GitHub CLI (gh)
A version with gh skill to install the skill; any recent version when the route opens
pull requests.
gh skill install takes the skill from the latest release. Keep the skill and the
herdsman binary on the same release: gh skill update for the skill,
brew upgrade --cask herdsman for the binary. A skill linked from a clone of
main needs a herdsman built from main. The daemon is on
main and comes with the next release.