The way people use Claude Code has quietly changed. A year ago it was a chat window: you sat in front of it, one prompt at a time, watching the diff scroll by. Now it’s just as often a goal handed to an agent running loose in a sandbox or a VM: kick off an overnight run, go to bed, see what got done in the morning.

Two things made that shift possible. Models got good enough to trust with a few unsupervised hours instead of one supervised prompt at a time. And Claude Code grew primitives built for that scale rather than back-and-forth: dynamic workflows can spin up hundreds of subagents from a single script and run them in the background while your session stays free, which is exactly the shape of the large sweeps and migrations nobody wants to babysit turn by turn. Both are good news. Neither comes with a fuel gauge, and the docs are upfront that a workflow like this can burn through meaningfully more usage than the same task worked one prompt at a time, all counted against the same rate limits as everything else you do.

That shift changes what running out of runway actually looks like. When you’re sitting there watching, hitting a limit is annoying but survivable: you notice, you deal with it, you move on. When the agent is unattended at 3 AM, hitting that same limit silently just means the task stopped, and nobody notices until morning.

A lot of these long runs happen on a subscription rather than metered API credits, because paying by the token for a multi-hour agent loop adds up fast, and most of us would rather spend that money on literally anything else. Subscriptions are flat-rate, which is great, right up until you hit the ceiling that flat-rate implies. Claude Code enforces that ceiling as a 5-hour usage window and a 7-day usage window, and neither one is visible to the agent itself. The model doing the work has no idea the runway is about to end.

So I built usage-guard , a small Claude Code plugin that keeps Claude aware of how much runway is left, every turn, and, if nobody’s watching, pauses the agent automatically before that runway runs out.

Where the Number Actually Lives

The natural place to check “how much usage is left” would be a hook, since Claude Code runs hooks before every prompt and before every tool call. That’s the first place I looked, and it’s a dead end: neither hook is ever told your usage percentage.

The only part of Claude Code that receives it is the statusline, that little strip of text at the bottom of the terminal most people never look at twice. On a Pro or Max subscription (API-key billing doesn’t have this kind of window at all), Claude Code hands the statusline a live 5-hour and 7-day usage percentage on every update, meant purely for a human to glance at. Nothing else in the product ever sees that number.

Claude Code statusline reading: Fable 5, ctx 5%, 5h 0% with 4h 49m left, 7d 47% with 4d 13h left

That’s what it looks like once usage-guard is wired in: model name, context usage, and both windows with a countdown to reset, sitting in the one line of the UI that was already there.

So the plugin needed a way to move that number from something only the statusline sees to something the hooks can act on. There’s no API for that, so it goes through the one thing both sides can touch: a small file on disk.

statusline updates  →  small state file on disk  →  hooks read it
                                                       ├─ before each prompt: add a plain-English usage line to context
                                                       └─ before each tool call: check the threshold, pause or stop if crossed

That’s also why the statusline has to stay wired up for good, not just during setup: it’s the only thing that ever writes to that file. If the file goes stale, the hooks notice and step aside rather than act on old numbers, which turned out to matter more than anything else in how this plugin behaves.

There’s a catch in this pipeline, and it lands on exactly the people this plugin is for. Statusline updates are event-driven: Claude Code re-runs the command when new messages arrive in the conversation. Sit there chatting and it refreshes constantly. But kick off a workflow that fans out into background subagents and the main conversation goes quiet: no new messages, no statusline runs, no fresh numbers in the file. Meanwhile, those subagents are burning usage faster than anything else you can do with the product. The fuel gauge freezes at the exact moment the fuel is draining fastest.

Claude Code has a setting for this: statusLine.refreshInterval re-runs the statusline on a timer, on top of the event-driven updates. Setup wires it to 60 seconds, so the state file keeps refreshing even while a fleet of subagents keeps the session busy. And because subagents run in the same process, their API traffic feeds the same usage numbers, so the mid-workflow readings are real, not just re-stamps of the last thing the main conversation saw.

Mostly, Telling Claude Is Enough

Every prompt, the plugin adds a line like this to context:

[usage-guard] Subscription usage: 5h window 61% used, resets in 1h 52m (Fri 14:30);
7d window 47% used, resets in 4d 13h (Wed 09:15). If the 5h window is above ~90%,
prefer finishing or checkpointing the current step over starting large new work.

I expected the hard stop to be the important half of this project. It isn’t. Just telling Claude the number and suggesting it wrap up is enough, most of the time, for it to checkpoint on its own well before things get tight. The automatic pause exists for the runs where nobody’s there to read that suggestion and the agent barrels on anyway.

The Automatic Pause

Before every tool call, a second check compares the current usage against a threshold (98% by default) and, if it’s crossed, does one of two things:

  • Wait mode (the default) puts the whole session to sleep until the usage window resets, then lets it continue on its own. Perfect for an unattended run: come back and it’s already picked back up.
  • Block mode stops the tool call and hands Claude a reason it can act on. Instead of retrying into a limit that hasn’t moved, it wraps up, summarises what’s left, and waits for you.

Wait mode sounds a little extreme once you say it out loud: freezing an entire session, sometimes for hours, because a five-hour window has ninety minutes left on it. But that’s exactly what you want for a job running hands-off in a VM overnight. Block mode is the better fit for anything you’re actively watching, since it hands control back to you immediately instead of just going quiet.

Fail Open, Always

The one rule I kept coming back to: this plugin should never be the reason real work stops. If the usage data is missing, stale, or unreadable for any reason, the correct behaviour is to act exactly as if the plugin weren’t installed, not to block you over broken instrumentation.

So everything defaults to “let it through”: no data yet, allow. Data older than fifteen minutes, allow. Usage below the threshold, allow. Only a fresh, high number ever triggers a pause. The one exception is right after a pause ends: since no new numbers arrive while the session was asleep, the plugin throws away what it had and waits for a fresh reading rather than trusting a number that might be hours old.

“Fresh” also needed a stricter definition than the file’s age, and the refresh timer is the reason why. With the statusline rewriting the file every minute, a recent timestamp proves nothing: an idle overnight session will happily keep re-stamping a 99% reading from a five-hour window that reset hours ago. So the hooks also check the window’s own reset time and discard any snapshot whose window has already rolled over. Without that, block mode could refuse tool calls over a limit that no longer exists, turning the plugin into the very outage it was built to prevent.

Installing It

A plugin can supply the logic, but Claude Code only reads one statusline command from your settings, so wiring that up is a separate step:

claude plugin marketplace add gokhanarkan/claude-usage-guard
claude plugin install usage-guard@claude-usage-guard

Then, inside Claude Code:

/usage-guard:setup

That last step points your statusline at the plugin’s capture script and sets the 60-second refresh timer. If you already have a statusline you like, it leaves it alone and tells you how to run both together. Restart Claude Code and the next update starts feeding the plugin.

Everything else is a handful of environment variables if the defaults don’t fit:

VariableDefaultMeaning
USAGE_GUARD_THRESHOLD98Percentage of the 5-hour window that triggers a pause
USAGE_GUARD_MODEwaitwait sleeps until reset; block stops and explains why
USAGE_GUARD_MAX_WAIT19800Safety cap on a single pause, in seconds
USAGE_GUARD_STALE_AFTER900How old the usage data can be before it’s ignored
USAGE_GUARD_REFRESH_INTERVAL60Seconds between timed statusline re-runs; re-run setup after changing it

Small, On Purpose

There’s no dashboard, no background service, nothing phoning home. It’s a couple of small scripts and a state file, which felt like the right amount of tooling for a problem this specific. The 7-day window is tracked and shown, but nothing pauses for it by default, on the theory that pausing a hands-off agent for days on end is rarely what anyone actually wants.

If you run Claude Code on a subscription and have had a long unattended job just stop with no warning, give it a try. It’s on GitHub , MIT licensed, and if you hit an edge case, open an issue .


URLs in this post: