Skip to content

Instantly share code, notes, and snippets.

@gsiener
Last active January 31, 2026 17:38
Show Gist options
  • Select an option

  • Save gsiener/ee75728f58c7156cd79cc4e1d844e25d to your computer and use it in GitHub Desktop.

Select an option

Save gsiener/ee75728f58c7156cd79cc4e1d844e25d to your computer and use it in GitHub Desktop.
Claude Code custom statusline script

Claude Code Custom Statusline

A powerline-style statusline for Claude Code displaying git info, model, context usage, cost, and timing information.

Screenshot

 ✗ main  (+5,-2)  Opus 4.5  13% [█░░░░░░░░░]  $40.09       resets in 25m | auto-compact in 87%

Segments (left to right)

Segment Color Description
Git branch White bg, black text Shows dirty indicator (✗) and branch name
Git changes Red bg, white text Lines added/deleted (+N,-N)
Model Purple bg, black text Current Claude model
Context Grey/Yellow/Red bg Usage percentage with progress bar
Cost Green bg, black text Session cost from claude-statusbar
Extra usage Red bg, white text Shows "EXTRA" when in extra usage mode

Right side: resets in Xm | auto-compact in X%

Context Colors

  • Grey: < 60% usage
  • Yellow: 60-80% usage
  • Red: > 80% usage

Setup

  1. Save statusline-command.sh to ~/.claude/statusline-command.sh
  2. Make executable: chmod +x ~/.claude/statusline-command.sh
  3. Add to ~/.claude/settings.json:
{
  "statusLine": {
    "type": "command",
    "command": "/path/to/.claude/statusline-command.sh"
  }
}

Dependencies

  • jq - JSON parsing
  • git - Repository info
  • claude-statusbar (optional) - Cost and timer data

Font

Requires a Powerline-compatible font (e.g., Nerd Fonts) for chevron separators.

#!/bin/bash
# ==============================================================================
# Claude Code Custom Statusline Script - Powerline Style
# ==============================================================================
# Read JSON input from stdin
input=$(cat)
# Powerline chevron separator (U+E0B0)
SEP=$(printf '\xee\x82\xb0')
# Background colors
BG_GREEN=42
BG_YELLOW=43
BG_RED=41
BG_PINK=45
BG_GREY="48;5;240"
BG_WHITE=47
# Foreground colors (for chevrons - must match bg colors)
FG_GREEN=32
FG_YELLOW=33
FG_RED=31
FG_PINK=35
FG_BLACK=30
FG_WHITE=37
FG_WHITE_BRIGHT=97
FG_GREY="38;5;240"
# Extract model name
model=$(echo "$input" | jq -r '.model.display_name // .model // "unknown"')
# Get context usage percentage, build progress bar, and determine color
ctx_content=""
ctx_bg=$BG_GREY
ctx_fg=$FG_GREY
ctx_text_color=$FG_WHITE_BRIGHT
usage=$(echo "$input" | jq '.context_window.current_usage')
if [ "$usage" != "null" ] && [ "$usage" != "" ]; then
current=$(echo "$usage" | jq '.input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens')
size=$(echo "$input" | jq '.context_window.context_window_size')
if [ "$size" != "null" ] && [ "$size" != "0" ] && [ -n "$size" ]; then
pct=$((current * 100 / size))
# Build progress bar with percentage in front
filled=$((pct / 10))
empty=$((10 - filled))
bar=""
for ((j=0; j<filled; j++)); do
bar="${bar}█"
done
for ((j=0; j<empty; j++)); do
bar="${bar}░"
done
ctx_content="${pct}% [${bar}]"
# Color based on percentage
if [ $pct -lt 60 ]; then
ctx_bg=$BG_GREY
ctx_fg=$FG_GREY
ctx_text_color=$FG_WHITE_BRIGHT
elif [ $pct -lt 80 ]; then
ctx_bg=$BG_YELLOW
ctx_fg=$FG_YELLOW
ctx_text_color=$FG_BLACK
else
ctx_bg=$BG_RED
ctx_fg=$FG_RED
ctx_text_color=$FG_WHITE_BRIGHT
fi
fi
fi
# Get data from claude-statusbar (JSON)
cost=""
timer=""
extra_usage=""
if command -v claude-statusbar &>/dev/null; then
statusbar_json=$(echo '{}' | claude-statusbar --json-output 2>/dev/null)
if [ -n "$statusbar_json" ]; then
# Extract cost
cost_raw=$(echo "$statusbar_json" | jq -r '.usage.cost_usd // empty')
if [ -n "$cost_raw" ] && [ "$cost_raw" != "null" ]; then
cost=$(printf '$%.2f' "$cost_raw")
fi
# Extract timer
timer=$(echo "$statusbar_json" | jq -r '.meta.reset_time // empty')
# Check for extra usage (when tokens exceed limit)
total_tokens=$(echo "$statusbar_json" | jq -r '.usage.total_tokens // 0')
token_limit=$(echo "$statusbar_json" | jq -r '.usage.token_limit // 999999')
if [ "$total_tokens" -gt "$token_limit" ] 2>/dev/null; then
extra_usage="EXTRA"
fi
# Also check source field for extra usage indicator
source=$(echo "$statusbar_json" | jq -r '.usage.source // empty')
if [ "$source" = "extra" ] || [ "$source" = "overflow" ]; then
extra_usage="EXTRA"
fi
fi
fi
# Get git info
git_branch=""
git_dirty=""
if git rev-parse --git-dir >/dev/null 2>&1; then
branch=$(git --no-optional-locks symbolic-ref --short HEAD 2>/dev/null || git --no-optional-locks rev-parse --short HEAD 2>/dev/null)
if [ -n "$branch" ]; then
git_branch="$branch"
if ! git --no-optional-locks diff --quiet 2>/dev/null || ! git --no-optional-locks diff --cached --quiet 2>/dev/null; then
git_dirty="✗ "
fi
fi
fi
# Get git line changes
git_changes=""
if [ -n "$git_branch" ]; then
if ! git --no-optional-locks diff --quiet 2>/dev/null || ! git --no-optional-locks diff --cached --quiet 2>/dev/null; then
stats=$(git --no-optional-locks diff --numstat 2>/dev/null | awk '{add+=$1; del+=$2} END {print add","del}')
adds=$(echo "$stats" | cut -d',' -f1)
dels=$(echo "$stats" | cut -d',' -f2)
if [ -n "$adds" ] && [ -n "$dels" ] && [ "$adds" != "" ] && [ "$dels" != "" ]; then
git_changes="(+${adds},-${dels})"
fi
fi
fi
# Get remaining context percentage
remaining_pct=""
remaining=$(echo "$input" | jq -r '.context_window.remaining_percentage // empty')
if [ -n "$remaining" ]; then
remaining_pct=$(printf "%.0f" "$remaining")
fi
# Build segments array: "content|bg_color|fg_for_chevron|text_color"
segments=()
plain_segments=()
# Segment 1: Git branch (White bg, black text)
if [ -n "$git_branch" ]; then
segments+=("${git_dirty}${git_branch}|${BG_WHITE}|${FG_WHITE}|${FG_BLACK}")
plain_segments+=("${git_dirty}${git_branch}")
fi
# Segment 2: Git changes (Red)
if [ -n "$git_changes" ]; then
segments+=("${git_changes}|${BG_RED}|${FG_RED}|${FG_WHITE_BRIGHT}")
plain_segments+=("${git_changes}")
fi
# Segment 3: Model (Purple)
if [ -n "$model" ] && [ "$model" != "unknown" ]; then
segments+=("${model}|${BG_PINK}|${FG_PINK}|${FG_BLACK}")
plain_segments+=("${model}")
fi
# Segment 4: Context usage (dynamic color with progress bar)
if [ -n "$ctx_content" ]; then
segments+=("${ctx_content}|${ctx_bg}|${ctx_fg}|${ctx_text_color}")
plain_segments+=("${ctx_content}")
fi
# Segment 5: Cost (Green)
if [ -n "$cost" ]; then
segments+=("${cost}|${BG_GREEN}|${FG_GREEN}|${FG_BLACK}")
plain_segments+=("${cost}")
fi
# Segment 6: Extra usage indicator (Red, if active)
if [ -n "$extra_usage" ]; then
segments+=("${extra_usage}|${BG_RED}|${FG_RED}|${FG_WHITE_BRIGHT}")
plain_segments+=("${extra_usage}")
fi
# Render the powerline
output=""
output_plain=""
num_segments=${#segments[@]}
for ((i=0; i<num_segments; i++)); do
IFS='|' read -r content bg chevron_fg text_color <<< "${segments[$i]}"
if [ $i -eq 0 ]; then
# First segment: just the content
output=$(printf '\033[%s;%sm %s \033[0m' "$bg" "$text_color" "$content")
else
# Get previous segment's chevron color
IFS='|' read -r _ prev_bg prev_chevron_fg _ <<< "${segments[$((i-1))]}"
# Chevron: previous bg color as foreground, current bg as background
output=$(printf '%s\033[%s;%sm%s\033[0m' "$output" "$prev_chevron_fg" "$bg" "$SEP")
# Content
output=$(printf '%s\033[%s;%sm %s \033[0m' "$output" "$bg" "$text_color" "$content")
fi
output_plain="${output_plain} ${content} "
done
# Final chevron (to default background)
if [ $num_segments -gt 0 ]; then
IFS='|' read -r _ _ last_chevron_fg _ <<< "${segments[$((num_segments-1))]}"
output=$(printf '%s\033[%sm%s\033[0m' "$output" "$last_chevron_fg" "$SEP")
output_plain="${output_plain}${SEP}"
fi
# Right side: Timer and context remaining
right_side=""
right_side_plain=""
if [ -n "$timer" ] && [ -n "$remaining_pct" ]; then
right_side="resets in ${timer} | auto-compact in ${remaining_pct}%"
elif [ -n "$timer" ]; then
right_side="resets in ${timer}"
elif [ -n "$remaining_pct" ]; then
right_side="auto-compact in ${remaining_pct}%"
fi
right_side_plain="$right_side"
# Calculate padding for right alignment
term_width=$(tput cols 2>/dev/null || echo 80)
left_len=${#output_plain}
right_len=${#right_side_plain}
padding=$((term_width - left_len - right_len - 1))
if [ $padding -lt 1 ]; then
padding=1
fi
# Output the complete statusline
printf '%s%*s%s' "$output" "$padding" "" "$right_side"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment