Skip to content

Instantly share code, notes, and snippets.

@HarryHuang
Last active March 29, 2026 10:26
Show Gist options
  • Select an option

  • Save HarryHuang/698ad0838e07f0267c44011a20f3f9ba to your computer and use it in GitHub Desktop.

Select an option

Save HarryHuang/698ad0838e07f0267c44011a20f3f9ba to your computer and use it in GitHub Desktop.
my claude-code statusline-command.sh
#!/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