Skip to content

Instantly share code, notes, and snippets.

@jeremyronking
Last active March 6, 2026 14:56
Show Gist options
  • Select an option

  • Save jeremyronking/7dc1978531b36a4d3741d2faef553a8e to your computer and use it in GitHub Desktop.

Select an option

Save jeremyronking/7dc1978531b36a4d3741d2faef553a8e to your computer and use it in GitHub Desktop.
Claude Code Status Line - Enhanced statusline showing model, mode, context usage, token counts, session cost, API limits with reset times, and git branch
#!/bin/bash
# ==============================================================================
# Claude Code Status Line
# ==============================================================================
# This script creates a rich status line for Claude Code showing:
# - Current model being used
# - Mode (if in plan/edit mode)
# - Context window usage percentage
# - Token usage (input/output totals for the session)
# - Session cost in USD
# - API usage limits (5-hour and 7-day windows) with time until reset
# - Current git branch
#
# Usage: Configure this script in your Claude Code settings as a statusline hook
# Requirements: jq, curl, git (optional)
#
# The script expects JSON input from Claude Code via stdin
# ==============================================================================
input=$(cat)
# ==============================================================================
# Extract Model Information
# ==============================================================================
MODEL=$(echo "$input" | jq -r '.model.display_name // "Unknown"')
# ==============================================================================
# API Usage Limits Fetching (with caching)
# ==============================================================================
# Fetches usage data from Anthropic API and caches it to avoid rate limiting
# Cache expires after CACHE_MAX_AGE seconds; backs off to BACKOFF_AGE on failures
CACHE_FILE="/tmp/claude-usage-cache"
CACHE_MAX_AGE=120
BACKOFF_FILE="/tmp/claude-usage-backoff"
BACKOFF_AGE=300
get_usage_limits() {
# Returns cached usage data if fresh, otherwise fetches new data
# This prevents hammering the API on every statusline refresh
local now cache_age max_age
now=$(date +%s)
# If backing off from a previous failure, use the longer backoff TTL
if [ -f "$BACKOFF_FILE" ]; then
max_age=$BACKOFF_AGE
else
max_age=$CACHE_MAX_AGE
fi
if [ -f "$CACHE_FILE" ]; then
cache_age=$((now - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0)))
if [ "$cache_age" -lt "$max_age" ]; then
cat "$CACHE_FILE"
return
fi
fi
# Retrieve OAuth credentials from macOS Keychain
local token
token=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty')
if [ -z "$token" ]; then
# No credentials - serve stale cache if available
[ -f "$CACHE_FILE" ] && cat "$CACHE_FILE"
return
fi
# Fetch fresh data from Anthropic API (capture HTTP status code)
local response http_code data
response=$(curl -s --max-time 2 -w "\n%{http_code}" \
-H "Authorization: Bearer $token" \
-H "anthropic-beta: oauth-2025-04-20" \
https://api.anthropic.com/api/oauth/usage 2>/dev/null)
http_code=$(echo "$response" | tail -1)
data=$(echo "$response" | sed '$d')
if [ "$http_code" = "200" ] && [ -n "$data" ]; then
# Success - update cache and clear backoff
echo "$data" > "$CACHE_FILE"
rm -f "$BACKOFF_FILE"
echo "$data"
else
# Failure (429, timeout, etc.) - enter backoff and serve stale cache
touch "$BACKOFF_FILE"
if [ -f "$CACHE_FILE" ]; then
touch "$CACHE_FILE"
cat "$CACHE_FILE"
fi
fi
}
# ==============================================================================
# Time Formatting Helper
# ==============================================================================
# Converts ISO 8601 timestamp to human-readable relative time
# Examples: "2h30m", "3d5h", "45m", "now"
format_time_until() {
local reset_at="$1"
if [ -z "$reset_at" ] || [ "$reset_at" = "null" ]; then
echo ""
return
fi
# Parse ISO timestamp (e.g., "2025-12-31T23:59:59.000Z") and convert to epoch
# Note: Uses macOS 'date -j' format - may need adjustment for GNU date
local reset_epoch now_epoch diff
reset_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "${reset_at%%.*}" "+%s" 2>/dev/null)
if [ -z "$reset_epoch" ]; then
echo ""
return
fi
now_epoch=$(date +%s)
diff=$((reset_epoch - now_epoch))
if [ "$diff" -le 0 ]; then
echo "now"
return
fi
# Calculate days, hours, and minutes
local days hours mins
days=$((diff / 86400))
hours=$(((diff % 86400) / 3600))
mins=$(((diff % 3600) / 60))
# Format output based on magnitude (show top 2 units)
if [ "$days" -gt 0 ]; then
echo "${days}d${hours}h"
elif [ "$hours" -gt 0 ]; then
echo "${hours}h${mins}m"
else
echo "${mins}m"
fi
}
# Fetch usage limits from API
USAGE_LIMITS=$(get_usage_limits)
# ==============================================================================
# Extract Mode Information
# ==============================================================================
# Mode shows if Claude is in a special state (e.g., "plan", "edit")
MODE=$(echo "$input" | jq -r '.mode // empty')
if [ -z "$MODE" ]; then
MODE_DISPLAY=""
else
MODE_DISPLAY=" | ${MODE} |"
fi
# ==============================================================================
# Context Window Usage Calculation
# ==============================================================================
# Calculate percentage of context window used (including cache tokens)
CONTEXT_SIZE=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
USAGE=$(echo "$input" | jq '.context_window.current_usage // null')
if [ "$USAGE" != "null" ]; then
# Sum all token types: regular input, cache creation, and cache reads
CURRENT=$(echo "$USAGE" | jq '.input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens')
PERCENT=$((CURRENT * 100 / CONTEXT_SIZE))
else
PERCENT=0
fi
# ==============================================================================
# Token Usage & Cost
# ==============================================================================
# Extract cumulative session token counts and cost
TOTAL_IN=$(echo "$input" | jq -r '.context_window.total_input_tokens // 0')
TOTAL_OUT=$(echo "$input" | jq -r '.context_window.total_output_tokens // 0')
COST_USD=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
# Format token counts as human-readable (e.g., 15234 → "15.2k", 1234567 → "1.2M")
format_tokens() {
local count=$1
if [ "$count" -ge 1000000 ]; then
printf "%.1fM" "$(echo "$count / 1000000" | bc -l)"
elif [ "$count" -ge 1000 ]; then
printf "%.1fk" "$(echo "$count / 1000" | bc -l)"
else
echo "$count"
fi
}
IN_DISPLAY=$(format_tokens "$TOTAL_IN")
OUT_DISPLAY=$(format_tokens "$TOTAL_OUT")
# Format cost (show 3 decimal places, or 2 if >= $1)
if [ "$(echo "$COST_USD >= 1" | bc -l 2>/dev/null)" = "1" ]; then
COST_DISPLAY=$(printf "\$%.2f" "$COST_USD")
else
COST_DISPLAY=$(printf "\$%.3f" "$COST_USD")
fi
# ==============================================================================
# Color Coding Helper
# ==============================================================================
# Returns ANSI color code based on usage percentage
# Green (0-59%), Yellow (60-79%), Red (80-100%)
get_color() {
local pct=$1
if [ "$pct" -ge 80 ]; then
echo "\033[31m" # Red
elif [ "$pct" -ge 60 ]; then
echo "\033[33m" # Yellow
else
echo "\033[32m" # Green
fi
}
# ==============================================================================
# ANSI Color Codes
# ==============================================================================
RESET="\033[0m"
CYAN="\033[36m"
MAGENTA="\033[35m"
WHITE="\033[97m"
# Apply color to context percentage
CTX_COLOR=$(get_color "$PERCENT")
# ==============================================================================
# Parse API Usage Limits
# ==============================================================================
# Anthropic enforces two rate limit windows:
# - 5-hour rolling window
# - 7-day rolling window
# This section displays both usage percentages with time until reset
if [ -n "$USAGE_LIMITS" ]; then
# Extract utilization percentages (strip decimal places)
FIVE_HOUR=$(echo "$USAGE_LIMITS" | jq -r '.five_hour.utilization // empty' | cut -d. -f1)
SEVEN_DAY=$(echo "$USAGE_LIMITS" | jq -r '.seven_day.utilization // empty' | cut -d. -f1)
FIVE_RESET=$(echo "$USAGE_LIMITS" | jq -r '.five_hour.resets_at // empty')
SEVEN_RESET=$(echo "$USAGE_LIMITS" | jq -r '.seven_day.resets_at // empty')
if [ -n "$FIVE_HOUR" ] && [ -n "$SEVEN_DAY" ]; then
# Apply color coding to each limit
FIVE_COLOR=$(get_color "$FIVE_HOUR")
SEVEN_COLOR=$(get_color "$SEVEN_DAY")
# Convert reset timestamps to human-readable format
FIVE_TIME=$(format_time_until "$FIVE_RESET")
SEVEN_TIME=$(format_time_until "$SEVEN_RESET")
# Build display strings with colored percentages and reset times
FIVE_DISPLAY="5h: ${FIVE_COLOR}${FIVE_HOUR}%${RESET}"
[ -n "$FIVE_TIME" ] && FIVE_DISPLAY="${FIVE_DISPLAY} → ${MAGENTA}${FIVE_TIME}${RESET}"
SEVEN_DISPLAY="7d: ${SEVEN_COLOR}${SEVEN_DAY}%${RESET}"
[ -n "$SEVEN_TIME" ] && SEVEN_DISPLAY="${SEVEN_DISPLAY} → ${MAGENTA}${SEVEN_TIME}${RESET}"
LIMITS_DISPLAY=" | ${FIVE_DISPLAY} | ${SEVEN_DISPLAY}"
else
LIMITS_DISPLAY=""
fi
else
LIMITS_DISPLAY=""
fi
# ==============================================================================
# Git Branch Detection
# ==============================================================================
# Shows current git branch if working directory is in a git repository
GIT_BRANCH=""
if git rev-parse --git-dir > /dev/null 2>&1; then
BRANCH=$(git branch --show-current 2>/dev/null)
if [ -n "$BRANCH" ]; then
GIT_BRANCH=" | ${CYAN}${BRANCH}${RESET}"
fi
fi
# ==============================================================================
# Final Output
# ==============================================================================
# Assemble all components into final statusline
# Format: [Model] | mode | Context: XX% | In: XXk Out: XXk | $X.XX | 5h: XX% → Xh | 7d: XX% → Xd | branch
YELLOW="\033[33m"
GREEN="\033[32m"
TOKENS_DISPLAY=" | ${WHITE}In:${RESET} ${CYAN}${IN_DISPLAY}${RESET} ${WHITE}Out:${RESET} ${CYAN}${OUT_DISPLAY}${RESET} | ${GREEN}${COST_DISPLAY}${RESET}"
echo -e "[${MODEL}]${MODE_DISPLAY} ${WHITE}Context:${RESET} ${CTX_COLOR}${PERCENT}%${RESET}${TOKENS_DISPLAY}${LIMITS_DISPLAY}${GIT_BRANCH}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment