Agent-driven development with an owner who decides: plan a batch of GitHub issues, run it unattended, accept it. Guardrails included.

AI agents build and test the software. A person, the owner, decides what gets built and approves what ships.
agent-kit is a plugin for Claude Code and the process behind it: the owner spends their attention on choosing the work and accepting the result, and the agents do everything in between, unattended.
Work moves in batches. The owner and an assistant plan a batch together, the agents work through it while the owner is away, and then the two of them go over the result.
flowchart LR
plan["Plan a batch<br/>owner and assistant"] --> work["Agents work it<br/>unattended"]
work --> accept["Accept the result<br/>owner and assistant"]
accept -->|next batch| plan
A batch's issues don't wait for one another. The batch splits them into lanes, Lane 1, Lane 2 … Lane N, and the lanes work side by side, each taking its issues in order. Some issues can also go to the cloud, where each one runs on a machine of its own and leaves the owner's computer free. So a batch of many issues takes about as long as its longest lane, not the sum of all of them. Whichever way an issue went, its work reaches the same merge gate.
flowchart LR
batch["A batch<br/>of ready issues"]
batch --> l1a
batch --> l2a
batch --> lna
batch --> cloud1
batch --> cloud2
subgraph lane1["Lane 1"]
l1a["Issue"] --> l1b["Next issue"]
end
subgraph lane2["Lane 2"]
l2a["Issue"] --> l2b["Next issue"]
end
subgraph laneN["Lane N"]
lna["Issue"] --> lnb["Next issue"]
end
subgraph cloud["In the cloud, a machine per issue"]
cloud1["Issue"]
cloud2["Issue"]
end
l1b --> gate["Merge gate<br/>review, then merge by risk"]
l2b --> gate
lnb --> gate
cloud1 --> gate
cloud2 --> gate
Each issue in a batch follows the same path. Tests come first, a separate reviewer checks the change, and only the risky changes wait for the owner.
flowchart LR
ready["Issue ready"] --> tests["Tests first"]
tests --> build["Implementation"]
build --> review["Review"]
review -->|changes| build
review -->|approved| pr["Pull request"]
pr --> risk{"Risk?"}
risk -->|low| auto["Merges by itself<br/>once checks pass"]
risk -->|high| owner["The owner merges it"]
hooks/status-line.ts 36 lines1// The batch's progress under the prompt (#108), from `agent-batch status --line`.
2// A plugin can't set Claude Code's `statusLine`, and that setting's command wouldn't find
3// `agent-batch` on its PATH: a mod finds it under the plugin's own folder.
4import type { Register } from 'claude-code'
5
6// Often enough to follow a lane from step to step; each time starts the runner's Python.
7const PERIOD_MS = 5000
8
9export const register: Register = on => {
10 on('session.start', async ($, e, next) => {
11 const started = await next(e)
12 // A `claude -p` run, as each lane's, draws no line: its timers would start Python for nothing.
13 if (!e.isInteractive || e.surface === null) return started
14 let isShowing = false
15 const show = async () => {
16 // A slow computer could take longer than a period: one run at a time.
17 if (isShowing) return
18 isShowing = true
19 try {
20 const ran = await $.process.run([`${$.plugin.root}/bin/agent-batch`, 'status', '--line'], {
21 stdin: JSON.stringify({ cwd: await $.session.cwd() }),
22 })
23 const line = ran.exitCode === 0 ? ran.stdout.split('\n')[0].trim() : ''
24 $.ui.status(line || undefined)
25 } catch {
26 $.ui.status(undefined)
27 } finally {
28 isShowing = false
29 }
30 }
31 $.clock.after(1, show)
32 $.clock.every(PERIOD_MS, show)
33 return started
34 })
35}
36