Created
April 29, 2026 11:23
-
-
Save o6uoq/bcfb0b53e0129630b43cdd707bc8eeed to your computer and use it in GitHub Desktop.
Claude Code: statusline.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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