A few weeks ago a measurement made the rounds: developer aidiveyt logged a
month of Claude Code usage — 455 sessions, 2,631 subagent runs — and found
subagents had consumed 48.1% of all tokens. The median subagent's first request alone was 47,117 tokens. Nobody approved that spend. It just
Task call at a time.
Simon Willison's reaction was the whole community's reaction: "hard budget caps, please, now."
I had the same problem and the same reaction. So I built
subagent-budget: per-agent-type token and dollar budgets that are
enforced by Claude Code hooks — an over-budget subagent is refused at
launch, with the reason shown to the agent. A dashboard that can't stop a
launch is a suggestion. This is a cap.
pip install subagent-budget
subagent-budget init --default-tokens 1000000 --default-usd 50
subagent-budget set-budget --pattern "Explore*" --tokens 200000 --usd 10
subagent-budget sync # backfill from ~/.claude/projects transcripts
subagent-budget report
Then the enforcement part — one block in ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Task",
"hooks": [{ "type": "command", "command": "subagent-budget hook --event pre" }]
}
]
}
}
Now every subagent launch goes through a budget check first. Under budget:
it launches, and the hook prints the remaining headroom. Over budget: exit
code 2 (Claude Code's blocking-hook signal), and Claude sees exactly why:
subagent-budget blocked launch of 'Explore auth code' (Explore):
token budget exceeded: used 210,441 / 200,000 tokens
That last part is the whole point. Prompt-level instructions ("please don't
spawn too many subagents") don't survive contact with an agent mid-task. A
hook that returns a non-zero exit sits below the prompt layer — the model
can't argue with it, negotiate with it, or forget it.
There's already a tool in this space — subagent-ledger — and it's honest
about what it is: a display. It shows token counts, clears at session end,
and can't stop anything. subagent-budget is built on the opposite premise:
~/.config/subagent-budget/ledger.jsonl and survives session ends. A
budget that resets every session isn't a budget.
Budget rules are globs matched against both the agent's description and its
subagent type, first match wins — so Explore* can have a tight shared pool
while everything else falls back to the default. Either dimension can be
left unlimited.
If you already track spend across claude --resume with
the ledger formats are deliberately compatible (ts, kind, cost_usd
records). One command folds resume spend into the same caps:
subagent-budget import-rbg
Imported spend counts toward the matching budget rules, so a resume can no
longer silently reset what a subagent cap was guarding. Idempotent — re-run
it anytime.
sync records one ledger entry per Task
call using a configurable estimate (default 47,117 — the measured median).
Use record --tokens with exact figures from --output-format json when
it matters.model_rates or pass --cost for exact numbers.record.
Stdlib only, zero dependencies, MIT. pip install subagent-budget —
GitHub ·
PyPI.