Context usage, cost, git status – things you want to keep an eye on while working with Claude Code, without having to ask for them. The status line shows that persistently at the bottom of the terminal. After each response, Claude Code runs a shell script that receives session data as JSON on stdin and outputs whatever you want to see.
Here’s what mine looks like:
[Claude Sonnet 4.6] ❯ my-project | ⑂ main ~2 | +41 -7
▓▓▓░░░░░░░ 31% ctx | $0.042 | ⧗ 3m 12s | 5h: 37% 7d: 23% | in:12k out:3.2k | cache w:8k r:21kThe script lives at ~/.claude/statusline.sh, wired up in ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}##Line 1 – Model, directory, git
[Claude Sonnet 4.6] ❯ my-project | ⑂ main ~2 | +41 -7[Claude Sonnet 4.6]– active model name, so it’s always clear which model is runningmy-project– last component of the current working directory, no full/home/...path⑂ main ~2– git branch;+Nfor staged files,~Nfor modified files+41 -7– lines added/removed across the entire session, not just the last commit
Git data is cached for 10 seconds (/tmp/claude-statusline-git-cache). Claude Code runs the script after every response – without caching, every git call would introduce noticeable lag.
##Line 2 – Context, cost, tokens
▓▓▓░░░░░░░ 31% ctx | $0.042 | ⧗ 3m 12s | 5h: 37% 7d: 23% | in:12k out:3.2k | cache w:8k r:21k▓▓▓░░░░░░░ 31% ctx– context bar (10 chars) with percentage; green below 50%, yellow up to 75%, red above$0.042– total session cost in USD so far⧗ 3m 12s– total session duration5h: 37% 7d: 23%– rate limit usage over 5 hours and 7 days; only available on Claude.ai Pro/Maxin:12k out:3.2k– input and output tokens for the current request, formatted compactly (e.g.1200 → 1.2k)cache w:8k r:21k– cache tokens written and read; high cache reads are good because cached tokens cost significantly less
##The full script
#!/bin/bash
input=$(cat)
# --- model & session ---
MODEL=$(echo "$input" | jq -r '.model.display_name // "Claude"')
DIR=$(echo "$input" | jq -r '.workspace.current_dir // ""')
DIR_NAME="${DIR##*/}"
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
LINES_ADDED=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
LINES_REMOVED=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')
# --- context window ---
PCT_RAW=$(echo "$input" | jq -r '.context_window.used_percentage // 0')
PCT=$(echo "$PCT_RAW" | cut -d. -f1)
IN_TOKENS=$(echo "$input" | jq -r '.context_window.current_usage.input_tokens // empty')
OUT_TOKENS=$(echo "$input" | jq -r '.context_window.current_usage.output_tokens // empty')
CACHE_W=$(echo "$input" | jq -r '.context_window.current_usage.cache_creation_input_tokens // empty')
CACHE_R=$(echo "$input" | jq -r '.context_window.current_usage.cache_read_input_tokens // empty')
# --- rate limits ---
FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
SEVEN_D=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
# --- colors ---
GREEN='\033[32m' YELLOW='\033[33m' RED='\033[31m'
CYAN='\033[36m' BOLD='\033[1m' DIM='\033[2m'
RESET='\033[0m'
# compact token format: 1234 -> 1.2k, 12345 -> 12k
fmt_tok() {
local n="$1"
[ -z "$n" ] || [ "$n" = "0" ] && { echo "0"; return; }
if [ "$n" -ge 1000 ]; then
local k=$(( n / 1000 )) r=$(( (n % 1000) / 100 ))
[ "$r" -gt 0 ] && [ "$k" -lt 10 ] && echo "${k}.${r}k" || echo "${k}k"
else
echo "$n"
fi
}
# --- git (cached 10s) ---
CACHE_FILE="/tmp/claude-statusline-git-cache"
if [ ! -f "$CACHE_FILE" ] || \
[ $(( $(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) )) -gt 10 ]; then
if git -C "${DIR:-.}" rev-parse --git-dir > /dev/null 2>&1; then
BRANCH=$(git -C "${DIR:-.}" branch --show-current 2>/dev/null)
STAGED=$(git -C "${DIR:-.}" diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
MODIFIED=$(git -C "${DIR:-.}" diff --numstat 2>/dev/null | wc -l | tr -d ' ')
printf '%s|%s|%s' "$BRANCH" "$STAGED" "$MODIFIED" > "$CACHE_FILE"
else
printf '||' > "$CACHE_FILE"
fi
fi
IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"
# --- line 1: model · dir · git · lines ---
LINE1="${BOLD}${CYAN}[${MODEL}]${RESET} ${DIM}❯${RESET} ${DIR_NAME}"
if [ -n "$BRANCH" ]; then
GIT_PART="${DIM}|${RESET} ⑂ ${GREEN}${BRANCH}${RESET}"
[ "${STAGED:-0}" -gt 0 ] && GIT_PART="${GIT_PART} ${GREEN}+${STAGED}${RESET}"
[ "${MODIFIED:-0}" -gt 0 ] && GIT_PART="${GIT_PART} ${YELLOW}~${MODIFIED}${RESET}"
LINE1="${LINE1} ${GIT_PART}"
fi
if [ "${LINES_ADDED:-0}" -gt 0 ] || [ "${LINES_REMOVED:-0}" -gt 0 ]; then
LINE1="${LINE1} ${DIM}|${RESET} ${GREEN}+${LINES_ADDED}${RESET} ${RED}-${LINES_REMOVED}${RESET}"
fi
# --- line 2: ctx bar · cost · time · tokens ---
if [ "${PCT:-0}" -ge 75 ]; then BAR_COLOR="$RED"
elif [ "${PCT:-0}" -ge 50 ]; then BAR_COLOR="$YELLOW"
else BAR_COLOR="$GREEN"
fi
BAR_WIDTH=10
FILLED=$(( PCT * BAR_WIDTH / 100 ))
[ "$FILLED" -gt "$BAR_WIDTH" ] && FILLED=$BAR_WIDTH
EMPTY=$(( BAR_WIDTH - FILLED ))
BAR=""
for ((i=0; i<FILLED; i++)); do BAR="${BAR}▓"; done
for ((i=0; i<EMPTY; i++)); do BAR="${BAR}░"; done
COST_FMT=$(LC_NUMERIC=C printf '$%.3f' "$COST")
MINS=$(( DURATION_MS / 60000 ))
SECS=$(( (DURATION_MS % 60000) / 1000 ))
LINE2="${BAR_COLOR}${BAR}${RESET} ${PCT}% ctx ${DIM}|${RESET} ${YELLOW}${COST_FMT}${RESET} ${DIM}|${RESET} ⧗ ${MINS}m ${SECS}s"
if [ -n "$FIVE_H" ] || [ -n "$SEVEN_D" ]; then
RATE="${DIM}|${RESET}"
[ -n "$FIVE_H" ] && RATE="${RATE} 5h: $(LC_NUMERIC=C printf '%.0f' "$FIVE_H")%"
[ -n "$SEVEN_D" ] && RATE="${RATE} 7d: $(LC_NUMERIC=C printf '%.0f' "$SEVEN_D")%"
LINE2="${LINE2} ${RATE}"
fi
if [ -n "$IN_TOKENS" ]; then
TOKEN_PART="${DIM}|${RESET} in:$(fmt_tok "$IN_TOKENS") out:${CYAN}$(fmt_tok "$OUT_TOKENS")${RESET}"
TOKEN_PART="${TOKEN_PART} ${DIM}| cache${RESET} w:$(fmt_tok "$CACHE_W") r:${GREEN}$(fmt_tok "$CACHE_R")${RESET}"
LINE2="${LINE2} ${TOKEN_PART}"
fi
printf '%b\n%b\n' "$LINE1" "$LINE2"##Conclusion
The script is a starting point. What’s shown here works for me – if you need different fields, different colors, or just less information, adjust it. The available JSON fields are documented in the script, the rest is just shell.