You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Surface Claude Code's 5-hour and 7-day rolling-window utilization
(the data behind the interactive /usage slash command) to
claude-swarm operators running on an OAuth (Pro / Max / Max 20x)
subscription. Two deliverables:
./usage.sh — one-shot helper that prints current rate_limits.five_hour.used_percentage, rate_limits.seven_day.used_percentage, and both resets_at
timestamps, formatted as a table (default) or JSON (--json).
Mirrors the shape of ./costs.sh.
Dashboard banner — add a single compact line to dashboard.sh's header, next to the model summary, showing 5h: 42% (reset 17:34) | 7d: 15% when the swarm is using
OAuth. Refreshes on the existing 3 s tick but throttles the
underlying probe to once every ~60 s (probes are non-free,
see below).
Both surfaces must be opt-in and OAuth-scoped. API-key
(auth: apikey / auth: key) swarms already get the equivalent
signal via costs.sh (dollar cost tracks real spend on flat-rate
API billing); rate_limits is absent for them and the helper
should print n/a (API-key auth) rather than probe.
Why it matters for swarms specifically
A single-user Claude Code session on Max 20x can run all day
without hitting the 5h cap. An N-agent swarm on the same
subscription cannot: empirically a 4-6 agent swarm with effort: high or max burns through the 5-hour window in
30-60 minutes, and the first signal we get today is all N
agents simultaneously returning rate_limit_error from the
API. At that point the existing retry path in lib/drivers/claude-code.sh:136-149 kicks in with MAX_RETRY_WAIT, but by then the swarm is already wedged --
and if the 7-day cap gets hit, MAX_RETRY_WAIT is useless
(5h backoff isn't going to recover a 7-day reset).
With /usage visibility, operators can:
Pre-flight: check headroom before launching a long run
(./usage.sh before ./launch.sh start).
In-flight: see the swarm crossing 75%/90% thresholds in
the dashboard and choose to stop the numbered agents before
the 5h reset wastes the last commits.
Sizing: correlate agent count × effort × duration against
actual window burn to right-size future swarms instead of
guessing.
The existing max-opus.json smoke-test config
(tests/configs/max-opus.json) already pins "auth": "oauth"
as a first-class swarm shape, so the target user segment is
established.
Design notes
Why we cannot invoke /usage directly
The swarm runs agents in Claude Code's headless/print mode
(claude -p --output-format stream-json --verbose, lib/drivers/claude-code.sh:24-39). Three constraints rule
out direct support:
Slash commands are TUI-only./usage is not exposed
as a CLI flag, and the -p prompt argument is treated as
conversation input, not a command.
stream-json does not carry rate-limit fields.
The documented event schema is system/init, stream_event, assistant, result, and system/api_retry. Only system/api_retry carries anything rate-limit-shaped
("error":"rate_limit", error_status, retry_delay_ms)
and that event fires after a 429, not proactively. None
of the other events expose anthropic-ratelimit-unified-*.
statusLine is TUI-only too. Claude Code 2.1.80 added rate_limits.{five_hour,seven_day}.{used_percentage,resets_at}
to the JSON piped to a user-configured statusLine
command -- but statusLine only runs inside the interactive
TUI event loop, not under -p.
A disposable container that does enter the TUI long enough
for one API roundtrip, writes the rate_limits JSON via a statusLine script, and exits. Sketch:
Write ~/.claude/settings.json with a statusLine command
that jq-emits .rate_limits as a single JSON blob to /usage-out/rate_limits.json.
Start claude (interactive), feed it one minimal prompt
(echo "hi" | claude --model claude-haiku-4-5 --dangerously-skip-permissions
or equivalent cheap cash-out), wait for the statusLine to
fire after the first API response, read the file, exit.
Host parses /usage-out/rate_limits.json and prints the
table / JSON.
Cost: one Haiku round-trip per probe (~<$0.001, a few hundred
input tokens) and ~2-4 s wall time for the probe container to
start, run, and tear down. For the dashboard loop, throttle
to one probe per 60 s; for the CLI (./usage.sh), probe once
per invocation.
Edge cases & behaviour
API-key auth: probe helper returns n/a (API-key auth)
without running; costs.sh is the correct tool for that mode.
Mixed swarms (OAuth + API-key groups in one swarm.json):
probe once using the OAuth credential; the window is account-wide, not per-swarm.
Older Claude Code (<2.1.80): the statusLine JSON will
lack rate_limits; helper returns unsupported (Claude Code < 2.1.80). claude_code_version in swarm.json is the
pin point; document the minimum.
Rate limits on the probe itself: if we're already at
100% the probe's own request gets 429'd. Catch system/api_retry
with "error":"rate_limit" from the probe's own stream-json
and report 5h: ≥100% (limit reached) instead of spinning
in the retry loop.
Probe container reuses the swarm's image so no extra
image build is needed. Entry point is a new lib/probe-usage.sh alongside lib/harness.sh.
Non-goals (for this issue)
Proxying anthropic-ratelimit-unified-* headers. A
MITM proxy on ANTHROPIC_BASE_URL would give us per-request
continuous visibility without a probe, but adds a long-lived
host-side process, cert management, and a failure mode that
can stall the entire swarm. Worth a follow-up issue if the
probe approach proves too coarse.
Per-agent /usage breakdown. The rolling window is
account-scoped; there is no per-agent or per-session
attribution to be had from this API.
/extra-usage configuration. That command configures
pay-as-you-go overflow billing, which is interactive-only
and orthogonal to this feature.
Acceptance criteria
./usage.sh prints a table with columns: Window, Used %, Resets at (local time). Exits 0 on success,
1 on probe failure.
On API-key auth the helper prints n/a (API-key auth; use ./costs.sh) and exits 0.
On Claude Code < 2.1.80 the helper prints unsupported (Claude Code < 2.1.80) and exits 0.
Dashboard header shows a compact rate-limit line when
any group uses OAuth. Line is absent (not just blank)
when all groups are API-key.
Unit coverage in tests/test_usage.sh: parse fixture
statusLine JSON with rate_limits present, absent,
partial (only five_hour), malformed; formatter
handles each. Gate the integration test on CLAUDE_CODE_OAUTH_TOKEN being set so CI without
credentials skips cleanly (same pattern as the existing
OAuth smoke test).
USAGE.md gets a new Rate-limit monitoring section
under the existing Cost tracking section. Explicitly
document the OAuth-only scope and the Claude Code ≥ 2.1.80
requirement.
README.md Drivers table gets a row note pointing at
the new section.
CHANGELOG.md entry under the next version with a
1-paragraph rationale (not just "added ./usage.sh").
Claude Code's /usage command (added around v2.1.x, refined in
2.1.77+) shows subscription-plan utilization for the rolling
5-hour window and the 7-day weekly cap. It reads the anthropic-ratelimit-unified-* headers the CLI already parses
from every API response. For Pro/Max subscribers -- who pay a
flat subscription rather than per-token -- this is the only
meaningful "how much budget is left" signal, because the total_cost_usd we currently track and surface in the dashboard
is a synthetic API-price estimate that has no relationship to
the subscription's rolling window.
Summary
Surface Claude Code's 5-hour and 7-day rolling-window utilization
(the data behind the interactive
/usageslash command) toclaude-swarm operators running on an OAuth (Pro / Max / Max 20x)
subscription. Two deliverables:
./usage.sh— one-shot helper that prints currentrate_limits.five_hour.used_percentage,rate_limits.seven_day.used_percentage, and bothresets_attimestamps, formatted as a table (default) or JSON (
--json).Mirrors the shape of
./costs.sh.Dashboard banner — add a single compact line to
dashboard.sh's header, next to the model summary, showing5h: 42% (reset 17:34) | 7d: 15%when the swarm is usingOAuth. Refreshes on the existing 3 s tick but throttles the
underlying probe to once every ~60 s (probes are non-free,
see below).
Both surfaces must be opt-in and OAuth-scoped. API-key
(
auth: apikey/auth: key) swarms already get the equivalentsignal via
costs.sh(dollar cost tracks real spend on flat-rateAPI billing);
rate_limitsis absent for them and the helpershould print
n/a (API-key auth)rather than probe.Why it matters for swarms specifically
A single-user Claude Code session on Max 20x can run all day
without hitting the 5h cap. An N-agent swarm on the same
subscription cannot: empirically a 4-6 agent swarm with
effort: highormaxburns through the 5-hour window in30-60 minutes, and the first signal we get today is all N
agents simultaneously returning
rate_limit_errorfrom theAPI. At that point the existing retry path in
lib/drivers/claude-code.sh:136-149kicks in withMAX_RETRY_WAIT, but by then the swarm is already wedged --and if the 7-day cap gets hit,
MAX_RETRY_WAITis useless(5h backoff isn't going to recover a 7-day reset).
With
/usagevisibility, operators can:(
./usage.shbefore./launch.sh start).the dashboard and choose to stop the numbered agents before
the 5h reset wastes the last commits.
actual window burn to right-size future swarms instead of
guessing.
The existing
max-opus.jsonsmoke-test config(
tests/configs/max-opus.json) already pins"auth": "oauth"as a first-class swarm shape, so the target user segment is
established.
Design notes
Why we cannot invoke
/usagedirectlyThe swarm runs agents in Claude Code's headless/print mode
(
claude -p --output-format stream-json --verbose,lib/drivers/claude-code.sh:24-39). Three constraints ruleout direct support:
Slash commands are TUI-only.
/usageis not exposedas a CLI flag, and the
-pprompt argument is treated asconversation input, not a command.
stream-jsondoes not carry rate-limit fields.The documented event schema is
system/init,stream_event,assistant,result, andsystem/api_retry. Onlysystem/api_retrycarries anything rate-limit-shaped(
"error":"rate_limit",error_status,retry_delay_ms)and that event fires after a 429, not proactively. None
of the other events expose
anthropic-ratelimit-unified-*.statusLineis TUI-only too. Claude Code 2.1.80 addedrate_limits.{five_hour,seven_day}.{used_percentage,resets_at}to the JSON piped to a user-configured
statusLinecommand -- but
statusLineonly runs inside the interactiveTUI event loop, not under
-p.Upstream requests to expose this in headless were closed.
See Expose rate limit / usage quota info in statusLine JSON input anthropics/claude-code#23843 (state=not_planned,
2026-03-07) and #29604 (closed as duplicate; the
statusLine-only support landed instead). We should not
block on upstream adding a headless hook.
MVP: TUI-probe helper
A disposable container that does enter the TUI long enough
for one API roundtrip, writes the
rate_limitsJSON via astatusLinescript, and exits. Sketch:Inside the probe container:
~/.claude/settings.jsonwith astatusLinecommandthat
jq-emits.rate_limitsas a single JSON blob to/usage-out/rate_limits.json.claude(interactive), feed it one minimal prompt(
echo "hi" | claude --model claude-haiku-4-5 --dangerously-skip-permissionsor equivalent cheap cash-out), wait for the statusLine to
fire after the first API response, read the file, exit.
/usage-out/rate_limits.jsonand prints thetable / JSON.
Cost: one Haiku round-trip per probe (~<$0.001, a few hundred
input tokens) and ~2-4 s wall time for the probe container to
start, run, and tear down. For the dashboard loop, throttle
to one probe per 60 s; for the CLI (
./usage.sh), probe onceper invocation.
Edge cases & behaviour
n/a (API-key auth)without running;
costs.shis the correct tool for that mode.swarm.json):probe once using the OAuth credential; the window is
account-wide, not per-swarm.
lack
rate_limits; helper returnsunsupported (Claude Code < 2.1.80).claude_code_versioninswarm.jsonis thepin point; document the minimum.
100% the probe's own request gets 429'd. Catch
system/api_retrywith
"error":"rate_limit"from the probe's own stream-jsonand report
5h: ≥100% (limit reached)instead of spinningin the retry loop.
image build is needed. Entry point is a new
lib/probe-usage.shalongsidelib/harness.sh.Non-goals (for this issue)
anthropic-ratelimit-unified-*headers. AMITM proxy on
ANTHROPIC_BASE_URLwould give us per-requestcontinuous visibility without a probe, but adds a long-lived
host-side process, cert management, and a failure mode that
can stall the entire swarm. Worth a follow-up issue if the
probe approach proves too coarse.
/usagebreakdown. The rolling window isaccount-scoped; there is no per-agent or per-session
attribution to be had from this API.
/extra-usageconfiguration. That command configurespay-as-you-go overflow billing, which is interactive-only
and orthogonal to this feature.
Acceptance criteria
./usage.shprints a table with columns:Window,Used %,Resets at(local time). Exits 0 on success,1 on probe failure.
./usage.sh --jsonemits{"five_hour":{"used_percentage":…,"resets_at":…}, "seven_day":{"used_percentage":…,"resets_at":…}, "probed_at":…}.n/a (API-key auth; use ./costs.sh)and exits 0.unsupported (Claude Code < 2.1.80)and exits 0.any group uses OAuth. Line is absent (not just blank)
when all groups are API-key.
tests/test_usage.sh: parse fixturestatusLine JSON with
rate_limitspresent, absent,partial (only
five_hour), malformed; formatterhandles each. Gate the integration test on
CLAUDE_CODE_OAUTH_TOKENbeing set so CI withoutcredentials skips cleanly (same pattern as the existing
OAuth smoke test).
USAGE.mdgets a new Rate-limit monitoring sectionunder the existing Cost tracking section. Explicitly
document the OAuth-only scope and the Claude Code ≥ 2.1.80
requirement.
README.mdDrivers table gets a row note pointing atthe new section.
CHANGELOG.mdentry under the next version with a1-paragraph rationale (not just "added
./usage.sh").References
https://docs.claude.com/en/docs/claude-code/statusline
-pmode:https://code.claude.com/docs/en/headless
https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-usage-and-rate-limits.md
Motivation
Claude Code's
/usagecommand (added around v2.1.x, refined in2.1.77+) shows subscription-plan utilization for the rolling
5-hour window and the 7-day weekly cap. It reads the
anthropic-ratelimit-unified-*headers the CLI already parsesfrom every API response. For Pro/Max subscribers -- who pay a
flat subscription rather than per-token -- this is the only
meaningful "how much budget is left" signal, because the
total_cost_usdwe currently track and surface in the dashboardis a synthetic API-price estimate that has no relationship to
the subscription's rolling window.