Claude Code statusline that answers one question: am I burning my usage too fast?
- The easiest to set up: one line in
settings.json. No API key, no login, no config file. - Zero tokens burnt: it never calls a model or an API. Every number comes from the data Claude Code already hands the statusline.
- Zero dependencies: plain Node, with a 30 ms startup budget on top of Node's own that CI enforces.
Read the second line as: the weekly window is 90% used with 2 days left, being spent 1.26x faster than the budget allows, so at this rate it runs dry today at 21:20 - well before it resets. The cause follows: this session's spend went 71% to Opus 5.5 and 27% to Sonnet 5.5 subagents.
Claude Code shows you a percentage. A percentage alone can't tell you whether 71% is fine or alarming - that depends entirely on how much of the window has elapsed. cc-pace divides the two.
Some usage trackers poll an API or ask a model, and that spends the very quota they are watching.
cc-pace does neither. Since Claude Code 2.1.80 the statusline payload already contains
rate_limits; cc-pace reads it from stdin and does arithmetic. It makes zero network requests,
consumes zero tokens, and cannot be rate limited. Run twenty sessions side by side if you like.
What it keeps on disk, beside the config file: burn-history.json, a short history of readings the
recent-burn warning needs; sessions/, one small file per open session for the session share in pool;
and models/, where it is up to in each session's transcript for the model mix. Drop burn and
pool from segments and set modelMix to false, and nothing is written. It reads the session
transcripts Claude Code already keeps, only what was added since the last render, and never sends
them anywhere.
The package is published as claude-code-pace; the command it installs is cc-pace.
npm install -g claude-code-pacecc-pace --setup prints that snippet.
refreshInterval redraws the line every minute while the session is idle. Without it the line
only changes when something happens, so the cache warning and the countdowns go stale during a break.
Restart Claude Code.
npm install -g claude-code-pace@latest
cc-pace --versionThe next redraw runs the new version; Claude Code needs no restart. cc-pace makes no network
calls, so it never reports a newer version itself: CHANGELOG.md and the
GitHub releases list what each one changes.
A statusline still set to npx -y claude-code-pace can keep running a copy npx cached earlier.
Install globally as above and set the command to cc-pace.
ratio = used% / elapsed%
A rate-limit window starts exactly its own length before it resets, so elapsed% is known without
any history: a 7-day window resetting in 2 days is 71.4% elapsed. Spending 71% of the budget by
then is a ratio of 0.99 - dead on target.
| Ratio | Colour | Meaning |
|---|---|---|
< 0.95 |
green | under budget; the ETA is hidden because the window never runs dry |
0.95 - 1.10 |
yellow | on the line |
> 1.10 |
red | the window depletes before it resets |
The ETA is the projected moment usage reaches 100% at the current average rate. When the ratio is
red, the model and its settings follow it - 1.26x Fri 21:20 Opus 5/high+ (+ is thinking) - because the
model is usually the cause and the thing to change. When subagents spend on other models, each
model's share of the session's recent spend follows too: Opus 5.5/med 71% · Sonnet 5.5 27%.
The shares come from the session's transcripts, priced per model, so they match what /usage
reports.
An average over the whole window cannot see a burst: a quiet week hides a hard hour. The burn
segment measures the rate over roughly the last hour and speaks only when that rate hits 100% before
the reset. When that window is on the line, its time replaces the average's - 2.76x today 16:18 -
since the recent rate describes what is happening now; otherwise it stands alone as
Week 1h7m to 100% (22:13). How it guards against stale readings from idle sessions:
docs/pace-accuracy.md.
| Segment | Source |
|---|---|
Session |
rate_limits.five_hour - 5-hour rolling window |
Week |
rate_limits.seven_day - 7-day window, carries the pace ratio by default |
Opus / Sonnet / Fable |
a model's own weekly window, such as seven_day_fable, when your plan reports it. Hidden unless showModelWindows is on; any model added later is labelled and paced the same way |
Pool |
Pool: 35% ↓24% stop Sat 05:10 -26h. First, this session's share of what all open sessions spent in the last hour, shown once a second session is burning. Then, with account rotation, whether the accounts together last at your working pace - green ↑40% to push, red ↓24% to slow down, with when they run dry and for how many hours - see With cc-account |
Cache |
Cache: 42% cold in 12m (270k). First, how full this conversation is - what the prompt cache holds and /compact clears; yellow from 70%, red from 85%. Each session has its own, so the figures of several sessions do not add up to anything. Then cold in 12m (270k), or cold, next turn re-sends 270k once expired: a conversation of at least 100k tokens whose prompt cache is within 15 minutes of expiring. Past expiry, the next turn re-sends the whole conversation as cache writes, which can cost more than an hour of work; /compact before a break avoids it. From prompt_cache in the payload (Claude Code 2.1.251+) |
| burn | the recent-rate warning above; empty while the recent rate is safe |
| rotation | account-switch status, when rotation is configured - see below |
| git | branch plus * modified, + untracked, ↓n behind, ↑n ahead |
| chain | the output of another statusline, when chain is configured - see Keep your current statusline |
! config |
appears when the config file cannot be read, so defaults are in force |
Buckets Claude Code starts sending that cc-pace has never heard of are rendered anyway, with a humanised label, rather than silently dropped.
| Flag | Effect |
|---|---|
--demo |
render a bundled sample payload; no stdin needed |
--dump |
pretty-print the raw statusline JSON and exit |
--json |
print the computed metrics as JSON instead of a line |
--no-color |
disable ANSI colour (NO_COLOR is honoured too) |
--setup |
print the settings.json snippet |
--config |
print the resolved config and where it was loaded from |
--dump is the fastest way to see what Claude Code actually hands a statusline command.
--json exposes numbers the line has no room for, including safe_rate_percent_per_day - how much
of the budget you can spend per day from now on and still land exactly at the reset.
Optional. Zero-config is the supported default. ~/.config/cc-pace/config.json:
{
"segments": ["limits", "burn", "pool", "cache", "rotation", "git", "chain"],
"separator": " | ",
"hideCalm": false,
"showModelWindows": false,
"modelMix": true,
"paceOn": ["seven_day"],
"eta": "when-early",
"minElapsedPct": 5,
"warn": 0.95,
"danger": 1.1,
"usedWarn": 50,
"usedDanger": 80,
"memoryWarn": 70,
"memoryDanger": 85,
"cacheWarnTokens": 100000,
"cacheWarnSecs": 900,
"ascii": false,
"reapStuck": true
}segments- order and contents of the line."limits"expands to every bucket present. Aliases:session,weekly, and any model's name for its own window (fable,opus). The older namesshareandmemorystill work and drawpoolandcache.showModelWindows- add every model's own window to"limits". Off by default: the account-wide windows are the ones that stop work. Naming one model insegmentsshows only that one.hideCalm- show a window only when it is at or over pace or pastusedWarn, and the cache percentage only pastmemoryWarn. The line then carries only what needs attention.paceOn- which buckets get the ratio and ETA. Add"five_hour"to pace the session too.eta-when-early(default),always, ornever.minElapsedPct- how much of a window must have elapsed, or been used, before its ratio is shown. Below both the denominator is near zero and the number is noise; a heavy early burn (12% used 3% into the week) shows at once.ascii- usev/^instead of↓/↑for fonts without the arrows.reapStuck- Windows only. Claude Code cancels a statusline render by killing the Git Bash that runs it, and a kill that lands while bash is starting node leaves that copy of cc-pace paused forever, ~4 MB each. Once an hour, in the background, cc-pace closes such copies: one paused thread, no CPU ever used, over a minute old, parent gone.falseturns it off.
Environment overrides: CC_PACE_SEGMENTS, CC_PACE_SEPARATOR, CC_PACE_ETA, CC_PACE_ASCII,
CC_PACE_CONFIG, NO_COLOR.
Claude Code runs one statusline command. To keep the one you had, give it to cc-pace as chain:
{ "chain": "npx -y other-statusline", "chainTimeoutMs": 1000 }cc-pace hands it the same payload, runs it while doing its own work, and adds its output where
"chain" sits in segments - last by default - each line of it joined like a segment, colours
kept. If it fails, prints nothing or takes longer than chainTimeoutMs, cc-pace's own line shows
alone. A chained command that leads back to cc-pace is not run again.
cc-pace works alone: pace, burn, cache and the session share need nothing else.
Installed with cc-account, it also switches accounts by itself. cc-pace watches usage and decides where to go; cc-account switches and keeps logins alive. The statusline payload carries no account identity, so cc-pace cannot name the account it describes - cc-account publishes which one is live and when each login stops refreshing, and cc-pace never switches into one about to log out.
With work and personal in rotation, the line also says whether the two together last, and what
to do about it:
Week: 80% (3d0h) 1.40x | Pool: ↑40%
Week: 90% (4d0h) 2.10x | Pool: ↓24% stop Sat 05:10 -26h
The pool forecast follows how you work, not the clock. It measures what the pool burns per hour of work, the last few hours counting most, and learns which hours of the day you work from Claude Code's own prompt history (timestamps only), so nights you sleep are not projected as burn. It then plays that forward - always drawing on the account whose reset comes first, since what it holds at its reset is lost - and finds the most you can burn per working hour and still last.
- green
↑40%- you can push: the pool lasts even 40% faster than your current pace. - red
↓24%- slow down by 24% to last.stop Sat 05:10 -26his when every account runs dry at your current pace, and how many hours until the first one refills.
The pool assumes the accounts are on the same plan.
Install it alongside cc-pace:
npm install -g cc-accountOr as a Claude Code plugin, which also gives a /cc-account command:
/plugin marketplace add poisons77/cc-account
/plugin install cc-account@cc-account
Save a snapshot once per account (cc-account save <name>), then connect the two at one of two
depths, cheap and full.
One command at a threshold. onThreshold runs any shell command once per window when a bucket
crosses a percentage. cc-pace never learns what the command does.
{
"onThreshold": { "bucket": "five_hour", "at": 95, "run": "cc-account rotate" }
}cc-account rotate moves to whichever account was switched away from longest ago. No account
names in the config, no state beyond a latch that clears when the window resets.
The rotation engine. Watches several windows, chooses a target on what it knows of each account, waits out the settle window after a move, and reports the outcome in the line itself:
{
"rotation": {
"accounts": ["work", "personal"],
"buckets": { "five_hour": 95, "seven_day": { "at": 95, "lateAt": 99, "lateWithinSecs": 129600 } },
"command": "cc-account"
}
}Session: 95% (2h18m) | Week: 67% (4d20h) | → switching to personal
Session: 2% (5h0m) | Week: 61% (4d2h) | ✓ on personal
A target is chosen on what was last read of it, and use made elsewhere - claude.ai, the desktop app
- never reaches that reading. So the first reading after a switch is checked: when the account is
already over its limit, rotation stops rather than moving on, and waits for you to pick an account.
askIfUnreadSecsalso stops before switching into an account last read longer ago than that:
Session: 3% (4h40m) | Week: 99% (1d20h) | ! personal already at 99% · pick an account
Session: 40% (2h10m) | Week: 95% (3d2h) | ! personal last read 3d4h ago · switch?
With the cc-account plugin, every open session shows the question in red until it is answered.
Name primary and fallback instead of accounts for a reserve account you return to rather than
peers you rotate between.
This tier needs cc-account specifically, because it reads the identity cc-account publishes to
~/.claude-accounts/rotation-state.json on every switch. Any tool writing that file would do; no
other tool does.
Thresholds per window, what is known about an account you are not on, why nothing is decided for
two minutes after a switch, and the failure reporting: docs/rotation.md.
The statusline re-renders on activity, so startup is the only cost that matters. Node's own boot time is a floor no Node CLI beats, and it swings by tens of milliseconds across platforms, so the number worth quoting is the overhead cc-pace adds on top of that floor.
The budget is 30 ms, and CI fails the build past it. Measure it on your own machine:
npm run benchOne Windows laptop reports:
runs 20
node boot 36.5 ms (floor, not ours)
cc-pace total 52.6 ms
our overhead 16.1 ms (budget 30 ms)
Zero runtime dependencies, and git state comes from a single git status --porcelain=v2 --branch
rather than four separate invocations.
A statusline that throws is worse than one that is briefly empty. Malformed JSON, a missing
rate_limits, a broken config file, a directory that is not a git repository - each degrades to a
shorter line, never to a stack trace.
Requires Claude Code >= 2.1.80 for rate_limits in the statusline payload, and Node >= 22. On
older Claude Code versions the limit segments are simply absent.
rate_limits is an internal payload shape and can change without notice. cc-pace treats every
field as optional for that reason.
npm test # 143 tests, node:test, no dependencies
npm run demo
npm run benchHow the pace ratio behaves early in a window, and why a cumulative ratio cannot see a burst:
docs/pace-accuracy.md.
MIT. See LICENSE.
Not affiliated with Anthropic. "Claude" and "Claude Code" are trademarks of their respective owner.