How to set up a Claude Code status line
The status line is the bar at the bottom of Claude Code. When the session updates, Claude Code runs whatever shell script you've configured, pipes it a JSON snapshot on stdin, and shows whatever the script prints. That's the whole mechanism. The fast paths come first below, then the manual setup.
The fast paths
Run /statusline inside Claude Code and describe what you want, like /statusline show model, directory and context usage. It writes the script to ~/.claude/ and updates your settings. Done. If you'd rather start from something that already looks good, copy one from the gallery: every config there shows exactly what it renders before you install it.
Wire up a script by hand
Save a script to ~/.claude/statusline.sh, make it executable (chmod +x ~/.claude/statusline.sh), and point the statusLine setting in ~/.claude/settings.json at it:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}Settings reload on their own, and the status line shows up on your next interaction. Two optional fields: padding adds horizontal spacing, and refreshInterval re-runs the script every N seconds if you show time-based data. The official docs list the rest.
The JSON Claude Code sends your script
Your script gets one JSON object on stdin per update. This is a real payload, the same one this site renders every gallery preview against:
{
"session_id": "b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10",
"transcript_path": "/home/user/.claude/projects/app/transcript.jsonl",
"cwd": "/home/user/app",
"model": {
"id": "claude-opus-4-8",
"display_name": "Opus 4.8"
},
"effort": {
"level": "high"
},
"thinking": {
"enabled": true
},
"workspace": {
"current_dir": "/home/user/app",
"project_dir": "/home/user/app",
"repo": {
"host": "github.com",
"owner": "acme",
"name": "app"
},
"added_dirs": []
},
"version": "2.1.155",
"cost": {
"total_cost_usd": 0.41,
"total_duration_ms": 612000,
"total_api_duration_ms": 48000,
"total_lines_added": 128,
"total_lines_removed": 34
},
"context_window": {
"context_window_size": 200000,
"used_percentage": 22,
"remaining_percentage": 78,
"total_input_tokens": 44000,
"total_output_tokens": 1400,
"current_usage": {
"input_tokens": 37000,
"output_tokens": 1400,
"cache_creation_input_tokens": 5000,
"cache_read_input_tokens": 2000
}
},
"exceeds_200k_tokens": false,
"rate_limits": {
"five_hour": {
"used_percentage": 26,
"resets_at": 1782007620
},
"seven_day": {
"used_percentage": 7,
"resets_at": 1782176400
}
},
"output_style": {
"name": "default"
},
"pr": {
"number": 1287,
"url": "https://github.com/acme/app/pull/1287"
}
}Most scripts only touch a few fields: model.display_name, workspace.current_dir, context_window.used_percentage, and cost.total_cost_usd. The rate_limits windows each carry a usage percentage and a resets_at unix timestamp.
A minimal working script
Three fields, one jq call each:
#!/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")
echo "[$model] ${dir##*/} · ${pct}% context"
For the payload above it prints [Opus 4.8] app · 22% context. You can try it without opening Claude Code: save the JSON to a file and run bash statusline.sh < sample.json. Our test suite runs this exact script against real payloads on every commit, so if it's on this page, it works.
Good to know
Two things that trip people up. Git status isn't in the payload: scripts run git themselves against workspace.current_dir, which is how gallery configs show a branch even though Claude Code never sends one. And context_window.used_percentage is null at the start of a fresh session, so guard it (// 0 in jq) or your status line reads "null" until the first response.
Going further
Want more than a few fields? The tools & resources list covers the full-featured tools and usage trackers. Or copy a status line from the gallery and tweak it. If you build one you like, submit it back.