Skip to content

Instantly share code, notes, and snippets.

@o6uoq
Created April 29, 2026 11:23
Show Gist options
  • Select an option

  • Save o6uoq/bcfb0b53e0129630b43cdd707bc8eeed to your computer and use it in GitHub Desktop.

Select an option

Save o6uoq/bcfb0b53e0129630b43cdd707bc8eeed to your computer and use it in GitHub Desktop.
Claude Code: statusline.sh
#!/bin/bash
# statusline.sh β€” Claude Code statusline
#
# Reads JSON from stdin (Claude Code statusLine spec), emits one line:
# πŸ€– model [🧠] [⚑️effort] Β· πŸ“ dir Β· 🌱 branch[❗] Β· πŸ’Ύ cache% Β· ⏱️ 5h: pct (resets) Β· [bar] ctx%
#
# Conditional sections (auto-hide when data unavailable):
# 🧠 β†’ only if .thinking.enabled = true
# ⚑️<lvl> β†’ only if .effort.level set
# 🌱+❗ β†’ only when .worktree.branch present (❗ = uncommitted changes)
# πŸ’Ύ % β†’ only when input tokens > 0 (skipped on cold start)
# ⏱️ 5h β†’ only when rate-limit field present
#
# To add/remove sections: edit BUILD SEGMENTS block.
# To debug field schema: STATUSLINE_DEBUG=1 dumps stdin JSON to /tmp/statusline-debug.json.
input=$(cat)
[ "${STATUSLINE_DEBUG:-0}" = "1" ] && printf '%s' "$input" > /tmp/statusline-debug.json
# ─── Constants ──────────────────────────────────────────────────────────
R=$'\033[0m'
GREEN=$'\033[92m'
DGREEN=$'\033[32m'
YELLOW=$'\033[93m'
RED=$'\033[91m'
BRED=$'\033[1;91m'
CYAN=$'\033[96m'
# Thresholds (tune here, not inline)
CACHE_GOOD=80; CACHE_OK=50 # cache hit %: high = good
FIVE_OK=50; FIVE_WARN=75 # 5h used %: low = good
CTX_GOOD=80; CTX_OK=60; CTX_WARN=40; CTX_BAD=25 # ctx remaining %: high = good
# Bar templates (10-cell)
BAR_FULL="β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“"
BAR_EMPTY="β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘"
# ─── Field extraction (single jq; positional read β€” keep order in sync) ─
{ read -r cwd # 1 workspace.project_dir
read -r model # 2 model.display_name
read -r ctx # 3 context_window.remaining_percentage
read -r ctx_size # 4 context_window.context_window_size
read -r five_pct # 5 rate_limits.five_hour.used_percentage
read -r five_resets # 6 rate_limits.five_hour.resets_at
read -r thinking # 7 thinking.enabled
read -r effort # 8 effort.level
read -r branch # 9 worktree.branch
read -r cache_read # 10 current_usage.cache_read_input_tokens
read -r cache_creation # 11 current_usage.cache_creation_input_tokens
read -r input_tok # 12 current_usage.input_tokens
} < <(
printf '%s' "$input" | jq -r '
.workspace.project_dir // .workspace.current_dir,
.model.display_name // .model // "sonnet",
.context_window.remaining_percentage // "?",
.context_window.context_window_size // 200000,
.rate_limits.five_hour.used_percentage // "",
.rate_limits.five_hour.resets_at // 0,
.thinking.enabled // false,
.effort.level // "",
.worktree.branch // "",
.context_window.current_usage.cache_read_input_tokens // 0,
.context_window.current_usage.cache_creation_input_tokens // 0,
.context_window.current_usage.input_tokens // 0
'
)
# ─── Build segments ─────────────────────────────────────────────────────
segs=()
# πŸ€– Model + 🧠 thinking + ⚑️ effort
m="πŸ€– ${model}"
[ "$thinking" = "true" ] && m="${m} 🧠"
[ -n "$effort" ] && m="${m} ⚑${effort}"
segs+=("$m")
# πŸ“ Project dir
segs+=("πŸ“ ${CYAN}${cwd##*/}${R}")
# 🌱 Branch (+ ❗ if dirty). Prefer .worktree.branch; fall back to git in cwd.
[ -z "$branch" ] && branch=$(git -C "$cwd" -c core.fsmonitor=false branch --show-current 2>/dev/null)
if [ -n "$branch" ]; then
s="🌱 ${branch}"
git -C "$cwd" -c core.fsmonitor=false status --porcelain 2>/dev/null | grep -q . && s="${s}❗"
segs+=("$s")
fi
# πŸ’Ύ Cache hit % (this turn's prefix served from cache vs total input)
total=$(( cache_read + cache_creation + input_tok ))
if (( total > 0 )); then
cache_pct=$(( cache_read * 100 / total ))
if (( cache_pct >= CACHE_GOOD )); then c=$GREEN
elif (( cache_pct >= CACHE_OK )); then c=$YELLOW
else c=$RED; fi
segs+=("πŸ’Ύ ${c}${cache_pct}%${R}")
fi
# ⏱️ 5h rate limit (with reset countdown)
if [ -n "$five_pct" ]; then
five_int=$(printf "%.0f" "$five_pct")
if (( five_int < FIVE_OK )); then c=$GREEN
elif (( five_int < FIVE_WARN )); then c=$YELLOW
else c=$RED; fi
if (( five_resets > 0 )); then
mins=$(awk -v r="$five_resets" -v n="$(date +%s)" 'BEGIN{m=int((r-n)/60); print (m<0?0:m)}')
segs+=("⏱ 5h: ${c}${five_int}%${R} (resets ${GREEN}$((mins/60))h$((mins%60))m${R})")
else
segs+=("⏱ 5h: ${c}${five_int}%${R}")
fi
fi
# [bar] ctx% with adaptive bands per context window size
# shift: smaller windows get higher thresholds (plumbing is bigger fraction)
# shift = (1M - ctx_size) / 160k β†’ 200k=5, 500k=3, 1M=0
if [ "$ctx" != "?" ]; then
ctx_int=${ctx%.*}
filled=$(( (ctx_int + 5) / 10 ))
bar="${BAR_FULL:0:filled}${BAR_EMPTY:0:$((10-filled))}"
shift=$(( (1000000 - ctx_size) / 160000 ))
if (( ctx_int > CTX_GOOD + shift )); then c=$GREEN
elif (( ctx_int > CTX_OK + shift )); then c=$DGREEN
elif (( ctx_int > CTX_WARN + shift )); then c=$YELLOW
elif (( ctx_int > CTX_BAD + shift )); then c=$RED
else c=$BRED; fi
segs+=("[${bar}] ${c}${ctx}%${R}")
else
segs+=("[??????????] ?%")
fi
# ─── Emit (join with " Β· ") ─────────────────────────────────────────────
out="${segs[0]}"
for s in "${segs[@]:1}"; do out="${out} Β· ${s}"; done
echo "$out"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment