Skip to content

Status line

statusline prints one short line for status bars:

Terminal window
llm-usage statusline
$270.20 today · 6d streak · ~$1,215.82 this month

The line shows today’s cost, the current streak (left out at zero), and month-to-date cost. A ~ marks a cost that leaves out unpriced events; a day without any known cost shows its token count instead. With monthlyBudgetUsd in the config file, the month reads ~$1,215.82/$2,000.00 this month, followed by ⚠ once spending or the month-end projection passes the budget.

It is built for frequent refreshes:

  • pricing is never fetched: it uses cached LiteLLM prices, or the bundled snapshot when there is no cache. The cache only refreshes when another report runs online, so if you use only the status line, a model released after your installed version stays unpriced until you run a report such as llm-usage daily. With a custom pricing.url in the config, run any report online once so that URL’s prices are cached; until then statusline exits with an error
  • the update check is skipped, and nothing is printed on stderr; errors still exit non-zero. Add --verbose to see the warnings it hides, and run llm-usage doctor if refreshes feel slow (an unusable event store means every run reparses all files). One exception: the first run that finds no usage logs at all prints a one-time hint on stderr
  • parsed events come from the event store, so repeat runs only reparse changed files
  • runs that overlap share a lock file next to the event store (events.db.statusline.lock beside the default events.db). A run that finds the lock held waits up to a second, then prints warming up… instead of parsing the same files again. On a first run over large logs, the tick holding the lock parses and the others print warming up… until it finishes. A lock left by a run that exited, or older than two minutes, is taken over by one of the runs that find it; the others wait as for a held lock

It accepts the source, provider, model, machine, timezone, cost-mode, pricing-override, and history flags of the other reports, so a status line can show one tool only:

Terminal window
llm-usage statusline --source claude

A filtered line shows no budget, since the budget covers all your usage.

The line uses cached or bundled prices and other machines’ cached usage, and prints nothing on stderr unless you pass --verbose. So statusline has no --json, --quiet, --pricing-url, --pricing-offline, or --no-sync. For structured data, use llm-usage summary --json.

Add a statusLine command to ~/.claude/settings.json:

{
"statusLine": {
"type": "command",
"command": "llm-usage statusline"
}
}

Claude Code passes the session’s details on stdin, and the line then starts with the session’s cost and how full its context window is:

$1.23 session · 42% context · $270.20 today · 6d streak · ~$1,215.82 this month

Both numbers come from Claude Code as it sends them: the session cost is Claude Code’s own estimate at list price and resets with /clear, so it can differ slightly from the same usage in llm-usage reports. Either part is left out until Claude Code has a value for it, such as before the first reply. Other status bars pass nothing on stdin. If a bar leaves stdin open, each run waits a quarter second for input that never comes. The wait overlaps building the line, but a warm run that would take 0.1 s takes 0.3 s. Add </dev/null to the command for such a bar:

Terminal window
llm-usage statusline </dev/null
Terminal window
set -g status-interval 60
set -g status-right '#(llm-usage statusline)'
[custom.llm_usage]
command = "llm-usage statusline"
when = true
shell = ["sh"]

starship runs custom commands on every prompt, and a warm run takes a few hundred milliseconds, so raise command_timeout if the module disappears, or prefer tmux or the Claude Code status line for a line that refreshes on its own.