OmniscioGuidesStatus line
Claude Code status line, and why it is blank or reads the wrong number
The status line is one settings field that points at a shell command. Claude Code sends that command a JSON object about the session on stdin, and prints whatever it writes back on a row of its own at the bottom of the screen. This page covers the field itself, the fields that JSON actually contains, and the two things people come looking for: the row that never appears, and the token number that is wrong.
| You want | Do this |
|---|---|
| A row with the model and the context percentage | Put a statusLine object in your settings with type set to command, or run /statusline and describe it |
| Rate limit or quota percentages | Read rate_limits, and handle it being absent: it only arrives for a Pro or Max subscriber or behind a gateway, and only after the first API response |
| A clock, or git state that moves while you are idle | Set refreshInterval, because the row otherwise only repaints on events |
| To know why nothing appears | Run claude --debug and work the blank-line checklist in order |
| You want | Read this |
|---|---|
| To set one up from an empty settings file | How to set up a status line |
| To know what your script can read | The JSON your script receives |
| To know when the script runs again | When the status line updates |
| To find a row that never appears | Why the status line is blank |
| To find a token or percentage that is wrong | Why the number is wrong |
| To show rate limit or quota usage | Rate limits and quota |
| To know how this differs from a mod | Status line or mod? |
Checked against Claude Code 2.1.310 documentation, October 25, 2026. By the Omniscio team. Updated .
What the status line actually is
Anthropic describes it as a customizable bar at the bottom of Claude Code that runs any shell script you configure. The script receives JSON session data on stdin, and the bar displays whatever the script prints. That is the whole contract: one command in, one string out. Everything else on this page is detail about the two halves of it.
It draws in its own row above the footer badges rather than replacing them, and it does cost you something: with a custom status line configured, Claude Code stops showing most of the footer's keyboard hints, including esc to interrupt, the ? shortcuts hint and the voice dictation hint. If those hints are the reason you want a custom row, this is not the setting for the badges themselves.
It runs locally and does not consume API tokens, so a script that calls git or reads a file is free in the only currency that matters here. It hides temporarily during the help menu and permission prompts, which is normal and not a fault in your script.
How to set up a status line
The setting is one object. type is command, command is a script path or an inline shell command, and three optional fields adjust it: padding in characters, refreshInterval in seconds, and hideVimModeIndicator. It can live in your user settings at ~/.claude/settings.json or in a project's settings, and with nothing set there is no row at all.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2,
"refreshInterval": 10
}
}A sibling field, subagentStatusLine, rewrites the rows in the panel below the prompt that lists the subagents running in your session. It takes the same type and command and receives its own JSON, so it is a separate setting rather than a second line of this one.
Because command runs in a shell, you do not need a file at all. This one prints the model name and the context percentage using jq:
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}There is also a command that writes this for you. /statusline takes a description in plain language — “show model name and context percentage with a progress bar” — and generates a script in ~/.claude/, updating your settings to point at it. Approve the file edits it asks for, and the row appears.
The shortest script that works
Save this as ~/.claude/statusline.sh, make it executable, and point command at it. Each echo you add becomes another row.
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
echo "[$MODEL] ${DIR##*/} | ${PCT}% context"Before you wire it in, run it by hand with mock input. A failure you find here is your script's, and a failure you find in Claude Code is usually the setting's, so the order saves you the guessing:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/you/project"},"context_window":{"used_percentage":25},"session_id":"test"}' | ~/.claude/statusline.shOn Windows
Claude Code runs the command through Git Bash when Git Bash is installed, and through PowerShell when it is not. The trap is the path: Git Bash treats an unquoted backslash as an escape character, so a command written as C:\Users\you\statusline.mjs reaches the runner with its separators eaten and fails without a visible error. Write the path with forward slashes, or use ~. To run a PowerShell script, invoke powershell yourself, as powershell -NoProfile -File C:/Users/you/.claude/statusline.ps1.
The JSON your script receives
One object arrives on stdin, and it is large. The groups that matter most are the model, the workspace, the cost block, the context window, and the rate limits. Here is the shape, trimmed to what a status line usually shows:
{
"model": { "id": "claude-opus-5-5", "display_name": "Opus" },
"cwd": "/project",
"workspace": {
"current_dir": "/project",
"project_dir": "/project",
"added_dirs": []
},
"version": "2.1.310",
"cost": {
"total_cost_usd": 0.01234,
"total_duration_ms": 45000,
"total_lines_added": 156,
"total_lines_removed": 23
},
"context_window": {
"total_input_tokens": 15500,
"total_output_tokens": 1200,
"context_window_size": 200000,
"used_percentage": 8,
"remaining_percentage": 92,
"current_usage": {
"input_tokens": 8500,
"output_tokens": 1200,
"cache_creation_input_tokens": 5000,
"cache_read_input_tokens": 2000
}
},
"exceeds_200k_tokens": false,
"rate_limits": {
"five_hour": { "used_percentage": 23.5, "resets_at": 1738425600 },
"seven_day": { "used_percentage": 41.2, "resets_at": 1738857600 }
},
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl"
}| Field | What it holds |
|---|---|
model.display_name | The model's display name. model.id is the identifier, if you want to switch on that instead |
workspace.current_dir | The working directory now. workspace.project_dir is where Claude Code was launched, which differs once you change directory mid-session |
context_window.total_input_tokens, total_output_tokens | The tokens in the context window as of the most recent API response. Input includes cache reads and cache writes |
context_window.context_window_size | The window's size: 200,000 by default, 1,000,000 for a model with extended context |
context_window.used_percentage | A ready-made percentage, computed from input tokens only. remaining_percentage is its complement |
context_window.current_usage | The same counts broken out: fresh input, output, cache writes, cache reads |
exceeds_200k_tokens | Whether the last response's total passed 200,000. A fixed threshold, not a fraction of your actual window |
cost.total_cost_usd | An estimate at list price unless an organization sets its own rates. It can differ from the bill |
rate_limits.five_hour, seven_day | How much of each subscription window you have used, 0 to 100, and the epoch second it resets |
session_id | A stable per-session identifier, which is the right key for a cache file |
Beyond those there are entries for a git worktree, the repository parsed from the origin remote, an open pull request or GitLab merge request, the reasoning effort, fast mode, thinking, the vim mode, the output style and the prompt cache. The workspace.git_worktree field is worth knowing about because it is not the same as the worktree object: the first appears in any linked worktree, the second only while the session is in a worktree session.
Two rules cover most of the confusion. A number of fields are absent rather than empty when they do not apply, so a lookup for them returns nothing at all: vim with vim mode off, pr when no pull request is open, rate_limits on an API key. And two are null at particular moments: current_usage is null before the first API call and again straight after /compact until the next call repopulates it, and the two percentages can be null early in a session. Handle both in the script, and a null stops being a blank bar later.
One thing your script cannot do is ask the terminal how wide it is. Claude Code captures the output instead of connecting it to the terminal, so tput cols cannot see the size; read the COLUMNS and LINES environment variables, which Claude Code sets before each run.
When the status line updates
The script runs once when a session starts, including on resume, and after that on events: a new assistant message, a finished /compact, a permission mode change, a vim mode toggle, a change to the command itself, a rate limit window reaching its reset time, or a warm prompt cache reaching its expiry. Updates are debounced at 300ms, and a new trigger cancels a run still in flight.
That list is the reason a clock or a git branch goes stale. The event-driven triggers go quiet while the main session is idle, for instance while a coordinator waits on background subagents, and the row keeps showing what it last printed. refreshInterval is the fix: it re-runs the command every N seconds, minimum 1, on top of the events. It was added in 2.1.97, so a build older than that ignores the field.
Something else competes for that row. Outside fullscreen rendering, notifications appear on the same line as your status line, on the right-hand side, and verbose mode adds a token counter there showing your context size as the last API response reported it. On a narrow terminal those notifications can truncate your own output, which is one more reason to keep the script's text short.
Editing the script itself does not repaint the row. The change appears the next time a trigger runs it, so if you edit and see nothing, send a message before deciding the edit did not work. A change to the command string is the exception, and runs immediately.
Why the status line is blank
Anthropic lists three reasons a configured status line shows nothing: the folder you are in has not been trusted, the script fails, or a setting has switched the row off. They look identical on screen, so work them in this order rather than guessing.
| Check | What to do |
|---|---|
| The folder is trusted | Until you accept the workspace trust dialog the row stays blank. Accept it for the folder or a parent, then restart |
| The setting is where you think | Read the file back. It is a statusLine object, type is command, and the path in command is the file you edited |
| The script is executable | chmod +x ~/.claude/statusline.sh |
| The script prints to stdout | Output on stderr does not count, and stderr is also where your own debugging disappears |
| The exit code is 0 | Claude Code displays output only after the script exits 0. A non-zero exit, or no output at all, leaves the row blank |
| The command works outside Claude Code | Run it yourself with mock JSON. If it fails there, it fails in the row for the same reason |
| Windows path separators | A backslash in command is eaten before the script runs. Use forward slashes |
| A switch has narrowed it | disableAllHooks outside managed settings, allowManagedHooksOnly, or starting with --safe-mode leaves only a managed statusLine, and with none deployed the row is skipped without warning |
The fastest way through that list is claude --debug. It logs your script's stderr on every invocation, and its exit code on the first one in a session, which separates “never ran” from “ran and failed” in a single line.
The trust case leaves a signature worth recognising: the debug log says Status line command skipped: workspace trust not accepted. Anthropic fixed a build that showed nothing at all here in 2.1.79, and the trust rule itself is deliberate, because a status line is a shell command and runs under the same rule as a hook in a settings file.
If the row shows a placeholder rather than nothing, that is a different problem with the same fix: fields can be null before the first API response completes, so the percentage reads empty until one lands. Give the value a fallback such as // 0 in jq, and restart Claude Code if it is still empty after several messages.
Two live reports describe a blank row that the checklist above will not explain, and both were still open when this page was checked. In #101734 a correctly configured statusLine is never invoked at all on macOS on 2.1.296, with a minimal script and a plugin both behaving the same way. In #95759 one failed invocation blanks the row until the next success, because the last good output is not kept, so a script that fails occasionally flickers on a schedule rather than leaving a stale line.
Why the number is wrong
The token number has one honest definition and several ways to get it wrong. The definition: context_window describes the context as of the most recent API response, not a running total for the session. That distinction was a bug once. Builds before 2.1.132 sent cumulative session totals instead, which is how a 200,000-token window came to display 169% used.
| Symptom | Cause |
|---|---|
The count climbs forever and never resets on /clear or compaction | A build older than 2.1.132, which sent session cumulative figures; or your own script, if it keeps a saved total and adds each run to it instead of printing the reading it was just given |
Your percentage is lower than /context | Two reasons, and both are documented. used_percentage counts input tokens only, leaving output out of the fraction. And /context adds an estimate for messages sent since the last response, so it reads higher until that response. A third only bites if you compute the percentage yourself: adding output tokens to the input sum reads higher than the built-in figure |
| A hard-coded 200,000 in your script | context_window_size is 1,000,000 on an extended-context model, so divide by the field rather than by the number you remember |
exceeds_200k_tokens is false while a 200,000-token window is nearly full | It is a fixed 200,000 threshold whatever your window size, so on a larger window it turns true early and stays true, and it answers a different question from the one you are asking |
| Everything reads 101% or more | A bug Anthropic fixed in 2.1.299, in /usage, the approaching-limit warning and the status line together |
| The percentage collapses to 0 after an error | A live report on 2.1.296: a failed turn appends a synthetic assistant message with all-zero usage, and the payload takes its counts from the last message that has usage. Open |
| The cost figure disagrees with your bill | cost.total_cost_usd is a client-side estimate at list price, and it only follows contracted rates when an organization sets them |
If you want your own arithmetic, match Anthropic's: used_percentage is the sum of input_tokens, cache_creation_input_tokens and cache_read_input_tokens, divided by the window. Adding output tokens to that sum is the commonest way to disagree with the built-in percentage.
The zero after compaction is the one worth writing a guard for, because it is expected rather than broken. current_usage is null immediately after /compact until the next API call fills it, and a script that computes its own percentage from that object prints 0 for that tick. Printing the pre-calculated used_percentage with a fallback is steadier than recomputing it.
One more number people misread: exceeds_200k_tokens. It is a fixed 200,000 threshold whatever your window size, so it answers a different question from the one a percentage answers. On a 200,000-token window it turns true only when that window is nearly full. On a 1,000,000-token window it turns true at 200,000 tokens, about a fifth of the way in, and stays true for most of a long session until a compaction brings the last response back under 200,000. Use context_window_size for the denominator and used_percentage for the reading.
Rate limits and quota in the status line
People asked for subscription usage on the bar for months before it existed, and the two requests that carry the ask are #5621 in August 2025 and #20636 in January 2026. Anthropic added the rate_limits object in 2.1.80: a rolling five_hour window and a weekly seven_day window, each with used_percentage from 0 to 100 and resets_at in Unix epoch seconds. The request that asked for it, #20636, was closed with a note that it was fixed as of that version.
It arrives under narrow conditions, and that is the usual reason a script shows nothing here. The object is present only for a claude.ai Pro or Max subscriber, or behind a Claude apps gateway that sets a spend limit, and only after the session's first API response. Each window can be absent independently, and a window is dropped once its resets_at passes. Handle all three: use // empty in jq so an absent window prints nothing rather than the string null.
Behind a gateway there is a third object, spend_limit, carrying a percentage and a reset time from 2.1.251, and dollar figures from 2.1.284 on both the gateway and Claude Code. The dollar fields are optional even when the object is there, and Claude Code reads them from the gateway in a separate request, about every five minutes while you send requests, so they can trail the percentage by minutes and briefly disagree with it.
Two gaps are worth knowing before you debug your own script. An enterprise claude.ai login reports its usage in /usage but sends no rate_limits in the payload at all (#96543), and on 2.1.278 a report found the field missing entirely despite the documented version (#95918). A pre-reset percentage that would not go back down after a window reset was fixed in 2.1.243, so check your build before believing a stale figure.
What people have reported
On October 25, 2026, 694 issues in Anthropic's Claude Code tracker carried statusline in the title, 60 of them open. These are the six that answer this page's two questions most directly, each read on that day.
| What was reported | Filed by | Date |
|---|---|---|
| Statusline context_window contains cumulative tokens instead of current usage — a report whose symptoms match the 2.1.132 fix, with the figures that made the mismatch plain. Closed | alberduris | December 12, 2025 |
| statusLine command in settings.json never invoked on macOS — a valid setting, a minimal script, and nothing under the input box. Open | panaalexandrucristian | October 13, 2026 |
| A single failed statusLine invocation blanks the status row — the last successful output is discarded rather than left on screen. Open | OakGimbap | September 21, 2026 |
| used_percentage drops to 0 after an API error — synthetic assistant entries carry all-zero usage, and the payload reads from them. Open | chrisgick | October 11, 2026 |
| No rate_limits for a Claude.ai enterprise login, while /usage shows usage credits — the payload has no rate limit keys at all. Open | a-wilhelm | September 24, 2026 |
Current session 101% used — filed against /cost, and the 2.1.299 fix names /usage, the approaching-limit warning and the status line. Closed | powell-clark | September 4, 2026 |
The two requests that shaped the JSON are worth reading for a different reason. #5621 asked for quota in the payload in August 2025 and was closed by a stale-issue bot rather than by a fix. #20636 asked for the two percentages in January 2026 and was answered by 2.1.80. Whether a report is closed is not the same question as whether the behaviour changed, which is why every row above is dated.
Status line or mod?
They overlap at exactly one point and are otherwise different things. A status line is this page's subject: one settings field pointing at a shell command that prints text. A mod is a UI extension, a plugin whose hooks can draw inside the app, and drawing a status line entry is only one of the things it can do. The mods guide owns that question and this page does not re-answer it.
Where Omniscio fits
Plainly: the status line is Claude Code's own, it lives in a settings file, and nothing on this page needs Omniscio. Where Omniscio sits is one level up. It is a desktop app for running many Claude Code agents side by side, built around one calm inbox rather than a wall of terminals, and its own line is “Any model, any harness — Claude Code, Codex, OpenCode, Devin, Pi, and Hermes all supported.”
The row itself is part of the interactive screen, and Omniscio's own agent sessions are not that. It starts the Claude CLI with -p and --output-format stream-json --input-format stream-json, Claude Code's print mode, so what those sessions show you is Omniscio's session list rather than a terminal row. A status line you configure still applies to any claude you run in a terminal yourself.
That includes one inside Omniscio: its inbuilt terminal is a real shell in the project's folder, and it is a Labs feature that is off by default. If you want the row and the dashboard side by side, that is the combination to use.
Two defaults are worth knowing before you read a cost or usage figure off any bar. Omniscio's per-session spend cap applies to sessions on a metered model key or on Omniscio credits, and never to a Claude Pro or Max subscription session. And the Free plan covers 8 projects and 10 agents at once, with Pro at $20/month or $192/year. The features page lists what the app does today and the pricing page has the plans.
How this page was checked
Every statement here about Claude Code comes from Anthropic's own documentation, read in its Markdown form on October 25, 2026, from the pages listed below. The version line comes from Anthropic's own changelog: the newest release listed is 2.1.310, dated October 23, 2026. No status line script was written on this machine to produce this page, so the behaviour described is Anthropic's account of it and, where noted, the account of the people who reported it.
The reports were read the same day through GitHub's API, in Anthropic's anthropics/claude-code repository, and each carries the login of the person who filed it and the day they filed it. We quote what the report says, not what the product does, which is why the open ones are labelled open.
What this page says about Omniscio comes from our own product pages, library and source code. If Anthropic's page and this one ever disagree about Claude Code, Anthropic's page is right, because this part of Claude Code changes on nearly every release.
Sources
Anthropic's Claude Code documentation, read October 25, 2026, at Claude Code 2.1.310:
- Customize your status line: the setting, the walkthrough, the full JSON, every field, the update triggers, the examples, the Windows notes and the whole troubleshooting section
- Settings reference: the
statusLineentry and its type,disableAllHooks,allowManagedHooksOnly, and the two status line gates - The changelog: release 2.1.310, the newest listed, and 2.1.79, 2.1.80, 2.1.97, 2.1.132, 2.1.243 and 2.1.299 where this page names a version
- Hooks: the workspace trust and settings-precedence rules a status line runs under, which this page names only where they cause a blank row
- Costs: the prompt cache statistics the payload's
prompt_cacheobject summarizes - Anthropic's Claude Code issue tracker: the reports above, read through GitHub's API on the same day
Omniscio's own pages, for what this section says about this app:
- Omniscio's homepage: the product description, which carries the harness line
- Features: the spend-cap rule and its subscription exemption, and what the app does today
- Pricing: the free plan and the Pro price
- Inbuilt terminal in the library: a real shell in the project's folder, on a Labs flag that is off by default
src/main/process/spawn-build.tsin our own source: the spawn argument list, which opens with-pand--output-format stream-json- Claude Code mods: the UI-extension question this page leaves to it
Common questions
Why is my Claude Code status line blank?
The usual causes, in the order worth checking: the folder is not trusted yet, the script is not executable, the script writes to stderr or exits non-zero, the command path has backslashes on Windows, or disableAllHooks or allowManagedHooksOnly has narrowed or switched the row off. Run claude --debug to see the script's stderr and its exit code.
Why does the status line show a different context number from /context?
They measure different moments. The status line reports the counts from the last API response, while /context adds an estimate for messages sent since that response, so /context can read higher until the next one. used_percentage also counts input tokens only, and leaves output tokens out of the fraction.
Why is the token count in my status line too big?
If the figure grows without ever falling, ask whether your script is printing the current reading or adding each run onto a saved total, and rule out figures written by a build older than 2.1.132, which sent cumulative session totals instead of the tokens in the current context. If it is simply too high by a little, you are probably adding output tokens to the input figure. Read used_percentage, or recompute it from input tokens only.
Why does the status line not show rate limit or quota usage?
The rate_limits object only arrives for a claude.ai Pro or Max subscriber, or behind a Claude apps gateway that sets a spend limit, and only after the first API response of the session. It is absent on an API key, and at least one open report describes an enterprise claude.ai login sending none at all, so a script has to treat every window as optional.
Does the status line cost tokens?
No. The status line runs locally and does not consume API tokens. What it can cost you is time, because a slow script blocks the row from updating until it finishes.
How do I turn the status line off?
Delete the statusLine field from your settings file, or run /statusline and ask it to remove or clear your status line. Setting disableAllHooks to true also stops it, because that switch covers a custom status line along with hooks and the file suggestion command.
Run the sessions behind those status lines, from one window
Install Omniscio, keep your Claude Code sessions in one list, and see what each one needs without hunting through terminals. The features page lists what is in it today.