Created
June 14, 2026 18:53
-
-
Save AlexMeuer/03973e03d5a9e83a5bf55cdccd755ba8 to your computer and use it in GitHub Desktop.
Claude Statusline
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
| #!/usr/bin/env bash | |
| # Claude Code statusline — Gruvbox Material Dark, minimal, single-line | |
| # Segment order: Model · Context tokens · RateLimit? · Worktree? · Agent? · Caveman? | |
| input=$(cat) | |
| # ── Gruvbox Material Dark palette (256-color approximations via truecolor) ── | |
| # Using \033[38;2;R;G;Bm for fg, \033[48;2;R;G;Bm for bg | |
| fg() { printf '\033[38;2;%s;%s;%sm' "$1" "$2" "$3"; } | |
| # bg() { printf '\033[48;2;%s;%s;%sm' "$1" "$2" "$3"; } # not needed for borderless style | |
| reset='\033[0m' | |
| bold='\033[1m' | |
| # Colors (R G B) | |
| c_fg="212 190 152" # #d4be98 fg | |
| c_red="234 105 98" # #ea6962 | |
| c_green="169 182 101" # #a9b665 | |
| c_yellow="216 166 87" # #d8a657 | |
| c_blue="125 174 163" # #7daea3 | |
| c_purple="211 134 155" # #d3869b | |
| c_aqua="137 180 130" # #89b482 | |
| c_orange="231 138 78" # #e78a4e | |
| c_gray="146 131 116" # #928374 | |
| c_dim="50 48 47" # #32302f — near-background, for separators | |
| SEP="│" # U+2502 — box-drawing vertical bar | |
| # Helper: emit a colored segment followed by a gray soft separator | |
| # Usage: seg COLOR_ARGS text [include_sep=1] | |
| emit() { | |
| local color="$1" text="$2" sep="${3:-1}" | |
| printf '%b%s%b' "$(fg $color)" "$text" "$reset" | |
| if [ "$sep" = "1" ]; then | |
| printf ' %b%s%b ' "$(fg $c_dim)" "$SEP" "$reset" | |
| fi | |
| } | |
| # ── 1. Model ────────────────────────────────────────────────────────────────── | |
| model=$(printf '%s' "$input" | jq -r '.model.display_name // empty') | |
| # Shorten common model names | |
| case "$model" in | |
| "Claude Sonnet 4.6"|"claude-sonnet-4-6") model="Sonnet 4.6" ;; | |
| "Claude Opus 4.8"|"claude-opus-4-8") model="Opus 4.8" ;; | |
| "Claude Haiku"*) model="${model/Claude /}" ;; | |
| "Claude "*) model="${model/Claude /}" ;; | |
| esac | |
| # ── 2. Context window bar + token count ────────────────────────────────────── | |
| ctx_used=$(printf '%s' "$input" | jq -r '.context_window.used_percentage // empty') | |
| ctx_tokens=$(printf '%s' "$input" | jq -r '.context_window.total_input_tokens // empty') | |
| ctx_part="" | |
| if [ -n "$ctx_used" ]; then | |
| ctx_int=$(printf '%.0f' "$ctx_used") | |
| # Color based on usage | |
| if [ "$ctx_int" -ge 90 ]; then | |
| ctx_color="$c_red" | |
| elif [ "$ctx_int" -ge 70 ]; then | |
| ctx_color="$c_yellow" | |
| else | |
| ctx_color="$c_green" | |
| fi | |
| # Circle progress bar: 10 chars wide (each = 10%) | |
| bar="" | |
| filled=$(( ctx_int / 10 )) | |
| empty=$(( 10 - filled )) | |
| for i in $(seq 1 $filled); do bar="${bar}●"; done | |
| for i in $(seq 1 $empty); do bar="${bar}○"; done | |
| # Format token count: show as e.g. "42k" or raw if < 1000 | |
| tok_label="" | |
| if [ -n "$ctx_tokens" ]; then | |
| if [ "$ctx_tokens" -ge 1000 ]; then | |
| tok_label="$(( ctx_tokens / 1000 ))k" | |
| else | |
| tok_label="${ctx_tokens}" | |
| fi | |
| fi | |
| ctx_part="${bar} ${tok_label}" | |
| fi | |
| # ── 3. Rate limit (5h only, when present) ──────────────────────────────────── | |
| rl_part="" | |
| rl_pct=$(printf '%s' "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty') | |
| rl_reset=$(printf '%s' "$input" | jq -r '.rate_limits.five_hour.resets_at // empty') | |
| if [ -n "$rl_pct" ]; then | |
| rl_int=$(printf '%.0f' "$rl_pct") | |
| rl_countdown="" | |
| if [ -n "$rl_reset" ]; then | |
| now=$(date +%s) | |
| rl_secs=$(( rl_reset - now )) | |
| if [ "$rl_secs" -gt 0 ]; then | |
| rl_h=$(( rl_secs / 3600 )) | |
| rl_m=$(( (rl_secs % 3600) / 60 )) | |
| if [ "$rl_h" -gt 0 ]; then | |
| rl_countdown=" ${rl_h}h ${rl_m}m" | |
| else | |
| rl_countdown=" ${rl_m}m" | |
| fi | |
| fi | |
| fi | |
| rl_bar="" | |
| rl_filled=$(( rl_int / 10 )) | |
| rl_empty=$(( 10 - rl_filled )) | |
| for i in $(seq 1 $rl_filled); do rl_bar="${rl_bar}●"; done | |
| for i in $(seq 1 $rl_empty); do rl_bar="${rl_bar}○"; done | |
| rl_part="${rl_bar}${rl_countdown}" | |
| fi | |
| # ── 7. Worktree (only when in a --worktree session) ─────────────────────────── | |
| wt_name=$(printf '%s' "$input" | jq -r '.worktree.name // empty') | |
| # ── 8. Agent (only when present) ────────────────────────────────────────────── | |
| agent_name=$(printf '%s' "$input" | jq -r '.agent.name // empty') | |
| # ── 9. Caveman badge ────────────────────────────────────────────────────────── | |
| caveman_part="" | |
| caveman_flag="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.caveman-active" | |
| if [ -f "$caveman_flag" ] && [ ! -L "$caveman_flag" ]; then | |
| cmode=$(head -c 64 "$caveman_flag" 2>/dev/null | tr -d '\n\r' | tr '[:upper:]' '[:lower:]') | |
| cmode=$(printf '%s' "$cmode" | tr -cd 'a-z0-9-') | |
| case "$cmode" in | |
| off|lite|full|ultra|wenyan-lite|wenyan|wenyan-full|wenyan-ultra|commit|review|compress) | |
| if [ -z "$cmode" ] || [ "$cmode" = "full" ]; then | |
| caveman_part="CAVEMAN" | |
| else | |
| caveman_part="CAVEMAN:$(printf '%s' "$cmode" | tr '[:lower:]' '[:upper:]')" | |
| fi | |
| ;; | |
| esac | |
| fi | |
| # ── Assemble ────────────────────────────────────────────────────────────────── | |
| out="" | |
| append_seg() { | |
| local text="$1" color="$2" | |
| if [ -n "$text" ]; then | |
| if [ -n "$out" ]; then | |
| out="${out} $(printf '%b%s%b' "$(fg $c_dim)" "$SEP" "$reset") " | |
| fi | |
| out="${out}$(printf '%b%s%b' "$(fg $color)" "$text" "$reset")" | |
| fi | |
| } | |
| # Model — blue | |
| [ -n "$model" ] && append_seg "$model" "$c_blue" | |
| # Context — color-coded (already computed ctx_color) | |
| if [ -n "$ctx_part" ]; then | |
| if [ -n "$out" ]; then | |
| out="${out} $(printf '%b%s%b' "$(fg $c_dim)" "$SEP" "$reset") " | |
| fi | |
| out="${out}$(printf '%b%s%b' "$(fg $ctx_color)" "$ctx_part" "$reset")" | |
| fi | |
| # Rate limit — red/yellow based on pct | |
| if [ -n "$rl_part" ]; then | |
| if [ "$rl_int" -ge 80 ]; then | |
| rl_color="$c_red" | |
| else | |
| rl_color="$c_yellow" | |
| fi | |
| append_seg "$rl_part" "$rl_color" | |
| fi | |
| # Worktree — purple (only when present) | |
| [ -n "$wt_name" ] && append_seg "$wt_name" "$c_purple" | |
| # Agent — green (only when present) | |
| [ -n "$agent_name" ] && append_seg "$agent_name" "$c_green" | |
| # Caveman badge — bold orange (only when active) | |
| if [ -n "$caveman_part" ]; then | |
| if [ -n "$out" ]; then | |
| out="${out} $(printf '%b%s%b' "$(fg $c_dim)" "$SEP" "$reset") " | |
| fi | |
| out="${out}$(printf '%b%b%s%b' "$bold" "$(fg $c_orange)" "$caveman_part" "$reset")" | |
| fi | |
| printf '%s' "$out" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment