Skip to content

Instantly share code, notes, and snippets.

@teamon
Created January 13, 2026 10:31
Show Gist options
  • Select an option

  • Save teamon/e862ac27efcb5312cbd10eb77863afba to your computer and use it in GitHub Desktop.

Select an option

Save teamon/e862ac27efcb5312cbd10eb77863afba to your computer and use it in GitHub Desktop.
#!/bin/bash
# Claude Usage SwiftBar Plugin
# Shows actual usage from claude.ai/settings/usage
#
# Filename: claude-usage.5m.sh (runs every 5 minutes)
# Install:
# 1. brew install --cask swiftbar jq
# 2. Copy this to your SwiftBar plugins folder
# 3. chmod +x claude-usage.5m.sh
set -e
FIVE_HOUR_SECS=18000 # 5 hours
SEVEN_DAY_SECS=604800 # 7 days
# Get OAuth token from macOS Keychain (stored by Claude Code)
get_token() {
security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | \
jq -r '.claudeAiOauth.accessToken // empty'
}
# Fetch usage from Anthropic API
fetch_usage() {
curl -s "https://api.anthropic.com/api/oauth/usage" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $1" \
-H "anthropic-beta: oauth-2025-04-20" \
-H "User-Agent: claude-code/2.0.31"
}
# Parse reset timestamp to epoch
parse_reset_epoch() {
local reset_at="$1"
[[ -z "$reset_at" || "$reset_at" == "null" ]] && { echo "0"; return; }
local clean_ts="${reset_at%%.*}"
clean_ts="${clean_ts%Z}"
date -u -j -f "%Y-%m-%dT%H:%M:%S" "$clean_ts" "+%s" 2>/dev/null || echo "0"
}
# Calculate time elapsed percentage in window
calc_time_pct() {
local reset_epoch="$1" window_secs="$2"
local now_epoch remaining elapsed pct
now_epoch=$(date "+%s")
remaining=$((reset_epoch - now_epoch))
(( remaining < 0 )) && remaining=0
elapsed=$((window_secs - remaining))
(( elapsed < 0 )) && elapsed=0
(( elapsed > window_secs )) && elapsed=$window_secs
pct=$((elapsed * 100 / window_secs))
echo "$pct"
}
# Format time remaining
format_time_remaining() {
local reset_epoch="$1"
local now_epoch diff
now_epoch=$(date "+%s")
diff=$((reset_epoch - now_epoch))
if (( diff <= 0 )); then echo "now"
elif (( diff < 3600 )); then echo "$((diff / 60))m"
elif (( diff < 86400 )); then echo "$((diff / 3600))h$((diff % 3600 / 60))m"
else echo "$((diff / 86400))d$((diff % 86400 / 3600))h"
fi
}
# ANSI color codes (8-bit colors)
RESET=$'\033[0m'
GREEN=$'\033[38;5;82m'
YELLOW=$'\033[38;5;220m'
GRAY=$'\033[38;5;245m'
DIM=$'\033[38;5;238m'
# Build bar: green=usage (on track), gray=buffer, dim=remaining
# If usage > time: yellow=usage (warning)
# Use same character (█) throughout for consistent height
build_usage_bar() {
local time_pct=$1 usage_pct=$2 width=${3:-10}
local time_blocks=$(( (time_pct * width + 50) / 100 ))
local usage_blocks=$(( (usage_pct * width + 50) / 100 ))
(( time_blocks > width )) && time_blocks=$width
(( usage_blocks > width )) && usage_blocks=$width
local bar=""
if (( usage_pct <= time_pct )); then
# On track: green for usage, gray for buffer, dim for remaining
for ((i=0; i<width; i++)); do
if ((i < usage_blocks)); then
bar+="${GREEN}█${RESET}"
elif ((i < time_blocks)); then
bar+="${GRAY}█${RESET}"
else
bar+="${DIM}█${RESET}"
fi
done
else
# Burning too fast: yellow for usage, dim for remaining
for ((i=0; i<width; i++)); do
if ((i < usage_blocks)); then
bar+="${YELLOW}█${RESET}"
else
bar+="${DIM}█${RESET}"
fi
done
fi
printf '%s' "$bar"
}
# --- Main ---
TOKEN=$(get_token)
if [[ -z "$TOKEN" ]]; then
echo "☁️ ⚠️ | color=#888888"
echo "---"
echo "Not logged in to Claude Code | color=#FF6666"
echo "Run: claude login | font=Monaco size=11"
exit 0
fi
USAGE_JSON=$(fetch_usage "$TOKEN")
if [[ -z "$USAGE_JSON" ]] || echo "$USAGE_JSON" | jq -e '.error' >/dev/null 2>&1; then
echo "☁️ ⚠️ | color=#888888"
echo "---"
echo "Failed to fetch usage | color=#FF6666"
echo "Token may be expired - run: claude login"
exit 0
fi
# Extract values with jq
FIVE_HOUR_UTIL=$(echo "$USAGE_JSON" | jq -r '.five_hour.utilization // 0')
SEVEN_DAY_UTIL=$(echo "$USAGE_JSON" | jq -r '.seven_day.utilization // 0')
FIVE_HOUR_RESET=$(echo "$USAGE_JSON" | jq -r '.five_hour.resets_at // empty')
SEVEN_DAY_RESET=$(echo "$USAGE_JSON" | jq -r '.seven_day.resets_at // empty')
# Parse reset times
FIVE_HOUR_RESET_EPOCH=$(parse_reset_epoch "$FIVE_HOUR_RESET")
SEVEN_DAY_RESET_EPOCH=$(parse_reset_epoch "$SEVEN_DAY_RESET")
# Calculate time progress in each window
FIVE_HOUR_TIME_PCT=$(calc_time_pct "$FIVE_HOUR_RESET_EPOCH" "$FIVE_HOUR_SECS")
SEVEN_DAY_TIME_PCT=$(calc_time_pct "$SEVEN_DAY_RESET_EPOCH" "$SEVEN_DAY_SECS")
# Usage percentages (already 0-100)
FIVE_HOUR_USAGE_PCT=$(printf "%.0f" "$FIVE_HOUR_UTIL")
SEVEN_DAY_USAGE_PCT=$(printf "%.0f" "$SEVEN_DAY_UTIL")
# Format remaining time
FIVE_HOUR_REMAINING=$(format_time_remaining "$FIVE_HOUR_RESET_EPOCH")
SEVEN_DAY_REMAINING=$(format_time_remaining "$SEVEN_DAY_RESET_EPOCH")
# Build bars for menubar
FIVE_HOUR_BAR=$(build_usage_bar "$FIVE_HOUR_TIME_PCT" "$FIVE_HOUR_USAGE_PCT" 5)
SEVEN_DAY_BAR=$(build_usage_bar "$SEVEN_DAY_TIME_PCT" "$SEVEN_DAY_USAGE_PCT" 5)
# Build larger bars for dropdown
FIVE_HOUR_BAR_LARGE=$(build_usage_bar "$FIVE_HOUR_TIME_PCT" "$FIVE_HOUR_USAGE_PCT" 10)
SEVEN_DAY_BAR_LARGE=$(build_usage_bar "$SEVEN_DAY_TIME_PCT" "$SEVEN_DAY_USAGE_PCT" 10)
# Menubar with ANSI colors
echo -e "5h${FIVE_HOUR_BAR} 7d${SEVEN_DAY_BAR} | font=Monaco size=13 ansi=true"
# Dropdown
echo "---"
echo "Claude Usage | size=14 color=#888888"
echo "---"
echo "5-Hour Session (${FIVE_HOUR_REMAINING} left) | size=12"
echo -e "${FIVE_HOUR_BAR_LARGE} | font=Monaco size=11 ansi=true"
echo "Time: ${FIVE_HOUR_TIME_PCT}% Usage: ${FIVE_HOUR_USAGE_PCT}% | size=11 color=#888888"
echo "---"
echo "Weekly (${SEVEN_DAY_REMAINING} left) | size=12"
echo -e "${SEVEN_DAY_BAR_LARGE} | font=Monaco size=11 ansi=true"
echo "Time: ${SEVEN_DAY_TIME_PCT}% Usage: ${SEVEN_DAY_USAGE_PCT}% | size=11 color=#888888"
echo "---"
echo "Green=on track Yellow=over budget | size=10 color=#666666"
echo "---"
echo "Refresh | refresh=true"
echo "Open Usage Settings | href=https://claude.ai/settings/usage"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment