Kontext-Auslastung, Kosten, Git-Status – das sind Dinge, die man beim Arbeiten mit Claude Code immer mal wieder wissen will, ohne extra nachfragen zu müssen. Die Statuszeile zeigt das permanent am unteren Terminalrand. Nach jeder Antwort führt Claude Code ein Shell-Script aus, das JSON-Sitzungsdaten via stdin bekommt und ausgibt was man sehen will.
So sieht meine Statuszeile aus:
[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:21kDas Script liegt unter ~/.claude/statusline.sh, eingetragen in ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}##Zeile 1 – Modell, Verzeichnis, Git
[Claude Sonnet 4.6] ❯ my-project | ⑂ main ~2 | +41 -7[Claude Sonnet 4.6]– aktives Modell, damit sofort klar ist womit man gerade arbeitetmy-project– nur der letzte Teil des aktuellen Verzeichnispfads, kein volles/home/...⑂ main ~2– Git-Branch;+Nfür gestagete,~Nfür modifizierte Dateien+41 -7– Lines added/removed der gesamten Sitzung, nicht nur des letzten Commits
Git-Daten werden 10 Sekunden gecacht (/tmp/claude-statusline-git-cache). Claude Code führt das Script nach jeder Antwort aus – ohne Cache würde jedes git-Kommando spürbare Verzögerung erzeugen.
##Zeile 2 – Kontext, Kosten, Token
▓▓▓░░░░░░░ 31% ctx | $0.042 | ⧗ 3m 12s | 5h: 37% 7d: 23% | in:12k out:3.2k | cache w:8k r:21k▓▓▓░░░░░░░ 31% ctx– Kontextbalken (10 Zeichen) mit Prozentzahl; grün unter 50 %, gelb bis 75 %, rot darüber$0.042– bisherige Kosten der Sitzung in USD⧗ 3m 12s– Gesamtlaufzeit der Sitzung5h: 37% 7d: 23%– Auslastung der Rate Limits über 5 Stunden und 7 Tage; nur bei Claude.ai Pro/Max verfügbarin:12k out:3.2k– Input- und Output-Token des aktuellen Requests, kompakt formatiert (z. B.1200 → 1.2k)cache w:8k r:21k– geschriebene und gelesene Cache-Token; hohe Cache-Reads sind gut, weil gecachte Token deutlich weniger kosten
##Das vollständige 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"##Fazit
Das Script ist ein Ausgangspunkt. Was hier gezeigt ist funktioniert für mich – wer andere Felder braucht, andere Farben will oder einfach weniger Informationen möchte, passt es an. Die verfügbaren JSON-Felder sind im Script dokumentiert, der Rest ist Shell.