Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

usage-guard

A Claude Code plugin that makes Claude aware of how much of your plan usage it has burned, and — near the limit — makes it save its work and wait instead of dying mid-task.

The problem it solves: a long overnight run hits the 5-hour limit at 3am, the session stops, and nobody is awake to type "continue" when the window resets. Hours of working time are lost.

What it does

usage behaviour
< 85% nothing, zero tokens spent
≥ 85% (warnAt) one-line nudge into context: finish work in progress, keep the task list current
≥ 95% (pauseAt) blocks the tool call, orders a checkpoint, then holds the session until the window resets and tells Claude to carry on

The pause happens inside a hook, so:

  • the same session survives — full context, no re-explaining, no --resume
  • no tokens are spent while waiting
  • work resumes automatically the moment the window rolls over

Where the usage number comes from

Claude Code's status line payload is the only local source of real plan utilization — rate_limits.five_hour.{used_percentage,resets_at} and the same for seven_day, straight from the server (the same numbers /usage shows). With statusLine.refreshInterval it keeps arriving even while a session is idle or held.

So /usage-guard install points statusLine at this plugin's feed script, which records every payload and chains through whatever status line you already had (your existing line still renders, with UG 5h:74% 7d:41% appended). settings.json is backed up first; /usage-guard uninstall puts it back.

Caveats, honestly:

  • The feed only fills in after the first API response of a session, and only for Claude subscription auth — never for API key / Bedrock / Vertex, where plan limits don't apply anyway.
  • Until a sample arrives, or if a sample goes stale (> staleSeconds), the guard fails open: it never blocks work on data it doesn't trust.
  • /usage-guard poll is an opt-in second feed that reads plan usage from GET /api/oauth/usage using the local OAuth token (macOS keychain or ~/.claude/.credentials.json). It exists for headless claude -p runs that have no status line. This path is unverified — it was written from the CLI's own call site but never executed here, because reading the keychain was blocked during development. Try it before relying on it.

Install

git clone https://github.com/ManvikPasula/usage-guard.git
cd usage-guard
claude plugin marketplace add ./          # "./", not "." — bare "." is rejected
claude plugin install usage-guard@usage-guard-local

The marketplace source is this directory, but installing copies it to ~/.claude/plugins/cache/usage-guard-local/usage-guard/<version>/. After editing the source, bump version in .claude-plugin/plugin.json, then:

claude plugin marketplace update usage-guard-local
claude plugin update usage-guard@usage-guard-local

Then, inside Claude Code:

/usage-guard install     # wire the usage feed into the status line
/usage-guard status      # confirm

Commands

/usage-guard                      status: usage, thresholds, session phase
/usage-guard on | off             toggle the guard (hooks stay registered, do nothing when off)
/usage-guard install [interval=10]  wire the status line feed
/usage-guard uninstall            restore the previous status line
/usage-guard set k=v [k=v ...]    change config
/usage-guard cancel               release a session that is currently held
/usage-guard reset                clear per-session guard state
/usage-guard doctor               check the feed end to end
/usage-guard poll                 one-off read via /api/oauth/usage (opt-in)

Config

~/.claude/usage-guard/config.json, editable with /usage-guard set:

key default meaning
enabled true master switch
warnAt 85 % at which to nudge
pauseAt 95 % at which to checkpoint + hold
resumeBelow 90 resume early if usage falls under this
windows ["five_hour","seven_day"] which plan windows to watch
maxWaitSeconds 21600 never hold longer than this (6h)
resumeBufferSeconds 90 grace added past resets_at
pollIntervalSeconds 20 how often a held session re-checks
staleSeconds 900 older sample ⇒ fail open
checkpointToolBudget 30 tool calls Claude gets to save state
gitCommit false opt-in git add -A && git commit during checkpoint
statuslineSegment true render the UG … segment
notifyCommand null shell command run once when a pause starts; message in $USAGE_GUARD_MESSAGE
maxResumesPerSession 6 loop protection

Example — pause earlier, and get a desktop notification:

/usage-guard set pauseAt=92 warnAt=80 notifyCommand=terminal-notifier -message "$USAGE_GUARD_MESSAGE"

How a pause actually plays out

  1. PreToolUse sees five_hour at 96%. It denies that one tool call and hands Claude checkpoint orders: no new work, no half-applied edits, update the task list, write a handoff note to ~/.claude/usage-guard/handoffs/<session>.md, then run ctl.mjs checkpoint-done.
  2. During the checkpoint only save-the-work tools are allowed (Read/Write/Edit/ Bash/Grep/task tools). Web fetches, subagents and workflows are refused. There's a hard budget so a non-cooperative model can't spin here forever.
  3. checkpoint-done flips the session to paused. Whichever comes first — Claude's next tool call (PreToolUse) or the end of its turn (Stop) — sleeps until resets_at + resumeBufferSeconds, polling every 20s.
  4. On reset, Stop returns decision: "block" with instructions to re-read the handoff note and continue; PreToolUse simply lets the held call through. Either way the same session keeps going.

Both hooks are registered with a ~6h timeout, which is what makes the hold legal — Claude Code's per-hook timeout is in seconds with no upper clamp.

Escape hatches

  • Esc / Ctrl-C aborts a hold immediately.
  • /usage-guard cancel from another terminal releases it within one poll.
  • /usage-guard off disables everything and releases any held session.
  • A weekly (seven_day) limit whose reset is days out is not waited on: anything beyond maxWaitSeconds is refused, and Claude is told to stop and report instead.

Deliberate non-behaviours

  • Never returns permissionDecision: "allow" — your normal permission prompts are untouched. The guard only ever denies or stays silent.
  • Never commits to your repo unless you set gitCommit=true. Files on disk are not at risk from a pause; a half-finished edit and lost intent are, which is what the checkpoint targets.
  • Every failure path is fail-open: a broken guard must not break a session.

Layout

.claude-plugin/plugin.json   hook registrations (incl. the multi-hour timeouts)
.claude-plugin/marketplace.json
commands/usage-guard.md      the /usage-guard slash command
scripts/lib.mjs              config, state, usage-feed normalisation
scripts/statusline.mjs       the feed: records rate_limits, chains your old line
scripts/gate.mjs             PreToolUse: warn / checkpoint / hold
scripts/stop.mjs             Stop: hold past the end of a turn, then resume
scripts/pause.mjs            the wait loop
scripts/session-start.mjs    SessionStart: state + context priming
scripts/prompt.mjs           UserPromptSubmit: near-limit nudge
scripts/ctl.mjs              CLI behind the slash command

State lives in ~/.claude/usage-guard/: config.json, usage.json (latest sample), sessions/*.json, handoffs/*.md, log.jsonl, timestamped settings.backup.*.json, and two generated shims — feed.mjs (what settings.json calls) and ctl.mjs (what the slash command calls). The shims resolve pluginRoot from config.json at run time, which SessionStart rewrites whenever the plugin's versioned path changes, so a plugin update can't leave a dangling status line.

Tested

scripts/ was exercised end to end against an isolated CLAUDE_CONFIG_DIR: feed capture and status-line chaining, install/uninstall round-trip on a pre-existing status line, silent below warnAt, warning at warnAt, deny + checkpoint orders at pauseAt, allow/deny split during a checkpoint, budget exhaustion, checkpoint-done → hold → resume via both the Stop and PreToolUse paths, early release via cancel, refusal to wait on a days-away weekly reset, fail-open on a stale sample, the off switch, resume cap, config validation, and doctor. /usage-guard poll is the one path that has not been run — see the caveat above.

About

Claude Code plugin: plan-usage awareness. Near the limit it checkpoints the work, holds the session until the quota window resets, then continues in the same session.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages