Skip to content

Instantly share code, notes, and snippets.

@mrf
Created February 9, 2026 18:54
Show Gist options
  • Select an option

  • Save mrf/bf166c6c2db4b98ed3c8e972c88733b5 to your computer and use it in GitHub Desktop.

Select an option

Save mrf/bf166c6c2db4b98ed3c8e972c88733b5 to your computer and use it in GitHub Desktop.
Claude Code multi-line statusline with context, rate limits, model, git branch, thinking mode, and cost
#!/bin/bash
# Claude Code Statusline - Multi-line, column-aligned status display
# Lines share a 3-column grid so pipes line up:
# [Col A: 31 chars] | [Col B: 31 chars] | [Col C: variable]
# Line 1: model | context bar | dir branch [wt]
# Line 2: current bar | weekly bar | extra bar
# Line 3: resets time | resets datetime | resets date
# Line 4: think ●●● | $cost
# Receives JSON via stdin from Claude Code
input=$(cat)
# ── Extract data ──
CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir // ""')
PROJECT_DIR=$(echo "$input" | jq -r '.workspace.project_dir // ""')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
TRANSCRIPT_PATH=$(echo "$input" | jq -r '.transcript_path // ""')
# Get the ACTUAL model from the transcript (last assistant message),
# since the statusline JSON model field reflects the global preference,
# not the per-session model.
MODEL="?"
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
ACTUAL_MODEL=$(tail -50 "$TRANSCRIPT_PATH" 2>/dev/null \
| jq -r 'select(.type == "assistant") | .message.model // empty' 2>/dev/null \
| tail -1)
if [ -n "$ACTUAL_MODEL" ] && [ "$ACTUAL_MODEL" != "null" ]; then
case "$ACTUAL_MODEL" in
*opus-4-6*) MODEL="Opus 4.6" ;;
*opus-4-5*) MODEL="Opus 4.5" ;;
*opus*) MODEL="Opus" ;;
*sonnet-4-5*) MODEL="Sonnet 4.5" ;;
*sonnet*) MODEL="Sonnet" ;;
*haiku-4-5*) MODEL="Haiku 4.5" ;;
*haiku*) MODEL="Haiku" ;;
*) MODEL="$ACTUAL_MODEL" ;;
esac
fi
fi
# Context window data
CONTEXT_MAX=$(echo "$input" | jq -r '.context_window.context_window_size // 0')
INPUT_TOKENS=$(echo "$input" | jq -r '.context_window.current_usage.input_tokens // 0')
OUTPUT_TOKENS=$(echo "$input" | jq -r '.context_window.current_usage.output_tokens // 0')
CACHE_READ=$(echo "$input" | jq -r '.context_window.current_usage.cache_read_input_tokens // 0')
CACHE_CREATION=$(echo "$input" | jq -r '.context_window.current_usage.cache_creation_input_tokens // 0')
USED_PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0')
CONTENT_TOKENS=$((CACHE_READ + INPUT_TOKENS + CACHE_CREATION + OUTPUT_TOKENS))
# Format cost
if [ "$COST" != "0" ] && [ "$COST" != "null" ]; then
COST_FMT=$(printf "%.2f" "$COST" 2>/dev/null || echo "$COST")
else
COST_FMT="0.00"
fi
# Shorten directory
if [ -n "$PROJECT_DIR" ] && [ "$CURRENT_DIR" != "$PROJECT_DIR" ]; then
DIR_DISPLAY="${CURRENT_DIR#"$PROJECT_DIR"/}"
else
DIR_DISPLAY="${CURRENT_DIR##*/}"
fi
# Git branch + worktree detection
GIT_BRANCH=""
IS_WORKTREE=false
if [ -n "$CURRENT_DIR" ] && [ -d "$CURRENT_DIR/.git" ] || git -C "$CURRENT_DIR" rev-parse --git-dir &>/dev/null 2>&1; then
BRANCH=$(git -C "$CURRENT_DIR" branch --show-current 2>/dev/null)
[ -n "$BRANCH" ] && GIT_BRANCH=" $BRANCH"
# In a worktree, --git-dir and --git-common-dir differ
GIT_DIR=$(git -C "$CURRENT_DIR" rev-parse --git-dir 2>/dev/null)
GIT_COMMON=$(git -C "$CURRENT_DIR" rev-parse --git-common-dir 2>/dev/null)
[ -n "$GIT_DIR" ] && [ -n "$GIT_COMMON" ] && [ "$GIT_DIR" != "$GIT_COMMON" ] && IS_WORKTREE=true
fi
# ── True-color ANSI codes ──
RST="\033[0m"
BOLD="\033[1m"
DIM="\033[2m"
BLUE="\033[38;2;0;153;255m"
ORANGE="\033[38;2;255;176;85m"
GREEN="\033[38;2;0;160;0m"
CYAN="\033[38;2;100;200;200m"
RED="\033[38;2;255;85;85m"
YELLOW="\033[38;2;230;200;0m"
WHITE="\033[38;2;220;220;220m"
GRAY="\033[38;2;180;180;180m"
MAGENTA="\033[35m"
# ── Layout constants ──
COL_W=31 # Fixed column width for cols A and B
BAR_WIDTH=15 # Progress bar dot count
SEP=" ${DIM}|${RST} "
# ── Helpers ──
# Print spaces to pad from current visible width to COL_W
pad() {
local gap=$(( COL_W - $1 ))
[ "$gap" -gt 0 ] && printf "%*s" "$gap" ""
}
build_bar() {
local pct=$1 width=$2
[ "$pct" -lt 0 ] 2>/dev/null && pct=0
[ "$pct" -gt 100 ] 2>/dev/null && pct=100
local filled=$(( pct * width / 100 ))
local empty=$(( width - filled ))
local bar_color
if [ "$pct" -ge 90 ]; then bar_color="$RED"
elif [ "$pct" -ge 70 ]; then bar_color="$YELLOW"
elif [ "$pct" -ge 50 ]; then bar_color="$ORANGE"
else bar_color="$GREEN"
fi
local filled_str="" empty_str=""
for ((i=0; i<filled; i++)); do filled_str+=""; done
for ((i=0; i<empty; i++)); do empty_str+=""; done
printf "%b%s%b%b%s%b" "$bar_color" "$filled_str" "$RST" "$DIM" "$empty_str" "$RST"
}
format_tokens() {
local n=$1
if [ "$n" -ge 1000000 ] 2>/dev/null; then
printf "%s" "$(echo "scale=1; $n / 1000000" | bc)m"
elif [ "$n" -ge 1000 ] 2>/dev/null; then
printf "%s" "$(( n / 1000 ))k"
else
printf "%s" "$n"
fi
}
format_reset_time() {
local iso="$1" style="$2"
[ -z "$iso" ] && return
local epoch
epoch=$(date -d "$iso" +%s 2>/dev/null)
[ -z "$epoch" ] && return
if [ "$style" = "time" ]; then
date -d "@$epoch" "+%-I:%M%P" 2>/dev/null
else
date -d "@$epoch" "+%b %-d, %-I:%M%P" 2>/dev/null
fi
}
cached_fetch() {
local cache_file="$1" max_age="$2"
shift 2
if [ -f "$cache_file" ]; then
local mtime now age
mtime=$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null || echo 0)
now=$(date +%s)
age=$(( now - mtime ))
if [ "$age" -lt "$max_age" ]; then
cat "$cache_file" 2>/dev/null
return
fi
fi
local resp
resp=$(curl -s --max-time 3 "$@" 2>/dev/null)
if [ -n "$resp" ] && [ "$resp" != "null" ]; then
echo "$resp" > "$cache_file" 2>/dev/null
echo "$resp"
elif [ -f "$cache_file" ]; then
cat "$cache_file" 2>/dev/null
fi
}
# ── Read settings ──
THINKING="off"
EFFORT=""
SETTINGS_FILE="$HOME/.claude/settings.json"
if [ -f "$SETTINGS_FILE" ]; then
THINK_VAL=$(jq -r '.alwaysThinkingEnabled // false' "$SETTINGS_FILE" 2>/dev/null)
[ "$THINK_VAL" = "true" ] && THINKING="on"
EFFORT=$(jq -r '.effortLevel // ""' "$SETTINGS_FILE" 2>/dev/null)
fi
# ── Context color ──
if [ "$USED_PCT" -lt 50 ]; then CTX_COLOR="$GREEN"
elif [ "$USED_PCT" -lt 75 ]; then CTX_COLOR="$YELLOW"
else CTX_COLOR="$RED"
fi
# ── Pre-compute display strings ──
TOKENS_USED_STR=$(format_tokens $CONTENT_TOKENS)
TOKENS_MAX_STR=$(format_tokens $CONTEXT_MAX)
# ═══════════════════════════════════════════════════════════
# LINE 1: model | context bar | dir branch [wt]
# ═══════════════════════════════════════════════════════════
# Col A: model
printf "%b%b%s%b" "$BOLD" "$BLUE" "$MODEL" "$RST"
pad ${#MODEL}
printf "%b" "$SEP"
# Col B: context bar
build_bar "$USED_PCT" $BAR_WIDTH
USED_STR="${USED_PCT}%"
TOK_STR="${TOKENS_USED_STR}/${TOKENS_MAX_STR}"
printf " %b%s%b %b%s%b" "$CTX_COLOR" "$USED_STR" "$RST" "$DIM" "$TOK_STR" "$RST"
pad $(( BAR_WIDTH + 1 + ${#USED_STR} + 1 + ${#TOK_STR} ))
printf "%b" "$SEP"
# Col C: dir branch [wt] (free to grow)
printf "%b%s%b" "$MAGENTA" "$DIR_DISPLAY" "$RST"
[ -n "$GIT_BRANCH" ] && printf "%b%b%s%b" "$DIM" "$GREEN" "$GIT_BRANCH" "$RST"
$IS_WORKTREE && printf " %b%bwt%b" "$BOLD" "$ORANGE" "$RST"
echo
# ═══════════════════════════════════════════════════════════
# LINE 2: current bar | weekly bar | extra bar
# LINE 3: resets | resets | resets
# LINE 4: think ●●● | $cost
# ═══════════════════════════════════════════════════════════
CREDS_FILE="$HOME/.claude/.credentials.json"
USAGE_DATA=""
if [ -f "$CREDS_FILE" ]; then
TOKEN=$(jq -r '.claudeAiOauth.accessToken // ""' "$CREDS_FILE" 2>/dev/null)
if [ -n "$TOKEN" ]; then
RESP=$(cached_fetch "/tmp/claude-statusline-usage-cache.json" 60 \
-H "Accept: application/json" \
-H "Authorization: Bearer $TOKEN" \
-H "anthropic-beta: oauth-2025-04-20" \
"https://api.anthropic.com/api/oauth/usage")
if [ -n "$RESP" ] && echo "$RESP" | jq -e '.five_hour' &>/dev/null; then
USAGE_DATA="$RESP"
fi
fi
fi
EXTRA_ENABLED=false
if [ -n "$USAGE_DATA" ]; then
FIVE_PCT=$(echo "$USAGE_DATA" | jq -r '.five_hour.utilization // 0' | xargs printf "%.0f" 2>/dev/null)
FIVE_RESET_ISO=$(echo "$USAGE_DATA" | jq -r '.five_hour.resets_at // ""')
FIVE_RESET=$(format_reset_time "$FIVE_RESET_ISO" "time")
SEVEN_PCT=$(echo "$USAGE_DATA" | jq -r '.seven_day.utilization // 0' | xargs printf "%.0f" 2>/dev/null)
SEVEN_RESET_ISO=$(echo "$USAGE_DATA" | jq -r '.seven_day.resets_at // ""')
SEVEN_RESET=$(format_reset_time "$SEVEN_RESET_ISO" "datetime")
EXTRA_ENABLED=$(echo "$USAGE_DATA" | jq -r '.extra_usage.is_enabled // false')
if [ "$EXTRA_ENABLED" = "true" ]; then
EXTRA_PCT=$(echo "$USAGE_DATA" | jq -r '.extra_usage.utilization // 0' | xargs printf "%.0f" 2>/dev/null)
EXTRA_USED=$(echo "$USAGE_DATA" | jq -r '.extra_usage.used_credits // 0')
EXTRA_LIMIT=$(echo "$USAGE_DATA" | jq -r '.extra_usage.monthly_limit // 0')
EXTRA_USED_D=$(echo "scale=2; $EXTRA_USED / 100" | bc 2>/dev/null || echo "0")
EXTRA_LIMIT_D=$(echo "scale=2; $EXTRA_LIMIT / 100" | bc 2>/dev/null || echo "0")
EXTRA_RESET=$(date -d "$(date +%Y-%m-01) +1 month" "+%b %-d" 2>/dev/null)
fi
fi
# ── LINE 2: rate limit bars ──
if [ -n "$USAGE_DATA" ]; then
# Col A: current (5h) bar
printf "%bcurrent:%b " "$WHITE" "$RST"
build_bar "$FIVE_PCT" $BAR_WIDTH
PCT_STR="${FIVE_PCT}%"
printf " %b%s%b" "$CYAN" "$PCT_STR" "$RST"
pad $(( 9 + BAR_WIDTH + 1 + ${#PCT_STR} ))
printf "%b" "$SEP"
# Col B: weekly (7d) bar
printf "%bweekly:%b " "$WHITE" "$RST"
build_bar "$SEVEN_PCT" $BAR_WIDTH
PCT_STR="${SEVEN_PCT}%"
printf " %b%s%b" "$CYAN" "$PCT_STR" "$RST"
pad $(( 9 + BAR_WIDTH + 1 + ${#PCT_STR} ))
printf "%b" "$SEP"
# Col C: extra bar (if enabled)
if [ "$EXTRA_ENABLED" = "true" ]; then
printf "%bextra:%b " "$WHITE" "$RST"
build_bar "$EXTRA_PCT" $BAR_WIDTH
printf " %b\$%s/\$%s%b" "$CYAN" "$EXTRA_USED_D" "$EXTRA_LIMIT_D" "$RST"
fi
fi
echo
# ── LINE 3: all resets (only if usage data) ──
if [ -n "$USAGE_DATA" ]; then
# Col A: 5h reset (under current bar)
RESET_A="resets ${FIVE_RESET}"
printf "%b%s%b" "$GRAY" "$RESET_A" "$RST"
pad ${#RESET_A}
printf "%b" "$SEP"
# Col B: 7d reset (under weekly bar)
RESET_B="resets ${SEVEN_RESET}"
printf "%b%s%b" "$GRAY" "$RESET_B" "$RST"
pad ${#RESET_B}
printf "%b" "$SEP"
# Col C: extra reset (under extra bar)
if [ "$EXTRA_ENABLED" = "true" ]; then
printf "%bresets %s%b" "$GRAY" "$EXTRA_RESET" "$RST"
fi
echo
fi
# ── LINE 4: think | $cost ──
# Col A: think effort
if [ "$THINKING" = "on" ]; then
case "$EFFORT" in
high) printf "%bthink %b●●●%b" "$WHITE" "$GREEN" "$RST"; TVLEN=9 ;;
medium) printf "%bthink %b●●%b%b○%b" "$WHITE" "$YELLOW" "$RST" "$DIM" "$RST"; TVLEN=9 ;;
low) printf "%bthink %b●%b%b○○%b" "$WHITE" "$RED" "$RST" "$DIM" "$RST"; TVLEN=9 ;;
*) printf "%bthink %b●●●%b" "$WHITE" "$GREEN" "$RST"; TVLEN=9 ;;
esac
else
printf "%bthink %b○○○%b" "$WHITE" "$DIM" "$RST"; TVLEN=9
fi
pad $TVLEN
printf "%b" "$SEP"
# Col B: $cost
COST_STR="\$${COST_FMT}"
printf "%b%s%b" "$DIM" "$COST_STR" "$RST"
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment