Created
March 27, 2026 19:42
-
-
Save andesco/b3e53a7ad3c70be64b4a5f5ffde81e86 to your computer and use it in GitHub Desktop.
Claude Code statusline with 5h/7d pacing indicators
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
| #!/usr/bin/env bash | |
| # Claude Code status line script | |
| # Format: {dir} | context: {remaining%} | 5h: {actual_remaining%} {expected_remaining%} {hh:mm} | 7d: {actual%} {expected%} {dd} | |
| input=$(cat) | |
| now=$(date +%s) | |
| # Working day configuration for 7d pacing | |
| # Adjust these to match your typical usage hours | |
| WORK_TZ="America/Toronto" # Your timezone (used for active window calculation) | |
| WORK_START_HOUR=8 # Hour you start using Claude (24h, in $WORK_TZ) | |
| WORK_END_HOUR=24 # Hour you stop (24 = midnight, in $WORK_TZ) | |
| # Colorize a percentage: green >50, orange >10, red <=10 | |
| colorpct() { | |
| local val="$1" | |
| if [ "$val" -gt 50 ]; then | |
| printf '\033[32m%s%%\033[0m' "$val" # green | |
| elif [ "$val" -gt 10 ]; then | |
| printf '\033[33m%s%%\033[0m' "$val" # orange/yellow | |
| else | |
| printf '\033[31m%s%%\033[0m' "$val" # red | |
| fi | |
| } | |
| # Directory name | |
| dir_path=$(echo "$input" | jq -r '.workspace.current_dir // empty') | |
| if [ -n "$dir_path" ]; then | |
| dir_name=$(basename "$dir_path") | |
| else | |
| dir_name="" | |
| fi | |
| # Context window remaining | |
| ctx_remaining=$(echo "$input" | jq -r '.context_window.remaining_percentage // empty') | |
| ctx_str="" | |
| if [ -n "$ctx_remaining" ]; then | |
| ctx_int=$(printf '%.0f' "$ctx_remaining") | |
| ctx_str="context: $(colorpct "$ctx_int")" | |
| fi | |
| # 5-hour window | |
| five_used=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty') | |
| five_resets=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty') | |
| five_str="" | |
| if [ -n "$five_used" ]; then | |
| five_used_int=$(printf '%.0f' "$five_used") | |
| five_remaining_int=$(( 100 - five_used_int )) | |
| # Calculate expected remaining % based on elapsed time in the 5-hour window | |
| # expected_remaining = (1 - elapsed_minutes / 300) * 100 | |
| five_expected_remaining_str="" | |
| if [ -n "$five_resets" ]; then | |
| five_start=$(( five_resets - 5 * 3600 )) | |
| five_expected_remaining_str=$(awk "BEGIN { | |
| elapsed_min = ($now - $five_start) / 60 | |
| if (elapsed_min < 0) elapsed_min = 0 | |
| if (elapsed_min > 300) elapsed_min = 300 | |
| expected_remaining = (1 - elapsed_min / 300) * 100 | |
| if (expected_remaining < 0) expected_remaining = 0 | |
| printf \"%.0f%%\", expected_remaining | |
| }") | |
| fi | |
| # Color actual remaining % based on how far behind expected pace | |
| # Green: actual remaining within 5pp of expected remaining or better (using less than expected) | |
| # Orange: actual remaining is 5-10pp below expected remaining | |
| # Red: actual remaining is more than 10pp below expected remaining | |
| if [ -n "$five_expected_remaining_str" ]; then | |
| five_expected_remaining_int=$(echo "$five_expected_remaining_str" | tr -d '%') | |
| five_color=$(awk "BEGIN { | |
| diff = $five_expected_remaining_int - $five_remaining_int | |
| if (diff <= 5) print \"green\" | |
| else if (diff <= 10) print \"orange\" | |
| else print \"red\" | |
| }") | |
| case "$five_color" in | |
| green) five_remaining_colored=$(printf '\033[32m%s%%\033[0m' "$five_remaining_int") ;; | |
| orange) five_remaining_colored=$(printf '\033[33m%s%%\033[0m' "$five_remaining_int") ;; | |
| red) five_remaining_colored=$(printf '\033[31m%s%%\033[0m' "$five_remaining_int") ;; | |
| esac | |
| else | |
| five_remaining_colored=$(printf '%s%%' "$five_remaining_int") | |
| fi | |
| # Time remaining in 5-hour window as hh:mm | |
| five_time="" | |
| if [ -n "$five_resets" ]; then | |
| five_secs=$(( five_resets - now )) | |
| if [ "$five_secs" -gt 0 ]; then | |
| five_time=$(awk "BEGIN { | |
| h = int($five_secs / 3600) | |
| m = int(($five_secs % 3600) / 60) | |
| printf \"%d:%02d\", h, m | |
| }") | |
| else | |
| five_time="0:00" | |
| fi | |
| fi | |
| five_str="5h: ${five_remaining_colored}" | |
| [ -n "$five_expected_remaining_str" ] && five_str="${five_str} ${five_expected_remaining_str}" | |
| [ -n "$five_time" ] && five_str="${five_str} ${five_time}" | |
| fi | |
| # 7-day window | |
| week_used=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty') | |
| week_resets=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty') | |
| week_str="" | |
| if [ -n "$week_used" ]; then | |
| week_remaining=$(echo "$week_used" | awk '{printf "%.0f", 100 - $1}') | |
| # Time remaining in 7d window as decimal days (e.g. 6.5d) | |
| week_time="" | |
| week_expected_str="" | |
| if [ -n "$week_resets" ]; then | |
| week_secs=$(( week_resets - now )) | |
| if [ "$week_secs" -gt 0 ]; then | |
| week_time=$(awk "BEGIN {printf \"%.1fd\", $week_secs / 86400}") | |
| else | |
| week_time="0.0d" | |
| fi | |
| # Calculate expected remaining % based on active usage hours (8 AM - midnight ET) | |
| # Billing period starts at 2 PM ET (week_start); active window = 8 AM to midnight ET = 16h/day | |
| # We sum active hours elapsed since week_start, second by second, by iterating calendar days. | |
| # | |
| # Algorithm (all in awk, operating on Unix timestamps): | |
| # For each calendar day boundary since week_start up to now, accumulate the overlap | |
| # between [day_start_ET_8am, day_end_ET_midnight] and [week_start, now]. | |
| week_start=$(( week_resets - 7 * 86400 )) | |
| # ET UTC offset in seconds (awk needs it as an integer) | |
| et_offset_sec=$(TZ="$WORK_TZ" date +%z | awk '{ | |
| sign = ($0 ~ /^-/) ? -1 : 1 | |
| s = substr($0,2) | |
| h = substr(s,1,2)+0; m = substr(s,3,2)+0 | |
| print sign * (h*3600 + m*60) | |
| }') | |
| week_expected_str=$(awk "BEGIN { | |
| week_start = $week_start | |
| now = $now | |
| et_off = $et_offset_sec # seconds east of UTC (negative for ET) | |
| work_start_h = $WORK_START_HOUR | |
| work_end_h = $WORK_END_HOUR | |
| total_active = 7 * (work_end_h - work_start_h) | |
| # Find the midnight in local TZ that is <= week_start, then walk forward day by day | |
| first_day = int((week_start + et_off) / 86400) | |
| active_elapsed = 0 | |
| for (d = first_day; d <= first_day + 8; d++) { | |
| # Local midnight for this day as Unix timestamp | |
| day_midnight_utc = d * 86400 - et_off | |
| # Active window for this day | |
| win_start = day_midnight_utc + work_start_h * 3600 | |
| win_end = day_midnight_utc + work_end_h * 3600 | |
| # Clamp to [week_start, now] | |
| clamp_start = (win_start > week_start) ? win_start : week_start | |
| clamp_end = (win_end < now) ? win_end : now | |
| if (clamp_end > clamp_start) { | |
| active_elapsed += (clamp_end - clamp_start) / 3600 | |
| } | |
| # Once we've passed 'now' there's nothing more to accumulate | |
| if (day_midnight_utc + 24 * 3600 > now) break | |
| } | |
| expected_remaining = (1 - active_elapsed / total_active) * 100 | |
| if (expected_remaining < 0) expected_remaining = 0 | |
| printf \"%.0f%%\", expected_remaining | |
| }") | |
| fi | |
| # Color 7d remaining based on how far behind the expected pace it is | |
| # Thresholds: 4h behind, 8h behind (as percentage points of total active hours) | |
| if [ -n "$week_expected_str" ]; then | |
| week_expected_int=$(echo "$week_expected_str" | tr -d '%') | |
| week_color=$(awk "BEGIN { | |
| diff = $week_expected_int - $week_remaining | |
| total = 7 * ($WORK_END_HOUR - $WORK_START_HOUR) | |
| pp4h = 4 / total * 100 | |
| pp8h = 8 / total * 100 | |
| if (diff <= pp4h) print \"green\" | |
| else if (diff <= pp8h) print \"orange\" | |
| else print \"red\" | |
| }") | |
| case "$week_color" in | |
| green) week_remaining_colored=$(printf '\033[32m%s%%\033[0m' "$week_remaining") ;; | |
| orange) week_remaining_colored=$(printf '\033[33m%s%%\033[0m' "$week_remaining") ;; | |
| red) week_remaining_colored=$(printf '\033[31m%s%%\033[0m' "$week_remaining") ;; | |
| esac | |
| else | |
| week_remaining_colored=$(colorpct "$week_remaining") | |
| fi | |
| week_str="7d: ${week_remaining_colored}" | |
| [ -n "$week_expected_str" ] && week_str="${week_str} ${week_expected_str}" | |
| [ -n "$week_time" ] && week_str="${week_str} ${week_time}" | |
| fi | |
| # Assemble output | |
| parts=() | |
| [ -n "$dir_name" ] && parts+=("$dir_name") | |
| [ -n "$ctx_str" ] && parts+=("$ctx_str") | |
| [ -n "$five_str" ] && parts+=("$five_str") | |
| [ -n "$week_str" ] && parts+=("$week_str") | |
| output="" | |
| for part in "${parts[@]}"; do | |
| if [ -z "$output" ]; then | |
| output="$part" | |
| else | |
| output="$output | $part" | |
| fi | |
| done | |
| printf "%s" "$output" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment