Skip to content

tmck-code/yet-another-statusline

Repository files navigation

YAS! (Yet Another Statusline)

🌈 Check out the official landing page here: YAS! Yet Another Statusline

image

Most common form is displaying the first few rows, which include the loaded plugins & skills. Extra sections appear below them as needed

Install/Update

Requires Python 3.10+, and a Nerd Font to render the icons.

curl -fsSL https://raw.githubusercontent.com/tmck-code/yet-another-statusline/main/ops/install.sh | bash

Or install manually:

claude plugin marketplace add tmck-code/yet-another-statusline
claude plugin install yas@yet-another-statusline
claude -p "/yas:init"

If you need to install non-interactively (e.g. in CI/docker)

curl -fsSL https://raw.githubusercontent.com/tmck-code/yet-another-statusline/main/ops/install.sh | YAS_NO_TTY=1 bash
# install with specific python version e.g. 3.15
curl -fsSL https://raw.githubusercontent.com/tmck-code/yet-another-statusline/main/ops/install.sh | YAS_NO_TTY=1 YAS_PYTHON=3.15 bash

Reconfigure later — /yas:config

Run /yas:config any time to re-run the wizard against the already-installed plugin — switch theme/glyph mode, toggle labels, change the soft limit, or move to Python 3.15. It re-wires settings.json without re-registering the marketplace or reinstalling the plugin.

Demo

A dummy session to demonstrate the layout:

yas-0 2 5

Widths

The statusline also renders differently according to available width

mode width screenshot
"medium" <=80 pixels image
"narrow" <=55 pixels image

Configuration

Every configurable knob resolves through one fixed precedence chain (highest wins):

CLI flag  →  canonical YAS_* env var  →  legacy-alias env var  →  yas.toml  →  built-in default

The first source in that chain that is present and valid wins; an absent or invalid source falls through to the next (an empty-string env var counts as absent). Canonical YAS_* env vars always win over their deprecated legacy aliases when both are set — the aliases keep working but are deprecated.

Knobs

Knob Env var yas.toml key Default Legacy alias
max_width YAS_MAX_WIDTH [layout].max_width 140
full_width YAS_FULL_WIDTH [layout].full_width false
soft_limit YAS_SOFT_LIMIT [tokens].soft_limit 150000
token_window YAS_TOKEN_WINDOW [tokens].token_window 60 STATUSLINE_TOKEN_WINDOW
theme YAS_THEME (also --theme CLI) [appearance].theme claude-dark CLAUDE_STATUSLINE_THEME
bg_shift YAS_BG_SHIFT (also --bg-shift CLI) [appearance].bg_shift warm
glyph_mode YAS_GLYPH_MODE (also --glyph-mode CLI) [appearance.glyphs].mode nerdfont
single_width YAS_GLYPH_SINGLE_WIDTH (also --glyph-single-width CLI) [appearance.glyphs].single_width false
context_state YAS_CONTEXT_STATE [context].state false
context_labels YAS_CONTEXT_LABELS [context].labels Smart,Coasting,Foggy,Cooked,Dumb
context_thresholds YAS_CONTEXT_THRESHOLDS [context].thresholds 25,50,70,90
show_render_time YAS_SHOW_RENDER_TIME [layout].show_render_time false

Valid values

  • theme — 14 built-in themes; unknown or unset falls back to claude-dark:
    • Dark: claude-dark, catppuccin-mocha, dracula, gruvbox-dark, nord, one-dark, solarized-dark, tokyo-night, palenight
    • Light: claude-light, catppuccin-latte, gruvbox-light, one-light, solarized-light
  • bg_shiftwarm or cool.
  • glyph_mode — all four modes preserve column geometry; an unknown value falls back to nerdfont:
    • nerdfont — default; full fidelity, needs a Nerd Font.
    • ascii — every non-ASCII glyph → width-1 ASCII; maximum compatibility.
    • unicode — only Nerd Font PUA icons → non-PUA Unicode; keeps box/block/arrow glyphs.
    • github — GitHub-paste-safe: folds every glyph a browser renders double-wide (the box-drawing frame, block/sparkline ramp, and EAW-ambiguous punctuation/icons) to a width-1, EAW-narrow/ASCII stand-in, so a render stays column-aligned when pasted into a GitHub markdown code block.

Behaviour notes

  • single_width — orthogonal boolean that folds double-width dynamic content (wide emoji, CJK in branch names/paths) to width-1; combinable with any glyph_mode. The statusline's own glyphs are already width-1, so column geometry is preserved.
  • full_width — when true, makes the box fill the terminal and ignore max_width.
  • show_render_time — when true, annotates the bottom-right border with the previous run's wall-clock render time (e.g. …47.2ms──╯). Off by default; each run shows the prior run's timing, so it is blank on a session's first render.
  • CLI flags--theme NAME / --bg-shift DIR also accept the --theme=NAME / --bg-shift=DIR form. Pass them in the statusLine.command of your ~/.claude/settings.json.
  • Legacy theme file~/.claude/statusline-theme (contents = a theme name) still works as the lowest-priority theme fallback, below [appearance].theme.

Context state word

context_state adds a morphing word to the context line that names how full the context window is — Smart → Coasting → Foggy → Cooked → Dumb — so you can read the state at a glance instead of doing percentage math. It is off by default; enable it with YAS_CONTEXT_STATE=1 or [context].state = true.

The word is tinted with the same threshold colour as the context bar and sits just before it; in a narrow box it sheds first (the bar and percentage stay).

  • context_labels — exactly 5 comma-separated words (Smart,Coasting,Foggy,Cooked,Dumb by default).
  • context_thresholds — exactly 4 strictly-ascending integers in 1..99 (25,50,70,90 by default), the start percentage of bands 2–5.

The percentage driving the word is YAS's soft-limit fill ratio (the same basis as the context bar), so the word and the bar always agree — the word turns Dumb exactly as the bar fills toward the compaction soft_limit.

Credit: the state-word idea and its default labels/thresholds are ported from Dumbometer by Maximo Correa Rosas (MIT). See NOTICE. Dumbometer maps the word to the full context window; YAS maps it to the soft-limit fill ratio for bar consistency.

yas.toml

yas.toml lives in CLAUDE_CONFIG_DIR (defaults to ~/.claude/). It is not auto-created — its absence simply means all-defaults — and /yas:init never writes it. See yas.example.toml for a fully-commented template; copy it to ~/.claude/yas.toml and uncomment what you want.

[layout]
max_width = 140

[tokens]
soft_limit = 150000
token_window = 60

[appearance]
theme = "claude-dark"
bg_shift = "warm"

[appearance.glyphs]
mode = "nerdfont"
single_width = false

[context]
state = false
labels = ["Smart", "Coasting", "Foggy", "Cooked", "Dumb"]
thresholds = [25, 50, 70, 90]

yas.toml requires Python 3.11+ — it is parsed with the stdlib tomllib. On Python 3.10 the file is silently skipped; environment variables work on every Python version.

Bad config never crashes the statusline. A malformed yas.toml is ignored wholesale, and a single bad / out-of-range / wrong-type value drops only that one knob back to its default. When any yas.toml value is rejected, a compact warning row — ⚠ yas.toml: N values ignored (...) — appears at the bottom of the box listing the rejected knob names. Detailed per-value reasons go to stderr only when YAS_DEBUG is set.

Per-model soft_limit overrides

Beyond the global [tokens].soft_limit, you can declare per-model overrides as an inline array under [tokens]:

[tokens]
model = [
    { match = "opus",         soft_limit = 200000 },   # the whole Opus family
    { match = "opus-4-8[1m]", soft_limit = 1000000 },  # 1M-context variant (longer match wins)
]
  • match is a case-insensitive plain substring (no glob/regex), tested against the model's id and display name.
  • When multiple entries match, the longest match wins; ties break by array order (first wins). If no entry matches, the global soft_limit is used. So to single out a variant from its family, give the variant the longer, more specific match (above, opus-4-8[1m] outranks opus for the 1M model).
  • A matching per-model override beats the global soft_limit from any source — including the YAS_SOFT_LIMIT environment variable. This is the one documented exception to the "env beats yas.toml" rule: specificity beats source precedence (there is intentionally no per-model env var). It lets you raise the compaction-risk threshold for a 1M-context model variant distinctly from the rest of its family.

Other environment variables

var default description
CLAUDE_CONFIG_DIR ~/.claude base dir for config/state files (yas.toml, theme file, width file, token-rate log, output payloads)
YAS_DEBUG (unset) when set, prints detailed per-value config-rejection reasons to stderr
COLUMNS (unset) terminal-width fallback when tmux / width-file detection fail

Terminal width

Width is detected by the first source that returns a positive value:

  1. tmux display-message -p '#{pane_width}'
  2. ~/.claude/terminal-width file
  3. COLUMNS env var
  4. shutil.get_terminal_size() / /dev/tty ioctl

Uninstalling

Remove the statusline config and uninstall the plugin in one step:

curl -fsSL https://raw.githubusercontent.com/tmck-code/yet-another-statusline/main/ops/install.sh | bash -s -- --uninstall --full

Or uninstall manually:

claude -p "/yas:uninstall"
claude plugin uninstall yas@yet-another-statusline

claude plugin uninstall only deletes the plugin cache — it leaves statusLine.command in ~/.claude/settings.json pointing at the now-missing script, so the statusline keeps trying to run. Run the uninstall script (or /yas:uninstall) first to remove that config, then uninstall the plugin. Reload Claude Code afterwards.


Commands

make test            # run pytest suite
make demo            # animated demo at current terminal width
make statusline/test # same as demo — use during development
make demo/img        # render snapshots into demo/
make mon/run         # launch multi-session monitor TUI

Contributing

Enable the git pre-commit hooks (runs ruff / mypy / pytest on staged Python before each commit):

make hooks

Installing via git clone

Edits to the checkout take effect immediately — no reinstall step.

git clone https://github.com/tmck-code/yet-another-statusline
cd yet-another-statusline

Wire statusLine.command in ~/.claude/settings.json to point at the checkout:

"statusLine": {
  "async": true,
  "command": "python3 \"/path/to/yet-another-statusline/claude/statusline_command.py\"",
  "type": "command"
}

Note: if you also have the plugin installed, claude plugin install will overwrite statusLine.command back to the plugin cache path. Either uninstall the plugin or bump the version in .claude-plugin/plugin.json before reinstalling to keep your local path.

This prompts before setting core.hooksPath. CI runs the same checks on every push, so the hook is fast local feedback rather than the gate.

About

A statusline for Claude Code inspired by terminal monitor programs

Topics

Resources

License

Stars

227 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors