Most Claude Code status bars render a flat string: model · tokens used. Mine renders two lines, and four things on those lines changed how I read them. This post walks through each mechanism with the exact bash, so you can port any of them into your own statusline.sh.
1. Pace, not fill, on rate-limit bars
Look at the screenshot above. The seven-day bar is almost full. It's also green.
A full bar is not the same as a bar that is going to overflow. The useful question isn't "how much have I used". It's "am I on track to make it to reset". The bar above is about 85% filled on day six of seven: tokens used tracks with week elapsed, so green. Zero panic. If that same 85% fill showed up on day three of seven, the bar would be red, and I'd know to throttle down for four days. Mine colors the bar by the gap between tokens used and time elapsed in the window.
local PACE_YELLOW_MAX=15
pace_state=$(awk -v u="$pct" -v n="$now" -v r="$resets_at" -v w="$window_seconds" \
-v ym="$PACE_YELLOW_MAX" 'BEGIN {
e = (w - (r - n)) / w * 100
if (e < 0) e = 0
if (e > 100) e = 100
d = u - e
if (d <= 0) print "green"
else if (d <= ym) print "yellow"
else print "red"
}')
e is the percentage of the window that has already elapsed. d = u - e is how far ahead of pace I am in percentage points. Three bands: on or under pace is green, up to 15 points ahead is yellow, more than 15 points ahead is red. With 40% of the week elapsed, 40% used is green, 55% used is yellow, 56%+ used is red.
I kept a pressure-based fallback for when the JSON is missing resets_at or window_seconds, so the bar never goes uncolored. The signal I actually look at is 7d, because the five-hour window resets often enough that being ahead of pace inside it is almost always fine. Being ahead of pace on day two of seven is the thing I want to know about.
The separation between "full" and "overspending" is the whole point. A bar at 40% is cheerful; a bar at 40% with 10% of the week elapsed is a problem I have three days to fix.
2. Deterministic per-project and per-model colors from a cksum hash
Every project I work in gets the same color every time, and every model name does too. The color is a pure function of the name. No config file, no manual mapping.
project_name_color() {
local name="$1"
local hash
hash=$(printf '%s' "$name" | cksum | awk '{print $1}')
local hue=$(( hash % 360 ))
# Exclude 70-100: shift into 100
if [ "$hue" -ge 70 ] && [ "$hue" -le 100 ]; then
hue=100
fi
hsl_to_rgb_escape "$hue" 70 65
}
cksum produces a 32-bit checksum; I take it mod 360 to get a hue. Saturation and lightness are fixed at 70 and 65, which is where HSL colors look crisp on a dark terminal without blowing out. The model color function is identical with a different input, so Opus 4.7 and Sonnet 4.6 come out distinct and recognizable across sessions.
Hues 70–100 get remapped to 100 because the source comment flags that range as washed-out yellow-green on my terminal. It's a one-line carve-out: I'd rather lose 30 degrees of hue space than squint at a low-contrast project label.
The HSL-to-RGB conversion is a single awk block that emits an ANSI truecolor escape:
hsl_to_rgb_escape() {
local h=$1 s=$2 l=$3
awk -v h="$h" -v s="$s" -v l="$l" 'BEGIN {
s = s / 100
l = l / 100
abs2l1 = 2*l - 1; if (abs2l1 < 0) abs2l1 = -abs2l1
C = (1 - abs2l1) * s
sector = int(h / 60) % 6
hmod2 = (h / 60) - int(h / 60 / 2) * 2
diff = hmod2 - 1; if (diff < 0) diff = -diff
X = C * (1 - diff)
m = l - C / 2
if (sector == 0) { r1=C; g1=X; b1=0 }
else if (sector == 1) { r1=X; g1=C; b1=0 }
else if (sector == 2) { r1=0; g1=C; b1=X }
else if (sector == 3) { r1=0; g1=X; b1=C }
else if (sector == 4) { r1=X; g1=0; b1=C }
else { r1=C; g1=0; b1=X }
R = int((r1 + m) * 255 + 0.5)
G = int((g1 + m) * 255 + 0.5)
B = int((b1 + m) * 255 + 0.5)
printf "\033[38;2;%d;%d;%dm", R, G, B
}'
}
This is the most portable chunk of the whole script. Drop it into any bash status line, pass a hue from a hash of whatever string you want colored, and you get stable truecolor without maintaining a palette.
3. Context bar with a visible autocompact reserve
Claude Code auto-compacts before you hit the hard context limit. The reserve is real, so the bar renders it. My context bar splits into three zones: used, free, and autocompact-reserved at the tail.
C_AUTOCOMPACT='\033[38;5;208m' # orange/amber
AUTOCOMPACT_TOKENS=33000
ac_blocks=1
if [ -n "$ctx_size" ] && [ "$ctx_size" -gt 0 ] 2>/dev/null; then
ac_blocks=$(awk -v ac="$AUTOCOMPACT_TOKENS" -v sz="$ctx_size" -v bw="$BAR_WIDTH" \
'BEGIN { b = ac / sz * bw; b = int(b) + (b > int(b) ? 1 : 0); if (b < 1) b = 1; print b }')
fi
ac_start=$(( BAR_WIDTH - ac_blocks ))
if [ "$filled" -ge "$BAR_WIDTH" ]; then
free_blocks=0; ac_visible=0
elif [ "$filled" -ge "$ac_start" ]; then
free_blocks=0; ac_visible=$(( BAR_WIDTH - filled ))
else
free_blocks=$(( ac_start - filled )); ac_visible=$ac_blocks
fi
33,000 tokens is the compaction buffer on my config. Divide by the context window size, multiply by the 20-block bar width, round up, floor at one, and that's how many tail blocks get painted in ANSI 208 orange. The filled portion uses ━; free and reserved both use ╌ so the color is the only distinction. When used tokens encroach on the reserve zone, the orange blocks start disappearing from the right, and I know compaction is imminent before it lands mid-thought.
The fallback path for when context data is missing renders BAR_WIDTH-1 dim hyphens plus one orange block, so the reserve is always visible as a shape even before the first tool call lands.
4. Worktree indicator from one git comparison
I juggle worktrees, and "am I on main or on a linked worktree" was the thing I kept losing track of. The fix is four lines of git plumbing.
git_common_dir=$(git -c gc.auto=0 rev-parse --git-common-dir 2>/dev/null)
git_dir=$(git -c gc.auto=0 rev-parse --git-dir 2>/dev/null)
if [ -n "$git_common_dir" ] && [ -n "$git_dir" ] && [ "$git_common_dir" != "$git_dir" ]; then
in_worktree=true
fi
In a primary checkout, --git-dir and --git-common-dir both point at .git. In a linked worktree, --git-dir points into .git/worktrees/<name>/ while --git-common-dir still points at the shared .git. When they differ, I'm in a worktree.
The render swap is one conditional:
if [ "$in_worktree" = true ]; then
git_ref="${WORKTREE}⎇ ${branch_label}${RESET}"
else
git_ref="${CYAN}${branch_label}${RESET}"
fi
WORKTREE is ANSI 256 color 215, a warm orange. The ⎇ glyph (U+2387, ALTERNATIVE KEY SYMBOL) renders as a single terminal cell and reads at a glance. Cyan for main-tree branches, warm orange with a glyph for worktrees. Two cheap differences, one clear answer.
What I'd add next
A sparkline of session length over the last N turns would replace my current habit of eyeballing the context bar for drift. A break-even indicator between the five-hour and seven-day bars — the point where I should throttle down so the weekly budget outlasts my plan period — is the other one I keep meaning to write. Both are additive; neither needs to change the existing bars. If you build either, tell me.
The full script is at ~/.claude/statusline.sh. Copy what's useful.
Top comments (0)