DEV Community

Cover image for A Claude Code status line that warns you before you hit the limit
Mike Meshok
Mike Meshok

Posted on

A Claude Code status line that warns you before you hit the limit

The built-in footer in Claude Code doesn't tell you that the 5-hour limit is about to run out, or that 420K tokens in a 1M window already costs real money. So I wrote my own status line in plain Python with no dependencies: a context bar, remaining 5-hour and weekly limits, a "when will it run out" projection, session cost, git and uptime. Below: how it works, the decisions I made, and where it has holes.

[Opus 5.5] | ━━━━━━━━━━ 12% (120K/1000K) | H:91% 4h21m | W:73% | ⚙M+t | session $2.84 | my-app | ⏱ 1h 12m
 model       context bar                    5h left+reset  week    mode   cost           dir      uptime
Enter fullscreen mode Exit fullscreen mode

Repository: claude-statusline, MIT license.

The status line in three states: a fresh session, a heavier one, and one where the 5-hour limit is almost gone. Real output of statusline.py for sample session data.

How Claude Code draws a status line

In settings.json you can set a command in the statusLine field. Claude Code runs it whenever the session updates and passes it JSON on stdin: model, context window, limits, cost and so on. Whatever the command prints to stdout is shown at the bottom. That gives the script its main property: no network, no tokens spent, one short Python process per render.

You can test it without Claude Code, straight from the shell:

echo '{"model":{"display_name":"Opus"},"context_window":{"used_percentage":12,"context_window_size":1000000}}' | python3 statusline.py
Enter fullscreen mode Exit fullscreen mode

Three decisions that make it useful

1. Context color is based on absolute tokens

Typical indicators color the bar by percentage. On a million-token window that is misleading: 42% looks fine, but it is 420K tokens and every request is already expensive. So the bar's length shows the share of the window, while its color depends on the absolute load. The threshold is in the config: thresholds.context_red_tokens, 300,000 by default. From that value the bar is fully red, whatever the window size.

2. Limits are shown as what's left

H:91% reads as "91% of the 5-hour window remains", not "used". What I care about is runway, and having green at the start of the scale lines up naturally with "plenty left". The default yellow threshold is 50% remaining, red is 20%. W: is the same for the weekly window. These segments appear for Pro and Max subscribers after the first response and are hidden when there is no data.

3. Exhaustion projection

This is the only place where the script computes something instead of just displaying it. When the value changes (or more than 10 minutes have passed), it appends a sample to a small trend file: time, percent of the 5-hour window used, and the reset timestamp. The file is trimmed to the last 300 lines, and only samples from the current window count. Once there are at least 5 minutes of history and usage is growing, the script makes the simplest possible estimate: it takes the rate from the window's first sample to now and divides the remaining percent by it. The projection code is short, and this is the gist:

span, delta = t - first["t"], five_used - first["u"]
if span < burn_min_span_s or delta <= 0 or five_used >= 100:
    return None
return t + (100 - five_used) / (delta / span)
Enter fullscreen mode Exit fullscreen mode

The result is shown only if the limit will run out before the window resets; otherwise there's no point alarming you. In the red zone the line looks like H:7% 2h10m, with a flame icon and "14m" next to it: at this pace the window ends in 14 minutes. The projection is linear and crude. It's the average rate since the start of the window, so a sudden spike in usage gets smoothed out and shows up late. That is a trade-off: I wanted neither a network call nor storage more complex than one small file.

The rest

Segments: model context five_hour weekly mode tokens cost dir git mcps uptime. Order, set, colors, bar width, separator and labels are configurable in ~/.config/claude-statusline/config.json, all optional. There are presets full, minimal (model, context, five hours) and compact, with English and Russian labels.

Notes on some segments:

  • mode shows the effort level L/M/H/XH/MAX, a thinking flag +t and +fast mode.
  • cost is hidden while it's zero and turns red from a threshold you set (10 dollars by default).
  • tokens is off by default because it reads the session and sub-agent transcripts, counting each streamed message once. The result is cached incrementally, but the files are read on every render, which is why it isn't enabled out of the box.
  • git and mcps are cached: the branch for 5 seconds, the MCP server count for 60.

Example config:

{
  "segments": ["model", "context", "five_hour", "tokens", "cost", "dir"],
  "colors": { "ok": "cyan" },
  "bar": { "width": 20 }
}
Enter fullscreen mode Exit fullscreen mode

Colors can be names, #rrggbb or a number 0-255. The NO_COLOR variable is respected.

Installation, and what I tried not to break

git clone https://github.com/meshoklv/claude-statusline.git
cd claude-statusline && ./install.sh
Enter fullscreen mode Exit fullscreen mode

Options: --lang ru, --preset minimal, --uninstall. Editing someone's settings.json is a little scary, so the installer does three things: it backs the file up first; it won't replace a different, already configured status line without --force; and --uninstall puts the previous one back. If settings.json is invalid JSON, the installer changes nothing and says so.

The second rule is that the script must never take down the status line. A broken segment is skipped, missing data is not an error, and if the cache can't be written it just runs without it. The state directory is created with mode 0700 (~/.cache/claude-statusline by default, overridable with CLAUDE_STATUSLINE_STATE_DIR).

Tests use the standard unittest: 29 in test_statusline.py and 8 in test_install.py. CI on GitHub Actions runs them on Python 3.8 and 3.12 and also checks that the demo page docs/demo.html hasn't drifted from the code. The demo is generated by the script itself (tools/render_demo.py) and shows real colored output in four variants: full, minimal, Russian, and "almost out of limit".

What isn't verified

  • Tested on Linux with Python 3.8 and 3.12 in CI. It should work on macOS but I haven't checked; Windows is not supported.
  • The JSON format Claude Code passes to the status line isn't mine and may change. If a field disappears, the corresponding segment silently disappears.
  • The projection is simple and linear (see above), with no rigorous science behind it.
  • I haven't measured render time or memory use, so the repo has no such numbers.

Also: timestamps on every message in the VS Code chat

While on the subject of small missing things around Claude Code: in the official VS Code extension, the chat never shows when you sent what. After a week-long session you can't remember what you asked for in the morning. There is a separate project for that, claude-native-timestamps: a small badge with the time, in your time zone and format, appears above each message.

A warning is due here. There is no API for extending the chat, so the project works as a patch: it appends to the end of webview/index.js inside the Claude Code extension a map of "normalized message text to earliest time" (built from your local transcripts in ~/.claude/projects) and a small script with a MutationObserver that inserts the labels. The original is saved next to it as index.js.cc-ts-backup, a Remove command restores everything, and a Claude Code update brings a clean file by itself. After an update the patch is applied again. If the chat markup changes, the labels simply won't appear and nothing breaks. Messages are matched by text, so where there is no transcript there will be no label. There is one label per turn, meaning a question and its answer count as one block. The project is not affiliated with Anthropic; it was checked on Claude Code 2.1.x on remote Linux and not tested from a desktop on macOS or Windows.

Links

If the status line shows wrong values on your plan or Claude Code version, open an issue with the output of echo '<your JSON>' | python3 statusline.py. A star on GitHub helps others looking for the same thing.

Mike Meshok - github.com/meshoklv - meshoklv.github.io

Top comments (0)