understatus
A calm, unobtrusive statusline addon for Claude Code — live CPU, memory, battery, disk, network, and AI session info in a permanent bottom bar that stays out of your way.

Why
Most statusline widgets are noisy. understatus is designed around one principle: permanent visibility without distraction.
- Calm glyph theme — load stages render as
○ ▁ ▄ ▆ ◆. Color touches the glyph only; numeric values stay uncolored. Labels and separators are dimmed. At ≥90% CPU the critical glyph◆breathes slowly in restrained terracotta (#b87848↔#7a5030) — not red, not flashing. - Reactive CPU — every render takes two snapshots ~25 ms apart and computes true instantaneous CPU% across all cores. No stale load-average guessing.
- Non-destructive chaining —
understatus installdetects your existingstatusLine.command, preserves it in config, and chains it.uninstallrestores byte-for-byte. Your current setup is never lost.
macOS only. Apple Silicon (arm64) + Intel (x86_64). Linux is not supported.
No Xcode required for the prebuilt paths. Homebrew and npm install prebuilt binaries (deployment target macOS 11.0), so they need no Xcode Command Line Tools and no Rust toolchain, and run on macOS 11 (Big Sur) and newer. Only
cargo install/ building from source compiles locally and therefore needs the Rust toolchain + Command Line Tools.
Install
Homebrew (recommended)
Installs a prebuilt binary — no Xcode Command Line Tools, no Rust, no compilation.
The formula lives in the tap
ictechgy/understatus(repo:ictechgy/homebrew-understatus).
npm
The npm package downloads the prebuilt macOS binary for your architecture (arm64 / x64) and verifies its SHA-256. No Xcode / Rust required. macOS only.
Cargo
Requires Rust 1.75+ and the Xcode Command Line Tools (this path compiles from source). If you want to avoid installing the toolchain, use Homebrew or npm above.
From source
# binary at ./target/release/understatus
Setup
Install into Claude Code
This patches ~/.claude/settings.json non-destructively:
- Reads your current
statusLine.command(if any). - Saves it as
chain_commandin~/.config/understatus/config.toml. - Replaces
statusLine.commandwith the understatus binary path. - Injects
"refreshInterval": Nintosettings.jsonand mirrors the same value toconfig.toml [refresh].interval_seconds.
Flags:
| Flag | Description |
|---|---|
--interval N |
Set refresh interval in seconds (integer ≥ 1). |
--theme NAME |
Set the theme (see Themes for valid names). |
--yes / -y |
Skip interactive prompts even in a TTY; use flags / inherited / default values. |
Interactive prompts (TTY only, without --yes): If a flag is omitted and stdin is a TTY, install asks for each missing value (up to 3 retries per item). Empty input accepts the inherited or default value.
Interval inheritance on reinstall: When --interval is not supplied, the existing [refresh].interval_seconds from config.toml is reused. Priority: --interval flag > existing config value > default (5 s). This prevents the interval from silently resetting to 5 when you reinstall to change only the theme.
Uninstall
Restores statusLine.command and refreshInterval to their exact pre-install state. If refreshInterval was absent before install, the key is deleted.
⚠️ Global side-effect: refreshInterval
understatus install writes "refreshInterval": N into settings.json (default N = 5). This value applies to the entire statusLine subsystem — not just understatus:
- understatus itself spawns as a new process every N seconds.
- Any chained command (e.g.
lterm-omc-hud.mjs) is also re-executed every N seconds.
To decouple heavy chain children, understatus caches their stdout via chain_cache_ttl_seconds (default 10 s). The chained child re-spawns at most once per TTL — not every N seconds.
To save battery on laptops, raise the interval:
# ~/.config/understatus/config.toml
[]
= 10 # default: 5
Note: increasing interval_seconds proportionally slows the terracotta breath animation. Adjust pulse_period_seconds accordingly to keep it smooth (pulse_period / interval >= 6). If pulse_period / interval < 6 at install time, understatus prints a warning to stderr.
understatus uninstall reverts refreshInterval precisely — no residue left behind.
Themes
understatus ships nine built-in themes. Set the active theme in one line:
# ~/.config/understatus/config.toml
= "vivid"
Or switch after install without reinstalling:
Theme table
| Name | Glyph ramp (idle → crit) | Description |
|---|---|---|
calm |
○ ▁ ▄ ▆ ◆ |
Cool blue-grey ladder + terracotta breath at critical. Default. |
mono |
○ ▁ ▄ ▆ ◆ |
Greyscale only — zero hue across all bands. |
vivid |
░ ▒ ▓ █ █ |
Traffic-light colors (green → amber → red) with block-fill glyphs. |
ember |
· ∙ • ● ◉ |
Warm amber/terracotta monochromatic ladder with dot glyphs. |
emoji |
😌 🙂 😅 🥵 🔥 |
Emoji face ramp. Each glyph occupies 2 terminal columns. |
neon |
░ ▒ ▓ █ █ |
Neon cyberpunk — electric cyan → magenta with hue-cycling pulse. |
aurora |
▁ ▃ ▅ ▆ █ |
Aurora borealis — teal → purple gradient with flash pulse. |
sunset |
· ∙ • ● ◉ |
Sunset — gold → coral → purple with flash pulse. |
spectrum |
▁ ▂ ▄ ▆ █ |
Per-band rainbow — green → magenta with hue-cycling pulse. |
COLOR-ONCE principle: Color is applied to the glyph character only. Numeric values (CPU%, memory, cost, etc.) are always uncolored regardless of theme.
Critical breath (≥90% CPU): The critical-band glyph breathes between pulse_palette[0] (bright) and pulse_palette[1] (dim) over pulse_period_seconds. Hue never shifts — only brightness. The animation requires at least 6 render frames per period (pulse_period / interval_seconds >= 6); if this is not satisfied, install prints a warning.
Per-key override: theme fills only the keys not explicitly set in your config. Any of the eight theme-owned keys (load_glyphs, band_tints, pulse_palette, label_color, separator, separator_color, hud_seam, pulse_style) written in your config take precedence over the preset.
Pulse styles
When the pulse is active (CPU ≥ pulse_on_threshold), pulse_style controls how the critical-band glyph animates. There are four styles:
| Style | Behavior |
|---|---|
calm |
Fixed glyph shape. Terracotta luminance breathing — hue never shifts, only brightness cycles between pulse_palette[0] (bright) and pulse_palette[1] (dim). Most subtle. Default for original themes. |
flash |
Fixed glyph shape. Uses the theme's own pulse_palette endpoints (the same high/low colors as that theme's calm breath), but applies a sharper sine curve — the midpoint brightness contrast is more pronounced (punchier breathing). |
hue |
Fixed glyph shape. The glyph tint cycles through the full hue wheel (rainbow shimmer) over one pulse_period_seconds. Saturation and value stay constant; only hue rotates 360°. |
swap |
Hue cycling (same as hue) plus glyph shape alternation: the critical glyph swaps between its filled and hollow forms (e.g. ◆ ↔ ◇) on the second half of each period. Most eye-catching. |
Flashy theme defaults: The four bold themes ship with a fitting default style — neon and spectrum use hue; aurora and sunset use flash. The original five themes (calm, mono, vivid, ember, emoji) all use calm. All are overridable per the per-key override rule above.
When pulse is OFF: Regardless of pulse_style, when CPU is below pulse_off_threshold the pulse is inactive and the glyph renders with its static band_tints color — no animation of any kind.
Trigger thresholds
# ~/.config/understatus/config.toml
[]
= 90 # default: CPU% at which pulse activates
= 80 # default: CPU% below which pulse deactivates (hysteresis)
The pulse turns ON when CPU reaches pulse_on_threshold (90) and stays ON until CPU drops below pulse_off_threshold (80) — so it keeps animating briefly during cool-down. This avoids flicker at the boundary.
Lower the thresholds to pulse at a lower CPU load:
[]
= 75
= 65
Changing the pulse style
# ~/.config/understatus/config.toml
[]
= "hue" # calm | flash | hue | swap
Or switch from the command line (takes effect on the next render):
Configuration
File: ~/.config/understatus/config.toml
All keys are optional; omitting a key uses its default.
| Key | Default | Description |
|---|---|---|
theme |
"calm" |
Active theme preset. Valid values: calm, mono, vivid, ember, emoji, neon, aurora, sunset, spectrum. The theme fills all eight visual keys not explicitly set in config; individual keys can still override it. |
[cpu] sample_window_ms |
25 |
Interval (ms) between the two CPU snapshots. Larger = less noise, more latency; the renderer caps this at 100 ms to keep the statusline hot path bounded. |
[cpu] load_glyphs |
["○","▁","▄","▆","◆"] |
Glyphs for idle→critical load stages. Color is applied to the glyph only. Filled by the active theme; override by writing this key explicitly. |
[pulse] pulse_on_threshold |
90 |
CPU% at which the critical glyph starts breathing. |
[pulse] pulse_off_threshold |
80 |
CPU% below which the breath turns off (hysteresis). |
[pulse] pulse_period_seconds |
30 |
One full breath cycle in seconds. Keep period / interval_seconds >= 6 for smooth animation. |
[pulse] pulse_style |
"calm" |
Pulse animation style when active. "calm" = theme's pulse_palette luminance breath, hue-invariant (most subtle). "flash" = same pulse_palette endpoints, sharper midpoint contrast. "hue" = hue-wheel cycling (rainbow shimmer). "swap" = hue cycling + glyph shape alternation (most eye-catching). See Pulse styles. |
[chain] chain_command |
"" |
Populated by install. The command that runs alongside understatus. |
[chain] order |
"self_first" |
"self_first" or "chain_first" — which output appears on the left. |
[chain] chain_cache_ttl_seconds |
10 |
How long (s) to cache the chained command's stdout before re-spawning it. |
[chain] chain_timeout_ms |
500 |
Max ms to wait for the chained command. On timeout, cached or empty output is used. |
[display] max_width |
80 |
Maximum character width. Lower-priority segments are omitted when exceeded. |
[display] show_model |
true |
Show Claude model name. |
[display] show_cost |
true |
Show cumulative session cost. |
[display] show_context |
true |
Show context usage %. Omitted automatically when null. |
[display] show_git |
true |
Show git branch (Claude workspace path, or lterm/codex cwd walk-up when available). |
[display] show_session |
true |
Show lterm/codex session/pane label such as codex/%3 when supplied by the input payload. |
[display] show_battery |
true |
Show battery (IOKit, 30 s TTL cache). Silently omitted on desktops. |
[display] show_disk |
true |
Show disk usage via statfs("/"). |
[display] show_network |
true |
Show network throughput (getifaddrs counter delta). First render has no delta — omitted silently. |
[display] show_rate_limit |
true |
Show Claude Code 5h/weekly rate-limit usage when rate_limits is present in stdin. Codex 5h/weekly usage is shown from Codex session enrichment when available. |
[display] rate_limit_warn_threshold |
not set | Optional percent threshold. When set, Claude rate-limit values at or above the threshold are tinted with the critical color. |
[color] mode |
"auto" |
"auto" | "truecolor" | "256" | "none". Respects NO_COLOR. |
[color] band_tints |
see below | Five hex colors for idle→critical glyph tint. Filled by the active theme; override by writing this key explicitly. |
[color] pulse_palette |
["#b87848","#7a5030"] |
High/low brightness endpoints for the breath animation. Filled by the active theme; override by writing this key explicitly. |
[color] label_color |
"#6b7280" |
Dimmed color for labels, units, arrows, and git marker. |
[color] separator |
" · " |
Segment separator string. |
[color] separator_color |
"#3b4048" |
Color for separator and HUD seam. |
[color] hud_seam |
"│" |
Character placed between understatus output and the chained command output. |
[refresh] interval_seconds |
5 |
Value written to settings.json as refreshInterval. Set via install --interval or the interactive prompt. On reinstall the existing value is inherited unless --interval overrides it. ⚠️ Global side-effect — see above. |
[codex] enabled |
true |
Enable best-effort Codex session enrichment for --source lterm / --source codex payloads whose agent looks like Codex. |
[codex] freshness_minutes |
240 |
Only consider Codex rollout files modified within this many minutes. |
[codex] scan_days |
3 |
Scan only this many recent Codex session date directories to keep ~/.codex reads bounded. |
[context] hold_ttl_seconds |
180 |
How long (s) to hold the previous native context % when Claude Code omits used_percentage. Lower = faster tracking of the real value; too low and ctx can briefly blank out when both native and token counts are 0. 0 disables hold. |
[context] drop_tolerance |
12 |
Downward threshold (percentage points) for breaking the hold and switching to the token fallback. The fallback must be at least this far below the held native to count as a real drop (e.g. /compact). ⚠️ Lowering below 12 can reintroduce flicker from the native↔token denominator gap (observed 86↔98 = 12 pp). |
Default band_tints (cool blue-grey brightness ladder, warm terracotta only at critical):
= ["#5a6878", "#6d8296", "#86a0b4", "#9fbfce", "#b87848"]
How it works
Claude Code (every refreshInterval seconds)
│ stdin: one JSON line
▼
understatus binary (new process per call — no daemon, no state files, no locks)
├─ read bounded stdin (1 MiB) → parse into ClaudeInput/lterm payload
├─ load bounded config (256 KiB; invalid/oversized = defaults)
├─ optional Codex enrich (`--source lterm|codex`, bounded ~/.codex scan)
├─ double-sample CPU → cpu_percent (0–100%, average across all cores)
│ on failure → loadavg fallback: min(load1 / ncpu × 100, 100)
├─ memory (host_statistics64)
├─ battery (IOKit, 30 s TTL cache) ← machine-global; omitted on desktops
├─ disk (statfs("/"))
├─ network (getifaddrs counter delta) ← omitted on first render
├─ glyph + band tint (color on glyph only, theme-driven)
│ at ≥90% CPU → brightness breath on the critical-band glyph
├─ chain_command child (TTL cache + 500 ms timeout)
└─ compose → stdout (single newline)
CPU measurement: Two macOS host_processor_info snapshots are taken ~25 ms apart within the same process invocation. The delta gives true instantaneous utilization — not a smoothed load average. If the syscall fails (rare), loadavg serves as a silent fallback. User-configured sample windows are capped at 100 ms so a statusline render cannot be delayed indefinitely by config.
Claude rate limits: When Claude Code includes rate_limits in its statusLine stdin payload, understatus renders the 5h/weekly usage percent and reset countdown without making network calls. The segment is gated by [display].show_rate_limit and can be optionally tinted via [display].rate_limit_warn_threshold.
Glyph + tint design (COLOR-ONCE): band_tints[0..3] are cool blue-grey values of increasing brightness (idle to high load). band_tints[4] is the lone warm color — terracotta — reserved for the critical stage. Only the glyph character receives color; all numeric values and labels stay uncolored. The active theme fills these colors; individual config keys override the preset.
Pulse animation: When CPU stays at or above pulse_on_threshold (default 90%), the critical-band glyph animates according to pulse_style. The "calm" style cycles between pulse_palette[0] (brighter) and pulse_palette[1] (dimmer) with hue-invariant brightness breathing. "flash" uses the same theme pulse_palette endpoints but a sharper curve (punchier breathing). "hue" rotates through the full hue wheel. "swap" adds glyph shape alternation on top of hue rotation. The pulse stays ON until CPU drops below pulse_off_threshold (default 80%) — keeping the animation going briefly during cool-down to avoid boundary flicker. Smooth animation requires pulse_period / interval_seconds >= 6 (6 or more render frames per cycle). See Pulse styles.
Session cache isolation: Per-render caches (chain command output, pulse state, network counter delta) are keyed by session_id. Multiple terminal windows running understatus simultaneously do not share or corrupt each other's cached values. Battery state is machine-global and is shared across sessions.
CLI Support Matrix
| CLI | Status | Notes |
|---|---|---|
| Claude Code | ✅ Full support | Custom statusLine.command, stdin JSON, refreshInterval, chain preservation, and Claude rate_limits rendering are supported. |
| lterm / cmux | ✅ Native pill surface | understatus render --source lterm --surface-format cmux-status emits one-line JSON for cmux status pills. The plain lterm path can also use --source lterm --oneline. |
| Codex CLI (standalone) | ⚠️ Manual/tmux path | Codex has a built-in [tui].status_line item list, not a Claude-style command hook. For tmux or shell status integrations, feed a small JSON payload to understatus render --source codex --oneline; understatus then enriches from ~/.codex on a best-effort basis. |
| Gemini CLI | ⏳ Forward-looking | /footer and /statusline expose built-in items only; custom command-backed statuslines are not supported by understatus today. |
Examples:
# Codex tmux-style one-line text
|
# lterm cmux native status pills (JSON, no ANSI)
|
For tmux status bars, prefer color.mode = "none" and let tmux apply its own #[fg=...] styling if raw ANSI SGR is not desired.
Platform
- macOS only. Uses
host_processor_info,host_statistics64, andIOPSCopyPowerSourcesInfo— all macOS-specific APIs. - Apple Silicon (arm64) + Intel (x86_64). Tested on macOS arm64 with a 12-core Apple Silicon chip.
- Linux: builds may succeed, but understatus is not a supported Linux target; macOS-specific metrics may degrade or be omitted.
- Rust edition 2021, MSRV 1.75+.
License
MIT — see LICENSE.
한국어 안내
macOS용 AI 코딩 CLI statusline 애드온입니다. CPU%, 메모리, 배터리, 디스크, 네트워크, AI 세션 정보(모델명·비용·컨텍스트·rate-limit)를 Claude Code 하단 표시줄에 조용하고 자연스럽게 표시합니다.
주요 특징
- 9종 테마 —
calm(기본),mono,vivid,ember,emoji,neon,aurora,sunset,spectrum. 테마는 8개 시각 키(글리프·색상 등)를 한 번에 설정하며, 개별 키를 config.toml에 명시하면 테마보다 우선합니다. - COLOR-ONCE 원칙 — 색은 글리프 문자에만 적용. 숫자 값(CPU%, 비용 등)은 항상 무색.
- ≥90% 호흡 — CPU가 90% 이상으로 유지되면 임계 밴드 글리프가 테라코타 명도로 천천히 숨쉽니다(hue 변화 없음). 부드러운 애니메이션에는
pulse_period / interval_seconds >= 6조건이 필요하며, 위반 시 설치 시점에 경고가 출력됩니다. - 반응형 CPU — 매 렌더마다 두 스냅샷(~25ms 간격)을 직접 측정합니다. loadavg가 아닙니다.
- 비파괴 설치 — 기존
statusLine.command를 체이닝으로 보존하고 정확히 복원합니다. - 세션 캐시 격리 — 체인 출력·펄스 상태·네트워크 델타 캐시는
session_id별로 분리되어 여러 터미널을 동시에 열어도 값이 섞이지 않습니다. 배터리는 머신 전역. - Claude rate-limit 표시 — Claude Code stdin에
rate_limits가 들어오면 5h/weekly 사용률과 리셋 카운트다운을 네트워크 호출 없이 표시합니다. - lterm/cmux·Codex 경로 —
--source lterm --surface-format cmux-status는 cmux pill JSON을 출력하고,--source codex --oneline은 tmux나 셸 status 연동 환경에서~/.codex를 best-effort로 보강합니다.
설치
# Homebrew
# Cargo
# npm
Claude Code에 적용
Codex를 tmux status에서 직접 쓰는 경우:
|
--interval/--theme 미지정 + TTY 환경이면 각 항목을 대화형으로 묻습니다. --yes(또는 비TTY)이면 플래그·기존값·기본값을 그대로 사용합니다. 재설치 시 --interval을 지정하지 않으면 기존 config.toml의 interval이 그대로 승계됩니다(기본 5초로 초기화되지 않습니다).
⚠️
install은settings.json에"refreshInterval": N을 전역 주입합니다. 체이닝된 기존 명령도 N초마다 재실행 대상이 됩니다. 배터리 절약이 필요하면config.toml에서interval_seconds = 10으로 올리세요.
테마 관리
| 이름 | 글리프 램프 (idle → crit) | 설명 |
|---|---|---|
calm |
○ ▁ ▄ ▆ ◆ |
차가운 blue-grey + 테라코타 호흡 (기본) |
mono |
○ ▁ ▄ ▆ ◆ |
무채색, 제로 색상 |
vivid |
░ ▒ ▓ █ █ |
신호등 색 + 블록 글리프 |
ember |
· ∙ • ● ◉ |
따뜻한 앰버/테라코타 단색 + 도트 글리프 |
emoji |
😌 🙂 😅 🥵 🔥 |
이모지 표정 램프 (각 글리프 2칸 폭) |
neon |
░ ▒ ▓ █ █ |
네온 사이버펑크 (일렉트릭 시안→마젠타, hue 순환 펄스) |
aurora |
▁ ▃ ▅ ▆ █ |
오로라 청록→보라 그라데이션 (flash 펄스) |
sunset |
· ∙ • ● ◉ |
노을 골드→코랄→퍼플 (flash 펄스) |
spectrum |
▁ ▂ ▄ ▆ █ |
밴드별 무지개 (초록→마젠타, hue 순환 펄스) |
펄스 스타일
CPU가 pulse_on_threshold(기본 90%) 이상이면 임계 밴드 글리프가 pulse_style에 따라 애니메이션됩니다.
| 스타일 | 동작 |
|---|---|
calm |
글리프 고정. 테라코타 명도 호흡 (hue 변화 없음). 가장 차분함. 기존 테마 기본. |
flash |
글리프 고정. 해당 테마의 pulse_palette 끝점(calm 호흡과 동일한 high/low 색)을 사용하되 더 가파른 사인 곡선 적용 — 중간 위상 대비가 더 강렬함. |
hue |
글리프 고정. 글리프 색이 한 주기(pulse_period_seconds) 동안 색상환 전체를 순환 (무지개 shimmer). |
swap |
hue 순환 + 글리프 모양 교대: 주기 후반부에 ◆↔◇ 등 채움/빈 형태를 번갈아 표시. 가장 눈에 띔. |
화려한 테마 기본: neon·spectrum = hue, aurora·sunset = flash. 기존 5종은 모두 calm. 개별 키로 재정의 가능.
펄스가 OFF(CPU < pulse_off_threshold)이면 스타일과 무관하게 정적 밴드 틴트로 표시됩니다(애니메이션 없음).
# ~/.config/understatus/config.toml
[]
= 90 # 펄스 발동 CPU% (기본)
= 80 # 펄스 해제 CPU% — 히스테리시스 (기본)
= "hue" # calm | flash | hue | swap
펄스는 CPU가 pulse_on_threshold(90)에 도달하면 켜지고, pulse_off_threshold(80) 미만으로 떨어질 때까지 유지됩니다 — 냉각 중에도 잠시 애니메이션이 지속되어 경계에서 깜빡임을 방지합니다.
설정 파일: ~/.config/understatus/config.toml (없으면 모두 기본값)
= "vivid" # 한 줄로 테마 지정; 개별 키 override 가능
macOS 전용 · Apple Silicon(arm64) + Intel(x86_64) · Rust 1.75+ · MIT 라이선스