Start in the terminal. Answer from the couch.
agentctl opens each task in Herdr with its own tabs. The same agents show up on your phone a second after they start.
From an issue to a running agent
Give it an issue ID, a link or plain words. agentctl lays the track you would lay by hand.
- Branch from a fresh
origin/main, named after the task. - Worktree in
.worktrees/eng-123, so folder trust carries over. - Ports and env. Copies
.env, picks a free port slot. - Prepare. Runs your install commands before any shell opens.
- Herdr opens
claude,shellanddifftabs.
Everything a herd needs
Small, plain tools that each do one thing. Together they let you run many agents without losing track.
.local. No /etc/hosts, no sudo. Logins and cookies just work.Can this be a named constant?
Yes, it is
REFUND_GRACE_DAYS now. Fixed in a3f9c1e.feedback gives reviewers' comments to the agent. ship posts the replies once the fix is pushed.Bash command: pnpm test src/auth
Fixed both review findings.
agentctl next works like an inbox.gc only removes merged and empty worktrees.agentctl ship. PR #412 is open.destroy yourselfIts own database, copied in a blink
Agents run migrations and write test data. Give each worktree its own copy of your local Postgres, so one agent never breaks the data of another.
{n}is the worktree's port slot, so the names never clash:myapp_wt1,myapp_wt2, and so on.prepareruns before any shell opens, so the copy is ready when the agent starts.teardownruns ondestroyandgc, so old copies do not pile up.- Instant with Postgres 18 and
file_copy_method = cloneon APFS, btrfs or XFS, also for a big database. - Postgres only copies a database nobody is connected to. Stop the dev server on main first, or keep a separate template database just for copying.
# ~/.config/agentctl/config.toml [repos.myapp] prepare = [ "npm ci", "psql -d postgres -c 'CREATE DATABASE myapp_wt{n} TEMPLATE myapp_dev'", ] teardown = [ "dropdb --if-exists myapp_wt{n}", ] [repos.myapp.env] # written to each worktree's .env DATABASE_URL = "postgresql://localhost/myapp_wt{n}" [repos.myapp.ports] PORT = 3000 VITE_PORT = 5173
The control room fits in your pocket
Start work from the couch, answer a permission prompt in the queue, and get a push when an agent is done. It is a small web app on your own tailnet.
Type or dictate an issue. All agents and loops, by repo.
See the agent's screen. Tap 1 to say yes, or type an answer.
Right away, also while you sit at the Mac. Pause them for an hour, or until 08:00.
- New work
- An issue ID, a Linear or Sentry link, or what to do. The Mac runs it like
agentctlin a terminal. - Keys for prompts
- Enter, Esc, 1–3, arrows, y/n. Enough for every question an agent asks.
- Actions
- Ask an agent to ship, rebase, share or merge. Or destroy it.
- Loops
- Last result, cost and next run. Run now, pause, or add one.
- Share sheet, Siri
- A Shortcut starts work right from the Linear or Sentry app.
- Clean up
- Runs
gcfor all repos. Shows what it removes, and asks first.
tailscale serve, never on the public internet. Only your own Tailscale user gets in, and it can only do the things above. Never a shell.agentctl phone puts the app on your tailnet and prints a QR code. Scan it, then Share → Add to Home Screen. No App Store, no account.
Work that runs while you sleep
Put prompts on a timetable. Move agents to machines that never sleep. Keep using the same commands.
A prompt on a timetable
Each run is a new worktree. It knows what earlier runs found, opens a PR or a report, and destroys itself when there is nothing to do.
# ~/.config/agentctl/config.toml [repos.myapp.loops.sentry] every = "2h" prompt = "Fix the most common Sentry issue." backoff = true # finds nothing? wait longer [repos.myapp.loops.deps] at = ["07:00"] prompt_file = "loops/deps.md" max_waiting = 1 # never two deps PRs
$ agentctl loop list LOOP SCHEDULE NEXT TODAY LAST RESULT sentry every 2h 14:00 $1.20 pr TypeError in cart, #415 deps 07:00 07:00 $0.35 nothing all up to date
More than one station
Add a Linux VM and work goes where there is room. Every machine keeps its own worktrees. Your Mac only talks to them over ssh.
$ agentctl machine add dev@vm-1 --group hetzner $ agentctl --on hetzner ENG-123 → vm-2 (3/8 agents, load 1.2)
$ agentctl list MACHINE NAME STATUS REVIEW COST local fix-auth blocked needed $3.10 vm-1 eng-123 working 2 findings $0.42 vm-2 eng-131 working - $0.18 vm-2 sentry-4x done clean $1.75
The commands
Inside a worktree, leave out the name. Outside, you get a picker with a live preview of each agent.
agentctl ENG-123New worktree and agent for an issueagentctl -a codex #30Another agent, a GitHub issueagentctl listAll agents, all repos, with costagentctl nextGo to the next agent that needs youagentctl go eng-123Focus it in Herdragentctl devStart the dev server, open its URLagentctl review --fixSecond agent reviews, author fixesagentctl feedbackAgent acts on PR commentsagentctl shipPush and open a PR the agent wroteagentctl upload shot.pngScreenshot into the PRagentctl shareOpen the app on your tailnet, with a QRagentctl boardThe agent explains its change on Whiteboardagentctl gcRemove worktrees whose PR mergedagentctl destroyStop, tear down, remove. Safely.agentctl config initClaude studies your repo and writes the configagentctl doctorCheck the setup, with a fix for each problemLay the first track
A few commands, and the next task gets its own worktree.
$ git clone https://github.com/hanse/agentctl $ cd agentctl && make install $ agentctl watch --install # notifications and local names, at login $ agentctl doctor # checks the rest
Needs macOS, Go, git, Herdr and an agent CLI. Optional: gh, fzf, hunk, Codex, Tailscale.
