Building a Custom Status Line for Claude Code #
Reading time: ~3 min
Claude Code lets you customize the status bar displayed between each response using an external script. Here's how I built mine.
Overview #
The script outputs two lines after each Claude response:
[Claude Opus 4.6] 📁 my-project | 🌿 main +2~1 | ⏰ 14:32:07
██████░░░░ 60% | $0.142 | ⏱️ 3m 24s
Line 1 — Model name, workspace directory, git branch with staged/modified indicators, timestamp
Line 2 — Context window progress bar (color-coded by threshold), session cost, total duration
The script #
The script receives a JSON payload on stdin containing all the Claude Code session info. It extracts what it needs with jq.
1#!/bin/bash
2input=$(cat)
3
4# Extract data with jq
5MODEL=$(echo "$input" | jq -r '.model.display_name')
6DIR=$(echo "$input" | jq -r '.workspace.current_dir')
7COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
8PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
9DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
ANSI colors #
An important gotcha: you must use $'...' (ANSI-C quoting) so bash interprets escape sequences at assignment time. Otherwise colors won't work when passed as %s arguments to printf.
1# Correct
2CYAN=$'\033[36m'
3
4# Wrong (outputs literal escape codes)
5CYAN='\033[36m'
Git integration with caching #
The script fetches the current branch and the number of staged/modified files. To avoid calling git on every refresh, a 42-second cache is used:
1CACHE_FILE="/tmp/claude-statusline-cache"
2CACHE_MAX_AGE=42
3
4cache_is_stale() {
5 [ ! -f "$CACHE_FILE" ] && return 0
6 local file_age
7 file_age=$(stat -c %Y "$CACHE_FILE" 2>/dev/null) \
8 || file_age=$(stat -f %m "$CACHE_FILE" 2>/dev/null) \
9 || file_age=0
10 [ $(($(date +%s) - file_age)) -gt $CACHE_MAX_AGE ]
11}
The cache_is_stale() function handles both Linux (stat -c %Y) and macOS (stat -f %m).
Important detail: all git commands use -C "$DIR" to run in the workspace directory rather than /tmp (the script's actual working directory).
Color-coded progress bar #
The bar changes color based on context window usage:
| Usage | Color |
|---|---|
| < 70% | Green |
| 70-89% | Yellow |
| >= 90% | Red |
1if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
2elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
3else BAR_COLOR="$GREEN"; fi
4
5FILLED=$((PCT / 10))
6EMPTY=$((10 - FILLED))
7BAR=$(printf "%${FILLED}s" | tr ' ' '█')$(printf "%${EMPTY}s" | tr ' ' '░')
Installation #
- Place the script at
~/.claude/statusline.sh - Make it executable:
chmod +x ~/.claude/statusline.sh - Configure Claude Code in
~/.claude/settings.json:
1{
2 "preferences": {
3 "statusline": "~/.claude/statusline.sh"
4 }
5}
Version history #
- v1.0 — Initial release with Claude Sonnet 4.5
- v1.1 — Fix git commands running in the wrong directory (
-C "$DIR") - v1.2 — Fix cache staleness on Linux + fix ANSI colors (
$'...'quoting) - v1.3 — Bump cache TTL from 5s to 42s
Architect: t0nt0n | Implementation: Claude Sonnet 4.5 (v1.0) & Claude Opus 4.6 (v1.1+)