This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Single-file Python statusline script for Claude Code CLI. Displays context window usage, model info, budget tracking, git status, and update notifications as a colored status bar (single or multi-line). Stdlib only (json, os, subprocess, sys, tempfile, time, datetime). Custom themes require tomllib (Python 3.11+ stdlib) or optional tomli package.
There is no build step, test suite, or linter. The script is a standalone executable.
# Demo: animated color gradient sweep
./claude-code-status-line.py --demo-scale
# Demo: static gradient views (min/mid/max per range)
./claude-code-status-line.py --demo-scale mid
# Demo: usage indicator scenarios
./claude-code-status-line.py --demo-usage
# Demo: gauge sweep (both vertical and blocks, ratio 2.0 → 0.0 → 2.0)
./claude-code-status-line.py --demo-gauge
# Demo: usage principle (10%, 90%, and varying usage in 5-hour window)
./claude-code-status-line.py --demo-principle
# Normal operation: receives JSON on stdin from Claude Code
echo '{"model":"claude-sonnet-4-20250514","cwd":"/tmp","contextWindow":{"used_percentage":42}}' | ./claude-code-status-line.pyThe script is a single pipeline: JSON stdin → parse → compute → render ANSI line → stdout.
Key sections in claude-code-status-line.py (~2,200 lines):
- Lines 34-115: Configuration —
SL_THEME/SL_USAGE_CACHE_DURATION/SL_UPDATE_CACHE_DURATION/SL_UPDATE_RETRY_DURATION/SL_UPDATE_CUSTOM_RETRY_DURATION/SL_UPDATE_VERSION_CMD/SL_UPDATE_VERSION_SOURCE/SL_THEME_FILEglobals, thenSL_SEGMENTSparsing (_parse_segments,_has_segment,_segment_opts). Width values capped at 128. - Lines ~116-165: Color conversion (
hex_to_rgb,hex_to_256) with hex length validation, and truecolor/256-color terminal detection viaCOLORTERMenv var - Lines ~170-355: Theme system —
THEMESdict (dark/light, Nord-inspired),_load_custom_theme()loads optional~/.claude/claude-code-theme.tomlviatomllib - Lines ~468:
get_git_branch()— subprocess call togit branch --show-current, strips ESC characters from output - Lines ~497:
get_git_status()— collects working directory state (staged/modified/deleted/renamed/untracked/conflicted), stash count, and ahead/behind status viagit status --porcelain=v1,git stash list, andgit rev-list - Lines ~571:
_normalize_usage_data()— converts CC 2.1.80+rate_limitsstdin JSON (unix timestamps, float percentages) to internal format (ISO 8601, utilization).fetch_usage_data()(deprecated) — OAuth API fallback for older CC versions, cached atomically to~/.claude/.usage_cache.json - Lines ~639-746: Update checker —
get_installed_version()runsclaude --version,fetch_latest_version()queries npm registry with caching to~/.claude/.update_cache.json,check_for_update()compares versions viaparse_semver() - Lines ~749-870: Usage gauge rendering — vertical (block chars ▁▂▃▄▅▆▇█) and horizontal blocks styles with forward-looking ratio logic
- Lines ~872:
format_usage_indicators()— returns dict with per-window usage strings - Lines ~1183:
_format_duration()/_format_duration_compact()— round durations for burndown; default uses rounded single-unit (3 d,8 h), compact uses compound no-space form (5d2h30m) - Lines ~1228:
_format_burndown()— three-mode burndown message withverbosityparam (default/short): Soon (<1h to depletion), Pace (>=48h, shows pace warning), Countdown (<48h, shows absolute time left) - Lines ~1125-1220: Segment renderers —
_render_model,_render_progress_bar,_render_percentage,_render_tokens,_render_directory,_render_added_dirs,_render_worktree,_render_git_branch,_render_git_status,_render_usage_5hour,_render_usage_weekly,_render_usage_burndown,_render_update,_render_new_line+SEGMENT_RENDERERSdict - Lines ~1530:
_join_parts()— joins segment parts with newline-aware flush-left behavior for multi-line layouts - Lines ~1547:
build_progress_bar()— builds ctx dict, iterates SEGMENTS calling renderers, uses_join_parts - Lines ~2086:
main()— entry point, handles demo modes and normal stdin flow
- Segment system:
SL_SEGMENTSenv var controls visibility, order, and per-segment options. Parsed into[(name, {opts}), ...]list. Each segment has a renderer function receiving(ctx, opts). Unknown names silently filtered. Specialnew_linesegment enables multi-line layouts;usage_burndownadapts to position in weekly window withverbosityoption (defaultorshort). Three modes with example output:- Soon (<1h left): default
"may run out soon but renew 1440 m away", short"out soon, renew 1440m away" - Pace (>=48h left): default
"may run out about 3 d sooner", short"out ~ 3d sooner" - Countdown (<48h left): default
"about 8 h usage left then 1 d to renew", short"~ 8h left -> 1d to renew" - Countdown omits renewal gap when early <= 1h: default
"about 8 h usage left", short"~ 8h left" - Bayesian shrinkage: the observed burn rate is blended toward the on-track rate using a hyperbolic trust curve
f = elapsed / (halftrust + elapsed), so early-window noise is dampened. Config:usage_burndown:halftrust=16(default 16h — at 16h elapsed, 50/50 blend). - Relevance filter: burndown is suppressed when the predicted "sooner" gap is too small relative to remaining time, using a power curve
days_remaining^coeff(in hours) divided by the trust factorf. The/fscaling makes the threshold inversely proportional to data confidence — early-window signals need proportionally larger gaps to display. Config:usage_burndown:coeff=1.4(default). - Config:
usage_burndown:verbosity=short:coeff=1.4:halftrust=16. Short uses compact compound durations (5d2h30m),~instead ofabout,->instead ofthen, dropsusage,out ~instead ofmay run out about;git_statusshows working directory state using symbols:+staged,!modified,xdeleted,rrenamed,?untracked,=conflicted,$stashed,>ahead,<behind,<>diverged;added_dirsshows directories added via/add-dir(fromworkspace.added_dirsJSON field), sorted alphabetically, joined by separator. Config:added_dirs:basename_only=1:separator=,(defaults: full path with~home shortening,•separator). Theme color:text_added_dirs(dark: Nord3 muted gray#4C566A, light: medium gray#7B8394);worktreeshows worktree info whenworktreefield is present in JSON input (from--worktreesessions). Displays in curly braces{name}. Config:worktree:show=name(default), supportsname,branch,path,originor comma-separated combo likeshow=name,branch.pathandoriginapply~home shortening.originmaps to JSONoriginalfield. Theme color:text_worktree(dark: Nord15 purple#B48EAD, light: muted purple#8B6B85).modelsegment supportseffortoption to show reasoning effort level inside the badge:model:effort=short(L/M/H/A) ormodel:effort=full(low/medium/high/auto). Reads from settings files (not in statusline JSON). Precedence:CLAUDE_CODE_EFFORT_LEVELenv var →.claude/settings.local.json→.claude/settings.json→~/.claude/settings.json.maxcannot be detected (session-only).
- Soon (<1h left): default
- Configuration: global settings via
SL_THEME,SL_USAGE_CACHE_DURATION,SL_UPDATE_CACHE_DURATION,SL_UPDATE_RETRY_DURATION,SL_UPDATE_CUSTOM_RETRY_DURATION,SL_UPDATE_VERSION_CMD,SL_UPDATE_VERSION_SOURCE,SL_SHOW_STATUSLINE_UPDATE,SL_THEME_FILE,SL_DUMP. All per-segment config (bar width, gauge style, hide default branch, basename-only directory) via colon-separated options inSL_SEGMENTS. - Dump mode:
SL_DUMP=1appends every stdin JSON input (with ISO timestamp) as JSONL to/tmp/claude-statusline-dump.jsonl. Normal rendering continues. Useful for inspecting what Claude Code sends. Auto-cleaned on reboot. - Update checker: fetches latest version from npm registry (
@anthropic-ai/claude-code) or custom command (SL_UPDATE_VERSION_CMD), compares withclaude --versionoutput. Custom command retries 3 times with 1s delay, then falls back to npm. Returns(version, source)tuple where source isnpm,custom, ornpm_fallback. Cached to~/.claude/.update_cache.json— success cached forUPDATE_CACHE_DURATION(1h), custom source failures retry afterUPDATE_CUSTOM_RETRY_DURATION(2min), total failures retry afterUPDATE_RETRY_DURATION(10min). Falls back to stale cache when offline. - Self-update:
--self-updateflag downloads latest version from GitHub and replaces the script atomically. Status line update notifications appear on a separate line below the main output with the update command. Controlled bySL_SHOW_STATUSLINE_UPDATE(default on). - Color handling: hex colors converted to both truecolor RGB escape sequences and 256-color fallbacks.
hex_to_rgb()validates 6-char length;hex_to_256()falls back to color 0 on invalid input. Theme colors are always hex strings; conversion happens at render time. - Custom themes: loaded via
tomllibfrom a TOML file (Python 3.11+,tomlifallback), only the defined keys override the base theme. Silently skipped if no TOML parser available. - Progress bar precision: Unicode fractional blocks (▏▎▍▌▋▊▉█) for sub-character precision with gradient color interpolation across 10 thresholds.
- Input validation: OAuth token characters allowlisted before HTTP use. Git branch names stripped of ESC chars. Segment widths capped at 128. Hex colors length-checked. Usage ratio guarded against near-zero divisor.
- Usage data source: CC 2.1.80+ sends
rate_limitsin stdin JSON (preferred). Falls back to deprecated OAuth API (fetch_usage_data()) for older CC versions. OAuth path (get_oauth_token(),USAGE_CACHE_PATH,SL_USAGE_CACHE_DURATION) will be removed in a future version. - Credential sources: macOS Keychain (
securitycommand) →~/.claude/.credentials.jsonfallback (deprecated, only used by OAuth fallback). - Cache writes: atomic via
tempfile.mkstemp()+os.replace()with temp file cleanup on failure. - No type hints per project convention — uses f-strings throughout.
- Effort level limitations: The statusline JSON from Claude Code does NOT include effort level. The
model:effortoption reads from settings files as a workaround. Known gaps:maxis session-only (never written to disk) — undetectable/effortalways writes to~/.claude/settings.json(user scope), even if a project-local override exists — creates split where current session disagrees with resolved settings/effort autoremoves the key entirely (shows as "A" in short mode)- In-memory
/effortoverrides within a session are not visible to the statusline - The only fully reliable fix would be upstream: adding effort to the statusline JSON