One line at the bottom of the Claude Code TUI: context-window usage, cwd, and model state.
[██████····] 118k/200k 59% · dotfiles/.config/claude · Opus 5 1M/high fast
- context window — a 10-cell bar plus
used/sizeand a percentage, green under 60%, yellow from 60%, red from 80%. - cwd — relative to the project root when you're inside it (shown as
<project-leaf>/rest), otherwise~-relative, otherwise absolute. - model — display name with
(1M context)collapsed to1M, then the reasoning effort, and thefast/no-thinktoggles when they're on.
Two implementations of the same output: statusline.sh (POSIX sh + jq) for
macOS and Linux/WSL, statusline.ps1 (PowerShell) for Windows. Pick the one
matching your box — they don't depend on each other.
Save the script somewhere stable and make it executable:
chmod +x /path/to/statusline.shThen register it in ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "/absolute/path/to/statusline.sh"
}
}The path must be absolute. The command string is handed to a shell whose
identity isn't guaranteed, so ~ and $HOME may not expand. On Windows the
command is pwsh -NoProfile -File C:\absolute\path\to\statusline.ps1.
If you run more than one Claude Code profile (a second CLAUDE_CONFIG_DIR,
e.g. a separate work profile), each profile's settings.json needs its own
registration.
Claude Code pipes a JSON status payload to the script on stdin. The fields these scripts read:
| field | used for |
|---|---|
context_window.context_window_size |
bar denominator; everything context-related is skipped when absent or 0 |
context_window.total_input_tokens |
bar numerator |
context_window.used_percentage |
percentage — null until the first API response of the session, so both scripts fall back to computing it from the two counts |
cwd |
current directory |
workspace.project_dir |
project root, for the relative path |
model.display_name |
model name |
effort.level |
reasoning effort |
fast_mode |
the fast toggle |
thinking.enabled |
rendered as no-think when explicitly false |
This is not a documented contract. These names were read out of the CLI
binary (2.1.218) rather than from any published schema, so they can change
without notice. Both scripts are written to degrade rather than break: a field
that's missing drops its segment, and unparseable input prints nothing and
exits 0. Still worth re-checking against a real payload after a CLI upgrade —
dump one with a temporary statusLine command of cat > /tmp/payload.json.
statusline.sh does all rendering inside jq, so there's no shell arithmetic
or word-splitting to get wrong; the shell part is a single jq -r invocation.
It needs jq on PATH.
statusline.ps1 forces [Console]::OutputEncoding to UTF-8 without a BOM.
When stdout is a pipe rather than a console, PowerShell defaults to the OEM code
page (CP437 on the box this was written on), which encodes the bar's █ as a
bare 0xDB — not valid UTF-8, so the whole line renders as tofu.
Path handling accepts either separator on both scripts, so a POSIX-shaped payload works under a Windows-installed pwsh and vice versa.
Public domain / CC0. Take it, change it, no attribution needed.