How to set up the Claude Code status line
The Claude Code status line is a customizable bar at the bottom of the interface that runs any shell script you configure. Claude Code pipes JSON session data into the script on stdin and shows whatever the script prints. The quickest setup is the /statusline command, where you describe what you want in plain English. Manual setup adds a statusLine entry to settings.json that points at your script.
What is the Claude Code status line?
The status line documentation describes a bar at the bottom of Claude Code that runs a shell script you configure. It gives you a persistent, at-a-glance view of context usage, cost, git status or anything else you can compute in a script. It is useful when you want to watch the context window while you work, track session cost, tell several sessions apart, or keep the git branch in view.
A few facts help set expectations. The status line renders in its own row above the built-in footer badges and does not replace them. With a custom status line configured, Claude Code stops showing most of the footer's keyboard hints, including esc to interrupt and the ? for shortcuts fallback. It runs locally and does not consume API tokens. It temporarily hides during certain UI interactions, including the help menu and permission prompts.
If you only want clickable link badges in the footer when an ID such as a ticket number appears in the conversation, the docs point to a different setting, footerLinksRegexes, which needs no script.
How do you set up a status line with /statusline?
The /statusline command accepts natural language instructions. Claude Code then generates a script file in ~/.claude/ and updates your settings automatically. Approve the file edit prompts if Claude Code asks for permission.
/statusline show model name and context percentage with a progress bar
Run /statusline without arguments to auto-configure from your shell prompt, according to the command reference. To remove the status line later, run /statusline delete, /statusline clear or /statusline remove it, or delete the statusLine field from settings.json by hand.
Use the command when you want a result in a minute. Use the manual route below when you want to understand and version the script, which matters once the script shows anything slow or personal to your setup.
How do you configure the status line manually?
Add a statusLine field to your user settings at ~/.claude/settings.json, or to project settings. Set type to "command" and point command at a script path or an inline shell command.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}| Field | Required | What it does |
|---|---|---|
type | Yes | Set to "command": run this shell command |
command | Yes | A script path or an inline shell command, run in a shell |
padding | No | Extra horizontal spacing in characters, default 0. It adds to the interface's built-in spacing, so it controls indentation, not distance from the terminal edge |
refreshInterval | No | Re-runs the command every N seconds in addition to event-driven updates. The minimum is 1. Use it for clocks or git state changed by background subagents |
hideVimModeIndicator | No | Set to true to hide the built-in -- INSERT -- text when your script renders vim.mode itself |
Claude Code reloads settings automatically and runs your script as soon as you save the file. Because the status line executes a shell command, Claude Code applies the same workspace trust rule as for hooks in settings files. Until you accept the trust dialog for the folder, the status line stays blank.
You can also skip the script file. The docs show an inline jq command that prints the model name and context percentage straight from the command field. That works for one-liners; anything with a branch or a condition is easier to read in a file.
What does a working status line script look like?
This one shows the model, the folder, the git branch, a ten-character context bar, the estimated session cost, minutes elapsed and, for subscribers, the rate-limit percentage for the week. It uses only fields listed in the docs, and every field has a fallback, because values can be null before the first API response. It needs jq, a command-line JSON parser you may have to install.
#!/bin/bash
# Claude Code sends session JSON on stdin; print one line to stdout.
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name // "Claude"')
DIR=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // "."')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
MINS=$(echo "$input" | jq -r '((.cost.total_duration_ms // 0) / 60000) | floor')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty' | cut -d. -f1)
# Git branch for the working directory; empty outside a repository
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
# Ten-character context bar
FILLED=$((PCT / 10))
[ "$FILLED" -gt 10 ] && FILLED=10
BAR=$(printf '%*s' "$FILLED" '' | tr ' ' '#')$(printf '%*s' "$((10 - FILLED))" '' | tr ' ' '-')
LINE="[$MODEL] ${DIR##*/}"
[ -n "$BRANCH" ] && LINE="$LINE ($BRANCH)"
LINE="$LINE | [$BAR] ${PCT}%"
LINE="$LINE | $(printf '%.2f' "$COST") USD | ${MINS}m"
[ -n "$WEEK" ] && LINE="$LINE | week ${WEEK}%"
echo "$LINE"Make it executable and register it:
chmod +x ~/.claude/statusline.sh
With sample input, the script prints a line like this:
[Opus] my-app (main) | [###-------] 37% | 1.23 USD | 12m | week 12%
Two cautions about the numbers. The cost field is an estimate: the docs say it is computed client-side at list price and may differ from your actual bill. And rate_limits is only present for claude.ai Pro, Max and Ultra subscribers, or behind a Claude apps gateway with spend limits, and only after the first API response. That is why the script reads it with // empty and leaves the segment out when it is absent. For what the limits mean, see the usage limits guide.
How do you test a status line script before using it?
Pipe fake JSON into it from your shell. The docs recommend this, since you see output and errors immediately instead of waiting for Claude Code to run it.
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"session_id":"test-session-abc"}' | ~/.claude/statusline.shTest a second time with an almost empty object, echo '{}' | ~/.claude/statusline.sh. If the script prints something sensible, it will survive the start of a session, before any API response has filled in the fields. Claude Code shows your script's output only after the script exits with code 0, so a non-zero exit or empty output makes the status line go blank.
What data can the status line show?
Claude Code sends JSON to your script on stdin. These are some of the fields documented as of October 2026; the docs page has the full list, including prompt cache and spend-limit fields.
| Field | What it holds |
|---|---|
model.id, model.display_name | Current model identifier and display name |
workspace.current_dir, workspace.project_dir | Current directory, and the directory Claude Code was launched in |
workspace.repo.owner, workspace.repo.name | Repository identity parsed from the origin remote; absent outside a repository |
context_window.used_percentage, remaining_percentage | Pre-calculated share of the context window used or remaining |
context_window.context_window_size | Maximum context window in tokens: 200000 by default, 1000000 for models with extended context |
cost.total_cost_usd, cost.total_duration_ms | Estimated session cost in USD and total wall-clock time; cost resets when /clear starts a new session |
cost.total_lines_added, cost.total_lines_removed | Lines of code changed |
rate_limits.five_hour.used_percentage, rate_limits.seven_day.used_percentage | Percentage of the five-hour or seven-day limit used, from 0 to 100. Subscribers only |
rate_limits.*.resets_at | Unix epoch seconds when the window resets |
effort.level, thinking.enabled, fast_mode | Reasoning effort, extended thinking and fast mode state for the session |
session_id, session_name, version | Session identifier, custom or generated session name, and the Claude Code version |
output_style.name, vim.mode | Current output style; vim mode when enabled |
Fields can be null early in a session, so give each a fallback, as // 0 does in jq. If the context percentage looks different from /context, the docs explain why: the status line reports counts from the last API response, while /context adds an estimate for messages added since. Use used_percentage for the simplest accurate reading. The context window guide covers what fills it.
When does the status line refresh?
Your script runs once when a session starts, including on resume. After that it runs again when:
- a new assistant message arrives
/compactfinishes- the permission mode changes
- vim mode toggles
- you change the
commandin yourstatusLinesettings - a
refreshIntervaltimer elapses, if you set one - a rate-limit window in the data your script last received reaches its reset time
Updates are debounced at 300 ms, so rapid changes batch into one run. If a new update triggers while your script is still running, Claude Code cancels the in-flight script. If you edit the script, the change appears the next time one of those triggers fires. The event triggers can go quiet when the main session is idle, for example while a coordinator waits on background subagents. Set refreshInterval if you show time-based or externally sourced segments.
Slow scripts block the status line from updating until they finish. The docs suggest caching slow operations such as git status, since the script runs often during active sessions, and keeping output short, since the bar has limited width.
Can the status line show colors, several lines or links?
- Multiple lines: each
echoorprintbecomes a separate row. - Colors: use ANSI escape codes such as
\033[32mfor green, in a terminal that supports them. - Links: OSC 8 escape sequences make text clickable (Cmd+click on macOS, Ctrl+click on Windows and Linux) in terminals that support hyperlinks, such as iTerm2, Kitty or WezTerm. The docs say Terminal.app does not support clickable links. If detection fails, set
FORCE_HYPERLINK=1before launchingclaude. - Terminal size: Claude Code captures your script's output, so
tput colscannot read the terminal. Read theCOLUMNSandLINESenvironment variables instead.
Complex escape sequences can occasionally garble output when they overlap with other UI updates, and multi-line scripts with escape codes are more prone to it than plain single-line text. If you see corrupted output, simplify to plain text first. For prebuilt themes, the docs mention community projects such as ccstatusline and starship-claude.
A status line is a small, buildable thing, which makes it a good first project for a Claude Code session. If you want ideas for tools in the same spirit, browse the developer tools category on the board.
Why is my Claude Code status line not showing?
Work down this list; the first four cause most blank status lines.
- The script is not executable. Run
chmod +x ~/.claude/statusline.sh. - The script writes to stderr, not stdout. Only stdout is displayed.
- Workspace trust is not accepted. The status line stays blank until you accept the trust dialog.
claude --debuglogsStatus line command skipped: workspace trust not accepted. Restart Claude Code and accept it. - The script exits non-zero or hangs. The output only shows after exit code 0. Run the script manually to see what happens.
- Hooks are disabled by policy. If
disableAllHooksis true outside managed settings, only a managedstatusLineruns. If your organization setsallowManagedHooksOnly, your custom status line disappears without warning. - Windows path. With Git Bash, backslashes in the
commandpath are likely consumed as escape characters. Use forward slashes.
Run claude --debug to log your script's stderr on every status line invocation and its exit code on the first. If values show -- or are empty, handle nulls with fallbacks and restart Claude Code if they remain empty after several messages. On narrow terminals, notifications that share the status line row can truncate your output.
The status line is a hooks-adjacent feature: it runs your own command with session data. For commands that run on events rather than draw a bar, see Claude Code hooks, and for the commands you can type, Claude Code commands.
Frequently asked questions
How do I set up the Claude Code status line?
Run /statusline followed by a plain-English description, such as /statusline show model name and context percentage with a progress bar. Claude Code writes a script into ~/.claude/ and updates settings for you. To do it by hand, add a statusLine entry with type command to ~/.claude/settings.json and point it at your script.
What can the Claude Code status line display?
Anything your script can compute from the JSON Claude Code sends: model name, directory, git branch, context window percentage, estimated cost, session duration, lines changed, effort level, vim mode, and for subscribers the rate-limit percentages for the five-hour window and the week. Scripts can use colors, several lines and clickable links.
Does the status line use tokens or cost money?
No. The documentation says the status line runs locally and does not consume API tokens. Your script runs on your machine, so keep it fast, because slow scripts delay updates. The cost figure it can show is a local estimate computed at list price and may differ from your bill.
Why is my Claude Code status line blank?
The usual causes are a script that is not executable, output sent to stderr instead of stdout, a non-zero exit code, or an untrusted workspace, since the status line stays blank until you accept the trust dialog. Run claude --debug to see the script's stderr and exit code.
How do I remove the Claude Code status line?
Run /statusline delete, /statusline clear or /statusline remove it. You can also delete the statusLine field from your settings.json by hand. Claude Code then returns to its default footer, including the usual keyboard hints that a custom status line suppresses.
How often does the status line update?
It runs when a session starts, when a new assistant message arrives, when /compact finishes, when the permission mode changes, and when vim mode toggles. Updates are debounced at 300 ms. Set refreshInterval in the statusLine settings to re-run on a fixed timer, with a minimum of 1 second.
Created with AI assistance and edited for this site. Examples are illustrative and do not represent customer research. About the publisher.