Skip to content

Instantly share code, notes, and snippets.

@bearlike
Created June 14, 2026 03:43
Show Gist options
  • Select an option

  • Save bearlike/d72d4a67556e7823d6aa8d6402c556e0 to your computer and use it in GitHub Desktop.

Select an option

Save bearlike/d72d4a67556e7823d6aa8d6402c556e0 to your computer and use it in GitHub Desktop.
Claude Code status line script
#!/usr/bin/env bash
# Claude Code status line script (Nerd Font enhanced - nf-md icons)
# Personal edition
input=$(cat)
cwd=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // ""')
host_part="$(whoami)@$(hostname -s)"
model_name=$(echo "$input" | jq -r '.model.display_name // ""')
used_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
ctx_total=$(echo "$input" | jq -r '.context_window.context_window_size // 0')
ctx_used=$(echo "$input" | jq -r '.context_window.tokens_used // 0')
five_hour_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
five_hour_resets=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
seven_day_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
seven_day_resets=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
# Try JSON input first, fall back to settings.json
effort=$(echo "$input" | jq -r '.effortLevel // empty')
[ -z "$effort" ] && effort=$(jq -r '.effortLevel // empty' ~/.claude/settings.json 2>/dev/null)
# Shorten cwd: replace $HOME with ~
home_dir="$HOME"
short_dir="${cwd/#$home_dir/\~}"
# ANSI colors
green=$'\033[32m'
red=$'\033[31m'
yellow=$'\033[33m'
cyan=$'\033[36m'
blue=$'\033[34m'
magenta=$'\033[35m'
bold=$'\033[1m'
reset=$'\033[0m'
# Nerd Font icons with colors baked in
icon_stash="${yellow}󰏗${reset}"
icon_gauge="${cyan}󰓅${reset}"
icon_folder="${blue}󰉋${reset}"
icon_branch="${magenta}󰘬${reset}"
icon_clock="${yellow}󰥔${reset}"
icon_load="${green}󰍛${reset}"
icon_user="${cyan}󰀄${reset}"
icon_pr="${blue}󰜂${reset}"
icon_team="${magenta}󰙃${reset}"
icon_limit="${red}󰔂${reset}"
# Git info
git_part=""
branch=""
pr_part=""
if git -C "$cwd" rev-parse --git-dir >/dev/null 2>&1; then
# Use repo toplevel so counts cover the entire repo, not just $cwd subdir
repo_root=$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null)
branch=$(git -C "$repo_root" -c core.hooksPath=/dev/null symbolic-ref --short HEAD 2>/dev/null || git -C "$repo_root" rev-parse --short HEAD 2>/dev/null)
stash_count=$(git -C "$repo_root" -c core.hooksPath=/dev/null stash list 2>/dev/null | wc -l | tr -d ' ')
staged=$(git -C "$repo_root" -c core.hooksPath=/dev/null diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
unstaged=$(git -C "$repo_root" -c core.hooksPath=/dev/null diff --numstat 2>/dev/null | wc -l | tr -d ' ')
untracked=$(git -C "$repo_root" -c core.hooksPath=/dev/null ls-files --others --exclude-standard 2>/dev/null | wc -l | tr -d ' ')
stash_part=""
[ "$stash_count" -gt 0 ] && stash_part=" ${icon_stash} stash:${stash_count}"
untracked_part=""
[ "$untracked" -gt 0 ] && untracked_part=" ${yellow}?${untracked}${reset}"
git_part=" | ${icon_branch} ${branch}${stash_part} ${green}+${staged}${reset} ${red}-${unstaged}${reset}${untracked_part}"
# PR detection: use a per-branch cache file (TTL 120s) to avoid gh slowness
if [ -n "$branch" ] && command -v gh >/dev/null 2>&1; then
cache_dir="${TMPDIR:-/tmp}/claude-statusline-pr"
mkdir -p "$cache_dir"
# Sanitise branch name for use as a filename
cache_key=$(printf '%s' "$branch" | tr '/' '_' | tr -dc '[:alnum:]_.-')
cache_file="$cache_dir/${cache_key}.cache"
cache_ttl=120 # seconds
pr_cached=""
if [ -f "$cache_file" ]; then
file_age=$(( $(date +%s) - $(stat -c %Y "$cache_file" 2>/dev/null || echo 0) ))
[ "$file_age" -lt "$cache_ttl" ] && pr_cached=$(cat "$cache_file" 2>/dev/null)
fi
if [ -z "$pr_cached" ]; then
# Run gh with a short timeout; write result to cache even if empty
pr_json=$(cd "$repo_root" && timeout 3 gh pr view --json number,title,state 2>/dev/null)
pr_number=$(echo "$pr_json" | jq -r '.number // empty' 2>/dev/null)
pr_state=$(echo "$pr_json" | jq -r '.state // empty' 2>/dev/null)
if [ -n "$pr_number" ]; then
pr_cached="${pr_number}:${pr_state}"
else
pr_cached="none"
fi
printf '%s' "$pr_cached" > "$cache_file"
fi
if [ "$pr_cached" != "none" ] && [ -n "$pr_cached" ]; then
pr_number="${pr_cached%%:*}"
pr_state="${pr_cached##*:}"
case "$pr_state" in
OPEN) pr_color="$green" ;;
MERGED) pr_color="$magenta" ;;
CLOSED) pr_color="$red" ;;
*) pr_color="$cyan" ;;
esac
pr_part=" | ${icon_pr} ${pr_color}PR #${pr_number}${reset}"
fi
fi
fi
# Format seconds-until-reset as "Xh Ym" or "Ym"
fmt_reset_in() {
local epoch=$1
[ -z "$epoch" ] && return
local now secs_left mins hours
now=$(date +%s)
secs_left=$(( epoch - now ))
[ "$secs_left" -le 0 ] && return
mins=$(( secs_left / 60 ))
hours=$(( mins / 60 ))
mins=$(( mins % 60 ))
if [ "$hours" -gt 0 ]; then
printf '%dh %dm' "$hours" "$mins"
else
printf '%dm' "$mins"
fi
}
# Rate limit parts
rate_limit_part=""
if [ -n "$five_hour_pct" ] || [ -n "$seven_day_pct" ]; then
rl_segments=""
if [ -n "$five_hour_pct" ]; then
fh_int=$(printf "%.0f" "$five_hour_pct" 2>/dev/null || echo "$five_hour_pct")
if [ "$fh_int" -ge 80 ] 2>/dev/null; then
fh_color="$red"
elif [ "$fh_int" -ge 50 ] 2>/dev/null; then
fh_color="$yellow"
else
fh_color="$green"
fi
fh_reset=$(fmt_reset_in "$five_hour_resets")
fh_label="${fh_color}5h:${fh_int}%${reset}"
[ -n "$fh_reset" ] && fh_label="${fh_label} ${dim}(resets in ${fh_reset})${reset}"
rl_segments="$fh_label"
fi
if [ -n "$seven_day_pct" ]; then
sd_int=$(printf "%.0f" "$seven_day_pct" 2>/dev/null || echo "$seven_day_pct")
if [ "$sd_int" -ge 80 ] 2>/dev/null; then
sd_color="$red"
elif [ "$sd_int" -ge 50 ] 2>/dev/null; then
sd_color="$yellow"
else
sd_color="$green"
fi
sd_reset=$(fmt_reset_in "$seven_day_resets")
sd_label="${sd_color}7d:${sd_int}%${reset}"
[ -n "$sd_reset" ] && sd_label="${sd_label} ${dim}(resets in ${sd_reset})${reset}"
if [ -n "$rl_segments" ]; then
rl_segments="${rl_segments} ${sd_label}"
else
rl_segments="$sd_label"
fi
fi
rate_limit_part=" | ${icon_limit} ${rl_segments}"
fi
# Format token count as K or M
fmt_tokens() {
local n=$1
if [ "$n" -ge 1000000 ] 2>/dev/null; then
awk "BEGIN {printf \"%.1fM\", $n/1000000}"
elif [ "$n" -ge 1000 ] 2>/dev/null; then
awk "BEGIN {printf \"%dK\", $n/1000}"
else
echo "${n}"
fi
}
# Context usage with color coding and token counts
ctx_part=""
if [ -n "$used_pct" ]; then
pct_int=$(printf "%.0f" "$used_pct" 2>/dev/null || echo "$used_pct")
if [ "$pct_int" -ge 75 ] 2>/dev/null; then
pct_color="$red"
elif [ "$pct_int" -ge 50 ] 2>/dev/null; then
pct_color="$yellow"
else
pct_color="$cyan"
fi
# Calculate used tokens from percentage if not provided directly
if [ "$ctx_used" -eq 0 ] && [ "$ctx_total" -gt 0 ] 2>/dev/null; then
ctx_used=$(awk "BEGIN {printf \"%d\", $used_pct/100 * $ctx_total}")
fi
used_fmt=$(fmt_tokens "$ctx_used")
total_fmt=$(fmt_tokens "$ctx_total")
ctx_part=" | ${icon_gauge} ctx ${pct_color}${pct_int}% (${used_fmt} / ${total_fmt})${reset}"
fi
# Load average (1-min)
load_1m=$(awk '{print $1}' /proc/loadavg 2>/dev/null || sysctl -n vm.loadavg 2>/dev/null | awk '{print $2}')
# Color-code load: <2 green, 2-4 yellow, >=4 red (rough heuristic)
load_color="$green"
load_int=$(printf "%.0f" "${load_1m:-0}" 2>/dev/null || echo 0)
[ "$load_int" -ge 4 ] 2>/dev/null && load_color="$red"
[ "$load_int" -ge 2 ] && [ "$load_int" -lt 4 ] 2>/dev/null && load_color="$yellow"
load_part="${load_color}${load_1m}${reset}"
# Time
time_part=$(date +%I:%M%p | sed 's/^0//')
# Effort label
effort_part=""
[ -n "$effort" ] && effort_part=" ${yellow}[${effort}]${reset}"
# Personal branded prefix
team_part="${bold}${cyan}Personal${reset}"
# Divider sized to terminal width (fallback 120)
dim=$'\033[2m'
width=${COLUMNS:-$(tput cols 2>/dev/null || echo 120)}
divider=$(printf '─%.0s' $(seq 1 "$width"))
printf "%s | %s %s | %s %s%s%s\n %s%s%s%s | %s load:%s | %s %s\n%s%s%s" \
"$team_part" \
"$icon_user" \
"$host_part" \
"$icon_folder" \
"$short_dir" \
"$git_part" \
"$pr_part" \
"$model_name" \
"$effort_part" \
"$ctx_part" \
"$rate_limit_part" \
"$icon_load" \
"$load_part" \
"$icon_clock" \
"$time_part" \
"$dim" \
"$divider" \
"$reset"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment