Skip to content

Instantly share code, notes, and snippets.

@n-WN
Last active March 21, 2026 16:37
Show Gist options
  • Select an option

  • Save n-WN/07abd31ac1a3bc960a542abdebc04303 to your computer and use it in GitHub Desktop.

Select an option

Save n-WN/07abd31ac1a3bc960a542abdebc04303 to your computer and use it in GitHub Desktop.
Claude Code custom statusline — shows rate limits, corrected cost, git branch, session duration
#!/bin/bash
# Claude Code custom statusline
# Shows: user@host:path branch [model] ctx% $cost duration 5h/7d quota Sonnet flag
#
# Setup:
# 1. Save this file to ~/.claude/statusline-command.sh
# 2. Add to ~/.claude/settings.json:
# {
# "statusLine": {
# "type": "command",
# "command": "bash ~/.claude/statusline-command.sh"
# }
# }
# 3. Restart Claude Code
#
# Features:
# - Rate limits (5h/7d) with reset countdown, cached across sessions
# - Session cost corrected for 1h cache pricing (Pro/Max users)
# - Git branch + dirty state
# - Context window usage %
# - Session duration
# - Sonnet model indicator with quota
#
# Requires: jq, git (optional)
# https://gist.github.com/n-WN/07abd31ac1a3bc960a542abdebc04303
input=$(cat)
NOW=$(date +%s)
# ---------- Parse all fields in one jq call ----------
eval "$(echo "$input" | jq -r '
@sh "cwd=\(.workspace.current_dir // .cwd // "")",
@sh "project_dir=\(.workspace.project_dir // .workspace.current_dir // "")",
@sh "model_id=\(.model.id // "")",
@sh "model_raw=\(.model.display_name // "")",
@sh "used=\(.context_window.used_percentage // "")",
@sh "session_id=\(.session_id // "")",
@sh "claude_cost=\(.cost.total_cost_usd // 0)",
@sh "duration_ms=\(.cost.total_duration_ms // 0)",
@sh "rl_5h=\(.rate_limits.five_hour.used_percentage // "")",
@sh "rl_5h_reset=\(.rate_limits.five_hour.resets_at // "")",
@sh "rl_7d=\(.rate_limits.seven_day.used_percentage // "")",
@sh "rl_7d_reset=\(.rate_limits.seven_day.resets_at // "")",
@sh "cache_create=\(.context_window.current_usage.cache_creation_input_tokens // 0)"
')" || exit 0
# ---------- Derived values ----------
cwd="${cwd/#$HOME/\~}"
model="${model_raw/ \(1M context\)/ 1M}"
model="${model/ \(200K context\)/ 200K}"
# ---------- Git branch (single git call) ----------
git_seg=""
if [ -n "$project_dir" ] && [ -d "$project_dir/.git" ]; then
branch=$(git -C "$project_dir" rev-parse --abbrev-ref HEAD 2>/dev/null)
if [ -n "$branch" ]; then
if [ -z "$(git -C "$project_dir" status --porcelain 2>/dev/null | head -1)" ]; then
git_seg=" \033[0;32m${branch}\033[0m"
else
git_seg=" \033[0;33m${branch}*\033[0m"
fi
fi
fi
# ---------- Cost (with 1h cache correction, single jq call) ----------
# Claude Code uses $6.25/M for cache writes, but firstParty (Pro/Max) get 1h at $10/M.
# Track cumulative correction by detecting when cache_create changes.
CCOST_FILE="/tmp/claude-ccost-${session_id}"
prev_corr=0
if [ -n "$session_id" ]; then
if [ -f "$CCOST_FILE" ]; then
read -r prev_cc prev_corr < "$CCOST_FILE"
prev_corr="${prev_corr:-0}"
if [ "$cache_create" != "${prev_cc:-}" ] && [ "$cache_create" != "0" ]; then
prev_corr=$(jq -n "$prev_corr + $cache_create * 3.75 / 1000000")
fi
elif [ "$cache_create" != "0" ]; then
prev_corr=$(jq -n "$cache_create * 3.75 / 1000000")
fi
echo "$cache_create $prev_corr" > "$CCOST_FILE"
fi
real_cost=$(jq -n "($claude_cost + $prev_corr) * 100 | round / 100")
cost_seg=""
[ "$real_cost" != "0" ] && cost_seg=" \033[0;90m\$\033[0;33m${real_cost}\033[0m"
# ---------- Duration ----------
duration_s=$(( duration_ms / 1000 ))
dur_h=$(( duration_s / 3600 ))
dur_m=$(( (duration_s % 3600) / 60 ))
dur_seg=""
if [ "$dur_h" -gt 0 ]; then
dur_seg=" \033[0;90m${dur_h}h${dur_m}m\033[0m"
elif [ "$dur_m" -gt 0 ]; then
dur_seg=" \033[0;90m${dur_m}m\033[0m"
fi
# ---------- Rate limits (live → cache fallback) ----------
CACHE_FILE="$HOME/.claude/rate-limits-cache.json"
rl_stale=""
if [ -n "$rl_5h" ] || [ -n "$rl_7d" ]; then
echo "{\"fh\":\"$rl_5h\",\"fr\":\"$rl_5h_reset\",\"sd\":\"$rl_7d\",\"sr\":\"$rl_7d_reset\"}" > "$CACHE_FILE"
elif [ -f "$CACHE_FILE" ]; then
eval "$(jq -r '@sh "rl_5h=\(.fh)",@sh "rl_5h_reset=\(.fr)",@sh "rl_7d=\(.sd)",@sh "rl_7d_reset=\(.sr)"' "$CACHE_FILE" 2>/dev/null)"
[ -n "$rl_5h" ] || [ -n "$rl_7d" ] && rl_stale="*"
fi
fmt_reset() {
[ -z "$1" ] && return
local d=$(( $1 - NOW ))
[ "$d" -le 0 ] && return
local h=$(( d / 3600 )) m=$(( d % 3600 / 60 ))
[ "$h" -gt 0 ] && echo "${h}h${m}m" || echo "${m}m"
}
# ---------- Build segments ----------
quota=""
if [ -n "$rl_5h" ]; then
r=$(fmt_reset "$rl_5h_reset")
quota="$quota \033[0;90m5h:\033[0;35m$(printf '%.0f' "$rl_5h")%${rl_stale}\033[0m"
[ -n "$r" ] && quota="$quota\033[0;90m(↺${r})\033[0m"
else
quota="$quota \033[0;90m5h:\033[0;37m--\033[0m"
fi
if [ -n "$rl_7d" ]; then
r=$(fmt_reset "$rl_7d_reset")
quota="$quota \033[0;90m7d:\033[0;35m$(printf '%.0f' "$rl_7d")%${rl_stale}\033[0m"
[ -n "$r" ] && quota="$quota\033[0;90m(↺${r})\033[0m"
else
quota="$quota \033[0;90m7d:\033[0;37m--\033[0m"
fi
sonnet=""
if [[ "$model_id" == *sonnet* ]] && [ -n "$rl_5h" ]; then
sonnet=" \033[0;33mSonnet\033[0m\033[0;90m[$(printf '%.0f' "$rl_5h")%]\033[0m"
elif [[ "$model_id" == *sonnet* ]]; then
sonnet=" \033[0;33mSonnet\033[0m"
fi
flag=""
FLAG_LOG="/tmp/claude-ctf-flags.log"
[ -f "$FLAG_LOG" ] && [ -s "$FLAG_LOG" ] && flag=" \033[0;90mflag:\033[0;32m$(wc -l < "$FLAG_LOG" | tr -d ' ')\033[0m"
ctx=""
[ -n "$used" ] && ctx=" \033[0;90mctx:${used%.*}%\033[0m"
# ---------- Render ----------
echo -ne "\033[0;32m${USER:-$(whoami)}\033[0m@\033[0;36m${HOSTNAME%%.*}\033[0m:\033[0;34m${cwd}\033[0m${git_seg} \033[0;33m[${model}]\033[0m${ctx}${cost_seg}${dur_seg}${quota}${sonnet}${flag}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment