Skip to content

Instantly share code, notes, and snippets.

@khun84
Created March 1, 2026 07:18
Show Gist options
  • Select an option

  • Save khun84/71a694583e50f9ef65bfa9ff07240eff to your computer and use it in GitHub Desktop.

Select an option

Save khun84/71a694583e50f9ef65bfa9ff07240eff to your computer and use it in GitHub Desktop.
Claude Code usage statusline — 5h/7d rate-limit stats, reset countdowns, context window (macOS Keychain fix for Claude Code v2.1+)
#!/bin/sh
# Fetches Claude API usage stats and writes them to /tmp/.claude_usage_cache.
# Line 1: five_hour.utilization (integer %)
# Line 2: seven_day.utilization (integer %)
# Line 3: five_hour.resets_at (raw ISO string, e.g. 2026-02-26T12:59:59.997656+00:00)
# Line 4: seven_day.resets_at (raw ISO string)
# All output is suppressed; meant to be run in background.
CACHE_FILE="/tmp/.claude_usage_cache"
TOKEN_CACHE="/tmp/.claude_token_cache"
CREDS_FILE="$HOME/.claude/.credentials.json"
TOKEN_TTL=900 # 15 minutes
# --- get token (with 15-min cache to avoid repeated credential reads) ---
token=""
if [ -f "$TOKEN_CACHE" ]; then
cache_age=$(( $(date -u +%s) - $(stat -f %m "$TOKEN_CACHE" 2>/dev/null || echo 0) ))
if [ "$cache_age" -lt "$TOKEN_TTL" ]; then
token=$(cat "$TOKEN_CACHE" 2>/dev/null)
fi
fi
if [ -z "$token" ]; then
# Try macOS Keychain first (Claude Code v2.1+)
keychain_json=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null)
if [ -n "$keychain_json" ]; then
token=$(printf '%s' "$keychain_json" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
fi
# Fall back to credentials file
if [ -z "$token" ] && [ -f "$CREDS_FILE" ]; then
token=$(jq -r '.claudeAiOauth.accessToken // empty' "$CREDS_FILE" 2>/dev/null)
fi
if [ -z "$token" ]; then
exit 0
fi
printf '%s' "$token" > "$TOKEN_CACHE"
fi
usage_json=$(curl -s -m 3 \
-H "accept: application/json" \
-H "anthropic-beta: oauth-2025-04-20" \
-H "authorization: Bearer $token" \
-H "user-agent: claude-code/2.1.11" \
"https://api.anthropic.com/oauth/usage" 2>/dev/null)
if [ -z "$usage_json" ]; then
exit 0
fi
five_h_raw=$(printf '%s' "$usage_json" | jq -r '.five_hour.utilization // empty' 2>/dev/null)
seven_d_raw=$(printf '%s' "$usage_json" | jq -r '.seven_day.utilization // empty' 2>/dev/null)
five_h_reset=$(printf '%s' "$usage_json" | jq -r '.five_hour.resets_at // ""' 2>/dev/null)
seven_d_reset=$(printf '%s' "$usage_json" | jq -r '.seven_day.resets_at // ""' 2>/dev/null)
if [ -n "$five_h_raw" ] && [ -n "$seven_d_raw" ]; then
five_h=$(printf "%.0f" "$five_h_raw")
seven_d=$(printf "%.0f" "$seven_d_raw")
printf '%s\n%s\n%s\n%s\n' "$five_h" "$seven_d" "$five_h_reset" "$seven_d_reset" > "$CACHE_FILE"
fi

Claude Code Usage Statusline

Compact usage stats in your Claude Code statusline — shows 5h/7d rate-limit utilization, reset countdowns, and context window usage.

Based on claude-watch with a fix for macOS Keychain credential storage.

What it looks like

Opus 4.6 | my-project • main
5h 6% (2h 30m) • 7d 12% (5d 4h) | ctx 45% (90k/200k)

Setup

1. Install scripts

curl -o ~/.claude/statusline-command.sh \
  https://gist.githubusercontent.com/<THIS_GIST>/raw/statusline-command.sh
curl -o ~/.claude/fetch-usage.sh \
  https://gist.githubusercontent.com/<THIS_GIST>/raw/fetch-usage.sh
chmod +x ~/.claude/statusline-command.sh ~/.claude/fetch-usage.sh

2. Add to ~/.claude/settings.json

{
  "statusLine": {
    "type": "command",
    "command": "bash ~/.claude/statusline-command.sh"
  },
  "hooks": {
    "PreToolUse": [{"matcher": "", "hooks": [{"type": "command", "command": "bash ~/.claude/fetch-usage.sh > /dev/null 2>&1 &"}]}],
    "Stop": [{"matcher": "", "hooks": [{"type": "command", "command": "bash ~/.claude/fetch-usage.sh > /dev/null 2>&1 &"}]}]
  }
}

3. Prime the cache

bash ~/.claude/fetch-usage.sh
cat /tmp/.claude_usage_cache   # should show 4 lines

OAuth Credentials — Where's the Token?

The fetch-usage.sh script needs your Claude OAuth access token to call the usage API. Where it's stored depends on your Claude Code version:

Version Storage Path
v2.1+ (current) macOS Keychain security find-generic-password -s "Claude Code-credentials" -w
Older versions JSON file ~/.claude/.credentials.json

This fork checks the macOS Keychain first, then falls back to the credentials file. The upstream claude-watch only checks the file, which silently fails on Claude Code v2.1+ on macOS.

If you're on Linux, credentials are likely still in ~/.claude/.credentials.json. If neither source works, try running /login in Claude Code to regenerate credentials.

Security Note

The fetch-usage.sh script caches your OAuth token in /tmp/.claude_token_cache for 15 minutes to avoid repeated keychain/file reads. This file is created with default permissions — on shared systems you may want to add umask 077 or chmod 600 after the write.

Requirements

  • jq (for JSON parsing)
  • curl (for API calls)
  • macOS (uses date -j for timestamp parsing and security for Keychain access)

Credits

Based on claude-watch by @xleddyl.

#!/bin/sh
input=$(cat)
# --- model ---
model=$(echo "$input" | jq -r '.model.display_name // ""')
# --- folder ---
dir=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // ""')
dir_name=$(basename "$dir")
# --- git branch ---
branch=""
if [ -d "${dir}/.git" ] || git -C "$dir" rev-parse --git-dir > /dev/null 2>&1; then
branch=$(git -C "$dir" symbolic-ref --short HEAD 2>/dev/null || git -C "$dir" rev-parse --short HEAD 2>/dev/null)
fi
# --- usage stats (5h / 7d) from cache ---
CACHE_FILE="/tmp/.claude_usage_cache"
five_h=""
seven_d=""
five_h_reset=""
seven_d_reset=""
if [ -f "$CACHE_FILE" ]; then
five_h=$(sed -n '1p' "$CACHE_FILE")
seven_d=$(sed -n '2p' "$CACHE_FILE")
five_h_reset=$(sed -n '3p' "$CACHE_FILE")
seven_d_reset=$(sed -n '4p' "$CACHE_FILE")
else
bash ~/.claude/fetch-usage.sh > /dev/null 2>&1 &
fi
# --- compute_delta: given a raw ISO timestamp, returns human-readable time until reset ---
compute_delta() {
clean=$(echo "$1" | sed 's/\.[0-9]*//' | sed 's/[+-][0-9][0-9]:[0-9][0-9]$//' | sed 's/Z$//')
reset_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$clean" "+%s" 2>/dev/null)
if [ -z "$reset_epoch" ]; then return; fi
now_epoch=$(date -u "+%s")
diff=$(( reset_epoch - now_epoch ))
if [ "$diff" -le 0 ]; then echo "now"; return; fi
days=$(( diff / 86400 ))
hours=$(( (diff % 86400) / 3600 ))
minutes=$(( (diff % 3600) / 60 ))
if [ "$days" -gt 0 ]; then
echo "${days}d ${hours}h"
elif [ "$hours" -gt 0 ]; then
echo "${hours}h ${minutes}m"
else
echo "${minutes}m"
fi
}
# --- context window ---
used=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
ctx_str=""
ctx_tokens_str=""
if [ -n "$used" ]; then
used_int=$(printf "%.0f" "$used")
ctx_str="${used_int}%"
ctx_used=$(echo "$input" | jq -r '(.context_window.current_usage.cache_read_input_tokens + .context_window.current_usage.cache_creation_input_tokens + .context_window.current_usage.input_tokens + .context_window.current_usage.output_tokens) // empty' 2>/dev/null)
ctx_total=$(echo "$input" | jq -r '.context_window.context_window_size // empty' 2>/dev/null)
if [ -n "$ctx_used" ] && [ -n "$ctx_total" ]; then
ctx_used_k=$(( ctx_used / 1000 ))
ctx_total_k=$(( ctx_total / 1000 ))
ctx_tokens_str="${ctx_used_k}k/${ctx_total_k}k"
fi
fi
# --- assemble output ---
SEP="\033[90m • \033[0m"
# line 1: model | folder • branch
printf "\033[38;5;208m\033[1m%s\033[22m\033[0m" "$model"
printf "\033[90m | \033[0m"
printf "\033[1m\033[38;2;76;208;222m%s\033[22m\033[0m" "$dir_name"
if [ -n "$branch" ]; then
printf "%b" "$SEP"
printf "\033[1m\033[38;2;192;103;222m%s\033[22m\033[0m" "$branch"
fi
# line 2: usage | ctx
printf "\n"
if [ -n "$five_h" ]; then
printf "\033[38;2;156;162;175m5h %s%%\033[0m" "$five_h"
if [ -n "$five_h_reset" ]; then
delta=$(compute_delta "$five_h_reset")
[ -n "$delta" ] && printf " \033[2m\033[38;2;156;162;175m(%s)\033[0m" "$delta"
fi
fi
if [ -n "$seven_d" ]; then
[ -n "$five_h" ] && printf "%b" "$SEP"
printf "\033[38;2;156;162;175m7d %s%%\033[0m" "$seven_d"
if [ -n "$seven_d_reset" ]; then
delta=$(compute_delta "$seven_d_reset")
[ -n "$delta" ] && printf " \033[2m\033[38;2;156;162;175m(%s)\033[0m" "$delta"
fi
fi
if [ -n "$ctx_str" ]; then
printf "\033[90m | \033[0m"
printf "\033[38;2;156;162;175mctx %s\033[0m" "$ctx_str"
[ -n "$ctx_tokens_str" ] && printf " \033[2m\033[38;2;156;162;175m(%s)\033[0m" "$ctx_tokens_str"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment