-- NORMAL -- ~/projects/claude-status-line.md project 03 / 06

claude-status-line

Your Claude Code subscription usage, measured on a single line, just below the prompt.

Stack
TypeScript · Bun
Year
2026
Repository
github.com/Popoch39/claude-status-line ↗
A

Anatomy

8 parts · only one counts
Fig. 1 Dial rail — 5-hour and weekly windows
2h143jv1.0.0
  1. Pick the source

    Takes the limits from Claude Code, failing that the API, failing that the cache.

    resolveRateLimits()

    214async function resolveRateLimits(

    215 fromStdin?: RateLimits,

    216): Promise<RateLimits | undefined> {

  2. Ask the API

    Queries the Anthropic API with your token, giving up after 2.5 s.

    readOAuthToken()

    157const CREDENTIALS_PATH = join(homedir(), ".claude", ".credentials.json");

    158const FETCH_TIMEOUT_MS = 2500; // borne la latence ajoutée au démarrage

    160function readOAuthToken(): string | undefined {

  3. 03~/.claudeL115–145 ↗

    Keep a copy

    Saves the latest limits to a fallback JSON file.

    readCache()

    120const CACHE_PATH = join(homedir(), ".claude", ".statusline-cache.json");

    122type RateLimits = NonNullable<StatusInput["rate_limits"]>;

    125function readCache(): RateLimits | undefined {

  4. 04render()L231–254 ↗

    Build the line

    Model · folder · 5h · sem, on a single terminal line.

    render()

    231async function render(): Promise<void> {

    232 const data = (await Bun.stdin.json().catch(() => ({}))) as StatusInput;

    234 const segments: string[] = [];

  5. Draw the bars

    Ten cells per window: at 68%, seven of them are filled.

    windowSegment()

    103function windowSegment(label: string, win?: Window): string {

    104 const pct = win?.used_percentage;

    105 if (pct == null) return `${C.dim}${label} —${C.reset}`;

  6. 06colorFor()L52–69 ↗

    Colour by usage

    Green below 50%, yellow up to 80%, red above that.

    colorFor()

    53function colorFor(pct: number): string {

    54 if (pct > 80) return C.red;

    55 if (pct >= 50) return C.yellow;

  7. 07fmtReset()L73–85 ↗

    Count down to the reset

    Time left before the limits reset: 3j, 2h14 or 14m.

    fmtReset()

    74function fmtReset(epochSec?: number): string {

    75 if (epochSec == null) return "";

    76 const delta = epochSec - Date.now() / 1000;

  8. 08refreshIntervalL280–285 ↗

    Run again every minute

    Claude Code reruns the binary every 60 seconds.

    settings.statusLine

    280settings.statusLine = {

    281 type: "command",

    282 command,

stdin · 5h 68% · reset 2h14 · sem 41% · 3j
Note view

Drag the ruler or the figure to move time forward; the triangles mark the eventsDrag the ruler or the figure with your finger; the triangles mark the events

Fig. 1 — Until Claude Code has passed its limits on stdin, the selector queries the API (one token, 2.5 s at most) or reads the cache back. Each percentage is then clamped to 0–100 and rounded to ten cells: a blade rises from 5, 15, …, 95%. The dial judges it under 50, from 50 to 80 or above, and the drums write the time left before the reset in days, hours and minutes, or minutes. The percentages are an example; the processing is the code’s own.

Parts list

  1. 01 Selector head Rotary plug with a lever and three detents, stdin, API and cache: it picks where the limits come from on every render. Code: statusline.ts, lines 211–227 ↗
  2. 02 API probe Spring plunger with a 2.5 s timer ring: it asks for a single token and comes back with the anthropic-ratelimit-unified-* headers. Code: statusline.ts, lines 157–209 ↗
  3. 03 Cache capsule Turned capsule screwed under the head: struck on every write, it shows its contents through a witness window and is only read back as a last resort. Code: statusline.ts, lines 115–145 ↗
  4. 04 Rail Section that carries the segments in order, model, folder, then the two windows, parted by three ‘·’ studs: it puts out a single line. Code: statusline.ts, lines 231–254 ↗
  5. 05 Ten-blade combs One module per window, 5h then sem: a comb of ten blades, one per cell of the bar. With no percentage, all ten blades stay down. Code: statusline.ts, lines 103–109 ↗
  6. 06 Two-needle dial Scale from 0 to 100% in three zones, under 50, 50 to 80 and above: it clamps, rounds and judges each percentage, long needle for 5h, short for the week. Code: statusline.ts, lines 52–69 ↗
  7. 07 Reset drums Counters set in the face of each module: the time left before the reset in days, hours and minutes, or minutes, ‘—’ with no percentage. Code: statusline.ts, lines 73–85 ↗
  8. 08 Time handwheel and Geneva drive One turn of the handwheel is sixty steps of the Geneva drive: one status line render per step, every 60 seconds of refreshInterval. Code: statusline.ts, lines 280–285 ↗