Claude Code renders a status line at the bottom of the terminal if you give it one: a shell script that receives the session state as JSON on stdin and prints whatever you want displayed. Mine shows the model, the directory, the git branch and the context percentage, and I glance at that last number far more than I expected to. Setup takes five minutes, less if you let Claude write the script itself. Commands below were run on Ubuntu 24.04 with the 2.1 CLI, though nothing here is OS-specific beyond the paths.
Quick setup with /statusline
The fastest route is the built-in slash command (one of many; the guide on how to use Claude Code covers the rest). Describe what you want in plain language:
/statusline show model name, git branch and context percentage with a progress bar
Claude generates a script under ~/.claude/, wires it into your settings and asks you to approve the file edits along the way. It works well and it's how I'd start; the manual route below is for when you want to know exactly what runs. Removal is the same command in reverse: /statusline remove it clears the configuration.
The statusLine setting in settings.json
What that command generates is a single key in ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}
type is always "command", command points at a script or an inline shell one-liner and the optional padding adds horizontal spacing. There's also refreshInterval, which re-runs the command every N seconds (minimum 1) for time-based displays like a clock; leave it unset otherwise, since the line already refreshes on session events. The full field reference lives in the status line documentation.
Example status line script with jq
The script has to parse JSON, so install jq first (sudo apt install jq on Ubuntu). Save this as ~/.claude/statusline.sh:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
BRANCH=$(git branch --show-current 2>/dev/null)
echo "[$MODEL] ${DIR##*/}${BRANCH:+ ($BRANCH)} | ${PCT}% context"
The // 0 fallback matters because used_percentage is null before the session's first API response. Mark the script executable, then test it with mock input before involving Claude Code at all:
chmod +x ~/.claude/statusline.sh
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/api"},"context_window":{"used_percentage":25}}' | ~/.claude/statusline.sh
Expected output is [Opus] api (main) | 25% context, branch depending on where you ran it. If that prints correctly, add the settings block from the previous section, start a session and the line appears after your first interaction.
Ready-to-paste variants
Minimal, no script file
The command field runs in a shell, so an inline jq call works with no file on disk:
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}
Cost and duration
The stdin payload carries a cost object with the estimated session spend and elapsed time, useful on API billing:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
printf '[%s] $%.2f | %dm\n' "$MODEL" "$COST" $((MS / 60000))
total_cost_usd is computed client-side and can drift from your bill, so treat it as a gauge rather than an invoice.
Rate limit tracking for subscriptions
Pro and Max sessions also receive rate_limits.five_hour.used_percentage and rate_limits.seven_day.used_percentage, the live numbers behind the windows I broke down in the article on Claude Code cost and usage limits. The object appears only after the first API response, so guard for absence:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
echo "[$MODEL]${FIVE_H:+ 5h: ${FIVE_H%.*}%}${WEEK:+ 7d: ${WEEK%.*}%}"
Other fields on stdin include session_id, version, workspace.project_dir and git repo identity; the doc's full JSON schema lists them all. Printing session_name is handy when several sessions run in parallel, and picking those sessions back up later is its own topic, covered in the guide to Claude Code sessions.
Troubleshooting a blank status line
When the line doesn't show up, it's nearly always one of these:
- The script isn't executable.
chmod +xit and check by running it manually. - Output went to stderr. Claude Code only renders stdout, and a script that exits non-zero or prints nothing produces a blank line.
- Workspace trust wasn't accepted. Because the status line executes a shell command, it stays blank until you accept the trust dialog for the folder.
- Fields were null early in the session. Fallbacks like
// 0in jq cover the window before the first API response.
For anything murkier, claude --debug logs the exit code and stderr of the first status line invocation, which pins down most script bugs in one look. Keep the script fast too: it runs on every assistant message (debounced at 300ms), so a git status in a huge repo adds visible lag, and the docs recommend caching slow calls to a temp file keyed by session_id. If you'd rather configure than script, community projects like ccstatusline ship themed, prebuilt status lines; I stayed with my ten-line bash file because there's less to break.

