Last active
March 30, 2026 16:30
-
-
Save ericboehs/c4340c6febd1b9848eb1656197bf17ca to your computer and use it in GitHub Desktop.
Claude Code status line — git status, proxy detection, pace-aware usage tracking, auto-wrapping
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| input=$(cat) | |
| # --- Configuration --- | |
| PACE_AHEAD_THRESHOLD=10 # Show warning when this many % ahead of expected pace | |
| PACE_BEHIND_THRESHOLD=25 # Show nudge when this many % behind expected pace | |
| USAGE_ALWAYS_SHOW=90 # Always show usage when actual % >= this value | |
| # --- Parse input JSON --- | |
| current_dir=$(echo "$input" | jq -r '.workspace.current_dir') | |
| total=$(echo "$input" | jq -r '.context_window.context_window_size // 200000') | |
| pct=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1) | |
| used=$(echo "$total * $pct / 100" | bc) | |
| model_id=$(echo "$input" | jq -r '.model.id // "unknown"') | |
| model=$(echo "$model_id" | sed 's/\[.*\]$//; s/^claude-//' | sed \ | |
| -e 's/^opus-4-6$/opus/' \ | |
| -e 's/^opus-4\.6$/opus/' \ | |
| -e 's/^sonnet-4-6$/sonnet/' \ | |
| -e 's/^sonnet-4\.6$/sonnet/' \ | |
| -e 's/^sonnet-4\.5$/sonnet-4.5/' \ | |
| -e 's/^haiku-4-5.*$/haiku/' \ | |
| ) | |
| # --- Proxy-specific context window overrides --- | |
| if [ "$ANTHROPIC_BASE_URL" = "http://localhost:4141" ]; then | |
| total=192000 | |
| pct=$(echo "$used * 100 / $total" | bc) | |
| elif [ "$ANTHROPIC_BASE_URL" = "http://localhost:8083" ]; then | |
| total=131000 | |
| pct=$(echo "$used * 100 / $total" | bc) | |
| fi | |
| # --- Format token counts --- | |
| if [ "$used" -ge 1000000 ]; then | |
| used_fmt="$(echo "$used/1000000" | bc)M" | |
| elif [ "$used" -ge 1000 ]; then | |
| used_fmt="$(echo "scale=0; $used/1000" | bc)k" | |
| else | |
| used_fmt="$used" | |
| fi | |
| if [ "$total" -ge 1000000 ]; then | |
| total_fmt="$(echo "$total/1000000" | bc)M" | |
| else | |
| total_fmt="$(echo "scale=0; $total/1000" | bc)k" | |
| fi | |
| # --- Context percentage color --- | |
| if [ "$pct" -ge 80 ] 2>/dev/null; then | |
| pct_color='\033[31m' | |
| elif [ "$pct" -ge 50 ] 2>/dev/null; then | |
| pct_color='\033[33m' | |
| else | |
| pct_color='\033[36m' | |
| fi | |
| # --- Time-aware usage projection --- | |
| # Projects usage to end of window. Outputs: "projected_pct color_code" | |
| # Args: usage_pct elapsed_pct (both 0-100 integers) | |
| pace_projected() { | |
| local usage=$1 elapsed=$2 | |
| local projected=$usage | |
| if [ "$elapsed" -gt 5 ] 2>/dev/null && [ "$usage" -gt 0 ] 2>/dev/null; then | |
| projected=$(( usage * 100 / elapsed )) | |
| fi | |
| local color | |
| if [ "$projected" -ge 100 ] 2>/dev/null; then color='\033[31m' | |
| elif [ "$projected" -ge 75 ] 2>/dev/null; then color='\033[33m' | |
| else color='\033[36m'; fi | |
| echo "${projected}:${color}" | |
| } | |
| # === Build segments (plain text for measuring, colored for display) === | |
| now=$(date +%s) | |
| cols=$(tput cols 2>/dev/null || echo 80) | |
| # --- Dir + branch --- | |
| if [ "$current_dir" = "$HOME" ]; then | |
| dir_display="~" | |
| else | |
| dir_display=$(basename "$current_dir") | |
| [ ${#dir_display} -gt 30 ] && dir_display="${dir_display:0:29}…" | |
| fi | |
| if [ -n "$SSH_CONNECTION" ]; then | |
| host=$(hostname -s) | |
| # Friendly hostname aliases | |
| case "$host" in | |
| OKL-*) host="gfe" ;; | |
| esac | |
| seg_dir_plain="${host}:${dir_display}" | |
| seg_dir_color="\033[32m${host}\033[0m:\033[34m${dir_display}\033[0m" | |
| else | |
| seg_dir_plain="$dir_display" | |
| seg_dir_color="\033[34m${dir_display}\033[0m" | |
| fi | |
| # --- Git branch + status --- | |
| seg_git_plain="" | |
| seg_git_color="" | |
| if [ -d "$current_dir/.git" ]; then | |
| cd "$current_dir" | |
| git_branch=$(git branch --show-current 2>/dev/null) | |
| if [ -n "$git_branch" ]; then | |
| [ ${#git_branch} -gt 30 ] && git_branch="${git_branch:0:29}…" | |
| git_indicators="" | |
| git_status=$(git status --porcelain 2>/dev/null | head -20) | |
| if [ -n "$git_status" ]; then | |
| echo "$git_status" | grep -q '^.[MD]' && git_indicators="${git_indicators}!" | |
| echo "$git_status" | grep -q '^??' && git_indicators="${git_indicators}?" | |
| echo "$git_status" | grep -q '^[MADRC]' && git_indicators="${git_indicators}+" | |
| fi | |
| if [ -n "$git_indicators" ]; then | |
| seg_git_plain=" ${git_branch} [${git_indicators}]" | |
| seg_git_color=" \033[35m${git_branch}\033[0m \033[33m[${git_indicators}]\033[0m" | |
| else | |
| seg_git_plain=" ${git_branch}" | |
| seg_git_color=" \033[35m${git_branch}\033[0m" | |
| fi | |
| fi | |
| fi | |
| # --- Proxy --- | |
| seg_proxy_plain="" | |
| seg_proxy_color="" | |
| usage_str="" | |
| usage_str_plain="" | |
| if [ "$ANTHROPIC_BASE_URL" = "http://localhost:4141" ]; then | |
| seg_proxy_plain=" copilot" | |
| seg_proxy_color=" \033[31mcopilot\033[0m" | |
| usage_pct=$(curl -s --max-time 1 http://localhost:4141/usage 2>/dev/null \ | |
| | jq -r '.quota_snapshots.premium_interactions | ((.entitlement - .remaining) / .entitlement * 100 | round)' 2>/dev/null) | |
| if [ -n "$usage_pct" ] && [ "$usage_pct" != "null" ]; then | |
| if [ "$usage_pct" -ge 80 ] 2>/dev/null; then uc='\033[31m'; elif [ "$usage_pct" -ge 50 ] 2>/dev/null; then uc='\033[33m'; else uc='\033[36m'; fi | |
| usage_str=" ${uc}${usage_pct}%%\033[0m" | |
| usage_str_plain=" ${usage_pct}%" | |
| fi | |
| elif proxy="${ANTHROPIC_BASE_URL:-${HTTPS_PROXY:-$HTTP_PROXY}}"; [ -n "$proxy" ]; then | |
| if echo "$proxy" | grep -q "localhost:8083"; then | |
| seg_proxy_plain=" cerebras" | |
| seg_proxy_color=" \033[31mcerebras\033[0m" | |
| cache_file="/tmp/cerebras-usage-cache" | |
| cache_max_age=60 | |
| if [ -f "$cache_file" ] && [ "$(( now - $(stat -f%m "$cache_file") ))" -lt "$cache_max_age" ]; then | |
| cerebras_info=$(cat "$cache_file") | |
| else | |
| ( | |
| CEREBRAS_API_KEY="${CEREBRAS_API_KEY:-$(op item get "cerebras.ai" --fields "label=API Key (Cerebras Code)" --reveal 2>/dev/null)}" | |
| if [ -n "$CEREBRAS_API_KEY" ]; then | |
| headers=$(curl -sS -D /dev/stdout --max-time 5 'https://api.cerebras.ai/v1/chat/completions' \ | |
| -H "Authorization: Bearer $CEREBRAS_API_KEY" \ | |
| -H 'Content-Type: application/json' \ | |
| -d '{"model":"zai-glm-4.7","messages":[{"role":"user","content":"hi"}],"max_tokens":1}' \ | |
| -o /dev/null 2>/dev/null) | |
| req_day_rem=$(echo "$headers" | grep -i 'x-ratelimit-remaining-requests-day' | cut -d' ' -f2 | tr -d '\r') | |
| tok_day_rem=$(echo "$headers" | grep -i 'x-ratelimit-remaining-tokens-day' | cut -d' ' -f2 | tr -d '\r') | |
| if [ -n "$req_day_rem" ]; then | |
| req_pct=$(( (72000 - req_day_rem) * 100 / 72000 )) | |
| tok_pct=$(( (24000000 - tok_day_rem) * 100 / 24000000 )) | |
| echo "${req_pct}:${tok_pct}" > "$cache_file" | |
| fi | |
| fi | |
| ) & | |
| [ -f "$cache_file" ] && cerebras_info=$(cat "$cache_file") | |
| fi | |
| if [ -n "$cerebras_info" ]; then | |
| req_pct=$(echo "$cerebras_info" | cut -d: -f1) | |
| tok_pct=$(echo "$cerebras_info" | cut -d: -f2) | |
| day_elapsed=$(( (now % 86400) * 100 / 86400 )) | |
| req_result=$(pace_projected "$req_pct" "$day_elapsed") | |
| req_proj=$(echo "$req_result" | cut -d: -f1) | |
| rc=$(echo "$req_result" | cut -d: -f2-) | |
| tok_result=$(pace_projected "$tok_pct" "$day_elapsed") | |
| tok_proj=$(echo "$tok_result" | cut -d: -f1) | |
| tc=$(echo "$tok_result" | cut -d: -f2-) | |
| if [ "$req_proj" -ge 90 ] 2>/dev/null; then | |
| usage_str="${usage_str} ${rc}r:~${req_proj}%%\033[0m" | |
| usage_str_plain="${usage_str_plain} r:~${req_proj}%" | |
| fi | |
| if [ "$tok_proj" -ge 90 ] 2>/dev/null; then | |
| usage_str="${usage_str} ${tc}t:~${tok_proj}%%\033[0m" | |
| usage_str_plain="${usage_str_plain} t:~${tok_proj}%" | |
| fi | |
| fi | |
| else | |
| proxy_short=$(echo "$proxy" | sed 's|^https\?://||') | |
| seg_proxy_plain=" ${proxy_short}" | |
| seg_proxy_color=" \033[31m${proxy_short}\033[0m" | |
| fi | |
| else | |
| # Direct Claude subscription — fetch 5h/7d usage with reset times | |
| # Cache format: five_pct:seven_pct:five_reset_epoch:seven_reset_epoch:extra_used:extra_limit | |
| cache_file="/tmp/claude-usage-cache" | |
| cache_max_age=120 | |
| if [ -f "$cache_file" ] && [ "$(( now - $(stat -f%m "$cache_file") ))" -lt "$cache_max_age" ]; then | |
| claude_usage=$(cat "$cache_file") | |
| else | |
| ( | |
| token=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null) | |
| if [ -n "$token" ]; then | |
| resp=$(curl -s --max-time 5 'https://api.anthropic.com/api/oauth/usage' \ | |
| -H "Authorization: Bearer $token" \ | |
| -H "anthropic-beta: oauth-2025-04-20" \ | |
| -H "Content-Type: application/json" 2>/dev/null) | |
| five=$(echo "$resp" | jq -r '.five_hour.utilization // empty' 2>/dev/null | cut -d. -f1) | |
| seven=$(echo "$resp" | jq -r '.seven_day.utilization // empty' 2>/dev/null | cut -d. -f1) | |
| five_reset=$(echo "$resp" | jq -r '.five_hour.resets_at // empty' 2>/dev/null) | |
| seven_reset=$(echo "$resp" | jq -r '.seven_day.resets_at // empty' 2>/dev/null) | |
| if [ -n "$five" ] && [ -n "$seven" ]; then | |
| five_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$five_reset" | cut -d. -f1)" +%s 2>/dev/null || echo "0") | |
| seven_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$seven_reset" | cut -d. -f1)" +%s 2>/dev/null || echo "0") | |
| extra_used=$(echo "$resp" | jq -r '.extra_usage.used_credits // 0' 2>/dev/null | cut -d. -f1) | |
| extra_limit=$(echo "$resp" | jq -r '.extra_usage.monthly_limit // 0' 2>/dev/null | cut -d. -f1) | |
| echo "${five}:${seven}:${five_epoch}:${seven_epoch}:${extra_used}:${extra_limit}" > "$cache_file" | |
| fi | |
| fi | |
| ) & | |
| [ -f "$cache_file" ] && claude_usage=$(cat "$cache_file") | |
| fi | |
| if [ -n "$claude_usage" ]; then | |
| five_pct=$(echo "$claude_usage" | cut -d: -f1) | |
| seven_pct=$(echo "$claude_usage" | cut -d: -f2) | |
| five_reset_epoch=$(echo "$claude_usage" | cut -d: -f3) | |
| seven_reset_epoch=$(echo "$claude_usage" | cut -d: -f4) | |
| extra_used=$(echo "$claude_usage" | cut -d: -f5) | |
| extra_limit=$(echo "$claude_usage" | cut -d: -f6) | |
| [ -z "$extra_used" ] && extra_used=0 | |
| [ -z "$extra_limit" ] && extra_limit=0 | |
| # Format time-to-reset as compact string (e.g. "4.5h" or "2.3d") | |
| fmt_reset() { | |
| local reset_epoch=$1 | |
| if [ -n "$reset_epoch" ] && [ "$reset_epoch" -gt 0 ] 2>/dev/null; then | |
| local remaining=$(( reset_epoch - now )) | |
| [ "$remaining" -lt 0 ] && remaining=0 | |
| if [ "$remaining" -lt 3600 ]; then | |
| echo "$((remaining / 60))m" | |
| elif [ "$remaining" -lt 86400 ]; then | |
| local h=$((remaining / 3600)) | |
| local m=$(( (remaining % 3600) / 60 )) | |
| if [ "$m" -ge 45 ]; then echo "$(( h + 1 ))h" | |
| elif [ "$m" -ge 15 ]; then echo "${h}.5h" | |
| else echo "${h}h"; fi | |
| else | |
| local d=$((remaining / 86400)) | |
| local h=$(( (remaining % 86400) / 3600 )) | |
| if [ "$h" -ge 12 ]; then echo "$(( d + 1 ))d" | |
| else echo "${d}.$(( h * 10 / 24 ))d"; fi | |
| fi | |
| fi | |
| } | |
| # How far ahead/behind expected pace for a window | |
| # ahead = actual_usage - expected_usage_at_this_point | |
| # expected = elapsed_pct = ((window_duration - remaining) / window_duration) * 100 | |
| # Positive = ahead (burning fast), negative = behind (on track) | |
| # Args: usage_pct reset_epoch window_seconds | |
| # Output: "ahead_pct:color_code" | |
| claude_pace() { | |
| local usage=$1 reset_epoch=$2 window_secs=$3 | |
| local ahead=0 | |
| if [ "$reset_epoch" -gt 0 ] 2>/dev/null; then | |
| local remaining=$(( reset_epoch - now )) | |
| [ "$remaining" -lt 0 ] && remaining=0 | |
| local elapsed=$(( window_secs - remaining )) | |
| [ "$elapsed" -lt 0 ] && elapsed=0 | |
| local expected=$(( elapsed * 100 / window_secs )) | |
| ahead=$(( usage - expected )) | |
| fi | |
| local color | |
| if [ "$ahead" -ge 20 ] 2>/dev/null; then color='\033[31m' | |
| elif [ "$ahead" -ge 10 ] 2>/dev/null; then color='\033[33m' | |
| else color='\033[36m'; fi | |
| echo "${ahead}:${color}" | |
| } | |
| # --- 5h window (18000s) --- | |
| if [ "$five_pct" -gt 0 ] 2>/dev/null; then | |
| five_result=$(claude_pace "$five_pct" "$five_reset_epoch" 18000) | |
| five_ahead=$(echo "$five_result" | cut -d: -f1) | |
| fc=$(echo "$five_result" | cut -d: -f2-) | |
| five_behind=$(( -five_ahead )) | |
| if [ "$five_ahead" -ge "$PACE_AHEAD_THRESHOLD" ] 2>/dev/null || [ "$five_behind" -ge "$PACE_BEHIND_THRESHOLD" ] 2>/dev/null || [ "$five_pct" -ge "$USAGE_ALWAYS_SHOW" ] 2>/dev/null; then | |
| if [ "$five_ahead" -gt 0 ] 2>/dev/null; then | |
| five_label="5h: +${five_ahead}%% @ ${five_pct}%%" | |
| five_label_plain="5h: +${five_ahead}% @ ${five_pct}%" | |
| else | |
| five_label="5h: ${five_ahead}%%" | |
| five_label_plain="5h: ${five_ahead}%" | |
| fi | |
| # Add time-to-reset when usage is high | |
| if [ "$five_pct" -ge 80 ] 2>/dev/null; then | |
| five_reset_str=$(fmt_reset "$five_reset_epoch") | |
| if [ -n "$five_reset_str" ]; then | |
| five_label="${five_label}↻${five_reset_str}" | |
| five_label_plain="${five_label_plain}↻${five_reset_str}" | |
| fi | |
| fi | |
| usage_str="${usage_str} ${fc}${five_label}\033[0m" | |
| usage_str_plain="${usage_str_plain} ${five_label_plain}" | |
| fi | |
| fi | |
| # --- 7d window (604800s) --- | |
| if [ "$seven_pct" -gt 0 ] 2>/dev/null; then | |
| seven_result=$(claude_pace "$seven_pct" "$seven_reset_epoch" 604800) | |
| seven_ahead=$(echo "$seven_result" | cut -d: -f1) | |
| sc=$(echo "$seven_result" | cut -d: -f2-) | |
| seven_behind=$(( -seven_ahead )) | |
| if [ "$seven_ahead" -ge "$PACE_AHEAD_THRESHOLD" ] 2>/dev/null || [ "$seven_behind" -ge "$PACE_BEHIND_THRESHOLD" ] 2>/dev/null || [ "$seven_pct" -ge "$USAGE_ALWAYS_SHOW" ] 2>/dev/null; then | |
| if [ "$seven_ahead" -gt 0 ] 2>/dev/null; then | |
| seven_label="7d: +${seven_ahead}%% @ ${seven_pct}%%" | |
| seven_label_plain="7d: +${seven_ahead}% @ ${seven_pct}%" | |
| else | |
| seven_label="7d: ${seven_ahead}%%" | |
| seven_label_plain="7d: ${seven_ahead}%" | |
| fi | |
| if [ "$seven_pct" -ge 80 ] 2>/dev/null; then | |
| seven_reset_str=$(fmt_reset "$seven_reset_epoch") | |
| if [ -n "$seven_reset_str" ]; then | |
| seven_label="${seven_label}↻${seven_reset_str}" | |
| seven_label_plain="${seven_label_plain}↻${seven_reset_str}" | |
| fi | |
| fi | |
| usage_str="${usage_str} ${sc}${seven_label}\033[0m" | |
| usage_str_plain="${usage_str_plain} ${seven_label_plain}" | |
| fi | |
| fi | |
| # --- Extra usage (overuse billing) — only show when 5h is at 100% --- | |
| if [ "$five_pct" -ge 100 ] 2>/dev/null && [ "$extra_limit" -gt 0 ] 2>/dev/null; then | |
| if [ "$extra_used" -ge 100 ]; then | |
| extra_used_fmt="\$$(( extra_used / 100 ))" | |
| else | |
| extra_used_fmt="\$0" | |
| fi | |
| extra_limit_fmt="\$$(( extra_limit / 100 ))" | |
| extra_pct=$(( extra_used * 100 / extra_limit )) | |
| if [ "$extra_pct" -ge 80 ] 2>/dev/null; then ec='\033[31m' | |
| elif [ "$extra_pct" -ge 50 ] 2>/dev/null; then ec='\033[33m' | |
| else ec='\033[36m'; fi | |
| usage_str="${usage_str} ${ec}${extra_used_fmt}/${extra_limit_fmt}\033[0m" | |
| usage_str_plain="${usage_str_plain} ${extra_used_fmt}/${extra_limit_fmt}" | |
| fi | |
| fi | |
| fi | |
| # --- Model + context --- | |
| seg_model_plain=" ${model}" | |
| seg_model_color=" \033[33m${model}\033[0m" | |
| seg_ctx_plain=" ${used_fmt}/${total_fmt}" | |
| seg_ctx_color=" ${pct_color}${used_fmt}/${total_fmt}\033[0m" | |
| # === Measure and output (wrap to second line if needed) === | |
| line1_plain="${seg_dir_plain}${seg_git_plain}${seg_proxy_plain}${seg_model_plain}${seg_ctx_plain}${usage_str_plain}" | |
| if [ ${#line1_plain} -le "$cols" ]; then | |
| # Fits on one line | |
| printf "${seg_dir_color}${seg_git_color}${seg_proxy_color}${seg_model_color}${seg_ctx_color}${usage_str}" | |
| else | |
| # Split: dir+branch on line 1, rest on line 2 | |
| printf "${seg_dir_color}${seg_git_color}" | |
| printf "\n" | |
| # Remove leading space from first segment on line 2 | |
| l2_proxy="${seg_proxy_color# }" | |
| l2_model="${seg_model_color}" | |
| if [ -z "$seg_proxy_plain" ]; then | |
| l2_model="${seg_model_color# }" | |
| fi | |
| printf "${l2_proxy}${l2_model}${seg_ctx_color}${usage_str}" | |
| fi |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Claude Code Status Line
A feature-rich status line script for Claude Code that packs directory, git, model, context, and usage tracking into a smart single-line (or auto-wrapping two-line) display.
Features
!modified,?untracked,+staged), truncated at 30 charslocalhost:4141), Cerebras (localhost:8083), or custom proxiesclaude-opus-4-6[1m]→opus,claude-sonnet-4-6→sonnet, etc.$X/$Yspend when 5h usage hits 100% and extra_usage is enabled↻30m,↻4.5h) when usage ≥80%OKL-*→gfe)~when in home dirExample Output
Installation
Add to
~/.claude/settings.json:Configuration
Configurable thresholds at the top of the script:
Other customizations:
72000req/day and24000000tok/day if your plan differs30char limit for dir/branchcaseblock (e.g.,OKL-*→gfe)Dependencies
jq— JSON parsingbc— arithmeticgit— branch/status infocurl— usage API calls (Cerebras, Claude OAuth, Copilot)tput— terminal width detectionOptional (for usage tracking)
Claude Code-credentialsentry (auto-created by Claude Code)CEREBRAS_API_KEYenv var, or 1Password CLI (op) with acerebras.aiitemlocalhost:4141with/usageendpointHow Pace Tracking Works
The script computes how far ahead or behind your expected usage you are at the current point in a window:
Display format:
5h: +20% @ 40%— you've used 40% but should have only used ~20%, so you're +20% ahead5h: -26%— you're 26% behind expected pace (room to use more)Color coding:
This means 40% usage early in a window can be red (burning too fast), while 60% near the end can be cyan (will reset before hitting limit).