Skip to content

Instantly share code, notes, and snippets.

@fcamblor
Last active April 8, 2026 01:25
Show Gist options
  • Select an option

  • Save fcamblor/0ad17cab5cc797d9e92d0620be795f99 to your computer and use it in GitHub Desktop.

Select an option

Save fcamblor/0ad17cab5cc797d9e92d0620be795f99 to your computer and use it in GitHub Desktop.
My custom Claude Code StatusLine settings (for Mac)
  • Download .config__ccstatusline__settings.json to ~/.config/ccstatusline/settings.json, replace and with your respective claude code email accounts
  • Download bashScripts__ccstatusline-usage.sh to ~/bashScripts/ccstatusline-usage.sh
{
"version": 3,
"lines": [
[
{
"id": "0c199d1c-30ab-4750-889c-1f20bdfe0370",
"type": "context-percentage",
"backgroundColor": "bgYellow",
"metadata": {
"inverse": "false"
}
},
{
"id": "3",
"type": "context-length",
"color": "brightBlack"
},
{
"id": "502d9f6f-2f4a-4dab-b698-f27b67e28e4c",
"type": "model",
"backgroundColor": "bgBrightBlue"
},
{
"id": "91fe07b8-6e3e-4fc4-b8f0-1e86e225fdbc",
"type": "block-timer",
"backgroundColor": "bgBrightMagenta"
},
{
"id": "b4e49d52-6c86-4d16-80d7-3540f3c03018",
"type": "session-cost",
"backgroundColor": "bgBrightWhite"
},
{
"id": "0f203b02-b843-44c0-bd24-a822e229197a",
"type": "session-clock",
"backgroundColor": "bgBrightYellow"
}
],
[
{
"id": "123be3ac-5e8e-41df-800a-2ece070a2570",
"type": "current-working-dir",
"backgroundColor": "bgMagenta"
},
{
"id": "38c8a1a1-4e57-4549-8cf6-2d9299f4f5ed",
"type": "git-worktree",
"backgroundColor": "bgWhite"
},
{
"id": "ecb97f54-78ef-4af7-a47d-ef3de5c5bdcc",
"type": "git-branch",
"backgroundColor": "bgBrightBlack"
},
{
"id": "28fb19a1-785a-4420-a0ad-18b42638995c",
"type": "git-changes",
"backgroundColor": "bgBrightGreen"
}
],
[
{
"id": "e3177294-eaeb-4bba-a26f-0adb004dcab6",
"type": "custom-command",
"backgroundColor": "bgWhite",
"commandPath": "~/bashScripts/ccstatusline-usage.sh email --email <email1> --bracket"
},
{
"id": "da18f30f-3005-4505-a7d0-ac604703d4b0",
"type": "custom-command",
"backgroundColor": "bgBrightYellow",
"commandPath": "~/bashScripts/ccstatusline-usage.sh ttl --email <email1>"
},
{
"id": "a19499a4-41f4-4f46-b8e7-e71314312d7c",
"type": "custom-command",
"backgroundColor": "bgBrightWhite",
"commandPath": "~/bashScripts/ccstatusline-usage.sh session --email <email1>"
},
{
"id": "3b58aaf0-1d76-44d9-baf0-6e0b4723d3cd",
"type": "custom-command",
"backgroundColor": "bgBrightBlue",
"commandPath": "~/bashScripts/ccstatusline-usage.sh weekly --email <email1>"
}
],
[
{
"id": "49d535a6-56f8-42f5-b033-8a8bbbb6cd5c",
"type": "custom-command",
"backgroundColor": "bgBrightBlack",
"commandPath": "~/bashScripts/ccstatusline-usage.sh email --email <email2> --bracket"
},
{
"id": "dc0a7aed-704a-47f9-8ef5-41755bde1950",
"type": "custom-command",
"backgroundColor": "bgBlue",
"commandPath": "~/bashScripts/ccstatusline-usage.sh ttl --email <email2>"
},
{
"id": "56f00408-9c69-44eb-a06b-411d34b64de3",
"type": "custom-command",
"backgroundColor": "bgMagenta",
"commandPath": "~/bashScripts/ccstatusline-usage.sh session --email <email2>"
},
{
"id": "79ed94da-4403-41d4-9cce-2420bb163c28",
"type": "custom-command",
"backgroundColor": "bgCyan",
"commandPath": "~/bashScripts/ccstatusline-usage.sh weekly --email <email2>"
}
]
],
"flexMode": "full-minus-40",
"compactThreshold": 60,
"colorLevel": 3,
"defaultPadding": " ",
"inheritSeparatorColors": false,
"globalBold": false,
"powerline": {
"enabled": true,
"separators": [
""
],
"separatorInvertBackground": [
false
],
"startCaps": [
""
],
"endCaps": [
""
],
"theme": "solarized",
"autoAlign": false
}
}
#!/bin/bash
#set -x
#set -e -o -u
USAGES_FILE="$HOME/.cache/ccstatusline/usages.json"
LOCK_FILE="$HOME/.cache/ccstatusline/usages.lock"
# Function to generate progress bar
make_bar() {
local pct="$1"
local width=15
local filled=$((pct * width / 100))
local empty=$((width - filled))
printf "["
for ((i=0; i<filled; i++)); do printf "█"; done
for ((i=0; i<empty; i++)); do printf "░"; done
printf "]"
}
# Format time delta as "Xd Yh Zm" (omitting zero values)
format_delta() {
local seconds="$1"
local days=$((seconds / 86400))
local hours=$(((seconds % 86400) / 3600))
local minutes=$(((seconds % 3600) / 60))
local result=""
[[ $days -gt 0 ]] && result="${result}${days}d "
[[ $hours -gt 0 ]] && result="${result}${hours}h "
[[ $minutes -gt 0 ]] && result="${result}${minutes}m"
echo "${result% }"
}
# Convert ISO timestamp to epoch seconds (UTC)
iso_to_epoch() {
TZ=UTC0 date -j -f "%Y-%m-%dT%H:%M:%S" "${1:0:19}" "+%s" 2>/dev/null
}
# Format epoch to readable date (UTC)
epoch_to_date() {
TZ=UTC0 date -r "$1" "+%y-%m-%d %H:%M" 2>/dev/null
}
# Format epoch to readable date (Europe/Paris timezone)
epoch_to_date_local() {
TZ=Europe/Paris date -r "$1" "+%y-%m-%d %H:%M" 2>/dev/null
}
# Get current epoch
now_epoch() {
date +%s
}
# Save error for email in usages.json
save_error_for_email() {
local email="$1"
local error_type="$2"
if [[ -z "$email" || -z "$error_type" ]]; then return 1; fi
mkdir -p "$(dirname "$USAGES_FILE")"
NOW=$(date -u +"%Y-%m-%dT%H:%M:%S+00:00")
if [[ -f "$USAGES_FILE" ]]; then
EXISTING=$(jq -r --arg email "$email" '.usages[] | select(.email == $email) | .email // empty' "$USAGES_FILE" 2>/dev/null)
if [[ -n "$EXISTING" ]]; then
jq --arg email "$email" --arg now "$NOW" --arg error "$error_type" \
'(.usages[] | select(.email == $email)) |= . + {lastError: {timestamp: $now, type: $error}}' \
"$USAGES_FILE" > "${USAGES_FILE}.tmp" && mv "${USAGES_FILE}.tmp" "$USAGES_FILE"
else
jq --arg email "$email" --arg now "$NOW" --arg error "$error_type" \
'.usages += [{email: $email, lastError: {timestamp: $now, type: $error}}]' \
"$USAGES_FILE" > "${USAGES_FILE}.tmp" && mv "${USAGES_FILE}.tmp" "$USAGES_FILE"
fi
else
jq -n --arg email "$email" --arg now "$NOW" --arg error "$error_type" \
'{usages: [{email: $email, lastError: {timestamp: $now, type: $error}}]}' > "$USAGES_FILE"
fi
}
# Find the closest future session reset time across all emails
get_closest_session_reset() {
if [[ ! -f "$USAGES_FILE" ]]; then return 1; fi
local now_epoch=$(now_epoch)
local closest=""
while IFS= read -r reset_time; do
if [[ -n "$reset_time" ]]; then
local reset_epoch=$(iso_to_epoch "$reset_time")
if [[ -n "$reset_epoch" && $reset_epoch -gt $now_epoch ]]; then
if [[ -z "$closest" || $reset_epoch -lt $closest ]]; then
closest="$reset_epoch"
fi
fi
fi
done < <(jq -r '.usages[].usage.sessionResetAt' "$USAGES_FILE" 2>/dev/null)
[[ -n "$closest" ]] && echo "$closest"
}
# Find the closest future weekly reset time across all emails
get_closest_weekly_reset() {
if [[ ! -f "$USAGES_FILE" ]]; then return 1; fi
local now_epoch=$(now_epoch)
local closest=""
while IFS= read -r reset_time; do
if [[ -n "$reset_time" ]]; then
local reset_epoch=$(iso_to_epoch "$reset_time")
if [[ -n "$reset_epoch" && $reset_epoch -gt $now_epoch ]]; then
if [[ -z "$closest" || $reset_epoch -lt $closest ]]; then
closest="$reset_epoch"
fi
fi
fi
done < <(jq -r '.usages[].usage.weeklyResetAt' "$USAGES_FILE" 2>/dev/null)
[[ -n "$closest" ]] && echo "$closest"
}
# Format usage data from usages.json for the given email and output
output_from_usages() {
local email="$1"
local entry
entry=$(jq -r --arg email "$email" '.usages[] | select(.email == $email)' "$USAGES_FILE" 2>/dev/null)
[[ -z "$entry" ]] && return 1
local session_int weekly_int session_reset weekly_reset
session_int=$(echo "$entry" | jq -r '.usage.sessionUsage')
weekly_int=$(echo "$entry" | jq -r '.usage.weeklyUsage')
session_reset=$(echo "$entry" | jq -r '.usage.sessionResetAt')
weekly_reset=$(echo "$entry" | jq -r '.usage.weeklyResetAt')
local session_epoch weekly_epoch session_reset_fmt weekly_reset_fmt
session_epoch=$(iso_to_epoch "$session_reset")
session_reset_fmt=$(epoch_to_date_local "$session_epoch")
weekly_epoch=$(iso_to_epoch "$weekly_reset")
weekly_reset_fmt=$(epoch_to_date_local "$weekly_epoch")
local now_epoch session_delta weekly_delta session_delta_fmt weekly_delta_fmt
now_epoch=$(now_epoch)
session_delta=$(( session_epoch - now_epoch ))
weekly_delta=$(( weekly_epoch - now_epoch ))
session_delta_fmt=$(format_delta "$session_delta")
weekly_delta_fmt=$(format_delta "$weekly_delta")
local session_bar weekly_bar
session_bar=$(make_bar "$session_int")
weekly_bar=$(make_bar "$weekly_int")
# Check if this is the closest reset time for session/weekly
local closest_session=$(get_closest_session_reset)
local closest_weekly=$(get_closest_weekly_reset)
local session_icon=""
local weekly_icon=""
if [[ "$session_epoch" == "$closest_session" ]]; then
session_icon="⏳"
fi
if [[ "$weekly_epoch" == "$closest_weekly" ]]; then
weekly_icon="⏳"
fi
local session_icon_fmt=""
local weekly_icon_fmt=""
[[ -n "$session_icon" ]] && session_icon_fmt=" $session_icon"
[[ -n "$weekly_icon" ]] && weekly_icon_fmt=" $weekly_icon"
local session_output="Session ($session_delta_fmt →$session_icon_fmt $session_reset_fmt): $session_bar ${session_int}%"
local weekly_output="Weekly ($weekly_delta_fmt →$weekly_icon_fmt $weekly_reset_fmt): $weekly_bar ${weekly_int}%"
if [[ "$SUBCOMMAND" == "session" ]]; then echo "$session_output"
elif [[ "$SUBCOMMAND" == "weekly" ]]; then echo "$weekly_output"
else echo "$session_output | $weekly_output"
fi
return 0
}
# Determine SUBCOMMAND: if first arg doesn't start with --, it's the subcommand
SUBCOMMAND="all"
if [[ "${1:-}" != "" && ! "${1:-}" =~ ^-- ]]; then
SUBCOMMAND="$1"
shift
fi
FORCE_EMAIL=""
BRACKET_MODE=false
# Parse remaining arguments to extract --email and --bracket options
while [[ $# -gt 0 ]]; do
case "$1" in
--email)
FORCE_EMAIL="$2"
shift 2
;;
--bracket)
BRACKET_MODE=true
shift
;;
*)
shift
;;
esac
done
# Get the active email from .claude.json
ACTIVE_EMAIL=$(cat ~/.claude.json 2>/dev/null | jq -r '.oauthAccount.emailAddress // empty')
# Use forced email if provided, otherwise use active email
EMAIL="${FORCE_EMAIL:-$ACTIVE_EMAIL}"
# Email subcommand doesn't need API call
if [[ "$SUBCOMMAND" == "email" ]]; then
if [[ "$BRACKET_MODE" == true && "$EMAIL" == "$ACTIVE_EMAIL" && -n "$EMAIL" ]]; then
echo "[$EMAIL]"
else
echo "${EMAIL:-[No email]}"
fi
exit 0
fi
# TTL subcommand: show time until next API refresh or error if present
if [[ "$SUBCOMMAND" == "ttl" ]]; then
if [[ -n "$EMAIL" && -f "$USAGES_FILE" ]]; then
# Check for error first
LAST_ERROR=$(jq -r --arg email "$EMAIL" '.usages[] | select(.email == $email) | .lastError // empty' "$USAGES_FILE" 2>/dev/null)
if [[ -n "$LAST_ERROR" ]]; then
ERROR_TYPE=$(echo "$LAST_ERROR" | jq -r '.type // "unknown"')
echo "[Error: $ERROR_TYPE]"
exit 0
fi
LAST_ACQUIRED=$(jq -r --arg email "$EMAIL" '.usages[] | select(.email == $email) | .lastUsageAcquiredOn // empty' "$USAGES_FILE" 2>/dev/null)
if [[ -n "$LAST_ACQUIRED" ]]; then
LAST_EPOCH=$(iso_to_epoch "$LAST_ACQUIRED")
NOW_EPOCH=$(now_epoch)
AGE=$(( NOW_EPOCH - LAST_EPOCH ))
TTL=$(( 180 - AGE ))
[[ $TTL -lt 0 ]] && TTL=0
MINUTES=$(( TTL / 60 ))
SECONDS=$(( TTL % 60 ))
printf "%d:%02d\n" "$MINUTES" "$SECONDS"
exit 0
fi
fi
echo "0:00"
exit 0
fi
# Check if email exists in cache
EMAIL_IN_CACHE=false
if [[ -n "$EMAIL" && -f "$USAGES_FILE" ]]; then
CACHE_EMAIL=$(jq -r --arg email "$EMAIL" '.usages[] | select(.email == $email) | .email // empty' "$USAGES_FILE" 2>/dev/null)
if [[ "$CACHE_EMAIL" == "$EMAIL" ]]; then
EMAIL_IN_CACHE=true
# Check if cache is fresh (< 180 seconds old)
LAST_ACQUIRED=$(jq -r --arg email "$EMAIL" '.usages[] | select(.email == $email) | .lastUsageAcquiredOn' "$USAGES_FILE" 2>/dev/null)
LAST_EPOCH=$(iso_to_epoch "$LAST_ACQUIRED")
NOW_EPOCH=$(now_epoch)
AGE=$(( NOW_EPOCH - LAST_EPOCH ))
if [[ $AGE -lt 180 ]]; then
output_from_usages "$EMAIL" && exit 0
fi
fi
fi
# If email doesn't match active email, never fetch fresh data from API
if [[ "$EMAIL" != "$ACTIVE_EMAIL" ]]; then
echo "DEBUG: Email mismatch, not fetching API" >&2
# Try to show cache
output_from_usages "$EMAIL" && exit 0
# No cache available, show ???
if [[ "$SUBCOMMAND" == "session" ]]; then echo "???"
elif [[ "$SUBCOMMAND" == "weekly" ]]; then echo "???"
else echo "??? | ???"
fi
exit 1
fi
# At this point, EMAIL == ACTIVE_EMAIL, safe to proceed with API call if needed
# Prevent concurrent API calls (with stale lock detection)
if [[ -f "$LOCK_FILE" ]]; then
# Check if lock is stale (older than 15 seconds = 3x curl timeout)
LOCK_AGE=$(( $(now_epoch) - $(stat -f %m "$LOCK_FILE" 2>/dev/null || echo "0") ))
if [[ $LOCK_AGE -lt 15 ]]; then
output_from_usages "$EMAIL" && exit 0
echo "[Timeout]" && exit 1
fi
# Stale lock detected, remove it and proceed
rm -f "$LOCK_FILE"
fi
touch "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"' EXIT
TOKEN=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty')
if [[ -z "$TOKEN" ]]; then
output_from_usages "$EMAIL" && exit 0
echo "[No credentials]"
exit 1
fi
RESPONSE=$(curl -s --max-time 5 "https://api.anthropic.com/api/oauth/usage" -H "Authorization: Bearer $TOKEN" -H "anthropic-beta: oauth-2025-04-20" 2>/dev/null)
if [[ -z "$RESPONSE" ]]; then
save_error_for_email "$EMAIL" "api_error"
output_from_usages "$EMAIL" && exit 0
echo "[API Error]"
exit 1
fi
SESSION=$(echo "$RESPONSE" | jq -r '.five_hour.utilization // empty' 2>/dev/null)
WEEKLY=$(echo "$RESPONSE" | jq -r '.seven_day.utilization // empty' 2>/dev/null)
WEEKLY_RESET=$(echo "$RESPONSE" | jq -r '.seven_day.resets_at // empty' 2>/dev/null)
FIVE_HOURS_RESET=$(echo "$RESPONSE" | jq -r '.five_hour.resets_at // empty' 2>/dev/null)
# If API failed, use stale cache or show error
if [[ -z "$SESSION" || -z "$WEEKLY" || -z "$FIVE_HOURS_RESET" || -z "$WEEKLY_RESET" ]]; then
ERROR_TYPE=$(echo "$RESPONSE" | jq -r '.error.type // "parse_error"' 2>/dev/null)
save_error_for_email "$EMAIL" "$ERROR_TYPE"
output_from_usages "$EMAIL" && exit 0
echo "[Parse Error]"
exit 1
fi
SESSION_INT=${SESSION%.*}
WEEKLY_INT=${WEEKLY%.*}
# Save usage data per email in usages.json
if [[ -n "$EMAIL" ]]; then
NOW=$(date -u +"%Y-%m-%dT%H:%M:%S+00:00")
USAGE_OBJ=$(jq -n \
--argjson session "$SESSION_INT" \
--arg sessionReset "$FIVE_HOURS_RESET" \
--argjson weekly "$WEEKLY_INT" \
--arg weeklyReset "$WEEKLY_RESET" \
'{sessionUsage: $session, sessionResetAt: $sessionReset, weeklyUsage: $weekly, weeklyResetAt: $weeklyReset, extraUsageEnabled: false}')
mkdir -p "$(dirname "$USAGES_FILE")"
if [[ -f "$USAGES_FILE" ]]; then
EXISTING=$(jq -r --arg email "$EMAIL" '.usages[] | select(.email == $email) | .email // empty' "$USAGES_FILE" 2>/dev/null)
if [[ -n "$EXISTING" ]]; then
jq --arg email "$EMAIL" --arg now "$NOW" --argjson usage "$USAGE_OBJ" \
'(.usages[] | select(.email == $email)) |= {email: $email, lastUsageAcquiredOn: $now, usage: $usage, lastError: null}' \
"$USAGES_FILE" > "${USAGES_FILE}.tmp" && mv "${USAGES_FILE}.tmp" "$USAGES_FILE"
else
jq --arg email "$EMAIL" --arg now "$NOW" --argjson usage "$USAGE_OBJ" \
'.usages += [{email: $email, lastUsageAcquiredOn: $now, usage: $usage, lastError: null}]' \
"$USAGES_FILE" > "${USAGES_FILE}.tmp" && mv "${USAGES_FILE}.tmp" "$USAGES_FILE"
fi
else
jq -n --arg email "$EMAIL" --arg now "$NOW" --argjson usage "$USAGE_OBJ" \
'{usages: [{email: $email, lastUsageAcquiredOn: $now, usage: $usage, lastError: null}]}' > "$USAGES_FILE"
fi
fi
# Output freshly fetched data
output_from_usages "$EMAIL"
@fcamblor
Copy link
Copy Markdown
Author

fcamblor commented Apr 4, 2026

This gives following ccstatusline display :
✳_Initial_chat_session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment