diff --git a/.claude/settings.json b/.claude/settings.json index 4e876ca..4208e04 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -69,5 +69,10 @@ ] } ] + }, + "statusLine": { + "type": "command", + "command": "bash \"${CLAUDE_PROJECT_DIR:-.}/.claude/statusline.sh\"", + "padding": 0 } } diff --git a/.claude/statusline.sh b/.claude/statusline.sh new file mode 100755 index 0000000..b5e344a --- /dev/null +++ b/.claude/statusline.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# +# Claude Code statusLine — shows working dir, model, and context-window usage. +# Wired via .claude/settings.json (statusLine.command). Receives the statusLine +# JSON on stdin; first stdout line is rendered (ANSI colour supported). +# +# Context usage comes straight from the input JSON — no transcript parsing: +# .context_window.used_percentage pre-calculated % of the window in use (input side) +# .context_window.context_window_size window size in tokens (1000000 for the 1M models) +# verified: Claude Code statusLine schema · code.claude.com/docs/en/statusline · 2026-06-17 +# +# Fails soft: any parse problem prints nothing and exits 0 (never breaks the prompt). +set -uo pipefail + +input=$(cat 2>/dev/null) || exit 0 +command -v jq >/dev/null 2>&1 || exit 0 + +# pctwindowdir-basenamemodel-name (used_percentage preferred, +# else derived from current_usage, else 0). @tsv keeps spaces in the dir safe. +parsed=$(printf '%s' "$input" | jq -r ' + (.workspace.current_dir // .cwd // "" | sub(".*/"; "")) as $dir + | (.model.display_name // "?") as $model + | (.context_window.context_window_size // 200000) as $win + | ( + if (.context_window.used_percentage // null) != null then + .context_window.used_percentage + elif (.context_window.current_usage // null) != null then + ((.context_window.current_usage.input_tokens + + (.context_window.current_usage.cache_creation_input_tokens // 0) + + (.context_window.current_usage.cache_read_input_tokens // 0)) / $win * 100) + else 0 end | floor + ) as $pct + | [$pct, $win, $dir, $model] | @tsv +' 2>/dev/null) || exit 0 +[ -z "$parsed" ] && exit 0 + +IFS=$'\t' read -r pct win dir model <<<"$parsed" + +# Human window label: 1000000 -> 1M, 200000 -> 200k, else Nk. +case "$win" in + 1000000) wlabel="1M" ;; + *) wlabel="$((win / 1000))k" ;; +esac + +# Colour the bar/percentage by pressure: green <70, yellow 70–89, red >=90. +if [ "$pct" -ge 90 ]; then col=$'\033[31m' # red +elif [ "$pct" -ge 70 ]; then col=$'\033[33m' # yellow +else col=$'\033[32m' # green +fi +dim=$'\033[2m'; rst=$'\033[0m' + +# 10-cell bar; clamp fill to [0,10] so an over-100 reading can't overflow. +filled=$((pct / 10)); [ "$filled" -gt 10 ] && filled=10; [ "$filled" -lt 0 ] && filled=0 +bar="" +for ((i = 0; i < 10; i++)); do + if [ "$i" -lt "$filled" ]; then bar+="█"; else bar+="░"; fi +done + +printf '%s%s%s · %s · %s%s %d%%%s %sctx/%s%s\n' \ + "$dim" "$dir" "$rst" \ + "$model" \ + "$col" "$bar" "$pct" "$rst" \ + "$dim" "$wlabel" "$rst"