Last active
April 16, 2026 13:52
-
-
Save msadig/bf3f4b3a0659b63f44a86457cbb030bd to your computer and use it in GitHub Desktop.
Claude Code statusline
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 | |
| # Enhanced Claude Code statusline with session-specific context tracking | |
| input=$(cat) | |
| # Extract basic values using jq with safe defaults | |
| MODEL_ID=$(echo "$input" | jq -r '.model.id // ""') | |
| MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name // "Claude"') | |
| PROJECT_DIR=$(echo "$input" | jq -r '.workspace.project_dir // ""') | |
| CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir // ""') | |
| SESSION_ID=$(echo "$input" | jq -r '.session_id // ""') | |
| CLAUDE_VERSION=$(claude --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "?") | |
| # Extract cost and metrics information (these remain global across sessions) | |
| TOTAL_COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0') | |
| TOTAL_DURATION=$(echo "$input" | jq -r '.cost.total_duration_ms // 0') | |
| LINES_ADDED=$(echo "$input" | jq -r '.cost.total_lines_added // 0') | |
| LINES_REMOVED=$(echo "$input" | jq -r '.cost.total_lines_removed // 0') | |
| # Git branch detection | |
| 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=" | πΏ \033[94m$BRANCH\033[0m " | |
| fi | |
| fi | |
| # Context usage from Claude Code's built-in calculation | |
| CONTEXT_USED_PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1) | |
| # Context limit warning thresholds (Claude auto-compacts at ~95%) | |
| COMPACT_THRESHOLD=95 | |
| WARNING_THRESHOLD=80 | |
| # Format cost display | |
| if (( $(echo "$TOTAL_COST < 0.01" | bc -l 2>/dev/null || echo "1") )); then | |
| COST_DISPLAY=$(printf "%.4f" "$TOTAL_COST") | |
| else | |
| COST_DISPLAY=$(printf "%.2f" "$TOTAL_COST") | |
| fi | |
| # Format session duration | |
| DURATION_SEC=$((TOTAL_DURATION / 1000)) | |
| DURATION_MIN=$((DURATION_SEC / 60)) | |
| DURATION_HOURS=$((DURATION_MIN / 60)) | |
| if [ $DURATION_HOURS -gt 0 ]; then | |
| DURATION_DISPLAY="${DURATION_HOURS}h $((DURATION_MIN % 60))m" | |
| elif [ $DURATION_MIN -gt 0 ]; then | |
| DURATION_DISPLAY="${DURATION_MIN}m" | |
| else | |
| DURATION_DISPLAY="${DURATION_SEC}s" | |
| fi | |
| # Color coding based on context usage (not remaining) | |
| if [ $CONTEXT_USED_PCT -lt $WARNING_THRESHOLD ]; then | |
| CONTEXT_COLOR="\033[32m" # Green - safe usage | |
| elif [ $CONTEXT_USED_PCT -lt $COMPACT_THRESHOLD ]; then | |
| CONTEXT_COLOR="\033[33m" # Yellow - approaching auto-compact | |
| else | |
| CONTEXT_COLOR="\033[31m" # Red - will auto-compact soon | |
| fi | |
| RESET_COLOR="\033[0m" # Reset color | |
| # Additional subtle colors for different info types | |
| TIME_COLOR="\033[36m" # Cyan for time information | |
| COST_COLOR="\033[33m" # Yellow for cost/financial | |
| METRIC_COLOR="\033[35m" # Magenta for metrics/tokens | |
| # Format directory display | |
| PROJECT_NAME="${PROJECT_DIR##*/}" | |
| CURRENT_NAME="${CURRENT_DIR##*/}" | |
| if [ "$PROJECT_NAME" = "$CURRENT_NAME" ] || [ -z "$PROJECT_NAME" ]; then | |
| DIR_DISPLAY="π $CURRENT_NAME" | |
| else | |
| DIR_DISPLAY="π― $PROJECT_NAME: π $CURRENT_NAME" | |
| fi | |
| # Context status message | |
| if [ $CONTEXT_USED_PCT -ge $COMPACT_THRESHOLD ]; then | |
| CONTEXT_STATUS="auto-compact soon" | |
| elif [ $CONTEXT_USED_PCT -ge $WARNING_THRESHOLD ]; then | |
| CONTEXT_STATUS="approaching limit" | |
| else | |
| CONTEXT_STATUS="healthy" | |
| fi | |
| # Global rate limit reset calculation (most APIs reset hourly) | |
| CURRENT_EPOCH=$(date +%s) | |
| CURRENT_HOUR=$(date +%H) | |
| CURRENT_MINUTE=$(date +%M) | |
| # Calculate next hour reset | |
| NEXT_HOUR=$(( (CURRENT_HOUR + 1) % 24 )) | |
| MINUTES_TO_RESET=$(( 60 - CURRENT_MINUTE )) | |
| # Format global reset time | |
| if [ $MINUTES_TO_RESET -eq 60 ] || [ $MINUTES_TO_RESET -eq 0 ]; then | |
| GLOBAL_RESET="now" | |
| elif [ $MINUTES_TO_RESET -gt 0 ] && [ $MINUTES_TO_RESET -lt 60 ]; then | |
| GLOBAL_RESET="${MINUTES_TO_RESET}m" | |
| else | |
| GLOBAL_RESET="1m" # Fallback for edge cases | |
| fi | |
| # Calculate next reset time | |
| RESET_EPOCH=$(( CURRENT_EPOCH + (MINUTES_TO_RESET * 60) )) | |
| GLOBAL_RESET_TIME=$(date -r $RESET_EPOCH +"%H:%M" 2>/dev/null || date -d "@$RESET_EPOCH" +"%H:%M" 2>/dev/null || echo "??:??") | |
| # Calculate cost per hour if duration is significant | |
| COST_PER_HOUR="" | |
| if [ $DURATION_MIN -gt 0 ]; then | |
| HOURS_DECIMAL=$(echo "scale=2; $DURATION_MIN / 60" | bc -l 2>/dev/null || echo "0") | |
| if (( $(echo "$HOURS_DECIMAL > 0" | bc -l 2>/dev/null || echo "0") )); then | |
| HOURLY_RATE=$(echo "scale=2; $TOTAL_COST / $HOURS_DECIMAL" | bc -l 2>/dev/null || echo "0") | |
| if (( $(echo "$HOURLY_RATE > 0" | bc -l 2>/dev/null || echo "0") )); then | |
| COST_PER_HOUR=" (\$${HOURLY_RATE}/h)" | |
| fi | |
| fi | |
| fi | |
| # Create a simple progress bar for context usage (shows used, not remaining) | |
| create_progress_bar() { | |
| local used_percent=$1 | |
| local width=10 | |
| local filled=$(( used_percent * width / 100 )) | |
| local empty=$(( width - filled )) | |
| local bar="[" | |
| for ((i=0; i<filled; i++)); do | |
| bar="${bar}β" | |
| done | |
| for ((i=0; i<empty; i++)); do | |
| bar="${bar}β" | |
| done | |
| bar="${bar}]" | |
| echo "$bar" | |
| } | |
| PROGRESS_BAR=$(create_progress_bar "$CONTEXT_USED_PCT") | |
| # 5-hour rate limit (Claude.ai subscription only; absent for API users) | |
| FIVE_HOUR_PCT=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty') | |
| FIVE_HOUR_RESETS_AT=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty') | |
| FIVE_HOUR_DISPLAY="" | |
| if [ -n "$FIVE_HOUR_PCT" ]; then | |
| FIVE_HOUR_INT=$(printf "%.0f" "$FIVE_HOUR_PCT") | |
| if [ "$FIVE_HOUR_INT" -ge 90 ]; then | |
| RATE_COLOR="\033[31m" # Red - near limit | |
| elif [ "$FIVE_HOUR_INT" -ge 70 ]; then | |
| RATE_COLOR="\033[33m" # Yellow - getting close | |
| else | |
| RATE_COLOR="\033[32m" # Green - fine | |
| fi | |
| # Show time until reset if available | |
| RESET_SUFFIX="" | |
| if [ -n "$FIVE_HOUR_RESETS_AT" ] && [ "$FIVE_HOUR_RESETS_AT" -gt 0 ] 2>/dev/null; then | |
| NOW=$(date +%s) | |
| SECS_LEFT=$(( FIVE_HOUR_RESETS_AT - NOW )) | |
| if [ $SECS_LEFT -gt 0 ]; then | |
| MINS_LEFT=$(( SECS_LEFT / 60 )) | |
| if [ $MINS_LEFT -ge 60 ]; then | |
| RESET_SUFFIX=" (~$((MINS_LEFT/60))h$((MINS_LEFT%60))m)" | |
| else | |
| RESET_SUFFIX=" (~${MINS_LEFT}m)" | |
| fi | |
| fi | |
| fi | |
| FIVE_HOUR_DISPLAY=" | ${RATE_COLOR}5h: ${FIVE_HOUR_INT}%${RESET_COLOR}${RESET_SUFFIX}" | |
| fi | |
| # 7-day rate limit (Claude.ai subscription only; absent for API users) | |
| SEVEN_DAY_PCT=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty') | |
| SEVEN_DAY_RESETS_AT=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty') | |
| SEVEN_DAY_DISPLAY="" | |
| if [ -n "$SEVEN_DAY_PCT" ]; then | |
| SEVEN_DAY_INT=$(printf "%.0f" "$SEVEN_DAY_PCT") | |
| if [ "$SEVEN_DAY_INT" -ge 90 ]; then | |
| WEEK_COLOR="\033[31m" | |
| elif [ "$SEVEN_DAY_INT" -ge 70 ]; then | |
| WEEK_COLOR="\033[33m" | |
| else | |
| WEEK_COLOR="\033[32m" | |
| fi | |
| WEEK_RESET_SUFFIX="" | |
| if [ -n "$SEVEN_DAY_RESETS_AT" ] && [ "$SEVEN_DAY_RESETS_AT" -gt 0 ] 2>/dev/null; then | |
| NOW=$(date +%s) | |
| SECS_LEFT=$(( SEVEN_DAY_RESETS_AT - NOW )) | |
| if [ $SECS_LEFT -gt 0 ]; then | |
| DAYS_LEFT=$(( SECS_LEFT / 86400 )) | |
| HOURS_LEFT=$(( (SECS_LEFT % 86400) / 3600 )) | |
| MINS_LEFT=$(( (SECS_LEFT % 3600) / 60 )) | |
| WEEK_RESET_PARTS="" | |
| [ $DAYS_LEFT -gt 0 ] && WEEK_RESET_PARTS="${DAYS_LEFT}d" | |
| if [ $HOURS_LEFT -gt 0 ]; then | |
| [ -n "$WEEK_RESET_PARTS" ] && WEEK_RESET_PARTS="${WEEK_RESET_PARTS}," | |
| WEEK_RESET_PARTS="${WEEK_RESET_PARTS}${HOURS_LEFT}h" | |
| fi | |
| if [ $MINS_LEFT -gt 0 ]; then | |
| [ -n "$WEEK_RESET_PARTS" ] && WEEK_RESET_PARTS="${WEEK_RESET_PARTS}," | |
| WEEK_RESET_PARTS="${WEEK_RESET_PARTS}${MINS_LEFT}m" | |
| fi | |
| [ -n "$WEEK_RESET_PARTS" ] && WEEK_RESET_SUFFIX=" (~${WEEK_RESET_PARTS})" | |
| fi | |
| fi | |
| SEVEN_DAY_DISPLAY=" | ${WEEK_COLOR}7d: ${SEVEN_DAY_INT}%${RESET_COLOR}${WEEK_RESET_SUFFIX}" | |
| fi | |
| # Build two-line status display | |
| # Line 1: Keep original format with project/directory info | |
| echo -e "[$MODEL_DISPLAY | v$CLAUDE_VERSION] π― ${PROJECT_DIR##*/}: π ${CURRENT_DIR##*/}${GIT_BRANCH}" | |
| # Line 2: Session duration + Cost + Context progress + 5-hour rate limit | |
| echo -e "β±οΈ ${TIME_COLOR}${DURATION_DISPLAY}${RESET_COLOR} | π° ${COST_COLOR}\$${COST_DISPLAY}${RESET_COLOR} | ${CONTEXT_COLOR}${PROGRESS_BAR} ${CONTEXT_USED_PCT}%${RESET_COLOR} (${CONTEXT_STATUS})${FIVE_HOUR_DISPLAY}${SEVEN_DAY_DISPLAY}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment