The default Claude Code status line is blank. That is a wasted channel. It is the one strip of screen the agent updates for free on every turn, and most people never wire it. This post explains exactly how the mechanism works, then takes apart the real Claude Code statusline we ship so you can see what belongs there and what is noise.
What a Claude Code statusline actually is
A Claude Code statusline is a script Claude Code runs and prints under your prompt. It hands the script a JSON payload on stdin and renders whatever the script writes to stdout, verbatim, on the status line. The payload is a real object you can read field by field: `model` with the display name and id, `workspace` with `current_dir` and the project root, a `cost` block with the running dollar total and the elapsed and active-API durations, and the context-window numbers. You parse the JSON, pull the two or three fields you care about, and print one line. That is the whole contract. Any language works, because the interface is just stdin in, one line of text out, so a five-line shell script and a 74-line Node navigator are the same kind of thing to Claude Code.
Because the payload carries live session state, the status line is the cheapest way to keep that state on screen. Without it you interrupt the agent to ask "which model is this" or "how much has this cost." With it, the answer is already sitting under your prompt, updated on the same events the session already fires.
How Claude Code invokes the script
Claude Code spawns the status line process on event-driven updates: after each assistant message, on /compact, and on permission-mode changes, debounced at 300ms. It does not poll on a timer unless you set refreshInterval. The script's stdout is a pipe, not a tty, so it must decide color from the environment rather than from isTTY.
Those two facts drive real decisions in our script. We deliberately leave refreshInterval unset, because everything the line shows changes on exactly the events Claude Code already refreshes on. A timer would just spawn a Node process to redraw an identical line. Here is the wiring, written into settings.json at install:
That absolute-path detail is not fussiness. We wire every engine script with an explicit Node path because a bare node command dies silently when the shell Claude Code spawns has a different PATH, and a status line that fails silently is worse than none. If you are still getting set up, our install guide covers the environment this depends on.
Reading our real Claude Code statusline
Our statusline is 74 lines and its job is to be the navigator: always on screen, never in the way. It reads the JSON on stdin, pulls the current directory, loads the kit's config and the project journey, and renders one line. The spine looks like this:
The design choices are visible in the code. When there is no plan, it prints ready · /start to plan: a status line that also tells a beginner the next move. When a plan exists it shows the project name, a progress bar, step N of M, and the current step title truncated to 24 characters. When every step is done it says all N steps done · time to /ship. The line always points at the next action, never just at the state.
Two more things it appends only when true. If the clarity engine is armed it adds · asking first in coral, so you can see at a glance that the agent will ask before it builds. If the tutor is off it dims in · tutor off. Nothing static ever renders. That is the rule for a status line: every character has to earn its place, because it costs attention on every single turn.

Fail open, always
A status line must never break the terminal. Because Claude Code prints your stdout verbatim, a script that crashes does not just show nothing, it can smear a Node stack trace across the one strip of screen you read on every turn. So the whole render is defensive. The entire body sits inside a single try, the catch writes nothing and exits zero, and every read that can miss (a config that is off, a directory with no saved journey, a malformed payload) resolves to an empty string instead of throwing. The worst case is a blank line. That is the trade a status line has to make: silence on failure is fine, a broken terminal is not.
The statusline is one part of the engine
The status line is the visible tip of a larger system. The kit wires eight hook scripts across eight lifecycle events: session start, prompt submit, pre-compact, subagent start, pre-tool-use, post-tool-use, stop, and session end. The clarity lock that forces the agent to ask first, the slop check that runs after edits, the tutor that fires at the right moment: those live in the hooks. The status line is where their state becomes something you can see.
That integration is why the line is worth the trouble. asking first is not decoration. It is the clarity hook reporting that it is armed. The progress bar is the journey the session-start hook loaded. The status line is a read-only window onto the engine, which is the honest use of the surface: show state, do not invent it.
What it costs, measured
We ran a clean-room benchmark of the kit against vanilla Claude Code, and we do not hide the losses. On asking-first the kit went 5 of 5 versus vanilla's 2 of 5, and produced about 40 percent less code for the same outcome. But the hooks that feed the status line are not free. On a simple todo app the kit was about twice as slow and about 75 percent pricier for near-identical output, and across the suite the hook context tokens added roughly a 25 percent average cost premium. That premium is real and disclosed. It buys the guardrails and the navigator you see on the line.
The takeaway is not that instrumentation is expensive. It is that instrumentation you never look at is pure cost. A status line is the one piece that pays for itself on every turn, because it turns state you would have interrupted to ask for into something already on screen. Wire it. Show the model, the directory, the cost, the step. Leave everything else off.
Common questions
What is a Claude Code statusline? It is a script you register in settings.json. Claude Code runs it, passes a JSON payload on stdin with the model, workspace, cost, and context window, and prints whatever the script writes to stdout on the line under your prompt. It refreshes on events like each assistant message.
How do I set up a Claude Code statusline? Add a statusLine key to settings.json with type command and command pointing at your script, then read the JSON on stdin and write a single line to stdout. Use an explicit absolute node path so the script does not die silently when Claude Code's shell has a different PATH.
What should a Claude Code statusline show? State you would otherwise have to ask for and that changes as you work: the model, the working directory, cost so far, and the plan or step you are on. Skip anything static or anything you can already see in the prompt. A status line is expensive attention, so spend it on signal.
Why is my Claude Code statusline not showing or not updating? The line renders your script's stdout on session events, so a blank or stale line is almost always the script failing silently rather than Claude Code. The usual cause is a bad node PATH: a bare node command dies when the shell Claude Code spawns cannot resolve it. Put the absolute node path in your settings.json statusLine.command, run the script by hand with a sample JSON on stdin to confirm it prints one line, and remember that a fail-open script swallows its own errors, so an empty line means it threw.
Are there good Claude Code statusline examples? A good example is minimal and made of state that changes: model plus current directory plus cost so far plus the step you are on, one line, nothing static. Our navigator line is a worked example: it prints a glyph, the project name, a progress bar, step N of M, and the current step title, and appends asking first only when the clarity engine is armed. Copy the shape, not every field, and drop anything you can already read in the prompt.
How do I change or customize my Claude Code statusline? Edit the script that your settings.json statusLine.command points at. The contract is just stdin JSON in, one line of stdout out, so it is plain programming in any language: read the payload, pick the fields you care about, format your line, print it. Add ANSI color from the environment rather than isTTY because the stdout is a pipe, and wrap the whole render in a try that returns an empty string so a bug never breaks your terminal.
The status line, wired for you
kitstarter ships the navigator status line, the clarity lock, and eight lifecycle hooks, for Claude Code, Codex, and Antigravity.
