Skip to content

Instantly share code, notes, and snippets.

@srigi
Created November 18, 2025 14:15
Show Gist options
  • Select an option

  • Save srigi/fc4cd8b7776c6cd7051afdc599a790c7 to your computer and use it in GitHub Desktop.

Select an option

Save srigi/fc4cd8b7776c6cd7051afdc599a790c7 to your computer and use it in GitHub Desktop.
Claude Code status-line script
Screenshot: https://i.ibb.co/NdNZhY3X/Screenshot-2025-11-18-at-15-04-18.png
#!/bin/bash
# Claude Code status-line - shows context usage with dynamic progress bar
# dependencies: jq
set -euo pipefail
# ============================================================================
# CONSTANTS
# ============================================================================
readonly CONTEXT_LIMIT=200000 # 200k tokens for basic subscription
# Color codes
readonly COLOR_RESET="\033[0m"
readonly COLOR_TEXT="\033[38;5;250m" # Light gray for text
# Model colors
readonly COLOR_SONNET="\033[38;5;114m" # Green
readonly COLOR_OPUS="\033[38;5;215m" # Orange
readonly COLOR_OTHER="\033[38;5;75m" # Blue
# Progress bar colors
readonly COLOR_BAR_0_20="\033[38;5;255m" # White
readonly COLOR_BAR_20_40="\033[38;5;153m" # Light blue/cyan
readonly COLOR_BAR_40_60="\033[38;5;117m" # Sky blue
readonly COLOR_BAR_60_80="\033[38;5;214m" # Orange
readonly COLOR_BAR_80_100="\033[38;5;196m" # Red
# Empty block colors (by usage percentage)
readonly COLOR_EMPTY_0_20="\033[38;5;242m"
readonly COLOR_EMPTY_20_40="\033[38;5;244m"
readonly COLOR_EMPTY_40_60="\033[38;5;246m"
readonly COLOR_EMPTY_60_80="\033[38;5;250m"
readonly COLOR_EMPTY_80_100="\033[38;5;254m"
# Progress bar sizing
readonly BAR_MIN_LENGTH=40
readonly BAR_MAX_LENGTH=120
# ============================================================================
# HELPER FUNCTIONS
# ============================================================================
parse_json_input() {
local input="$1"
# extract all needed fields
echo "$input" | jq -r '
(.model.display_name // "Claude"),
(.model.id // ""),
(.transcript_path // ""),
(.session_id // ""),
(if .exceeds_200k_tokens == null then "unknown" else (.exceeds_200k_tokens | tostring) end),
(.cost.total_cost_usd // 0 | tostring)
' 2>/dev/null || {
# default outputs on error
echo "Claude"
echo ""
echo ""
echo ""
echo "unknown"
echo "0"
}
}
get_model_color() {
local model_name="$1"
if [[ "$model_name" == *"Sonnet"* ]]; then
echo -n "$COLOR_SONNET"
elif [[ "$model_name" == *"Opus"* ]]; then
echo -n "$COLOR_OPUS"
else
echo -n "$COLOR_OTHER"
fi
}
get_terminal_width() {
if [[ -n "${COLUMNS:-}" ]]; then
echo "$COLUMNS"
else
tput cols 2>/dev/null || echo 160 # fallback
fi
}
get_display_length() {
local str="$1"
local len
# strip ANSI codes
len=$(echo -e "$str" | sed 's/\x1b\[[0-9;]*m//g' | wc -c | tr -d ' ')
echo $((len - 1)) # Account for newline
}
# Get progress bar colors based on percentage
get_bar_colors() {
local pct="$1"
if [[ $pct -lt 20 ]]; then
echo "$COLOR_BAR_0_20 $COLOR_EMPTY_0_20"
elif [[ $pct -lt 40 ]]; then
echo "$COLOR_BAR_20_40 $COLOR_EMPTY_20_40"
elif [[ $pct -lt 60 ]]; then
echo "$COLOR_BAR_40_60 $COLOR_EMPTY_40_60"
elif [[ $pct -lt 80 ]]; then
echo "$COLOR_BAR_60_80 $COLOR_EMPTY_60_80"
else
echo "$COLOR_BAR_80_100 $COLOR_EMPTY_80_100"
fi
}
build_statusline() {
local left="$1"
local right="$2"
local term_width="$3"
local left_len right_len padding pad_string
left_len=$(get_display_length "$left")
right_len=$(get_display_length "$right")
padding=$((term_width - left_len - right_len))
if [[ $padding -lt 1 ]]; then padding=1; fi
pad_string=$(printf "%*s" $padding "")
echo -e "${left}${pad_string}${right}"
}
# ============================================================================
# MAIN CONTEXT CALCULATION
# ============================================================================
calculate_context() {
# Read JSON input from stdin
local input
input=$(cat)
# Parse JSON once to extract all fields
local json_output model_name model_id transcript_path session_id exceeds_200k total_cost
json_output=$(parse_json_input "$input")
model_name=$(echo "$json_output" | sed -n '1p')
model_id=$(echo "$json_output" | sed -n '2p')
transcript_path=$(echo "$json_output" | sed -n '3p')
session_id=$(echo "$json_output" | sed -n '4p')
exceeds_200k=$(echo "$json_output" | sed -n '5p')
total_cost=$(echo "$json_output" | sed -n '6p')
local term_width
term_width=$(get_terminal_width)
local model_color
model_color=$(get_model_color "$model_name")
if [[ ! -f "$transcript_path" ]]; then
# No transcript - show N/A
local left_side="${model_color}${model_name}${COLOR_RESET} "
local right_side="${COLOR_TEXT}context size N/A${COLOR_RESET}"
build_statusline "$left_side" "$right_side" "$term_width"
return
fi
# Parse transcript to get token usage
local total_tokens
# Use bash loop + jq to parse JSONL and find most recent usage
local last_record=""
while IFS= read -r line; do
# Check if line has usage data and is not sidechain
if echo "$line" | jq -e '.isSidechain != true and .message.usage' >/dev/null 2>&1; then
last_record="$line"
fi
done < "$transcript_path"
if [[ -n "$last_record" ]]; then
# Extract usage data and calculate context length
total_tokens=$(echo "$last_record" | jq '
.message.usage |
(.input_tokens // 0) +
(.cache_read_input_tokens // 0) +
(.cache_creation_input_tokens // 0)
')
else
total_tokens=0
fi
# Check if no usage data available
if [[ $total_tokens -eq 0 ]]; then
local left_side="${model_color}${model_name}${COLOR_RESET} "
local right_side="${COLOR_TEXT}context size N/A${COLOR_RESET}"
build_statusline "$left_side" "$right_side" "$term_width"
return
fi
# Calculate usage percentage
local progress_pct_int
progress_pct_int=$(( total_tokens * 100 / CONTEXT_LIMIT ))
if [[ $progress_pct_int -gt 100 ]]; then
progress_pct_int=100
fi
# Format token counts
local formatted_tokens formatted_limit
formatted_tokens="$(( total_tokens / 1000 ))k"
formatted_limit="$(( CONTEXT_LIMIT / 1000 ))k"
# Calculate dynamic progress bar length
local bar_length
bar_length=$(( term_width / 2 ))
if [[ $bar_length -lt $BAR_MIN_LENGTH ]]; then bar_length=$BAR_MIN_LENGTH; fi
if [[ $bar_length -gt $BAR_MAX_LENGTH ]]; then bar_length=$BAR_MAX_LENGTH; fi
# Calculate filled blocks
local filled_blocks empty_blocks
filled_blocks=$(( (progress_pct_int * bar_length) / 100 ))
if [[ $filled_blocks -gt $bar_length ]]; then filled_blocks=$bar_length; fi
empty_blocks=$((bar_length - filled_blocks))
# Get colors based on percentage
local colors bar_color empty_color
colors=$(get_bar_colors "$progress_pct_int")
bar_color=$(echo "$colors" | cut -d' ' -f1)
empty_color=$(echo "$colors" | cut -d' ' -f2)
# Build progress bar with centered percentage
local pct_text="${progress_pct_int}%"
local pct_len=${#pct_text}
local center_pos=$(( (bar_length - pct_len) / 2 ))
local progress_bar=""
for ((i=0; i<bar_length; i++)); do
if [[ $i -eq $center_pos ]]; then
progress_bar+="${COLOR_RESET}${COLOR_TEXT}${pct_text}${COLOR_RESET}"
i=$((i + pct_len - 1))
elif [[ $i -lt $filled_blocks ]]; then
progress_bar+="${bar_color}"
else
progress_bar+="${empty_color}"
fi
done
# Build left and right sides
local left_side="${model_color}${model_name}${COLOR_RESET} "
local right_side="${progress_bar}${COLOR_RESET} ${COLOR_TEXT}(${formatted_tokens}/${formatted_limit})${COLOR_RESET}"
# Output aligned statusline
build_statusline "$left_side" "$right_side" "$term_width"
}
# ============================================================================
# MAIN ENTRY POINT
# ============================================================================
calculate_context
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment