Claude Code statusline: setup and examples

Updated 24 October 2026

Claude Code’s status line (the statusline, as most people type it) is a row at the bottom of the interface that shows whatever a script of yours prints. Claude Code runs the script with a JSON description of the session on stdin: model, folder, cost, context usage, rate limits and more. You can have Claude write the script for you with /statusline, or write it yourself. Below are both routes, every field the script receives, when it runs, five scripts to copy, and fixes for a blank status line.

Quick answer

You want toDo this
Get one fast/statusline show model, folder and context %
Configure it yourselfstatusLine in ~/.claude/settings.json, see below
Show model and folderExample 1
Show the git branchExample 2
Show context and cost, in colorExample 3
Show rate limits, session name, worktreeExample 4
Skip installing jqExample 5 (Python)
Remove it/statusline delete, or delete the statusLine key

1. Let Claude write it: /statusline

Type /statusline followed by what you want to see:

Claude Code/statusline show model name, git branch and context percentage with a progress bar

Claude Code writes a script into ~/.claude/ and adds the statusLine setting for you. Approve the file edits when it asks. To change it later, run /statusline again with new instructions. To remove it, run /statusline delete (clear or remove it work too).

This is the quickest route, and the result is an ordinary script you can open and edit. The rest of this page is for when you want to know what it did or write your own.

2. Configure it by hand

Add a statusLine object to your user settings, ~/.claude/settings.json, to use it in every project, or to a project’s .claude/settings.json for that project only:

~/.claude/settings.json{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 0
  }
}
FieldWhat it does
typeAlways "command".
commandA script path or an inline shell command. It runs in a shell, so pipes and ~ work.
paddingExtra horizontal spacing in characters, on top of Claude Code’s own. Default 0.
refreshIntervalAlso re-run the command every N seconds (minimum 1). Leave it out to run only on events.
hideVimModeIndicatortrue hides the built-in -- INSERT -- text, for scripts that print vim.mode themselves.

For a one-liner you don’t need a script file. This prints the model and the folder name with jq:

~/.claude/settings.json{
  "statusLine": {
    "type": "command",
    "command": "jq -r '\"[\\(.model.display_name)] \\(.workspace.current_dir | split(\"/\") | last)\"'"
  }
}

Claude Code reloads settings when you save the file, so the status line appears without a restart. It sits on its own row above the footer badges. With a custom status line, Claude Code hides most footer hints, such as esc to interrupt and ? for shortcuts.

3. The JSON your script receives

Every run, Claude Code pipes one JSON object to your script’s stdin. These are the fields you’ll use most:

FieldContains
model.id, model.display_nameModel ID and short name, such as Opus
workspace.current_dir (same as cwd)Current working directory
workspace.project_dirWhere Claude Code was started; can differ from the current directory
workspace.repo.owner, .name, .hostRepository parsed from the origin remote
cost.total_cost_usdEstimated session cost in USD, worked out locally at list price
cost.total_duration_ms, cost.total_api_duration_msWall-clock time, and time spent waiting on the API
cost.total_lines_added, cost.total_lines_removedLines changed this session
context_window.used_percentage, .remaining_percentageContext used and left, already calculated
context_window.context_window_size200,000, or 1,000,000 for extended-context models
rate_limits.five_hour, rate_limits.seven_dayused_percentage and resets_at (Unix seconds), for Pro and Max subscribers
session_id, session_nameSession ID, and the name from --name, /rename or the generated title
effort.level, thinking.enabled, fast_modeReasoning effort, extended thinking, fast mode
worktree.name, workspace.git_worktreeWorktree session name, or the linked git worktree you’re in
pr.number, pr.url, pr.review_stateOpen pull request (or GitLab merge request) for the branch
vim.mode, output_style.name, agent.nameVim mode, output style, and the agent name when you use --agent
version, transcript_pathClaude Code version and the transcript file

Not every field is always there, and your script has to cope:

In jq, // 0 gives a default for null, and // empty prints nothing for a missing field.

Two numbers are often misread. used_percentage counts input tokens only (fresh input plus cache reads and writes), not output, and it comes from the last API response, so /context can show a higher number until the next one. total_cost_usd is an estimate made on your machine: it may not match your bill, and it resets to $0 when /clear starts a new session.

4. When it updates

The script runs when a session starts or resumes, then again when:

Updates are debounced by 300 ms, so a burst of changes runs the script once. If a new update arrives while the script is still running, Claude Code cancels the old run. Editing the script file doesn’t trigger a run; your change shows up on the next update.

Because nothing fires while the session sits idle, a clock, or a git status changed by something else, goes stale. Set "refreshInterval": 5 for that. The status line runs locally and doesn’t use any tokens. It hides while a permission prompt or the help menu is on screen.

5. Example scripts

Save each Bash example as ~/.claude/statusline.sh, make it executable with chmod +x ~/.claude/statusline.sh, and point command at it as in section 2. They need jq (brew install jq on macOS, sudo apt install jq on Debian and Ubuntu); example 5 needs only Python. Each one was tested against sample input, including the null values you get early in a session.

Example 1: model and folder

~/.claude/statusline.sh#!/bin/bash
input=$(cat)
model=$(jq -r '.model.display_name' <<< "$input")
dir=$(jq -r '.workspace.current_dir' <<< "$input")
echo "[$model] ${dir##*/}"

Prints [Opus] checkout-api. ${dir##*/} keeps only the last part of the path.

Example 2: git branch with a dirty marker

~/.claude/statusline.sh#!/bin/bash
input=$(cat)
model=$(jq -r '.model.display_name' <<< "$input")
dir=$(jq -r '.workspace.current_dir' <<< "$input")

export GIT_OPTIONAL_LOCKS=0
branch=$(git -C "$dir" branch --show-current 2>/dev/null)
dirty=""
if [ -n "$(git -C "$dir" status --porcelain --untracked-files=no 2>/dev/null)" ]; then
  dirty="*"
fi

if [ -n "$branch" ]; then
  echo "[$model] ${dir##*/} on $branch$dirty"
else
  echo "[$model] ${dir##*/}"
fi

Prints [Opus] checkout-api on feature/login*, with * when tracked files have changes. GIT_OPTIONAL_LOCKS=0 stops git status from taking the index lock, so your status line doesn’t collide with a git command Claude is running. --untracked-files=no keeps it quick in big repositories. If it’s still slow, cache the result in a file named after session_id and refresh it every few seconds.

Example 3: context bar and cost, in color

~/.claude/statusline.sh#!/bin/bash
input=$(cat)
IFS=$'\t' read -r model pct cost < <(jq -r '[
  .model.display_name,
  (.context_window.used_percentage // 0 | floor),
  (.cost.total_cost_usd // 0)
] | @tsv' <<< "$input")

green='\033[32m'; yellow='\033[33m'; red='\033[31m'; reset='\033[0m'
if [ "$pct" -ge 90 ]; then color=$red
elif [ "$pct" -ge 70 ]; then color=$yellow
else color=$green; fi

filled=$((pct / 10)); [ "$filled" -gt 10 ] && filled=10
bar=$(printf '%*s' "$filled" '' | tr ' ' '#')$(printf '%*s' $((10 - filled)) '' | tr ' ' '-')
cost=$(LC_NUMERIC=C printf '%.2f' "$cost")

printf '%b\n' "[$model] ${color}${bar} ${pct}%${reset} | \$${cost}"

Prints [Opus] #######--- 73% | $1.23, with the bar green under 70%, yellow from 70% and red from 90%. One jq call reads all three values, which matters because the script runs after every message. LC_NUMERIC=C stops locales that use a decimal comma from breaking printf. printf '%b' turns the \033 color codes into real escape characters.

Example 4: session name, worktree and rate limits

~/.claude/statusline.sh#!/bin/bash
jq -r '
  def pct(w): if w then "\(w.used_percentage | floor)%" else empty end;
  [
    .model.display_name,
    (.session_name // empty),
    (.worktree.name // .workspace.git_worktree // empty | "worktree " + .),
    ("5h " + pct(.rate_limits.five_hour)),
    ("7d " + pct(.rate_limits.seven_day))
  ] | join(" | ")
'

Prints Opus | login fix | 5h 23% | 7d 41%. Anything missing, such as rate limits when you use an API key, or the worktree when you aren’t in one, is skipped instead of printing null. The whole script is one jq program reading stdin.

Example 5: Python, no jq

~/.claude/statusline.py#!/usr/bin/env python3
import json, os, subprocess, sys

data = json.load(sys.stdin)
model = data["model"]["display_name"]
cwd = data["workspace"]["current_dir"]
pct = int(data.get("context_window", {}).get("used_percentage") or 0)

try:
    branch = subprocess.run(
        ["git", "-C", cwd, "branch", "--show-current"],
        capture_output=True, text=True, timeout=2,
    ).stdout.strip()
except Exception:
    branch = ""

parts = [f"[{model}]", os.path.basename(cwd)]
if branch:
    parts.append(f"on {branch}")
parts.append(f"{pct}% context")
print(" ".join(parts))

Prints [Opus] checkout-api on feature/login 73% context. The two-second timeout keeps a slow git from holding up the status line. Set command to ~/.claude/statusline.py, or to python3 ~/.claude/statusline.py if you’d rather not make it executable.

6. Colors, multiple lines, links and width

7. Windows

On Windows, Claude Code runs the command through Git Bash when it’s installed, or PowerShell when it isn’t. Two rules avoid most problems:

settings.json{
  "statusLine": {
    "type": "command",
    "command": "powershell -NoProfile -File C:/Users/you/.claude/statusline.ps1"
  }
}
C:/Users/you/.claude/statusline.ps1$input_json = $input | Out-String | ConvertFrom-Json
$cwd = $input_json.cwd
$model = $input_json.model.display_name
$used = $input_json.context_window.used_percentage
$dirname = Split-Path $cwd -Leaf

if ($used) {
    Write-Host "$dirname [$model] ctx: $used%"
} else {
    Write-Host "$dirname [$model]"
}

With Git Bash installed, the Bash examples above work on Windows too, as long as jq is on your PATH. Example 5 works anywhere Python 3 does. In WSL, follow the Linux instructions.

8. Troubleshooting

Test the script on its own first. This sends it sample input the way Claude Code would:

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

Every session at once with Agent Pets

A status line describes the session in the terminal you’re looking at. When several sessions run at once, Agent Pets shows all of them while you’re in Chrome:

npx getagentpets init adds hooks next to the ones you already have, and everything runs on your computer.

Get Agent Pets — was $25, now $6Get Agent Pets — $25

Questions

How do I add a status line to Claude Code?

Run /statusline followed by what you want to see, and Claude Code writes the script and the setting for you. Or add a statusLine object with "type": "command" and your script’s path to ~/.claude/settings.json.

How often does the Claude Code status line update?

When the session starts, after each new assistant message, after /compact, and when the permission mode or vim mode changes, debounced by 300 ms. Set refreshInterval to also run it every few seconds.

Can the status line show how much context is used?

Yes. Read context_window.used_percentage from the JSON on stdin. It counts input tokens from the last response and can be null at the start of a session, so give it a default.

Why is my Claude Code statusline not showing?

Usually the script failed: it must exit with code 0 and print to stdout. It also stays blank until you trust the folder, and when disableAllHooks is true. Run claude --debug to see the script’s errors.

How do I remove the Claude Code status line?

Run /statusline delete, or delete the statusLine key from your settings file.

Do I need jq for a Claude Code statusline?

No. jq is just a convenient way to read the JSON in a shell script. Any language that reads stdin and prints to stdout works; Example 5 above is a Python script that needs no jq.

Sources: Claude Code’s documentation on the status line, settings and environment variables; the git documentation on GIT_OPTIONAL_LOCKS.

More guides