Skip to content
,

Claude Code Status Line: Setup Guide, JSON Fields and 8 Examples

10โ€“16 minutes
Shocked woman in a server room holding a card with the Claude logo, text see your limits, Claude Code

The bottom row of Claude Code can show far more than a blinking cursor. With a custom status line you can see your model, context use, session cost, git branch and even how much of your 5-hour limit is left, all updated live.

This guide shows the one-line setup with /statusline, the manual settings.json method, every JSON field you can print, eight copy-paste examples, Windows setup and the fixes for a blank status line.

TL;DR

  • Fastest way: type /statusline show model name and context percentage with a progress bar inside Claude Code. It writes the script and updates your settings.
  • Manual way: add a statusLine block with "type": "command" and a command path to ~/.claude/settings.json.
  • How it works: Claude Code pipes session JSON to your script on stdin and prints whatever your script writes to stdout.
  • Cost: free. It runs locally and uses no API tokens.
  • Most useful fields: context_window.used_percentage, cost.total_cost_usd, rate_limits.five_hour.used_percentage, model.display_name.
  • Blank line? Make the script executable, trust the folder, exit with code 0, and use forward slashes on Windows.

What the Claude Code Status Line Does

The status line is a row at the bottom of the Claude Code terminal UI that runs any shell command you choose. It sits above the built-in footer badges and does not replace them.

Your script gets a JSON object describing the session and prints text. That text can be one line or several, with colours and clickable links.

People use it to:

  • Watch context: see how full the context window is before Claude auto-compacts.
  • Track spend: show the estimated session cost in dollars.
  • Guard plan limits: show how much of the 5-hour and 7-day subscription windows you have used.
  • Tell sessions apart: print the repo, branch, worktree or session name when you run several at once.

Note: once you set a custom status line, Claude Code hides most footer key hints, such as esc to interrupt and ? for shortcuts. The shortcuts still work. My Claude Code commands cheat sheet lists them all.

Quick Setup With /statusline

The /statusline slash command takes plain English. Claude Code writes a script into ~/.claude/ and adds it to your settings for you.

  1. Start Claude Code in any folder with claude. Not installed yet? Follow my install guide for Windows, Mac and Linux.
  2. Describe what you want, for example: /statusline show model, folder, git branch and context percentage with a coloured bar.
  3. Approve the file edits when Claude asks for permission to write the script and change settings.json.
  4. Send any message. The line appears straight away and refreshes after each reply.

To remove it later, run /statusline delete (or “clear” or “remove it”). You can also delete the statusLine block from your settings by hand.

Tip: be specific in the prompt. “Show 5h and 7d usage as percentages, cost with 2 decimals, and turn the context bar red above 90%” gives a much better first script than “make a nice status line”.

Manual Setup in settings.json

Prefer to write it yourself? It takes three steps on macOS or Linux. The Bash examples use jq, so install it first (brew install jq or sudo apt install jq).

1. Create the script

Save this as ~/.claude/statusline.sh:

#!/bin/bash
# Claude Code sends session JSON on stdin
input=$(cat)

MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
# "// 0" is a fallback when the field is null
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)

echo "[$MODEL] ๐Ÿ“ ${DIR##*/} | ${PCT}% context"

2. Make it executable

chmod +x ~/.claude/statusline.sh

3. Point your settings at it

Open ~/.claude/settings.json (user level) or a project’s .claude/settings.json and add:

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 2
  }
}

Claude Code reloads settings on save, so the line shows up without a restart. You will see something like [Opus] ๐Ÿ“ my-app | 8% context.

Inline command, no script file

The command runs in a shell, so a short jq one-liner works too:

{
  "statusLine": {
    "type": "command",
    "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
  }
}

All statusLine Options

Key Value What it does
type "command" Required. Means “run this shell command”.
command Script path or inline shell Required. What runs on each refresh.
padding Number, default 0 Extra horizontal spacing in characters, added to the UI’s own spacing.
refreshInterval Seconds, minimum 1 Also re-runs the script on a timer. Use it for clocks or git changes while the session is idle.
hideVimModeIndicator true / false Hides the built-in -- INSERT -- text when your script prints vim.mode itself.

The user file (~/.claude/settings.json) applies everywhere. A project file applies only in that repo and wins over the user file there.

Every JSON Field You Can Show

These are the fields Claude Code sends on stdin, from the official docs. Some only appear in certain situations, so always use a fallback.

Field What it holds
model.id, model.display_name Model ID (for example claude-opus-5-5) and short name (Opus)
workspace.current_dir (same as cwd) Current working folder
workspace.project_dir Folder where you launched Claude Code
workspace.added_dirs Folders added with /add-dir
workspace.git_worktree Linked worktree name (absent in the main tree)
workspace.repo.host / owner / name Repo parsed from the origin remote
cost.total_cost_usd Estimated session cost at list price (not your bill)
cost.total_duration_ms, cost.total_api_duration_ms Session wall time and time spent waiting on the API
cost.total_lines_added, cost.total_lines_removed Lines changed this session
context_window.used_percentage, remaining_percentage Context fill, pre-calculated
context_window.context_window_size 200,000 by default, 1,000,000 for extended-context models
context_window.current_usage Input, output, cache write and cache read tokens of the last call
exceeds_200k_tokens true when the last response passed 200k tokens
rate_limits.five_hour.used_percentage, .resets_at 5-hour subscription window use (0-100) and reset time (epoch seconds)
rate_limits.seven_day.used_percentage, .resets_at Weekly window use and reset time
prompt_cache.warm, prompt_cache.hit_ratio Whether the cache is warm, and cache reads as a share of input (0-1)
effort.level low, medium, high, xhigh or max
thinking.enabled, fast_mode Extended thinking and fast mode on or off
session_id, session_name Unique ID and the /rename or AI-generated title
pr.number, pr.url, pr.review_state Open PR (or GitLab MR) for the branch
worktree.name, .branch, .path Active worktree session details
vim.mode, agent.name, output_style.name, version Vim mode, --agent name, output style, Claude Code version
Quick answer: rate_limits only appears for claude.ai Pro and Max subscribers (or behind a gateway with spend limits), and only after the first reply. On an API key you will not see it. See Claude Code pricing for how the windows work.

The used_percentage counts input tokens only (fresh input plus cache writes plus cache reads), not output. That is why it can read lower than /context, which also estimates messages added since the last reply.

When the Status Line Refreshes

Your script runs when a session starts or resumes, then again when any of these happen:

  • A new assistant message arrives
  • /compact finishes
  • The permission mode changes or vim mode toggles
  • You edit the command in settings
  • A refreshInterval timer fires, if set
  • A rate-limit window or warm prompt cache reaches its reset or expiry time

Updates are debounced at 300 ms. If a new update fires while your script is still running, Claude Code cancels the old run, so slow scripts show stale data.

Tip: tput cols does not work inside the script because Claude Code captures the output. Read the COLUMNS and LINES environment variables instead.

8 Ready-to-Use Status Line Examples

Save each one as ~/.claude/statusline.sh, run chmod +x, and point command at it. Test any script with fake input first:

echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/me/app"},"context_window":{"used_percentage":25},"session_id":"test"}' | ~/.claude/statusline.sh

1. Context progress bar

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
printf -v F "%${FILLED}s"; printf -v E "%${EMPTY}s"
echo "[$MODEL] ${F// /โ–“}${E// /โ–‘} $PCT%"

Output: [Opus] โ–“โ–“โ–“โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 30%.

2. Colour-coded context (green, yellow, red)

#!/bin/bash
input=$(cat)
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
if [ "$PCT" -ge 90 ]; then C='\033[31m'
elif [ "$PCT" -ge 70 ]; then C='\033[33m'
else C='\033[32m'; fi
echo -e "${C}ctx ${PCT}%\033[0m"

3. Git branch with staged and modified counts

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
if git rev-parse --git-dir > /dev/null 2>&1; then
  BRANCH=$(git branch --show-current 2>/dev/null)
  STAGED=$(git diff --cached --numstat | wc -l | tr -d ' ')
  MOD=$(git diff --numstat | wc -l | tr -d ' ')
  echo -e "[$MODEL] ๐Ÿ“ ${DIR##*/} | ๐ŸŒฟ $BRANCH \033[32m+$STAGED\033[0m \033[33m~$MOD\033[0m"
else
  echo "[$MODEL] ๐Ÿ“ ${DIR##*/}"
fi

4. Session cost and duration

#!/bin/bash
input=$(cat)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
printf '๐Ÿ’ฐ $%.2f | โฑ๏ธ %dm %ds\n' "$COST" $((MS/60000)) $(((MS%60000)/1000))

Note: on a Pro or Max plan this dollar figure is what the session would cost on the API. It is handy for comparing, but you are not billed it. It resets to $0 after /clear.

5. 5-hour and weekly plan usage

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
FIVE=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
OUT="[$MODEL]"
[ -n "$FIVE" ] && OUT="$OUT 5h: $(printf '%.0f' "$FIVE")%"
[ -n "$WEEK" ] && OUT="$OUT 7d: $(printf '%.0f' "$WEEK")%"
echo "$OUT"

6. Two-line status line

Every echo becomes its own row. Line one shows where you are, line two shows the numbers:

#!/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)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
BR=$(git branch --show-current 2>/dev/null)
echo "[$MODEL] ๐Ÿ“ ${DIR##*/} ${BR:+| ๐ŸŒฟ $BR}"
printf 'ctx %s%% | $%.2f\n' "$PCT" "$COST"

7. Python version (no jq needed)

#!/usr/bin/env python3
import json, sys
d = json.load(sys.stdin)
model = d["model"]["display_name"]
pct = int((d.get("context_window") or {}).get("used_percentage") or 0)
cache = (d.get("prompt_cache") or {}).get("hit_ratio")
eff = (d.get("effort") or {}).get("level", "")
extra = f" | cache {cache:.0%}" if cache is not None else ""
print(f"[{model} {eff}] ctx {pct}%{extra}")

Save as ~/.claude/statusline.py and set "command": "~/.claude/statusline.py" after chmod +x.

8. Cached git info for big repos

Running git status on every refresh can lag in a large repo. Cache it for 5 seconds, keyed on session_id (not $$, which changes every run):

#!/bin/bash
input=$(cat)
SID=$(echo "$input" | jq -r '.session_id')
CACHE="/tmp/sl-git-$SID"
AGE=$(( $(date +%s) - $(stat -c %Y "$CACHE" 2>/dev/null || stat -f %m "$CACHE" 2>/dev/null || echo 0) ))
if [ ! -f "$CACHE" ] || [ $AGE -gt 5 ]; then
  git branch --show-current 2>/dev/null > "$CACHE"
fi
echo "๐ŸŒฟ $(cat "$CACHE")"

Windows Setup (PowerShell and Git Bash)

On Windows, Claude Code runs the status line through Git Bash if it is installed, otherwise through PowerShell.

Warning: always write paths in command with forward slashes, like C:/Users/you/.claude/statusline.ps1. Git Bash eats backslashes, and the line fails silently.

PowerShell status line

settings.json:

{
  "statusLine": {
    "type": "command",
    "command": "powershell -NoProfile -File C:/Users/you/.claude/statusline.ps1"
  }
}

C:/Users/you/.claude/statusline.ps1:

$data = $input | Out-String | ConvertFrom-Json
$dir = Split-Path $data.cwd -Leaf
$model = $data.model.display_name
$used = $data.context_window.used_percentage
if ($used) { Write-Host "$dir [$model] ctx: $used%" }
else { Write-Host "$dir [$model]" }

With Git Bash installed you can also use the Bash scripts above with "command": "~/.claude/statusline.sh". Install jq with winget install jqlang.jq, or use the Python version.

Pre-Built Status Lines: ccstatusline and starship-claude

If you would rather not write scripts, the official docs point to two community projects:

Option Best for Effort
/statusline prompt A custom line in one minute Lowest
ccstatusline Themes and a menu-driven widget picker Low
starship-claude People who already use the Starship prompt Medium
Your own script Full control, any language Medium

Community tools change fast, so read their README for the current install command. Anything they add still comes from the same JSON fields listed above.

Subagent Status Lines

A second setting, subagentStatusLine, lets you restyle each row in the agent panel that lists running subagents.

{
  "subagentStatusLine": {
    "type": "command",
    "command": "~/.claude/subagent-statusline.sh"
  }
}

Your script receives a tasks array (with id, name, status, tokenCount, model and more) and writes one JSON line per row, like {"id": "<task id>", "content": "<row text>"}. Leave a task out to keep its default row.

Troubleshooting

Status line not appearing at all

  • Not executable: run chmod +x ~/.claude/statusline.sh.
  • Wrong stream: print to stdout, not stderr.
  • Non-zero exit: the line only shows after the script exits with code 0. Run it by hand with the mock input above.
  • Windows path: swap backslashes for forward slashes in command.
  • Hooks disabled: disableAllHooks: true also disables custom status lines. Company-managed allowManagedHooksOnly does the same.

“Status line command skipped: workspace trust not accepted”

You see this in claude --debug output. The status line runs a shell command, so it waits until you accept the folder trust dialog. Restart Claude Code in that folder and accept it.

Status line shows --, null or empty values

Fields such as used_percentage and current_usage are null before the first reply and right after /compact. Add fallbacks like // 0 in jq or or 0 in Python.

“jq: command not found”

Install jq (brew install jq, sudo apt install jq, winget install jqlang.jq) or switch to the Python or Node example.

OSC 8 links need iTerm2, Kitty, WezTerm or a similar terminal; macOS Terminal.app does not support them. Try FORCE_HYPERLINK=1 claude, and use printf '%b' instead of echo -e if you see raw \e]8;; text.

Status line is slow or out of date

Slow commands block the update and get cancelled by the next one. Cache git calls (example 8) and keep the script well under a second.

Still stuck? Run claude --debug. It logs your script’s stderr on every run and the exit code on the first run.

My Take

If you only add one thing, make it a colour-coded context bar plus the 5-hour usage figure. Those two numbers tell you when to /compact and when you are about to hit your plan limit, which saves more frustration than any other tweak.

Start with /statusline to get a working script, then edit it by hand. If you watch costs closely, pair it with my guide to prompt caching and show prompt_cache.hit_ratio on the line.

FAQ

How do I add a status line in Claude Code?

Run /statusline followed by what you want to see, or add a statusLine block with "type": "command" and a script path to ~/.claude/settings.json.

Does the status line use tokens or cost money?

No. The script runs locally on your machine and does not call the API.

How do I show the 5-hour usage limit in the status line?

Print rate_limits.five_hour.used_percentage and rate_limits.seven_day.used_percentage. They appear only for Pro and Max subscribers after the first reply in a session.

How do I remove or reset the status line?

Run /statusline delete, or remove the statusLine field from your settings.json file.

Can the status line have more than one line?

Yes. Each line your script prints becomes its own row, and ANSI colours and OSC 8 links are supported.

Why is my status line blank on Windows?

Usually a backslash path in command. Use forward slashes, and call PowerShell scripts with powershell -NoProfile -File.

Can I set a different status line per project?

Yes. Put the statusLine block in the project’s .claude/settings.json. It overrides your user setting inside that repo.

Sources

Free weekly email

Get new AI guides every week

One short weekly email with our newest local AI, ComfyUI and AI tool guides. No spam, no pop-ups, just the good stuff.

Free. Unsubscribe anytime in one click. We only use your email to send this newsletter. See our privacy policy.

Shocked woman in a server room holding a card with the Claude logo, text see your limits, Claude Code