Created
February 17, 2026 14:22
-
-
Save minac/97ce060c5206738d9d4c40e99c5d2558 to your computer and use it in GitHub Desktop.
Claude Code Enhanced Status Line
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
| # Claude Code Enhanced Statusline | |
| # Credits: This was heavily inspired/copied from Michael Shostack. Then Miguel David improved upon it. | |
| # What is this? | |
| # A rich 3-line statusline for Claude Code on macOS that displays: | |
| # - Line 1: Model name, token usage, context window progress bar, current directory, git branch and changes | |
| # - Line 2: Current (5h) and weekly (7d) usage bars with percentages | |
| # - Line 3: Reset times for both usage limits | |
| # Setup | |
| # 1. Save this script as ~/.claude/statusline-command.sh | |
| # 2. Make it executable: chmod +x ~/.claude/statusline-command.sh | |
| # 3. Add to settings ~/.claude/settings.json | |
| # "statusLine": { | |
| # "type": "command", | |
| # "command": "/Users/YOUR_USERNAME/.claude/statusline-command.sh" | |
| # } | |
| # 4. Replace YOUR_USERNAME with your actual username | |
| # 5. Replace YOUR_CC_CREDENTIALS in the script below with the macOS Keychain secret name | |
| # 6. Ensure you have the following: macOS, Claude Code and jq, stat, date, git, curl, bash, basename, grep | |
| # Script | |
| #!/bin/bash | |
| # Rich 3-line statusline for Claude Code on macOS | |
| # Error handling - always output something | |
| trap 'echo "Claude"' ERR | |
| # Color definitions | |
| BLUE='\033[38;2;0;153;255m' | |
| ORANGE='\033[38;2;255;176;85m' | |
| GREEN='\033[38;2;0;160;0m' | |
| CYAN='\033[38;2;46;149;153m' | |
| RED='\033[38;2;255;85;85m' | |
| YELLOW='\033[38;2;230;200;0m' | |
| PURPLE='\033[38;2;180;150;255m' | |
| PEACH='\033[38;2;255;180;130m' | |
| DIM='\033[2m' | |
| RESET='\033[0m' | |
| # Read JSON from stdin | |
| input=$(</dev/stdin) | |
| # Extract all fields from input JSON in a single jq call | |
| IFS=$'\t' read -r model context_size used_pct input_tokens output_tokens cache_creation cache_read current_dir <<< \ | |
| "$(echo "$input" | /usr/bin/jq -r '[ | |
| (.model.display_name // "Unknown"), | |
| (.context_window.context_window_size // 0), | |
| (.context_window.used_percentage // 0), | |
| (.context_window.current_usage.input_tokens // 0), | |
| (.context_window.current_usage.output_tokens // 0), | |
| (.context_window.current_usage.cache_creation_input_tokens // 0), | |
| (.context_window.current_usage.cache_read_input_tokens // 0), | |
| (.workspace.current_dir // .cwd // "") | |
| ] | @tsv')" | |
| current_tokens=$((input_tokens + output_tokens + cache_creation + cache_read)) | |
| # Format token counts with k/m suffixes | |
| format_tokens() { | |
| local tokens=${1%.*} | |
| if ((tokens >= 1000000)); then | |
| printf "%d.%dm" $((tokens / 1000000)) $(( (tokens % 1000000) / 100000 )) | |
| elif ((tokens >= 1000)); then | |
| printf "%dk" $((tokens / 1000)) | |
| else | |
| printf "%d" "$tokens" | |
| fi | |
| } | |
| # Build a ●/○ progress bar with color thresholds | |
| # Usage: build_bar <percent> <show_pct> [threshold_profile] | |
| # show_pct: 1 to prefix "NN% ", 0 for bar only | |
| # threshold_profile: "context" (default: green<40, yellow<75, red>=75) | |
| # "usage" (green<50, orange<70, yellow<90, red>=90) | |
| build_bar() { | |
| local percent=$1 | |
| local show_pct=${2:-0} | |
| local profile=${3:-context} | |
| local width=10 | |
| local pct_int=${percent%.*} | |
| local filled=$((pct_int * width / 100)) | |
| local empty=$((width - filled)) | |
| # Choose color based on profile | |
| local bar_color="${GREEN}" | |
| if [[ "$profile" == "usage" ]]; then | |
| if ((pct_int >= 90)); then bar_color="${RED}" | |
| elif ((pct_int >= 70)); then bar_color="${YELLOW}" | |
| elif ((pct_int >= 50)); then bar_color="${ORANGE}" | |
| fi | |
| else | |
| if ((pct_int >= 75)); then bar_color="${RED}" | |
| elif ((pct_int >= 40)); then bar_color="${YELLOW}" | |
| fi | |
| fi | |
| local bar="" | |
| for ((i = 0; i < filled; i++)); do bar="${bar}●"; done | |
| for ((i = 0; i < empty; i++)); do bar="${bar}○"; done | |
| if ((show_pct)); then | |
| printf "${bar_color}%.0f%% %s${RESET}" "$percent" "$bar" | |
| else | |
| printf "${bar_color}%s${RESET}" "$bar" | |
| fi | |
| } | |
| current_display=$(format_tokens $current_tokens) | |
| total_display=$(format_tokens $context_size) | |
| context_bar=$(build_bar "$used_pct" 1 context) | |
| # Shorten HOME to ~ | |
| current_dir="${current_dir/#$HOME/\~}" | |
| dir_basename=$(basename "${current_dir/#\~/$HOME}") | |
| # Git info | |
| git_branch="" | |
| git_changes="" | |
| git_worktree="" | |
| if git rev-parse --git-dir > /dev/null 2>&1; then | |
| git_branch=$(git branch --show-current 2> /dev/null || git rev-parse --short HEAD 2> /dev/null) | |
| diff_stat=$(git diff --shortstat 2> /dev/null) | |
| if [ -n "$diff_stat" ]; then | |
| ins=$(echo "$diff_stat" | grep -o '[0-9]* insertion' | grep -o '[0-9]*' || true) | |
| del=$(echo "$diff_stat" | grep -o '[0-9]* deletion' | grep -o '[0-9]*' || true) | |
| [ -z "$ins" ] && ins=0 | |
| [ -z "$del" ] && del=0 | |
| git_changes="+${ins},-${del}" | |
| fi | |
| git_dir=$(git rev-parse --git-dir 2> /dev/null) | |
| git_common=$(git rev-parse --git-common-dir 2> /dev/null) | |
| if [ "$git_dir" != "$git_common" ]; then | |
| git_worktree=$(basename "$(git rev-parse --show-toplevel 2> /dev/null)") | |
| fi | |
| fi | |
| # Line 1: Model | Tokens | Context Bar | Dir | Branch (changes) [worktree] | |
| printf "🤖 ${BLUE}%s${RESET} ${DIM}|${RESET} 🎰 ${ORANGE}%s${RESET} ${DIM}/${RESET} ${ORANGE}%s${RESET} ${DIM}|${RESET} 📊 %b" \ | |
| "$model" "$current_display" "$total_display" "$context_bar" | |
| [ -n "$dir_basename" ] && printf " ${DIM}|${RESET} 📁 ${CYAN}%s${RESET}" "$dir_basename" | |
| [ -n "$git_branch" ] && printf " ${DIM}|${RESET} 🌿 ${GREEN}%s${RESET}" "$git_branch" | |
| [ -n "$git_changes" ] && printf " ✏️ ${PEACH}%s${RESET}" "$git_changes" | |
| [ -n "$git_worktree" ] && printf " 🌳 ${DIM}[${RESET}${PURPLE}%s${RESET}${DIM}]${RESET}" "$git_worktree" | |
| printf '\n' | |
| # --- Usage data (lines 2-3) --- | |
| CACHE_FILE="/tmp/claude-statusline-usage-cache.json" | |
| CACHE_TTL=60 | |
| is_cache_valid() { | |
| [ -f "$CACHE_FILE" ] || return 1 | |
| local age=$(( $(/bin/date +%s) - $(/usr/bin/stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0) )) | |
| ((age < CACHE_TTL)) | |
| } | |
| # Fetch usage data from API, write to CACHE_FILE | |
| # Set STATUSLINE_DEBUG=1 to enable debug logging | |
| fetch_usage_data() { | |
| local debug_log="/tmp/claude-statusline-debug.log" | |
| log() { [[ "${STATUSLINE_DEBUG:-0}" == "1" ]] && echo "$(date): $1" >> "$debug_log"; } | |
| local credentials | |
| credentials=$(security find-generic-password -s "YOUR_CC_CREDENTIALS" -w 2>/dev/null) || { | |
| log "Failed to get credentials from keychain"; return 1 | |
| } | |
| [ -n "$credentials" ] || { log "Empty credentials"; return 1; } | |
| local access_token | |
| access_token=$(echo "$credentials" | /usr/bin/jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null) | |
| [ -n "$access_token" ] || { log "Failed to parse access token"; return 1; } | |
| log "Fetching usage data..." | |
| local response | |
| response=$(curl -s -H "Authorization: Bearer $access_token" \ | |
| -H "anthropic-beta: oauth-2025-04-20" \ | |
| "https://api.anthropic.com/api/oauth/usage" 2>/dev/null) | |
| if [ -n "$response" ] && echo "$response" | /usr/bin/jq -e '.five_hour' >/dev/null 2>&1; then | |
| log "Success" | |
| echo "$response" > "$CACHE_FILE" | |
| return 0 | |
| fi | |
| log "Invalid or empty response" | |
| rm -f "$CACHE_FILE" | |
| return 1 | |
| } | |
| # Get or fetch usage data | |
| usage_data="" | |
| if is_cache_valid; then | |
| usage_data=$(<"$CACHE_FILE") | |
| elif fetch_usage_data; then | |
| usage_data=$(<"$CACHE_FILE") | |
| fi | |
| # Display lines 2-3 if we have valid usage data | |
| if [ -n "$usage_data" ] && echo "$usage_data" | /usr/bin/jq -e '.five_hour' >/dev/null 2>&1; then | |
| # Single jq call for all usage fields | |
| IFS=$'\t' read -r current_pct current_reset weekly_pct weekly_reset <<< \ | |
| "$(echo "$usage_data" | /usr/bin/jq -r '[ | |
| (.five_hour.utilization // 0), | |
| (.five_hour.resets_at // "N/A"), | |
| (.seven_day.utilization // 0), | |
| (.seven_day.resets_at // "N/A") | |
| ] | @tsv')" | |
| current_bar=$(build_bar "$current_pct" 0 usage) | |
| weekly_bar=$(build_bar "$weekly_pct" 0 usage) | |
| # Format reset times (ISO 8601 → local) | |
| format_reset_time() { | |
| local t=$1 | |
| [[ -z "$t" || "$t" == "null" || "$t" == "N/A" ]] && { echo "N/A"; return; } | |
| local clean=${t%%.*} # strip fractional seconds and everything after | |
| local fmt | |
| fmt=$(/bin/date -j -f "%Y-%m-%dT%H:%M:%S" "$clean" "+%b %d %I:%M %p" 2>/dev/null) && echo "$fmt" || echo "$t" | |
| } | |
| current_reset_fmt=$(format_reset_time "$current_reset") | |
| weekly_reset_fmt=$(format_reset_time "$weekly_reset") | |
| # Line 2: Usage bars | |
| printf "${DIM}Current (5h):${RESET} %s ${CYAN}%.0f%%${RESET} ${DIM}|${RESET} ${DIM}Weekly (7d):${RESET} %s ${CYAN}%.0f%%${RESET}\n" \ | |
| "$current_bar" "$current_pct" "$weekly_bar" "$weekly_pct" | |
| # Line 3: Reset times | |
| printf "${DIM}Resets:${RESET} ${CYAN}%s${RESET} ${DIM}|${RESET} ${DIM}Weekly:${RESET} ${CYAN}%s${RESET}\n" \ | |
| "$current_reset_fmt" "$weekly_reset_fmt" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment