The Claude Code status line: what it is, how to set it up, and how Bumeter uses it

#claude-code#status-line#quota

There is one row at the bottom of Claude Code that you fully control. Whatever your command prints shows up there, all the time. It is also the only place Claude Code tells you how much of your 5-hour and weekly limit you have used. That is why Bumeter lives there.

What the status line is

The status line is a bar under the prompt in Claude Code. You give it a shell command. Claude Code runs that command, hands it a JSON description of the current session on stdin, and shows whatever the command prints to stdout.

It does not run all the time. Claude Code runs it when a session starts or resumes, and again when:

Updates are debounced at 300 ms, and if a new update comes in while your command is still running, Claude Code cancels the old run. In practice that means one rule: your command has to be fast.

What Claude Code sends it

The JSON on stdin describes the session. Here is a trimmed example with the fields this post uses:

{
  "model": { "display_name": "Opus" },
  "workspace": { "current_dir": "/home/me/project/bumeter" },
  "transcript_path": "/home/me/.claude/projects/…/session.jsonl",
  "context_window": { "used_percentage": 8, "context_window_size": 200000 },
  "rate_limits": {
    "five_hour": { "used_percentage": 23.5, "resets_at": 1738425600 },
    "seven_day": { "used_percentage": 41.2, "resets_at": 1738857600 }
  }
}

rate_limits is the interesting part. It is there only for claude.ai Pro and Max subscribers (or behind a gateway that sets a spend limit), only after the first API response of the session, and each window can be missing on its own. A good status line treats every field as optional.

How to set one up

The quick way: ask Claude Code to write it for you. It creates a script in ~/.claude/ and updates your settings:

/statusline show model name and context percentage with a progress bar

By hand: add a statusLine entry to ~/.claude/settings.json:

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 0
  }
}

Then write the script. This one shows the model, the folder, the context used and, when Claude Code sends it, the 5-hour limit:

#!/bin/bash
input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name')
dir=$(echo "$input" | jq -r '.workspace.current_dir')
ctx=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
five=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
echo "[$model] ${dir##*/} · ctx ${ctx}%${five:+ · 5h ${five%.*}%}"

Make it executable with chmod +x ~/.claude/statusline.sh. A few things worth knowing:

Why Bumeter needs it

Bumeter shows where your AI coding tokens go, from the logs Claude Code writes on your disk. Those logs say how many tokens each turn used. What they do not say is how close you are to your limit: no log line and no public endpoint gives the 5-hour or weekly percentage. The status line JSON is the one place it appears.

So Bumeter ships a status line command, bum statusline. On every refresh it:

  1. reads the JSON from stdin,
  2. writes rate_limits to a small file, limits.json, which the Bumeter app watches like any other log,
  3. and prints its own part of the line.

Each reading is kept, so the app can draw your usage over time and estimate when you will run out (a burn rate, not a guess from one number). The tray icon, notifications and the bum status command all read from there.

Fitting in next to your own status line

If you have no status line yet, Bumeter's command simply becomes it. Many people already have one they like, though, and replacing it would be rude. So Bumeter wraps the command you already have, in one of two ways:

# Show Bumeter's part after your own line
"bum" statusline --around '<your command>'

# Only record the numbers; your line stays exactly as it was
{ "bum" statusline --tee 2>/dev/null || cat; } | { <your command>; }

With --around, Bumeter runs your command with the same input, prints its output unchanged, then adds its own part at the end of the last line. With --tee, it records the numbers and passes the JSON straight through to your command, which does not know Bumeter is there.

Because this runs inside Claude Code's interface, a few rules are not negotiable:

Turning it on

In the Bumeter app, open Settings → Limit tracking and click Turn on limit tracking. Before anything is written, a dialog shows the exact statusLine.command before and after. The switch Show limits and context on the Claude Code status line chooses between the two forms above. Remove puts your settings back.

If you prefer to do it yourself, point your status line at Bumeter directly:

{ "statusLine": { "type": "command", "command": "bum statusline" } }

Bumeter's Settings recognises a hook you wrote by hand and rebuilds it with the full path to bum when you switch modes. If it cannot rebuild your hook without losing part of it, it says so and changes nothing.

What it looks like

Bumeter's part has up to three pieces: the 5-hour window with its reset time, the 7-day window, and the context the session carries right now.

Next to your own line, Bumeter leaves out a "5h —" placeholder when there are no numbers yet, so it adds nothing until it has something to say. The text follows the app's language setting, English or Vietnamese.

Bumeter is free and open source (GPL-3.0), and everything stays on your machine. The source is at github.com/chieund/bumeter. The status line code lives in crates/bum-cli/src/statusline.rs and crates/bum-adapters/src/claude_code/statusline.rs.

Download Bumeter