A context-window meter for Claude Code. Shows how much of the model's context window the current session has consumed — tokens used, capacity, and percentage — in a status line under the input box.
Opus 5 my-project main [=======...] 134k/200k (67%) $0.42
Colour-coded: green under 60%, yellow 60–85%, red above 85%.
Two implementations are included. statusline.sh is the one you want — it's the default,
it's ~5× faster, and it has fewer ways to fail. statusline.ps1 exists for Windows machines
with no git bash (see Which script).
Claude Code has a /context command that gives a detailed one-off breakdown. This is the
always-on version: a single line that tells you at a glance whether you're about to hit a
compaction, without typing anything.
It reads real numbers reported by Claude Code, not an estimate. Since v2.1.x the status
line receives a context_window object on stdin:
{
"context_window": {
"total_input_tokens": 130000,
"total_output_tokens": 4000,
"context_window_size": 200000,
"current_usage": { "input_tokens": 2, "cache_read_input_tokens": 42978, "...": "..." },
"used_percentage": 67.0,
"remaining_percentage": 33.0
}
}Capacity tracks the actual model, so it reads 1M on a 1M-context model and 200k on a
standard one, with no configuration.
- Claude Code v2.1.x or newer (developed against 2.1.220; the
context_windowpayload doesn't exist in older versions — see Compatibility) statusline.sh: POSIXsh+awk. On Windows these come with git bash, which you almost certainly already have if you use Claude Code there. macOS/Linux: built in.statusline.ps1: PowerShell 7+ (pwsh).
# Windows
git clone https://github.com/amarashi/claude-code-statusline.git
cd claude-code-statusline
.\install.ps1# macOS / Linux
git clone https://github.com/amarashi/claude-code-statusline.git
cd claude-code-statusline
./install.shThe installer copies the script to ~/.claude/, backs up your settings.json, and merges
in the statusLine key without touching your other settings.
-
Copy
statusline.shto~/.claude/statusline.sh. -
Add this to
~/.claude/settings.json(create the file with{}if it doesn't exist):{ "statusLine": { "type": "command", "command": "sh ~/.claude/statusline.sh", "padding": 0 } }On Windows, use an absolute path with forward slashes:
"command": "sh C:/Users/YOURNAME/.claude/statusline.sh"
-
Restart Claude Code.
settings.jsonis read only at startup — there is no reload.
Do not run
/statuslineto "apply" this. That slash command launches a setup agent that rewrites yourstatusLineconfig, overwriting what you just installed.
On Windows, Claude Code picks the shell it runs your status line command in like this (decompiled from 2.1.220):
function Dp() { if (platform() !== "windows") return true; return findGitBash() !== null }
function YRt() { return Dp() ? "bash" : "powershell" }If git bash is installed, your command runs under bash — even on Windows, and even if you
live in PowerShell. So the sh script is the native fit; a PowerShell command there means
spawning a nested pwsh inside bash, which adds two failure modes for no benefit:
- stdin has to survive two process hops
- if
pwshcame from the Microsoft Store, the only stable-path entry is a 0-byte App Execution Alias (%LOCALAPPDATA%\Microsoft\WindowsApps\pwsh.exe), which frequently fails to spawn from non-shell process launchers
Use statusline.ps1 only if sh genuinely isn't available on the machine.
statusline.sh |
statusline.ps1 |
|
|---|---|---|
| Runtime | POSIX sh + awk | PowerShell 7+ |
| Measured render | ~54 ms | ~284 ms |
| Windows shell hops | 0 | 1 (nested pwsh) |
Timings measured on one Windows 11 machine; treat them as a ratio, not a benchmark.
Both scripts are short and have no dependencies — edit them directly.
- Colour thresholds — in
statusline.sh, the linecol = (pct >= 85) ? "91" : (pct >= 60) ? "93" : "92"(ANSI codes: 91 red, 93 yellow, 92 green). - Bar width — change
slots = 10. - Drop the cost / branch / directory — delete the relevant chunk of the
line = sprintf(...)assembly.
Other fields available on stdin include model.display_name, workspace.current_dir,
version, output_style.name, exceeds_200k_tokens, cost.*, and rate_limits.*
(five_hour / seven_day utilisation).
The full schema Claude Code accepts — there is no shell or args option on statusLine
(unlike hooks), so the command is always a shell command line:
| Key | Type | Notes |
|---|---|---|
type |
"command" |
required, only valid value |
command |
string | required |
padding |
number | 0 = flush left |
refreshInterval |
number (≥1) | re-run every N seconds on top of event-driven updates |
hideVimModeIndicator |
boolean | hide the built-in -- INSERT -- line |
The single most common cause: you didn't restart. settings.json is read at startup only.
This is expected, and it's the reason this section exists. Claude Code swallows status line failures silently:
let a = s.stderr.trim();
if (a) w(`StatusLine [...] stderr: ${a}`); // needs non-empty stderr
if (s.status === 0) { ... }
else if (r) w(`StatusLine [...] completed with status ${s.status}`) // needs verboseA spawn failure produces no stderr and doesn't log unless verbose is on. So "no error" tells you nothing. Get ground truth instead:
CLAUDE_STATUSLINE_DEBUG=1 claude # then, after it starts:
cat ~/.claude/statusline.last- File exists → the command ran. Your problem is rendering or trust, not the script.
- File missing → Claude Code never invoked it. See the gates below.
Any failure reproduces here, with visible errors:
printf '%s' '{"model":{"display_name":"Opus 5"},"workspace":{"current_dir":"/tmp/x"},"cost":{"total_cost_usd":1.5},"context_window":{"total_input_tokens":130000,"total_output_tokens":4000,"context_window_size":200000}}' \
| sh ~/.claude/statusline.shExpect exit 0, one line of output, and empty stderr.
If you build this test command yourself, quote the JSON with
printf '%s'and single quotes. Usingecho "$VAR"with backslashes in the JSON mangles the escapes and produces invalid JSON — which looks exactly like a broken script. (Yes, this cost us a detour.)
Reading the execution path, these all cause a silent skip:
| Gate | Meaning | Fix |
|---|---|---|
| Workspace trust not accepted | logs Skipping StatusLine command execution - workspace trust not accepted |
accept the trust prompt for the directory |
disableAllHooks: true |
disables hooks and status line together | remove it from settings |
Managed policySettings |
an admin policy file overrides your statusLine entirely |
check C:\ProgramData\ClaudeCode\managed-settings.json or the managed settings path on your OS |
| Empty stdout | a command that succeeds but prints nothing renders nothing | check your edits |
On Windows with a non-PowerShell shell, Claude Code rewrites \ → / in the command string.
Write the path with forward slashes to begin with so the rewrite is a no-op, and prefer an
absolute path.
Developed and verified against Claude Code 2.1.220 on Windows 11. The behaviours documented
here — the context_window payload, the statusLine schema, shell selection, silent failure —
were confirmed by inspecting that build. They are internal details and may change between
versions.
On a version without context_window, the scripts degrade to [..........] 0/0 (0%) rather
than crashing. If you see that, your Claude Code is too old — upgrade.
macOS and Linux should work (the sh path is the native one there) but are untested — reports welcome.
MIT — see LICENSE.