Claude Code has a customizable status line at the bottom of the terminal. By default it shows basic info like the model and token count, but you can make it much more useful with a custom script.
Here's what we built β a Starship-inspired status line with icons, git info, context usage, and auto-compact status:
π 12:35 π ~/my-project on main* β’ π€ Opus 4.6 β’ π 28% (56k/160k) β’ π auto-compact
When auto-compact is enabled, the max shows the effective limit (80% of 200k = 160k) since that's when compaction triggers. With auto-compact off, you get the full window:
π 12:35 π ~/my-project on main* β’ π€ Opus 4.6 β’ π 28% (56k/200k) β’ β auto-compact
When the terminal is narrow, it automatically splits into two lines:
π 12:35 π ~/my-project on main* β’ π€ Opus 4.6
π 28% (56k/160k) β’ π auto-compact
Save this as ~/.claude/statusline-command.sh:
#!/bin/bash
# Claude Code status line β Starship-inspired with icons
# Receives JSON on stdin from Claude Code
input=$(cat)
# Extract JSON data
cwd=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // "~"')
model=$(echo "$input" | jq -r '.model.display_name // .model.id // "Claude"')
pct=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
used_k=$(echo "$input" | jq -r '((.context_window.current_usage | .input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens + .output_tokens) // 0) / 1000 | floor')
max_k=$(echo "$input" | jq -r '(.context_window.context_window_size // 0) / 1000 | floor')
# Format directory (~ for home)
if [[ "$cwd" == "$HOME"* ]]; then
display_dir="${cwd/#$HOME/~}"
else
display_dir="$cwd"
fi
# Git branch
git_info=""
if git -C "$cwd" rev-parse --git-dir >/dev/null 2>&1; then
branch=$(git -C "$cwd" symbolic-ref --short HEAD 2>/dev/null || git -C "$cwd" rev-parse --short HEAD 2>/dev/null)
if [ -n "$branch" ]; then
dirty=""
if ! git -C "$cwd" diff-index --quiet HEAD -- 2>/dev/null; then
dirty="*"
fi
git_info=" on ${branch}${dirty}"
fi
fi
# Auto-compact: key only exists in ~/.claude.json when disabled (false)
# Absent = enabled (default), false = disabled
# When enabled, triggers at ~80% so effective max is 80% of context window
compact_val=$(jq -r 'if .autoCompactEnabled == false then "false" else "true" end' ~/.claude.json 2>/dev/null)
compact_pct=${CLAUDE_AUTOCOMPACT_PCT_OVERRIDE:-80}
if [ "$compact_val" = "false" ]; then
compact_icon="β auto-compact"
else
compact_icon="π auto-compact"
max_k=$((max_k * compact_pct / 100))
fi
# Time
now=$(date '+%H:%M')
# Build one-line version, split to two if too long
line1=$(printf 'π %s π %s%s β’ π€ %s' "$now" "$display_dir" "$git_info" "$model")
line2=$(printf 'π %s%% (%sk/%sk) β’ %s' "$pct" "$used_k" "$max_k" "$compact_icon")
oneline="$line1 β’ $line2"
# Get terminal width (fallback 80)
cols=${COLUMNS:-$(tput cols 2>/dev/null || echo 80)}
# If one line fits, use it; otherwise split
if [ ${#oneline} -le "$cols" ]; then
printf '%s' "$oneline"
else
printf '%s\n%s' "$line1" "$line2"
fichmod +x ~/.claude/statusline-command.shAdd the statusLine field to ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline-command.sh"
}
}That's it! The status line updates after each interaction.
| Icon | Info | Source |
|---|---|---|
| π | Current time | date |
| π | Working directory (with ~) |
.workspace.current_dir |
| Git branch + dirty indicator | git symbolic-ref |
|
| π€ | Model name | .model.display_name |
| π | Context usage % and tokens (effective max) | .context_window |
| π/β | Auto-compact on/off | ~/.claude.json |
When auto-compact is enabled, it triggers at ~80% of the context window. This means you never actually get to use the full 200k β compaction kicks in at ~160k. The script adjusts the displayed max to reflect this reality:
- Auto-compact on:
π 35% (56k/160k)β shows effective usable limit - Auto-compact off:
π 28% (56k/200k)β shows full context window
You can override the threshold with CLAUDE_AUTOCOMPACT_PCT_OVERRIDE env var (default: 80).
Claude Code pipes JSON to your script's stdin on every interaction. Key fields:
{
"model": { "id": "claude-opus-4-6", "display_name": "Opus 4.6" },
"workspace": { "current_dir": "/home/user/project" },
"context_window": {
"used_percentage": 28,
"context_window_size": 200000,
"current_usage": {
"input_tokens": 3,
"output_tokens": 94,
"cache_creation_input_tokens": 372,
"cache_read_input_tokens": 55664
}
},
"cost": { "total_cost_usd": 1.55 },
"version": "2.1.71"
}The trickiest part was showing auto-compact status. It's not in the statusLine JSON input. After digging through the Claude Code binary with strings, we found:
autoCompactEnabledis read from~/.claude.json(home directory root, not~/.claude/.claude.json)- The key only exists when set to
falseβ when enabled (default), it's absent - jq's
//operator treatsfalseas falsy, so you needif .autoCompactEnabled == falseinstead of.autoCompactEnabled // true
jqβ for JSON parsinggitβ for branch detection- A font with emoji support (most modern terminals)
- Add
cost.total_cost_usdto show session spend - Add
cost.total_duration_msfor session duration - Add
vim.modeif you use vim mode - Add
worktree.nameif you use worktrees - Change icons to match your terminal theme
Built with Claude Code on a lazy Saturday afternoon.