Files
dots/common/.config/claude/StatusBar.md
T

84 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Status line (`statusline.py`)
The custom Claude Code status line, ordered **work info (left) → system info (right)**,
segments joined by a light-gray `│`. It's **responsive**: always a single line — if the
rendered width would exceed the terminal (`$COLUMNS`, which Claude Code re-exports on
resize), trailing (lowest-priority) segments are dropped from the right until it fits.
Wide — everything:
```
📁 ~/.dots │ 🌿 .dots main* │ 💬 #42 3 · codex 2 │ 🤖 Fable 5 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m │ 💰 $0.40 $12.00/h │ 🔢 ↑63M ↓595k │ ⚡ 63 t/s │ ♻️ 91% 1h │ 💾 81%
```
Narrow — trailing segments trimmed:
```
📁 ~/.dots │ 🌿 .dots main* │ 💬 #42 3 · codex 2 │ 🤖 Fable 5 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m
```
Every segment is defensive: if its data is missing or a command fails, the segment is
simply omitted (the bar never crashes the UI). Secondary detail (the parts in light gray)
is supplementary to the main colored value.
## Live segments
| | Segment | Shows | Notes |
|---|---|---|---|
| | **Vim mode** | `[I]`/`[N]`/`[V]` editor mode | only with `editorMode: vim`; pair with `hideVimModeIndicator: true` so the built-in one doesn't duplicate it |
| 📁 | **Directory** | Current working dir, with `~` for home | cyan |
| 🌿 | **Git** | `repo branch` + `*` if dirty + `↑N`/`↓N` ahead/behind upstream | repo = magenta; branch = green when clean, yellow + red `*` when dirty; `↑` cyan, `↓` yellow. Omitted outside a repo |
| 💬 | **PR review threads** | `#N` of the current branch's open PR + its **unresolved review threads** (any author, outdated ones included; `+` past 100), Codex's share broken out as `· codex N`; green `✓` when none are left | A thread counts for whoever opened it (its first comment's author; Codex = login matching `codex`, as in `/pr-loop`). PR-conversation comments are not counted — they have no resolved state, so a summary or an LGTM would never let it reach zero. A GitHub round trip is ~0.7 s, so a render only reads the per-branch cache `~/.claude/cache/statusline.pr.<hash>.json` and, once it is older than 90 s, spawns a detached `statusline.py --pr-refresh` that updates it (one at a time, via a `.lock` claim) — new counts show on the next render. A failed fetch (no GitHub remote — e.g. this repo on gitea —, gh not authed, offline) backs off 10 min and keeps the last counts with a gray `?`. Omitted with no `gh`, no open PR or a detached HEAD |
| 🤖 | **Model** | Active model display name + `· effort` level (`low`/`medium`/`high`/`xhigh`) | name = blue, effort = light gray; effort omitted for models without the param |
| 📝 | **Context** | `% of context window used` + `(Nk)` tokens | usage comes from the payload's `context_window` (`used_percentage` + `current_usage`, Claude Code 2.1.x); the transcript's last `message.usage` is only a fallback. Window from `context_window.context_window_size`, then `(1M …)` in the display name, else 200k — **not** from `model.id`, which arrives with any `[1m]` suffix already stripped. Adds a red **⚠compact** at ≥80% |
| 📊 | **5h usage** | `% of the 5-hour rolling limit used` + time until it resets | from `rate_limits.five_hour`; Pro/Max only, and absent until the first API response of a session |
| 💰 | **Cost** | `$` session cost so far + `$/h` burn rate | burn rate shown once the session exceeds ~30s |
| 🔢 | **Session tokens** | `↑` input + `↓` output tokens used so far this session (cumulative, `k`/`M`) | summed over the transcript's assistant `message.usage` blocks, de-duplicated by message id (one line per content block shares the same usage). Input = `input + cache_creation + cache_read` — everything billed as input, so on a 1M session it dwarfs output (every call re-reads the cached prefix). Main transcript only: subagent usage lives in separate sidechain files. Incremental: a per-session state file `~/.claude/cache/statusline.tokens.<session>.json` remembers the byte offset already summed, so each refresh parses only the new tail (warm ≈ 0.2 ms), at most 8 MiB per refresh (≈ 150 ms) — Claude Code kills a status script when the next refresh fires, so a cold scan of a huge resumed transcript is spread over a few refreshes (totals climb, then settle) instead of restarting from zero each time. Stale state files are pruned after 7 days once 50+ pile up. Omitted when there is no transcript; `↑` cyan, `↓` green, no thresholds |
| ⚡ | **Decode speed** | `N t/s` — tokens per second the model is generating | duration-weighted mean over the last 8 thinking blocks of the main conversation: `thinking_tokens / thinkingDurationMs` from the same transcript scan (blocks under 1 s or 50 tokens skipped). The only clean per-call timing Claude Code records: the payload's `cost.total_api_duration_ms` also absorbs subagent and advisor calls (it grew 28 s in a 12 s wall interval with no main-conversation completion) and its `output_tokens` is sometimes a mid-stream partial, so neither can give a rate. Includes first-token latency, so short blocks read a little low and long ones converge on the real decode speed (Fable 5.1 measured 60–85 t/s). Needs thinking enabled — omitted until a thinking block has completed, never shown with thinking off |
| ♻️ | **Prompt cache** | hit `%` (higher is better) + cache TTL (`5m` / `1h`), yellow `·cold` once it has expired | from the payload's `prompt_cache` (2.1.260+: `hit_ratio` 0–1, `ttl`, `warm`); computed from usage on older payloads. A `1h` TTL is what keeps a 10-min `/pr-loop` cadence warm |
| 💾 | **Disk** | `% used` of the filesystem at the cwd | from `statvfs` |
## Colour legend
Most numbers are **green / yellow / red** by threshold — green = healthy, red = needs
attention. Light gray = secondary detail. Thresholds (`green < … < yellow < … ≤ red`):
| Segment | green | yellow | red |
|---|---|---|---|
| Context | `< 50%` | `50–80%` | `≥ 80%` (⚠compact) |
| PR review threads | `✓` none unresolved | thread count | `codex N` |
| 5h usage | `< 50%` | `50–80%` | `≥ 80%` |
| Decode speed (inverted) | `≥ 40 t/s` | `20–40 t/s` | `< 20 t/s` |
| Prompt cache (inverted) | `≥ 80%` | `50–80%` | `< 50%` |
| Disk | `< 75%` | `75–90%` | `≥ 90%` |
> Light gray is `\033[37m`; dimming (`\033[2m`) is disabled because it blended the gray
> detail text into dark terminal backgrounds. To go brighter, set `"gray"` to `\033[97m`
> (bright white) in the `C` table near the top of `statusline.py`.
## Optional segments (defined but not shown)
These functions exist in `statusline.py` but aren't in the output. Enable one by adding it
to the `work` or `system` list in `main()`:
RAM/CPU/temp were **retired 2026-07-26**: the status line only refreshes on message
events, so system metrics sat visibly stale between turns — the tmux status bar
(`tmux/scripts/`) owns live system metrics now.
| | Function | Shows |
|---|---|---|
| 🧠 | `ram_segment` | `% used` + `used/totalG` from `/proc/meminfo` (green `<70%` / yellow / red `≥85%`) |
| 🖥️ | `cpu_segment` | `% busy` (diffed `/proc/stat` snapshot) + loadavg + cores (green `<60%` / red `≥85%`) |
| 🌡️ | `temp_segment` | hottest CPU thermal zone °C (green `<60` / red `≥80`) |
| ✏️ | `velocity_segment` | `+added/-removed` lines this session + lines/min |
| ⚙️ | `api_segment` | share of wall-clock time spent in API/inference |
| | `version_segment` | Claude Code version (`vX.Y.Z`) |
| 🎨 | `style_segment` | active output-style name |
## Where it lives
`statusline.py` is tracked in this repo and symlinked to `~/.claude/statusline.py`;
`settings.json` runs it via `statusLine` (`/usr/bin/env python3 ~/.claude/statusline.py`) on every
message event and, through `refreshInterval: 60`, once a minute while the session idles — so the PR
count moves while you wait on a review. Edits take effect on the next status refresh.