The Claude Code status line is a row under the prompt that shows whatever a script of yours prints. Claude Code runs the script with session data as JSON on stdin (model, folder, context use, estimated cost, rate limits) and displays its output. The fastest setup is /statusline plus a description of what you want; for full control, set statusLine in ~/.claude/settings.json to your own script. It runs locally and uses no tokens. Details below are as of October 2026.
Set it up
The quick way
Describe what you want, and Claude Code writes the script and the setting for you:
/statusline show model, current folder, git branch and context percentage
Approve the file edits it asks for. To undo it later, run /statusline delete.
The manual way
Point statusLine at a command in ~/.claude/settings.json, or in a project's .claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 1,
"refreshInterval": 10
}
}commandruns in a shell, so it can be a script path or an inline command.paddingadds horizontal space, in characters.refreshIntervalreruns the command every N seconds (minimum 1), on top of the normal updates. Leave it out unless you show a clock or data that changes while Claude is idle.hideVimModeIndicator: truehides the built-in-- INSERT --text when your script showsvim.modeitself.
When it updates
The script runs when a session starts or resumes, then again when a new assistant message arrives, /compact finishes, the permission mode changes, vim mode toggles, a refreshInterval timer fires, or a rate-limit window reaches its reset time. Updates are debounced by 300ms, and a new update cancels a script that's still running, so a slow script means a stale or empty row. Output appears only when the script exits with code 0. The row hides while a permission prompt is open.
The data your script gets
The full schema has more, but these are the fields people show most:
| Field | What it holds |
|---|---|
model.display_name | Model name, such as Opus |
workspace.current_dir, workspace.project_dir | Current folder, and the folder Claude Code started in |
context_window.used_percentage | Share of the context window in use, counted from input tokens |
context_window.context_window_size | Window size in tokens |
cost.total_cost_usd | Estimated session cost at list price |
cost.total_duration_ms | Wall-clock time the session has run |
cost.total_lines_added, cost.total_lines_removed | Lines changed this session |
rate_limits.five_hour, rate_limits.seven_day | used_percentage and resets_at, for Pro and Max, after the first response |
session_id, session_name | Which session this is |
pr.number, pr.review_state | Open pull request for the current branch |
worktree.name, worktree.branch | Set while the session runs in a worktree |
effort.level, vim.mode, agent.name | Reasoning effort, vim mode, and the --agent name |
Some fields are missing or null early in a session, so always give jq a fallback (// 0 or // empty). Note that cost.total_cost_usd is computed on your machine at list prices; on a subscription it is not what you pay.
Scripts you can copy
The scripts use jq. Save one as ~/.claude/statusline.sh and run chmod +x ~/.claude/statusline.sh.
Model, folder, branch and a context bar
The percentage turns yellow at 50% and red at 80%:
#!/bin/bash
input=$(cat)
model=$(jq -r '.model.display_name' <<<"$input")
dir=$(jq -r '.workspace.current_dir' <<<"$input")
pct=$(jq -r '.context_window.used_percentage // 0' <<<"$input" | cut -d. -f1)
branch=$(git -C "$dir" branch --show-current 2>/dev/null)
filled=$((pct / 10))
printf -v fill '%*s' "$filled" ''
printf -v rest '%*s' $((10 - filled)) ''
bar="${fill// /▓}${rest// /░}"
if [ "$pct" -ge 80 ]; then c='\033[31m'; elif [ "$pct" -ge 50 ]; then c='\033[33m'; else c='\033[32m'; fi
printf '%b\n' "[$model] ${dir##*/}${branch:+ ($branch)} ${c}${bar} ${pct}%\033[0m"Cost, time and lines changed
#!/bin/bash input=$(cat) cost=$(jq -r '.cost.total_cost_usd // 0' <<<"$input") ms=$(jq -r '.cost.total_duration_ms // 0' <<<"$input") added=$(jq -r '.cost.total_lines_added // 0' <<<"$input") removed=$(jq -r '.cost.total_lines_removed // 0' <<<"$input") printf '$%.2f | %dm | +%s -%s\n' "$cost" $((ms / 60000)) "$added" "$removed"
The cost resets when /clear starts a new session. For limits across your whole plan, use /usage or the next script.
Usage limits and when they reset
#!/bin/bash input=$(cat) model=$(jq -r '.model.display_name' <<<"$input") five=$(jq -r '.rate_limits.five_hour.used_percentage // empty' <<<"$input") reset=$(jq -r '.rate_limits.five_hour.resets_at // empty' <<<"$input") week=$(jq -r '.rate_limits.seven_day.used_percentage // empty' <<<"$input") out="[$model]" [ -n "$five" ] && out="$out 5h $(printf '%.0f' "$five")%" [ -n "$reset" ] && out="$out until $(date -d "@$reset" +%H:%M 2>/dev/null || date -r "$reset" +%H:%M)" [ -n "$week" ] && out="$out | 7d $(printf '%.0f' "$week")%" echo "$out"
date -d is the Linux form and date -r the macOS one; the script tries both. The dashboard guide covers usage monitors that keep history and predict when you'll hit a limit.
Keep it fast in big repositories
The script runs often, and git status can be slow in a large repository. Cache slow results for a few seconds in a file named after session_id, which stays the same for a session but differs between sessions:
#!/bin/bash
input=$(cat)
dir=$(jq -r '.workspace.current_dir' <<<"$input")
cache="/tmp/statusline-git-$(jq -r '.session_id' <<<"$input")"
age=$(( $(date +%s) - $(stat -c %Y "$cache" 2>/dev/null || stat -f %m "$cache" 2>/dev/null || echo 0) ))
if [ "$age" -gt 5 ]; then
b=$(git -C "$dir" branch --show-current 2>/dev/null)
n=$(git -C "$dir" status --porcelain 2>/dev/null | wc -l | tr -d ' ')
printf '%s\n%s\n' "$b" "$n" > "$cache"
fi
{ read -r branch; read -r changed; } < "$cache"
echo "${branch:-no git} | ${changed:-0} changed"Don't use $$ for the file name: it changes on every run and defeats the cache.
Layout tips
- Each line your script prints becomes its own row, so two
echolines give a two-row status line. - ANSI colors work, and so do OSC 8 links in terminals that support them, such as iTerm2, Kitty and WezTerm.
tput colscan't see the terminal from inside the script. Read theCOLUMNSenvironment variable instead, which Claude Code sets before each run.- Test without Claude Code by piping sample JSON in:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/me/app"},"context_window":{"used_percentage":42},"session_id":"test"}' | ~/.claude/statusline.shReady-made status lines
If you'd rather configure than script, two community projects are linked from Anthropic's docs:
- ccstatusline opens a terminal configurator with widgets for model, git, tokens and usage, Powerline styling and themes. Start it with
npx -y ccstatusline@latest; it writes thestatusLinesetting for you. - starship-claude renders the status line with Starship, including a context progress bar. It installs as a Claude Code plugin or as a standalone script.
Status rows for subagents
A separate subagentStatusLine setting replaces the default row for each subagent in the agent panel. Its command receives every visible row at once (with fields such as name, status, label, model and tokenCount) and prints one JSON line per row it wants to change, in the form {"id": "...", "content": "..."}. The agent teams guide covers when subagents run in the background.
If it doesn't show
- The script must be executable, print to stdout and exit 0. Empty output leaves the row blank.
- Until you accept the workspace trust dialog for the folder, the status line doesn't run, because it executes a shell command just like a hook.
disableAllHooksalso turns off the status line, and an organization setting,allowManagedHooksOnly, allows only a status line from managed settings.- On Windows with Git Bash, write the script path with forward slashes.
- Start
claude --debugto log the script's stderr and its first exit code.
One session's status line, every session's status
A status line describes the one session in front of you. Once you run several, in different terminals, on a server or in Codex and Cursor too, you need the same at-a-glance view across all of them. Briefkin turns each session into a card that shows whether it is working, waiting on you or done, with a one-line status summary. Your own agent CLI writes that summary on your machine (Claude Haiku through claude -p, for example), so Briefkin never sees or pays for it. The docs show the one-command setup.
Questions
How do I add a status line to Claude Code?
Run /statusline followed by what you want to see, for example /statusline show model, git branch and context percentage. Claude Code writes a script in ~/.claude/ and adds a statusLine entry to your settings. You can also write the script yourself and point statusLine.command at it.
Does the Claude Code status line use tokens?
No. The script runs locally on data Claude Code already has, and it doesn't make API calls unless your own script does.
Why is my Claude Code status line blank?
Usually the script isn't executable, writes to stderr instead of stdout, exits with a non-zero code, or prints nothing. The status line also stays blank until you accept the workspace trust dialog for the folder, and when disableAllHooks is on. Run the script by hand with sample JSON, or start claude --debug to log its errors.
Can the status line show my 5-hour and weekly limits?
Yes, on claude.ai Pro and Max plans. Claude Code sends rate_limits.five_hour and rate_limits.seven_day, each with used_percentage and resets_at, after the first response of a session. With an API key the fields are absent, so read them with a fallback such as // empty in jq.
How do I remove the status line?
Run /statusline delete, or delete the statusLine entry from your settings file.