Skip to content

Instantly share code, notes, and snippets.

@anderssonjohan
Created March 16, 2026 16:13
Show Gist options
  • Select an option

  • Save anderssonjohan/d3a379a1f5f008e8a70ed7f0cc71d116 to your computer and use it in GitHub Desktop.

Select an option

Save anderssonjohan/d3a379a1f5f008e8a70ed7f0cc71d116 to your computer and use it in GitHub Desktop.
~/.claude/statusline_command.sh
#!/usr/bin/env bash
# Claude Code status line — enhanced with cost, git, color thresholds, rate limits
input=$(cat)
# --- Extract fields ---
model=$(echo "$input" | jq -r '.model.display_name // "?"')
cwd=$(echo "$input" | jq -r '.cwd // .workspace.current_dir // "?"')
session_id=$(echo "$input" | jq -r '.session_id // ""')
session_name=$(echo "$input" | jq -r '.session_name // empty')
used_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
vim_mode=$(echo "$input" | jq -r '.vim.mode // empty')
cost_usd=$(echo "$input" | jq -r '.cost.total_cost_usd // empty')
duration_ms=$(echo "$input" | jq -r '.cost.total_duration_ms // empty')
lines_added=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
lines_removed=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')
# --- Colors (use $'\e' so escapes are resolved at assignment time) ---
ESC=$'\e'
reset="${ESC}[0m"
dim="${ESC}[2m"
bold="${ESC}[1m"
green="${ESC}[32m"
yellow="${ESC}[33m"
red="${ESC}[31m"
cyan="${ESC}[36m"
magenta="${ESC}[35m"
blue="${ESC}[34m"
color_for_pct() {
local pct=${1%.*}
if [ "$pct" -lt 50 ] 2>/dev/null; then
printf '%s' "$green"
elif [ "$pct" -lt 70 ] 2>/dev/null; then
printf '%s' "$green"
elif [ "$pct" -lt 80 ] 2>/dev/null; then
printf '%s' "$yellow"
else
printf '%s' "$red"
fi
}
# --- Git info (cached for 5s) ---
git_cache="/tmp/claude-statusline-git-$$"
git_info=""
if [ -d "$cwd/.git" ] || git -C "$cwd" rev-parse --git-dir >/dev/null 2>&1; then
cache_file="/tmp/claude-statusline-git-$(echo "$cwd" | md5 -q 2>/dev/null || echo "$cwd" | md5sum 2>/dev/null | cut -d' ' -f1)"
cache_age=999
if [ -f "$cache_file" ]; then
cache_age=$(( $(date +%s) - $(stat -f%m "$cache_file" 2>/dev/null || stat -c%Y "$cache_file" 2>/dev/null || echo 0) ))
fi
if [ "$cache_age" -gt 5 ]; then
branch=$(git -C "$cwd" --no-optional-locks symbolic-ref --short HEAD 2>/dev/null || git -C "$cwd" --no-optional-locks rev-parse --short HEAD 2>/dev/null || echo "")
dirty=""
if [ -n "$(git -C "$cwd" --no-optional-locks status --porcelain 2>/dev/null | head -1)" ]; then
dirty="*"
fi
echo "${branch}${dirty}" > "$cache_file" 2>/dev/null
fi
git_info=$(cat "$cache_file" 2>/dev/null)
fi
# --- Rate limits (cached for 60s) ---
rate_info=""
rate_cache="/tmp/claude-statusline-rate"
rate_cache_age=999
if [ -f "$rate_cache" ]; then
rate_cache_age=$(( $(date +%s) - $(stat -f%m "$rate_cache" 2>/dev/null || stat -c%Y "$rate_cache" 2>/dev/null || echo 0) ))
fi
if [ "$rate_cache_age" -gt 60 ]; then
# Try to get OAuth token from credentials file
creds_file="$HOME/.claude/.credentials.json"
if [ -f "$creds_file" ]; then
token=$(jq -r '.oauth_tokens // {} | to_entries[0].value.accessToken // empty' "$creds_file" 2>/dev/null)
if [ -n "$token" ]; then
usage_json=$(curl -sf -m 3 -H "Authorization: Bearer $token" "https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
if [ -n "$usage_json" ]; then
echo "$usage_json" > "$rate_cache" 2>/dev/null
fi
fi
fi
fi
if [ -f "$rate_cache" ]; then
five_hr=$(jq -r '.five_hour.utilization_percentage // empty' "$rate_cache" 2>/dev/null)
daily=$(jq -r '.daily.utilization_percentage // empty' "$rate_cache" 2>/dev/null)
if [ -n "$five_hr" ]; then
five_hr_int=${five_hr%.*}
rate_color=$(color_for_pct "$five_hr_int")
rate_info="${rate_color}5h:${five_hr_int}%${reset}"
if [ -n "$daily" ]; then
daily_int=${daily%.*}
daily_color=$(color_for_pct "$daily_int")
rate_info="${rate_info} ${daily_color}day:${daily_int}%${reset}"
fi
fi
fi
# --- Format duration ---
duration_str=""
if [ -n "$duration_ms" ] && [ "$duration_ms" != "null" ] && [ "$duration_ms" -gt 0 ] 2>/dev/null; then
total_secs=$(( duration_ms / 1000 ))
if [ "$total_secs" -ge 3600 ]; then
hrs=$(( total_secs / 3600 ))
mins=$(( (total_secs % 3600) / 60 ))
duration_str="${hrs}h${mins}m"
elif [ "$total_secs" -ge 60 ]; then
mins=$(( total_secs / 60 ))
secs=$(( total_secs % 60 ))
duration_str="${mins}m${secs}s"
else
duration_str="${total_secs}s"
fi
fi
# --- Line 1: git branch + directory (clickable) + model ---
osc_open="${ESC}]8;;"
osc_close="${ESC}\\"
dir_link="${osc_open}file://${cwd}${osc_close}${dim}${cwd}${reset}${osc_open}${osc_close}"
git_part=""
if [ -n "$git_info" ]; then
git_part="${magenta} ${git_info}${reset} "
fi
line1="${git_part}${dir_link} ${bold}${model}${reset}"
# --- Line 2: context bar (color-coded) + cost + duration + churn ---
if [ -n "$used_pct" ]; then
used_int=${used_pct%.*}
bar_color=$(color_for_pct "$used_int")
filled=$(( used_int / 5 ))
empty=$(( 20 - filled ))
bar=""
for i in $(seq 1 $filled); do bar="${bar}█"; done
for i in $(seq 1 $empty); do bar="${bar}░"; done
line2="${bar_color}ctx ${bar} ${used_pct}%${reset}"
else
line2="${dim}ctx —${reset}"
fi
# Append cost
if [ -n "$cost_usd" ] && [ "$cost_usd" != "null" ]; then
# Format to 2 decimal places
cost_fmt=$(printf '%.2f' "$cost_usd" 2>/dev/null || echo "$cost_usd")
line2="${line2} ${cyan}\$${cost_fmt}${reset}"
fi
# Append duration
if [ -n "$duration_str" ]; then
line2="${line2} ${dim}${duration_str}${reset}"
fi
# Append code churn
if [ "$lines_added" -gt 0 ] || [ "$lines_removed" -gt 0 ] 2>/dev/null; then
line2="${line2} ${green}+${lines_added}${reset}${red}-${lines_removed}${reset}"
fi
# --- Line 3: session link + rate limits + vim mode ---
if [ -n "$session_id" ]; then
session_url="https://claude.ai/code/sessions/${session_id}"
label="${session_name:-session}"
session_link="${osc_open}${session_url}${osc_close}${blue}${label}${reset}${osc_open}${osc_close}"
else
session_link="${dim}no session${reset}"
fi
line3="${session_link}"
if [ -n "$rate_info" ]; then
line3="${line3} ${rate_info}"
fi
if [ -n "$vim_mode" ]; then
line3="${line3} ${dim}vim:${vim_mode}${reset}"
fi
printf '%s\n%s\n%s\n' "$line1" "$line2" "$line3"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment