Statusline for Claude Code, context at a glance in 15 minutes
How to build your own statusline with one shell script line in settings.json. Current model, context percentage, Git branch, cost ticker. With a concrete example script and the traps I collected on my first attempt.
Claude Code normally only shows you the essentials at the bottom edge. Which model, whether Vim mode is on, that is it. When you work longer, you notice you miss things. How much percent of the context is used. Which Git branch are you on. How much do the costs of the running session add up to. Exactly that you can draw in yourself, with one line in settings.json and a shell script. I will show you how it works and which beginner mistakes I made.
1. Understand how the statusline ticks
Claude Code calls a shell command every few seconds and shows its stdout output as statusline. The command gets JSON on stdin, with the most important session data. Model, context window, working directory, token counts. Everything you need to display something useful.
That means: whatever you can do in Bash or with jq, you can show in the statusline. Model name, branch, costs, a green dot when tests are green, whatever.
2. The first entry in settings.json
Open your ~/.claude/settings.json and add the block:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}
type is always command, no other option exists. command is either a path to a script or an inline shell command. padding packs horizontal space around it, optional. There is also refreshInterval (milliseconds) if you want your script to restart periodically, and hideVimModeIndicator if you want to hide the built-in Vim mode indicator.
3. The minimal script
Drop ~/.claude/statusline.sh, make it executable, three lines in it:
#!/bin/bash
input=$(cat)
echo "$input" | jq -r '"[\(.model.display_name)] \(.workspace.current_dir | split("/") | last)"'
chmod +x ~/.claude/statusline.sh
Restart Claude Code, you see something like [Opus 4.7] academy at the bottom. Model name and last folder name from the working dir. Not more, but the blueprint stands.
4. Draw in context utilisation
This is the entry I need most. When I am at 80 percent context, I have to think about compact or new tab. To do that you extend the jq line:
#!/bin/bash
input=$(cat)
echo "$input" | jq -r '
"[\(.model.display_name)] " +
"\(.workspace.current_dir | split("/") | last) " +
"ctx:\(.context_window.used_percentage // 0 | floor)%"
'
Result: [Opus 4.7] academy ctx:62%. As soon as the number goes over 75, I see it. On the first version I had used used_percentage without the default operator // 0, on the first call after start the field was still null and the statusline showed nothing. Lesson: set defensive defaults, the stdin JSON is not always complete.
5. Git branch on top
When you work in a Git repo, the branch is a no-brainer. But careful, you are not always in a Git directory, the command has to handle that.
branch=$(cd "$(echo "$input" | jq -r '.workspace.current_dir')" 2>/dev/null && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
[ -n "$branch" ] && branch=" git:$branch"
Then append to the echo line: ... ctx:62% git:main.
Trap here: the cd has to be in a subshell, otherwise you change the current directory of the script. My first script did not wrap the cd in $(...) and suddenly there were follow-up errors because the next command ran somewhere else than intended.
6. Build in a cost ticker
Stdin also delivers total_cost_usd if you want to display it. In my case this runs for sessions that go long.
cost=$(echo "$input" | jq -r '.total_cost_usd // 0 | . * 100 | floor / 100')
[ "$cost" != "0" ] && cost=" \$$cost"
Caution: the exact JSON structure can differ between Claude Code versions. If the field is called differently for you, dump the full stdin once into a log file and look. An echo "$input" >> /tmp/statusline-debug.log is your friend.
7. Inline instead of script
If you want to save the script wrapping, you can also pack the command directly into settings.json. Looks like this:
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% ctx\"'"
}
}
Works, but with longer statuslines it is a nightmare due to JSON escaping. From three jq filters on rather use a script file, then you also get syntax highlighting in the editor.
8. Team setup with project statusline
You can also set the statusline project-wide by putting the block into .claude/settings.json in the repo instead of the global one under ~/.claude/. Sensible when the team wants uniform display, for example "we all want to see the test suite colour".
When you call scripts from the repo, remember that the scripts must land executable in Git. git update-index --chmod=+x .claude/statusline.sh sets the executable bit, otherwise it does not work after git clone for colleagues.
My case was: script file checked in, locally everything ran. Colleague pulled, had no chmod bit on it, statusline stayed empty. Three Slack messages, then found. Since then always with update-index --chmod=+x.
9. Debugging when nothing comes
Three things that have hit me on this. Sometimes the settings.json is broken after an edit and Claude Code silently does not load it, jq . ~/.claude/settings.json shows the syntax error. Other times the script has no executable bit, ls -la checks that and chmod +x fixes it. And once the script ran but stdout was empty because of a jq error inside, pipe the script manually with example JSON to see the real output.
echo '{"model":{"display_name":"Opus 4.7"},"workspace":{"current_dir":"/tmp/test"}}' | ~/.claude/statusline.sh
If that outputs something meaningful, the script is OK and the error is in the input JSON from Claude Code. Then only echo "$input" > /tmp/claude-statusline-input.log helps, build it in and see what really comes in.
10. What's next
When the statusline runs, look at Context window managing in Claude Code, because the display is only half of it, reacting to the number is the other half. If you also want costs in view, Claude Code Cost Controls for daily drivers has the mechanics. And if you also want to adjust the style of the answers, the matching playbook is Output Styles for Claude Code.
For the lesson layer Hooks and Skills fits as a concept refresher, because the statusline is a local side effect and thus conceptually related to the hook pattern.
Source
Statusline officially: https://code.claude.com/docs/en/statusline