claude-headroom
v0.1.0
Published
Track Claude Code usage budgets, compute burn rate, and gate prompts via session/week-scoped warnings.
Maintainers
Readme
claude-headroom
Track Claude Code usage budgets, compute burn rate, and nudge or halt the autonomous loop when thresholds are hit.
claude-headroom is a small CLI that reads the Anthropic usage API, caches a snapshot, and feeds it into Claude Code via statusline + hooks. The goal: make Claude aware of its own budget without you having to do mental arithmetic.
Quick start
npm install -g claude-headroom # or wire it locally
claude-headroom refresh # fetch and cache a snapshot
claude-headroom show # print the latest snapshot
claude-headroom burn # show burn rate across windowsThe CLI reads your existing OAuth token from ~/.claude/.credentials.json (left there by claude login), so there's nothing to authenticate.
Let Claude help you set this up
Watchers especially can take some thought to tune. The fastest path is to ask Claude Code itself — paste this into a session and it will read the source, summarise your options, and propose a config:
Explore https://github.com/Promaia/claude-headroom. List me the options for the statusline and the options for watchers, including how I can configure these things globally and per-repo. Then suggest a starting config for me.
Add detail about your usage patterns (e.g. "I run long autonomous loops; halt me at 85%" or "interactive only, just nudge me at 70%") for a tighter recommendation.
Configuration
Settings are layered. Each layer is optional; missing keys fall back to the layer below. Defaults are baked into the binary.
| Scope | Path | Use for |
| -------------- | ------------------------------------------- | --------------------------------------------------------- |
| Global | ~/.claude/claude-headroom.json | Your personal defaults across every project. |
| Project-shared | <repo>/.claude/claude-headroom.json | Per-repo config you want to commit (e.g. team watchers). |
| Project-local | <repo>/.claude/claude-headroom.local.json | Per-repo overrides you don't want to commit (gitignored). |
Precedence (highest first): project-local → project-shared → global → built-in defaults. Nested sections (refresh, burn, statusline, …) merge per key. The watchers array is replaced by the highest layer that sets it — it does not merge.
Configurable keys (all optional):
refresh.maxAgeSec— how stale the cache can be before a foreground refresh (default 60s).history.maxAgeSec— how long to keep history samples for burn-rate calculation (default 7 days).burn.windowsSec— windows shown byclaude-headroom burnand available asburn.<scope>.<window>.*watcher metrics.statusline.*— toggles forshowWeekly,showSonnet,showBranch,showDuration,showCost,showBurn, plusburnSessionSec/burnWeekSecwindow choices.watchers— array of watchers (see below).
Inspect and seed:
claude-headroom settings view computed # what the tool actually sees after merging
claude-headroom settings view global # one layer's contents
claude-headroom settings seed project-shared # write a starter file in the right placeWiring claude-headroom into Claude Code itself (statusline + hooks) is a separate edit to ~/.claude/settings.json — see the next two sections.
Status line
Wire claude-headroom statusline into Claude Code's statusLine setting to render a one-line usage summary at the bottom of the chat. It refreshes the cache in the background if it's stale; it never throws and always produces output.
// ~/.claude/settings.json
{
"statusLine": { "type": "command", "command": "claude-headroom statusline" },
}Watchers
Watchers are edge-triggered rules that react to usage thresholds. You declare them in ~/.claude/claude-headroom.json; the hook reads the cached snapshot, computes which watcher bucket each watcher is in, and fires actions on every bucket transition.
Mechanism
- Buckets partition the metric space. Gaps between buckets are dead zones — when a metric sits in a gap, the watcher's bucket doesn't change and nothing fires. This makes hysteresis fall out for free:
[0,55]and[60,100]buckets give you a 5-point dead zone where jitter can't flap. - Edge-triggered. An action fires the moment the watcher enters a new bucket. It does not re-fire while the bucket is stable.
- Per-session state at
~/.claude/headroom/state/<session-id>.json. Sub-agents each get their ownsession_idfrom Claude Code and their own state. - Window resets re-arm watchers when a session/week window rolls over.
Actions
message: "<text>"— appended to the PreToolUseadditionalContext. Templated (see tokens below).stop: "<reason>"— setscontinue: falsewith the reason. Halts the autonomous loop cleanly.{ "type": "none" }— fires nothing; only records the bucket transition so other buckets can re-arm.
Multiple watchers can fire on the same hook call: messages concatenate (each prefixed with [watcher-id]), and any stop blocks.
Example
See examples/watchers.json for a complete file. The core shape:
{
"watchers": [
{
"id": "session_tier",
"startup": "low",
"buckets": [
{
"id": "low",
"match": { "metric": "session.percent", "range": [0, 55] },
"action": { "message": "Session {session.consumed}% — fine." },
},
{
"id": "panic",
"match": { "metric": "session.percent", "range": [85, 100] },
"action": {
"stop": "Session at {session.consumed}% — halting to preserve budget.",
},
},
],
},
],
}Match conditions
A match is one of:
- Single condition (shorthand):
{ "metric": "<id>", "range": [lo, hi] }or{ "metric": "<id>", "equals": "<value>" }. Range is[lo, hi)— exclusive-high — excepthi = 100is inclusive, so[60, 100]covers 60%–100%. all: array of conditions, all must match (AND).any: array of conditions, at least one must match (OR).
all and any don't nest. If you need that, use multiple watchers.
Metric ids
session.percent,session.remainingweek.percent,week.remaining,week_sonnet.percent,week_sonnet.remainingburn.<scope>.<window>.status—ok|running_outburn.<scope>.<window>.rate— %/hr as a number
<scope> is session | week | week_sonnet; <window> is a duration token matching one of your configured burn.windowsSec (e.g. 5m, 10m, 30m, 1h, 2h, 3h, 6h, 12h, 24h).
Template tokens
Substituted into message: and stop: strings when an action fires. Unknown tokens pass through literally; missing data renders as —.
| Token | Meaning |
| -------------------------------------------------- | -------------------------------- |
| {session.consumed} / .remaining / .resets_in | session metrics |
| {week.*} / {week_sonnet.*} | same for the two weekly windows |
| {burn.<scope>.<window>.rate} | burn rate (e.g. 30.0%/hr) |
| {burn.<scope>.<window>.status} | ok or running_out |
| {burn.<scope>.<window>.eta} | humanised time-to-full, or — |
| {burn.table} | full rendered burn table |
| {watcher} / {bucket} | ids of the firing watcher/bucket |
Wiring the hook
Add this to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "claude-headroom hook pre-tool-use",
"timeout": 2000,
},
],
},
],
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "claude-headroom hook session-start",
"timeout": 2000,
},
],
},
],
},
}hook pre-tool-use evaluates watchers and emits additionalContext / continue: false as appropriate. hook session-start with matcher: "compact" re-injects a usage summary after the context is compacted. Both always exit 0 and emit {} on internal failure so they can't block the loop.
Inspecting watchers
claude-headroom watchers list # all watchers + all active sessions
claude-headroom watchers list --session-id abc # one session's current buckets
claude-headroom watchers dry-run --session-id abc # evaluate against current cache, show what would firedry-run doesn't write state, so you can iterate on your config safely.
CLI reference
claude-headroom refresh— fetch the Anthropic usage API and update the cache + history.claude-headroom show— print the latest cached snapshot.claude-headroom burn— burn rate per window with projected time to full.claude-headroom statusline— Claude Code statusline renderer (reads stdin, prints one line).claude-headroom settings <seed|view> <scope>— inspect or seed layered settings.claude-headroom hook <pre-tool-use|session-start>— Claude Code hook endpoints.claude-headroom watchers <list|dry-run>— introspect configured watchers.
License
Apache-2.0.
