Esc

Claude Code statusLine

The output

Claude Code statusLine

This statusLine shows:

  • Current directory
  • Current git branch
  • Model being used
  • 5 hour limit’s usage and reset time
  • 7 day limit’s usage and reset time
  • Size of the context window

The script was taken and modified from this gist by jeremyronking

Tell Claude Code to run a script to display

Add the following to ~/.claude/settings.json

{
...
"statusLine": {
    "type": "command",
    "command": "/Users/MYUSERNAME/.claude/statusline-command.sh"
  },
...
}

Creae the script

This is the contents of /Users/MYUSERNAME/.claude/statusline-command.sh

#!/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
# 
# This script was taken and modified from:
# https://gist.github.com/jeremyronking/7dc1978531b36a4d3741d2faef553a8e 
# ==============================================================================
  
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
  
# ==============================================================================
# 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
}
  
# ==============================================================================
# Bar Drawing Helper
# ==============================================================================
# Returns a graphical progress bar based on numerical percentage
draw_bar() {
  local percent=$1
  local width=5
  # Sanitize input: remove decimals and force 0 if empty
  percent="${percent%.*}"
  [ -z "$percent" ] && percent=0
  
  # Clamp range between 0 and 100
  if [ "$percent" -lt 0 ]; then percent=0; fi
  if [ "$percent" -gt 100 ]; then percent=100; fi
  
  # Calculate filled blocks (5 total steps)
  # Math: (percent * 5) / 100
  local filled_blocks=$(( (percent * width) / 100 ))
  local bar=""
  # 1. Add solid blocks (█)
  for ((i=0; i<filled_blocks; i++)); do bar+="█"; done
  # 2. Add track (░)
  local remaining=$((width - filled_blocks))
  for ((i=0; i<remaining; i++)); do bar+="░"; done
  echo "$bar"
}
  
# ==============================================================================
# ANSI Color Codes
# ==============================================================================
RESET="\033[37m"
DIM="\033[90m"
CYAN="\033[36m"
MAGENTA="\033[35m"
WHITE="\033[97m"
  
# ==============================================================================
# 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')
    CTX_PERCENT=$((CURRENT * 100 / CONTEXT_SIZE))
else
    CTX_PERCENT=0
fi
  
CTX_COLOR=$(get_color "$CTX_PERCENT")
  
CTX_DISPLAY="Context ${CTX_COLOR}${CTX_PERCENT}%${RESET}"
  
# ==============================================================================
# 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
    # Get bar view for each limit
      FIVE_BAR=$(draw_bar $FIVE_HOUR)
       SEVEN_BAR=$(draw_bar $SEVEN_DAY)
        # 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="${FIVE_COLOR}${FIVE_BAR} ${FIVE_HOUR}%${RESET}"
        [ -n "$FIVE_TIME" ] && FIVE_DISPLAY="${FIVE_DISPLAY} ${DIM}(${FIVE_TIME})${RESET}"
  
        SEVEN_DISPLAY="${SEVEN_COLOR}${SEVEN_BAR} ${SEVEN_DAY}%${RESET}"
        [ -n "$SEVEN_TIME" ] && SEVEN_DISPLAY="${SEVEN_DISPLAY} ${DIM}(${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="${BRANCH}"
    fi
fi
  
  
# ==============================================================================
# Current directory
# ==============================================================================
# Shows current directory's basename
CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir')
DIR_NAME=$(basename "$CURRENT_DIR")
  
# ==============================================================================
# Final Output
# ==============================================================================
# Assemble all components into final statusline
# Format: current_dir branch [Model] | mode • ██░░░ XX% (XhXm) • █░░░░ XX% (XdXh) • Context XX%
echo -e "${RESET}${DIR_NAME} ${CYAN}${GIT_BRANCH}${RESET} ${DIM}[${MODEL}${MODE_DISPLAY}]${RESET}${LIMITS_DISPLAY}${CTX_DISPLAY}"