Fully Custom Claude Code Status Line: A JSON-Driven 3-Line Readout of Quota, Cost, and Health A developer built a custom three-line status readout for Claude Code that displays plan quota, context usage, session cost, and automation health. The implementation reads JSON piped from stdin, eliminating the need for separate API calls, and caches the data to a file for fallback when stdin is empty. The status line helps the developer monitor consumption at a glance, preventing issues like running out of quota during overnight automated runs. This is part of my "Claude Code environment" series. Last time I wrote about the Codex worktree swarm https://zenn.dev/bokuwalily/articles/codex-worktree-swarm , a setup that gets Codex and Claude Code collaborating. This time the topic is something far less flashy but that you look at every single turn: customizing the status line . Claude Code can print a single status line at the bottom of the window. By default it shows little more than the model name. Using the feature that lets you point statusLine.command in settings.json at an arbitrary shell script, I built a version that formats your 5h/7d plan quota, context usage, session cost, and automation health into three lines β€” and I'll walk through the whole implementation. The nice part is that the quota numbers flow in directly from stdin , so no API calls and no auth are required. Once I started running autopilot.sh overnight, I kept hitting the same accident: "the 5h block quota is at zero by morning, so every job after that got SKIPPED." The issue was that checking cost or quota meant running ccusage every time . Because checking was a hassle, I stopped checking, and I noticed problems too late. The ideal state is "one glance at the screen tells me my current plan consumption." If I can just put that on Claude Code's status line, I don't even need to open a terminal. settings.json config: how JSON drives it Lines 269–274 of ~/.claude/settings.json are just this: "statusLine": { "type": "command", "command": "~/.claude/scripts/statusline.sh", "padding": 0 } With type: "command" , every turn Claude Code launches that command and pipes JSON into stdin . Standard output is displayed verbatim as the status line. So what's in that stdin? The actual fields are: | Path | Contents | |---|---| .rate limits.five hour.used percentage | 5h block usage rate 0–100 | .rate limits.five hour.resets at | Reset time UNIX epoch | .rate limits.seven day.used percentage | 7-day block usage rate | .rate limits.seven day.resets at | 7-day reset time UNIX epoch | .context window.used percentage | Context usage rate pre-computed | .cost.total cost usd | Current session cost USD | .model.display name | Model name | .effort.level | Effort level | Note: rate limits only appearsafter the first API responsefor subscription members. Before the first turn it's empty, so the script needs to fall back to -- . statusline.sh : reading stdin and falling back At the top of the script it reads stdin and, if it's valid JSON, caches it to /tmp/cc-statusline-last.json . RAW INPUT="$ cat 2 /dev/null || true " if printf '%s' "$RAW INPUT" | jq -e 'type == "object" and length 0' /dev/null 2 &1; then INPUT="$RAW INPUT" printf '%s' "$INPUT" /tmp/cc-statusline-last.json 2 /dev/null || true elif -s /tmp/cc-statusline-last.json ; then INPUT="$ cat /tmp/cc-statusline-last.json 2 /dev/null || echo '{}' " else INPUT='{}' fi Even when stdin is empty e.g. when another tool references it , it can read back from the cache. claude-limits-segment.sh is set up to receive this cache path via the CLAUDE STATUSLINE JSON environment variable: STATUS JSON="${CLAUDE STATUSLINE JSON:-/tmp/cc-statusline-last.json}" Hide the jq call behind a helper function: j { printf '%s' "$INPUT" | jq -r "$1" 2 /dev/null; } Fetching plan quota and context is just this: CTX=$ j '.context window.used percentage // empty' H5=$ j '.rate limits.five hour.used percentage // empty' H5R=$ j '.rate limits.five hour.resets at // empty' D7=$ j '.rate limits.seven day.used percentage // empty' D7R=$ j '.rate limits.seven day.resets at // empty' SESSION USD=$ j '.cost.total cost usd // 0' pcolor converts a number to an ANSI color: pcolor { local n="${1%%. }"; -z "$n" && { printf '%s' "$DIM"; return; } if "$n" -ge 80 2 /dev/null; then printf '%s' "$RED" elif "$n" -ge 50 2 /dev/null; then printf '%s' "$YEL" else printf '%s' "$GRN"; fi; } Apply this to all three: context, 5h, and 7d: CTXC=$ pcolor "$CTX" C5=$ pcolor "$H5" C7=$ pcolor "$D7" The spec in the script's comments maps directly to the final output: line1: πŸ“ dir βŒ₯ branch line2: πŸ€– modelΒ·effort 🧠 ctx% πŸ• 5h N% βͺreset πŸ“… 7d N% βͺreset line3: πŸ’΄ session β‰ˆΒ₯x 今ζ—₯ β‰ˆΒ₯y