Last active
January 19, 2026 07:01
-
-
Save jeremyronking/7dc1978531b36a4d3741d2faef553a8e to your computer and use it in GitHub Desktop.
Claude Code Status Line - Enhanced statusline showing model, mode, context usage, API limits with reset times, and git branch
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 | |
| # ============================================================================== | |
| # 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 | |
| # - 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 60 seconds to keep data reasonably fresh | |
| CACHE_FILE="/tmp/claude-usage-cache" | |
| CACHE_MAX_AGE=60 | |
| fetch_usage_limits() { | |
| # Retrieves OAuth credentials from macOS Keychain and fetches usage data | |
| # Note: This uses macOS 'security' command - may need adjustment for Linux | |
| local creds token | |
| creds=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null) | |
| if [ -z "$creds" ]; then | |
| echo "" | |
| return | |
| fi | |
| # Extract OAuth access token from credentials JSON | |
| token=$(echo "$creds" | jq -r '.claudeAiOauth.accessToken // empty') | |
| if [ -z "$token" ]; then | |
| echo "" | |
| return | |
| fi | |
| # Query Anthropic API for usage statistics (5-hour and 7-day windows) | |
| curl -s --max-time 2 -H "Authorization: Bearer $token" \ | |
| -H "anthropic-beta: oauth-2025-04-20" \ | |
| https://api.anthropic.com/api/oauth/usage | |
| } | |
| get_usage_limits() { | |
| # Returns cached usage data if fresh, otherwise fetches new data | |
| # This prevents hammering the API on every statusline refresh | |
| if [ -f "$CACHE_FILE" ]; then | |
| local cache_age=$(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0))) | |
| if [ "$cache_age" -lt "$CACHE_MAX_AGE" ]; then | |
| cat "$CACHE_FILE" | |
| return | |
| fi | |
| fi | |
| # Cache is stale or missing - fetch fresh data | |
| local data | |
| data=$(fetch_usage_limits) | |
| if [ -n "$data" ]; then | |
| echo "$data" > "$CACHE_FILE" | |
| echo "$data" | |
| 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 | |
| # ============================================================================== | |
| # 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% | 5h: XX% → Xh | 7d: XX% → Xd | branch | |
| echo -e "[${MODEL}]${MODE_DISPLAY} ${WHITE}Context:${RESET} ${CTX_COLOR}${PERCENT}%${RESET}${LIMITS_DISPLAY}${GIT_BRANCH}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment