Last active
March 29, 2026 10:26
-
-
Save HarryHuang/698ad0838e07f0267c44011a20f3f9ba to your computer and use it in GitHub Desktop.
my claude-code statusline-command.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
| #!/usr/bin/env bash | |
| input=$(cat) | |
| # ── Colors ─────────────────────────────────────────────────────────────────── | |
| readonly CLR_RED='\033[0;31m' # bar: high usage (≥80%) | |
| readonly CLR_YELLOW='\033[0;33m' # bar: medium usage (≥50%), model, cost | |
| readonly CLR_GREEN='\033[0;32m' # bar: low usage (<50%) | |
| readonly CLR_MAGENTA='\033[0;35m' # session time | |
| readonly CLR_CYAN='\033[0;36m' # api time, git branch | |
| readonly CLR_RESET='\033[0m' # reset | |
| # ── Layout constants ───────────────────────────────────────────────────────── | |
| readonly BAR_CTX_WIDTH=10 # context window bar width (chars) | |
| readonly BAR_FIVE_WIDTH=10 # five-hour quota bar width (chars) | |
| readonly BAR_FILLED='█' | |
| readonly BAR_EMPTY='░' | |
| readonly LBL_SESSION='session' # label shown before session time | |
| readonly LBL_API='last' # label shown before last-api time | |
| readonly LBL_API_TIME='time' # label shown before total api duration | |
| readonly LBL_FIVE_HOUR='5H' # label for five-hour quota countdown | |
| # ── Input fields ───────────────────────────────────────────────────────────── | |
| cwd=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // empty') | |
| model=$(echo "$input" | jq -r '.model.display_name // empty') | |
| transcript_path=$(echo "$input" | jq -r '.transcript_path // empty') | |
| used_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty') | |
| ctx_size=$(echo "$input" | jq -r '.context_window.context_window_size // empty') | |
| total_in=$(echo "$input" | jq -r '.context_window.total_input_tokens // empty') | |
| total_out=$(echo "$input" | jq -r '.context_window.total_output_tokens // empty') | |
| cur_in=$(echo "$input" | jq -r '.context_window.current_usage.input_tokens // empty') | |
| cur_out=$(echo "$input" | jq -r '.context_window.current_usage.output_tokens // empty') | |
| cur_cc=$(echo "$input" | jq -r '.context_window.current_usage.cache_creation_input_tokens // empty') | |
| cur_cr=$(echo "$input" | jq -r '.context_window.current_usage.cache_read_input_tokens // empty') | |
| five_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty') | |
| five_resets=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty') | |
| cost_usd=$(echo "$input" | jq -r '.cost.total_cost_usd // empty') | |
| cost_api_ms=$(echo "$input" | jq -r '.cost.total_api_duration_ms // empty') | |
| # ── Format helpers ─────────────────────────────────────────────────────────── | |
| # Format token count as compact string (e.g. 19000 -> "19k", 500 -> "0.5k") | |
| fmt_tok() { | |
| local n="$1" | |
| if [ -z "$n" ] || [ "$n" = "null" ]; then echo "0"; return; fi | |
| awk -v n="$n" 'BEGIN { | |
| if (n >= 1000) { | |
| v = n / 1000 | |
| if (v == int(v)) printf "%dk", v | |
| else printf "%.1fk", v | |
| } else { | |
| printf "%d", n | |
| } | |
| }' | |
| } | |
| # Format context window size (e.g. 200000 -> "200k") | |
| fmt_ctx() { | |
| local n="$1" | |
| if [ -z "$n" ] || [ "$n" = "null" ]; then echo "?"; return; fi | |
| awk -v n="$n" 'BEGIN { printf "%dk", n/1000 }' | |
| } | |
| # Format elapsed seconds as compact duration (e.g. 3723 -> "1h02m03s") | |
| fmt_elapsed() { | |
| local secs="$1" | |
| if [ -z "$secs" ] || [ "$secs" -le 0 ] 2>/dev/null; then echo "0s"; return; fi | |
| local h=$(( secs / 3600 )) | |
| local m=$(( (secs % 3600) / 60 )) | |
| local s=$(( secs % 60 )) | |
| if [ "$h" -gt 0 ]; then printf '%dh%02dm%02ds' "$h" "$m" "$s" | |
| elif [ "$m" -gt 0 ]; then printf '%dm%02ds' "$m" "$s" | |
| else printf '%ds' "$s" | |
| fi | |
| } | |
| # Format milliseconds as human-readable duration (e.g. 1234 -> "1.23s", 65000 -> "1m05s") | |
| fmt_ms() { | |
| local ms="$1" | |
| if [ -z "$ms" ] || [ "$ms" = "null" ]; then echo "0s"; return; fi | |
| awk -v ms="$ms" 'BEGIN { | |
| secs = ms / 1000 | |
| if (secs >= 3600) { printf "%dh%02dm%02ds", int(secs/3600), int((secs%3600)/60), int(secs%60) } | |
| else if (secs >= 60) { printf "%dm%02ds", int(secs/60), int(secs%60) } | |
| else if (secs >= 10) { printf "%.1fs", secs } | |
| else { printf "%.2fs", secs } | |
| }' | |
| } | |
| # ── Calculations ───────────────────────────────────────────────────────────── | |
| # Derive session_time_str and api_time_str from transcript file timestamps | |
| calc_transcript_times() { | |
| session_time_str="" | |
| api_time_str="" | |
| [ -z "$transcript_path" ] || [ ! -f "$transcript_path" ] && return | |
| local now; now=$(date +%s) | |
| # Session start: file birth time, falling back to ctime | |
| local session_start | |
| session_start=$(stat -c '%W' "$transcript_path" 2>/dev/null) | |
| if [ -z "$session_start" ] || [ "$session_start" = "0" ]; then | |
| session_start=$(stat -c '%Z' "$transcript_path" 2>/dev/null) | |
| fi | |
| [ -n "$session_start" ] && [ "$session_start" != "0" ] && \ | |
| session_time_str=$(fmt_elapsed $(( now - session_start ))) | |
| # Last API call: last modification time | |
| local api_mtime | |
| api_mtime=$(stat -c '%Y' "$transcript_path" 2>/dev/null) | |
| [ -n "$api_mtime" ] && api_time_str="$(fmt_elapsed $(( now - api_mtime )))" | |
| } | |
| # Pick a usage-based color: green / yellow / red | |
| usage_color() { | |
| local pct_int="$1" | |
| if [ "$pct_int" -ge 80 ]; then printf '%s' "$CLR_RED" | |
| elif [ "$pct_int" -ge 50 ]; then printf '%s' "$CLR_YELLOW" | |
| else printf '%s' "$CLR_GREEN" | |
| fi | |
| } | |
| # ── Render functions ───────────────────────────────────────────────────────── | |
| # Each function prints its element (or nothing if data is absent). | |
| # Generic progress bar: render_bar <pct> <width> | |
| render_bar() { | |
| local pct="$1" width="$2" | |
| local pct_int; pct_int=$(printf '%.0f' "$pct") | |
| local color; color=$(usage_color "$pct_int") | |
| local filled=$(( pct_int * width / 100 )) | |
| local empty=$(( width - filled )) | |
| local bar="" | |
| for ((i=0; i<filled; i++)); do bar="${bar}${BAR_FILLED}"; done | |
| for ((i=0; i<empty; i++)); do bar="${bar}${BAR_EMPTY}"; done | |
| printf "${color}[%s] %d%%${CLR_RESET}" "$bar" "$pct_int" | |
| } | |
| render_ctx_bar() { | |
| [ -z "$used_pct" ] && return | |
| render_bar "$used_pct" "$BAR_CTX_WIDTH" | |
| } | |
| render_five_bar() { | |
| [ -z "$five_pct" ] && return | |
| render_bar "$five_pct" "$BAR_FIVE_WIDTH" | |
| } | |
| render_session_time() { | |
| [ -z "$session_time_str" ] && return | |
| local out; out=$(printf "${CLR_MAGENTA}${LBL_SESSION}: %s${CLR_RESET}" "$session_time_str") | |
| [ -n "$api_time_str" ] && \ | |
| out="${out} $(printf "${CLR_CYAN}${LBL_API}: %s${CLR_RESET}" "$api_time_str")" | |
| printf '%s' "$out" | |
| } | |
| render_cost() { | |
| [ -z "$cost_usd" ] || [ "$cost_usd" = "0" ] && return | |
| local cost_fmt; cost_fmt=$(awk -v c="$cost_usd" 'BEGIN { printf "$%.2f", c }') | |
| local out; out=$(printf "${CLR_YELLOW}%s${CLR_RESET}" "$cost_fmt") | |
| if [ -n "$cost_api_ms" ] && [ "$cost_api_ms" != "0" ]; then | |
| out="${out} $(printf "${CLR_YELLOW}${LBL_API_TIME}: %s${CLR_RESET}" "$(fmt_ms "$cost_api_ms")")" | |
| fi | |
| printf '%s' "$out" | |
| } | |
| render_five_countdown() { | |
| [ -z "$five_pct" ] || [ -z "$five_resets" ] && return | |
| local now; now=$(date +%s) | |
| local delta=$(( five_resets - now )) | |
| local resets_str | |
| if [ "$delta" -gt 0 ]; then | |
| local hrs=$(( delta / 3600 )) mins=$(( (delta % 3600) / 60 )) | |
| if [ "$hrs" -gt 0 ]; then resets_str=$(printf '%dh%02dm' "$hrs" "$mins") | |
| else resets_str=$(printf '%dm' "$mins") | |
| fi | |
| else | |
| resets_str="now" | |
| fi | |
| local color; color=$(usage_color "$(printf '%.0f' "$five_pct")") | |
| printf "${color}${LBL_FIVE_HOUR}: %s${CLR_RESET}" "$resets_str" | |
| } | |
| render_git_branch() { | |
| [ -z "$cwd" ] && return | |
| git -C "$cwd" rev-parse --is-inside-work-tree >/dev/null 2>&1 || return | |
| local branch | |
| branch=$(git -C "$cwd" -c core.useBuiltinFSMonitor=false symbolic-ref --short HEAD 2>/dev/null \ | |
| || git -C "$cwd" -c core.useBuiltinFSMonitor=false rev-parse --short HEAD 2>/dev/null) | |
| [ -n "$branch" ] && printf "${CLR_CYAN}\xe2\x8e\x87 %s${CLR_RESET}" "$branch" | |
| } | |
| render_model() { | |
| [ -z "$model" ] && return | |
| printf "${CLR_YELLOW}%s${CLR_RESET}" "$model" | |
| } | |
| render_ctx_size() { | |
| [ -z "$ctx_size" ] && return | |
| printf '%s ctx' "$(fmt_ctx "$ctx_size")" | |
| } | |
| render_total_tokens() { | |
| [ -z "$total_in" ] && [ -z "$total_out" ] && return | |
| printf 'W:%s R:%s' "$(fmt_tok "$total_in")" "$(fmt_tok "$total_out")" | |
| } | |
| render_cur_tokens() { | |
| [ -z "$cur_in" ] && [ -z "$cur_out" ] && [ -z "$cur_cc" ] && [ -z "$cur_cr" ] && return | |
| printf 'cur: W:%s R:%s cW:%s cR:%s' \ | |
| "$(fmt_tok "$cur_in")" "$(fmt_tok "$cur_out")" \ | |
| "$(fmt_tok "$cur_cc")" "$(fmt_tok "$cur_cr")" | |
| } | |
| # ── Layout ─────────────────────────────────────────────────────────────────── | |
| # Append a rendered element to a row variable (space-separated, skips empty) | |
| row_append() { | |
| local -n _row="$1" | |
| local elem="$2" | |
| [ -z "$elem" ] && return | |
| if [ -n "$_row" ]; then _row="${_row} ${elem}" | |
| else _row="$elem" | |
| fi | |
| } | |
| calc_transcript_times | |
| # Row 1 elements (inline, space-separated) | |
| ROW1="" | |
| row_append ROW1 "$(render_ctx_bar)" | |
| row_append ROW1 "$(render_five_bar)" | |
| row_append ROW1 "$(render_session_time)" | |
| row_append ROW1 "$(render_cost)" | |
| # Row 2 elements (pipe-separated) | |
| parts=() | |
| for elem in \ | |
| "$(render_five_countdown)" \ | |
| "$(render_git_branch)" \ | |
| "$(render_model)" \ | |
| "$(render_ctx_size)" \ | |
| "$(render_total_tokens)" \ | |
| "$(render_cur_tokens)" | |
| do | |
| [ -n "$elem" ] && parts+=("$elem") | |
| done | |
| ROW2="$(IFS='|'; printf '%s' "${parts[*]}" | sed 's/|/ | /g')" | |
| # ── Output ─────────────────────────────────────────────────────────────────── | |
| if [ -n "$ROW1" ] && [ -n "$ROW2" ]; then printf '%s\n%s' "$ROW1" "$ROW2" | |
| elif [ -n "$ROW1" ]; then printf '%s' "$ROW1" | |
| else printf '%s' "$ROW2" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment