Build a Custom Claude Code Statusline With One Prompt
How to use the /statusline command in Claude Code to generate a compact, single-line statusline showing model name, git branch, token usage, and a color-coded progress bar, all from one prompt.
If you live in Claude Code's terminal interface like I do, you've probably wondered how much context you've already burned through halfway into a long debugging session. The default UI gives you a hint, but it's never quite as visible as you want it to be, especially when you're juggling git branches across worktrees and watching token consumption climb.
Turns out Claude Code has a first-class feature for this called /statusline, and it's drastically underused. In this post I'll walk through the prompt I gave it, what gets generated, and why this is one of the highest leverage 30-second customizations you can make to your dev environment.
What is /statusline in Claude Code?
/statusline is a slash command inside Claude Code that generates a custom status line for the bottom of your terminal interface. Claude Code pipes a JSON blob of session data, model, workspace, context window usage, cost, and more, into a shell script you point it at, and whatever that script prints gets rendered as your statusline.
In other words: it's not a plugin system, not a config schema, not a TUI. It's just a shell script that reads JSON from stdin and prints a string. That simplicity is the whole point, you can ask Claude to write the script for you, and /statusline will both generate it and wire it up in ~/.claude/settings.json automatically.
If you're new to it, the official docs are at code.claude.com/docs/en/statusline.
The Prompt I Used
Here's the exact prompt I ran. The more specific you are about format, thresholds, and edge cases, the less you'll need to iterate:
/statusline Create a compact single-line statusline showing: the model display
name, the current git branch, current token usage and a soft cap of 150k
formatted like "32k/150k", and a 10-character progress bar showing percentage
of the 150k used. Color-code the token count and bar: green under 50%, yellow
at 50-90%, red above 90%. Use the input JSON's context_window data — if it
provides used_tokens use that directly, otherwise compute it from
used_percentage against a 200k window. Separate sections with " | ". Cache git
branch lookups for 2 seconds since the script runs frequently. Make it work on
macOS with bash and standard tools (jq, git). Save the script to
~/.claude/statusline.sh and update ~/.claude/settings.json.Let me unpack why each part of this prompt matters.
Why a "soft cap" of 150k against a 200k window
Claude's context window is 200k tokens, but the used_percentage Claude Code provides is calculated against the full window. The problem is that performance and recall start degrading well before you actually hit 200k, and once you cross ~80% of the real window, automatic compaction kicks in and you lose detail from the conversation.
By setting a soft cap of 150k and color-coding against that, the bar turns red while you still have time to wrap up the current task, commit, and start a fresh session. It's an early warning system, not a hard limit.
Why cache the git branch lookup
The statusline script runs on every tick. If you're inside a large monorepo, git rev-parse --abbrev-ref HEAD adds noticeable latency, and the docs explicitly warn that slow scripts block the statusline from updating. A 2-second file-based cache in /tmp cuts that down to a single git call every couple seconds without any visible staleness, your branch doesn't change that often.
This is the kind of optimization that's easy to skip when you're writing a quick script by hand, but trivially cheap to ask for in the prompt.
Why specify the JSON fields
The context_window object in Claude Code's JSON input has evolved over versions. Older versions only exposed used_percentage; newer ones include used_tokens directly. By telling Claude to prefer used_tokens and fall back to computing from used_percentage, the script stays forward and backward compatible without you having to think about it.
What You Get
After running the prompt, Claude Code writes a script to ~/.claude/statusline.sh that looks roughly like this (yours may differ slightly based on the model):
bash
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
USED_TOKENS=$(echo "$input" | jq -r '.context_window.used_tokens // empty')
if [ -z "$USED_TOKENS" ]; then
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0')
USED_TOKENS=$(echo "$PCT * 2000" | bc | cut -d. -f1)
fi
CAP=150000
PCT_OF_CAP=$(( USED_TOKENS * 100 / CAP ))
# Cached git branch
CACHE_FILE="/tmp/.claude_branch_$$"
if [ -f "$CACHE_FILE" ] && [ $(($(date +%s) - $(stat -f %m "$CACHE_FILE"))) -lt 2 ]; then
BRANCH=$(cat "$CACHE_FILE")
else
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "—")
echo "$BRANCH" > "$CACHE_FILE"
fi
# Color thresholds
if [ "$PCT_OF_CAP" -lt 50 ]; then COLOR="\033[32m"
elif [ "$PCT_OF_CAP" -lt 90 ]; then COLOR="\033[33m"
else COLOR="\033[31m"
fi
RESET="\033[0m"
# Progress bar
FILLED=$(( PCT_OF_CAP / 10 ))
BAR=$(printf '█%.0s' $(seq 1 $FILLED))$(printf '░%.0s' $(seq 1 $((10 - FILLED))))
USED_K=$(( USED_TOKENS / 1000 ))
printf "%s | %s | ${COLOR}%dk/150k${RESET} ${COLOR}%s${RESET}" \
"$MODEL" "$BRANCH" "$USED_K" "$BAR"And it adds this to ~/.claude/settings.json:
json
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}The result is a statusline that looks something like:
Claude Opus 4.7 | feature/ticket-batching | 32k/150k ██░░░░░░░░Green under 50%, yellow as you cross into the danger zone, red when it's time to wrap up.
Why This Is Worth 30 Seconds of Your Time
A custom statusline sounds like premature optimization until you've used one. Three reasons it pays off immediately:
The first is context anxiety. When you can't see your token usage, you compensate by guessing, usually pessimistically, and start fresh sessions earlier than you need to. A visible bar lets you push longer sessions confidently and compact at exactly the right moment.
The second is branch awareness in worktrees. If you work across multiple git worktrees (and if you don't, you should), it's easy to type a command in the wrong terminal. Having the branch in your statusline makes that mistake roughly impossible.
The third, and this is the real reason I'm writing this post, is that /statusline is a perfect example of how Claude Code's slash commands aren't just shortcuts; they're delegations. You're not configuring a tool, you're describing the outcome you want and letting Claude handle the wiring. Once you start thinking about your dev environment that way, a lot of "I should set this up someday" tasks become "done in a single prompt."
Tips for Writing Good /statusline Prompts
A few patterns that consistently produce good results:
Be specific about format. "Show token usage" is ambiguous. "Formatted like 32k/150k" is unambiguous. Specifying the exact visual format saves you a round trip.
Name your fallbacks. Whenever there's a JSON field that may or may not be present, tell Claude what to do in both cases. It'll write defensive code without you needing to ask.
Mention performance. The statusline runs constantly. If you mention that any external command should be cached, Claude will add the file-based caching pattern automatically.
State your platform. "macOS with bash and standard tools" tells Claude not to reach for GNU-only flags or shell features that don't exist on the BSD utilities macOS ships with. If you're on Linux or use both, say so.
Specify destinations. Asking for the script to be saved to ~/.claude/statusline.sh and the settings updated to ~/.claude/settings.jsonmeans you're done after one prompt, no copy-paste step.
Going Further
Once you've got a basic statusline working, there's a whole ecosystem of more elaborate options. ccusage adds session cost and burn rate tracking. ccstatusline is a fully customizable formatter with 30+ widgets. claude-powerline gives you a vim-style powerline aesthetic.
But honestly, for most workflows, a hand-rolled bash script generated by /statusline is the sweet spot. It does exactly what you asked for, has no dependencies you didn't already have, and you can read every line of it.
That's the whole pitch: one prompt, one script, a permanently better terminal. Hard to beat.
If you're already using a custom statusline, I'd love to hear what you put on yours, especially the metrics that turned out to matter more than you expected.