Product case study · November 6, 2026

Build a Claude Code Status Line That Handles Missing Context

Create a small Claude Code status line, test zero and missing context values locally, and verify the display in a disposable project.

Reading time
7 min
Checked
Nov 6, 2026
A paper gauge and magnifying lens compare a blue filled arc with an empty circular evidence card on warm paper
Keep a measured zero distinct from a missing reading.
Bottom line

Test the display with synthetic inputs before trusting it during a coding session; unknown context usage should stay unknown.

An empty context reading should not look like an unused context window. This tutorial builds a small Claude Code status line with three explicit states: a reported percentage, an unknown reading and invalid input. You will test those states before connecting the script to a practice session.

Sources checked November 6, 2026. The Python renderer and synthetic fixture checks below were tested locally. We did not run an authenticated Claude conversation or verify the live footer. The session steps are a reader exercise, not a reported integration result.

You need Python 3, a Bash-compatible terminal on macOS or Linux, and an installed Claude Code CLI for the optional live step. If you need the initial setup, follow the first project-session lesson.

Decide what the display means

Claude Code passes session JSON to a status-line command through standard input and displays its standard output. The documented context_window.used_percentage can be null early in a session. Our renderer preserves that absence instead of substituting zero. Official status-line documentation.

The design is deliberately narrow: a label and a percentage. It does not calculate a bill, estimate remaining prompts or judge whether the agent’s work is correct. Anthropic treats context and account usage as separate concerns; consult its cost and usage guide for account-level controls. A context gauge alone cannot answer whether you have paid usage remaining.

These are our acceptance criteria, using invented inputs:

Supplied valueRequired displayWhy
42.5Context: 42.5% usedPreserve the provided reading.
0Context: 0% usedZero is a valid measurement.
Missing or nullContext: unknownNo measurement was supplied.
String, boolean or value outside 0–100Context: unknownReject unexpected types and ranges.
Broken JSONContext: invalid inputMake a parsing failure distinguishable.

These fixtures do not describe an actual Claude session or a measured model capacity.

Write the renderer in a fresh folder

Run the following in one Bash terminal. Keep that terminal open so the lab_root variable remains available:

lab_root=$(mktemp -d /tmp/claude-statusline-lab.XXXXXX)
mkdir -p "$lab_root/project/.claude" "$lab_root/evidence"
cd "$lab_root/project"
cat > .claude/context-status.py <<'PY'
import json
import math
import sys

try:
    data = json.load(sys.stdin)
except (ValueError, UnicodeError):
    print("Context: invalid input")
    sys.exit(0)

context = data.get("context_window") if isinstance(data, dict) else None
value = context.get("used_percentage") if isinstance(context, dict) else None
valid = (
    type(value) in (int, float)
    and 0 <= value <= 100
    and math.isfinite(value)
)
print(f"Context: {value:g}% used" if valid else "Context: unknown")
PY
printf 'Keep this lab folder: %s\n' "$lab_root"

The type check intentionally excludes booleans: Python otherwise treats True as a kind of integer. Checking the range before math.isfinite also prevents an enormous integer from being converted to a float. No input value becomes a shell command, path or terminal escape sequence. The script reads standard input and prints a fixed-format line; it does not read transcripts, write files or contact a service.

Returning exit code zero for malformed input is a display choice. Here the visible diagnostic carries the failure; the tests must inspect the output, not just the process exit code.

Exercise the failure cases without a model call

Run this from the same practice project:

python3 - <<'PY' | tee ../evidence/fixture-results.txt
import json
import subprocess
import sys

cases = [
    ({"context_window": {"used_percentage": 42.5}}, "Context: 42.5% used"),
    ({"context_window": {"used_percentage": 0}}, "Context: 0% used"),
    ({"context_window": {"used_percentage": 100}}, "Context: 100% used"),
    ({}, "Context: unknown"),
    ({"context_window": None}, "Context: unknown"),
    ({"context_window": {"used_percentage": None}}, "Context: unknown"),
    ({"context_window": {"used_percentage": "42"}}, "Context: unknown"),
    ({"context_window": {"used_percentage": True}}, "Context: unknown"),
    ({"context_window": {"used_percentage": -1}}, "Context: unknown"),
    ({"context_window": {"used_percentage": 101}}, "Context: unknown"),
    ([], "Context: unknown"),
]
for index, (payload, expected) in enumerate(cases, 1):
    result = subprocess.run(
        [sys.executable, ".claude/context-status.py"],
        input=json.dumps(payload), text=True, capture_output=True, check=True,
    )
    assert result.stdout.strip() == expected, (index, result.stdout)
    assert not result.stderr, (index, result.stderr)
result = subprocess.run(
    [sys.executable, ".claude/context-status.py"],
    input="{broken", text=True, capture_output=True, check=True,
)
assert result.stdout.strip() == "Context: invalid input"
assert not result.stderr
print("PASS: 12 synthetic renderer cases; live integration not tested")
PY

Look for the final PASS line in the evidence file. Because this command pipes output through tee, a successful pipeline exit alone is insufficient: a Python traceback or missing PASS means the checks did not complete. Fix the script before attaching it to a session.

Connect only the practice project

Claude Code distinguishes user settings from shared project and project-local settings. This exercise uses .claude/settings.local.json, keeping the choice within this new project. If adapting the exercise to an existing repository, merge only the new key into a backed-up file; do not replace existing permissions or hooks. See settings scope and precedence.

In the fresh practice folder, create the configuration with absolute, shell-quoted paths:

python3 - <<'PY'
import json
import pathlib
import shlex
import sys

script = pathlib.Path(".claude/context-status.py").resolve()
settings = pathlib.Path(".claude/settings.local.json")
command = shlex.join([sys.executable, str(script)])
with settings.open("x") as handle:
    json.dump({"statusLine": {"type": "command", "command": command}}, handle, indent=2)
    handle.write("\n")
print(settings.read_text())
PY
claude --version > ../evidence/cli-version.txt
claude

Exclusive creation with open("x") stops if that settings file already exists. Do not change it to an overwrite mode to bypass the error. Inspect the path and return to the disposable-folder setup instead.

The official configuration uses a statusLine object with type: command and a shell command. Status-line scripts execute locally; review the script before trusting its folder. The documentation also explains that the display can temporarily disappear during permission prompts or help. Status-line setup and troubleshooting.

In Claude, verify that you opened the practice directory. If you choose to make a model request, send a small prompt such as “Reply with one sentence explaining what a context window is. Do not use tools.” Normal account usage applies. Record the displayed line before and after the response, the CLI version and any errors in ../evidence/live-notes.txt using your editor after exiting. Do not report a live pass unless you actually observed the display.

A visible percentage establishes that this integration rendered a supplied value. It does not establish the model’s correctness, remaining subscription allowance or completion of a coding task.

Recover without changing the rest of your setup

SymptomNext check
Fixture output is wrongRecheck the copied Python and rerun the local cases.
No footer appearsInspect the configured absolute paths, folder trust and applicable setting overrides.
Context: unknown appearsThe renderer received no acceptable number; do not relabel it zero.
Context: invalid input appearsTest the configured command with a synthetic payload in the shell.
A fixture passes but the live footer failsRecord separate outcomes; local parsing does not verify CLI integration.

To undo this fresh lab’s setting, exit Claude and rename .claude/settings.local.json to .claude/settings.local.json.disabled. If you adapted an existing file, remove only the statusLine key you added or restore its previous value. Restart in the same folder to check the result. Preserve the script and evidence if you want to compare a later revision.

For a useful handoff, keep three separate records: fixture pass or failure, live display observed or not observed, and any account usage checked elsewhere. That separation is more informative than a single “working” label.

Official sources

Checked November 6, 2026:

Put this to work

Distinguish a local renderer test from a live session observation.

Try

Build a Python status-line script and connect it through project-local settings.

Prove it worked

Save fixture results and separately record whether the live footer appears.

Keep in view

  • Render context usage without turning missing data into zero.
  • Check normal, zero, null and malformed inputs before connecting Claude.
  • Try the display in one disposable project and retain a rollback path.
Learn the workflow: Set up a first Claude Code project session