- Want to monitor context window usage as you work
- Need to track session costs
- Work across multiple sessions and need to distinguish them
- Want git branch and status always visible

Set up a status line
Use the/statusline command to have Claude Code generate a script for you, or manually create a script and add it to your settings.
Use the /statusline command
The/statusline command accepts natural language instructions describing what you want displayed. Claude Code generates a script file in ~/.claude/ and updates your settings automatically:
Manually configure a status line
Add astatusLine field to your user settings (~/.claude/settings.json, where ~ is your home directory) or project settings. Set type to "command" and point command to a script path or an inline shell command. For a full walkthrough of creating a script, see Build a status line step by step.
command field runs in a shell, so you can also use inline commands instead of a script file. This example uses jq to parse the JSON input and display the model name and context percentage:
padding field adds extra horizontal spacing (in characters) to the status line content. Defaults to 0. This padding is in addition to the interface’s built-in spacing, so it controls relative indentation rather than absolute distance from the terminal edge.
Disable the status line
Run/statusline and ask it to remove or clear your status line (e.g., /statusline delete, /statusline clear, /statusline remove it). You can also manually delete the statusLine field from your settings.json.
Build a status line step by step
This walkthrough shows what’s happening under the hood by manually creating a status line that displays the current model, working directory, and context window usage percentage.Running
/statusline with a description of what you want configures all of this for you automatically.
Create a script that reads JSON and prints output
Claude Code sends JSON data to your script via stdin. This script uses
jq, a command-line JSON parser you may need to install, to extract the model name, directory, and context percentage, then prints a formatted line.Save this to ~/.claude/statusline.sh (where ~ is your home directory, such as /Users/username on macOS or /home/username on Linux):Add to settings
Tell Claude Code to run your script as the status line. Add this configuration to Your status line appears at the bottom of the interface. Settings reload automatically, but changes won’t appear until your next interaction with Claude Code.
~/.claude/settings.json, which sets type to "command" (meaning “run this shell command”) and points command to your script:How status lines work
Claude Code runs your script and pipes JSON session data to it via stdin. Your script reads the JSON, extracts what it needs, and prints text to stdout. Claude Code displays whatever your script prints. When it updates Your script runs after each new assistant message, when the permission mode changes, or when vim mode toggles. Updates are debounced at 300ms, meaning rapid changes batch together and your script runs once things settle. If a new update triggers while your script is still running, the in-flight execution is cancelled. If you edit your script, the changes won’t appear until your next interaction with Claude Code triggers an update. What your script can output- Multiple lines: each
echoorprintstatement displays as a separate row. See the multi-line example. - Colors: use ANSI escape codes like
\033[32mfor green (terminal must support them). See the git status example. - Links: use OSC 8 escape sequences to make text clickable (Cmd+click on macOS, Ctrl+click on Windows/Linux). Requires a terminal that supports hyperlinks like iTerm2, Kitty, or WezTerm. See the clickable links example.
The status line runs locally and does not consume API tokens. It temporarily hides during certain UI interactions, including autocomplete suggestions, the help menu, and permission prompts.
Available data
Claude Code sends the following JSON fields to your script via stdin:| Field | Description |
|---|---|
model.id, model.display_name | Current model identifier and display name |
cwd, workspace.current_dir | Current working directory. Both fields contain the same value; workspace.current_dir is preferred for consistency with workspace.project_dir. |
workspace.project_dir | Directory where Claude Code was launched, which may differ from cwd if the working directory changes during a session |
cost.total_cost_usd | Total session cost in USD |
cost.total_duration_ms | Total wall-clock time since the session started, in milliseconds |
cost.total_api_duration_ms | Total time spent waiting for API responses in milliseconds |
cost.total_lines_added, cost.total_lines_removed | Lines of code changed |
context_window.total_input_tokens, context_window.total_output_tokens | Cumulative token counts across the session |
context_window.context_window_size | Maximum context window size in tokens. 200000 by default, or 1000000 for models with extended context. |
context_window.used_percentage | Pre-calculated percentage of context window used |
context_window.remaining_percentage | Pre-calculated percentage of context window remaining |
context_window.current_usage | Token counts from the last API call, described in context window fields |
exceeds_200k_tokens | Whether the total token count (input, cache, and output tokens combined) from the most recent API response exceeds 200k. This is a fixed threshold regardless of actual context window size. |
session_id | Unique session identifier |
transcript_path | Path to conversation transcript file |
version | Claude Code version |
output_style.name | Name of the current output style |
vim.mode | Current vim mode (NORMAL or INSERT) when vim mode is enabled |
agent.name | Agent name when running with the --agent flag or agent settings configured |
Full JSON schema
Full JSON schema
Your status line command receives this JSON structure via stdin:Fields that may be absent (not present in JSON):
vim: appears only when vim mode is enabledagent: appears only when running with the--agentflag or agent settings configured
null:context_window.current_usage:nullbefore the first API call in a sessioncontext_window.used_percentage,context_window.remaining_percentage: may benullearly in the session
Context window fields
Thecontext_window object provides two ways to track context usage:
- Cumulative totals (
total_input_tokens,total_output_tokens): sum of all tokens across the entire session, useful for tracking total consumption - Current usage (
current_usage): token counts from the most recent API call, use this for accurate context percentage since it reflects the actual context state
current_usage object contains:
input_tokens: input tokens in current contextoutput_tokens: output tokens generatedcache_creation_input_tokens: tokens written to cachecache_read_input_tokens: tokens read from cache
used_percentage field is calculated from input tokens only: input_tokens + cache_creation_input_tokens + cache_read_input_tokens. It does not include output_tokens.
If you calculate context percentage manually from current_usage, use the same input-only formula to match used_percentage.
The current_usage object is null before the first API call in a session.
Examples
These examples show common status line patterns. To use any example:- Save the script to a file like
~/.claude/statusline.sh(or.py/.js) - Make it executable:
chmod +x ~/.claude/statusline.sh - Add the path to your settings
jq to parse JSON. Python and Node.js have built-in JSON parsing.
Context window usage
Display the current model and context window usage with a visual progress bar. Each script reads JSON from stdin, extracts theused_percentage field, and builds a 10-character bar where filled blocks (▓) represent usage:

Git status with colors
Show git branch with color-coded indicators for staged and modified files. This script uses ANSI escape codes for terminal colors:\033[32m is green, \033[33m is yellow, and \033[0m resets to default.

Cost and duration tracking
Track your session’s API costs and elapsed time. Thecost.total_cost_usd field accumulates the cost of all API calls in the current session. The cost.total_duration_ms field measures total elapsed time since the session started, while cost.total_api_duration_ms tracks only the time spent waiting for API responses.
Each script formats cost as currency and converts milliseconds to minutes and seconds:

Display multiple lines
Your script can output multiple lines to create a richer display. Eachecho statement produces a separate row in the status area.

print or echo statement creates a separate row:
Clickable links
This example creates a clickable link to your GitHub repository. It reads the git remote URL, converts SSH format to HTTPS withsed, and wraps the repo name in OSC 8 escape codes. Hold Cmd (macOS) or Ctrl (Windows/Linux) and click to open the link in your browser.

printf '%b' which interprets backslash escapes more reliably than echo -e across different shells:
Cache expensive operations
Your status line script runs frequently during active sessions. Commands likegit status or git diff can be slow, especially in large repositories. This example caches git information to a temp file and only refreshes it every 5 seconds.
Use a stable, fixed filename for the cache file like /tmp/statusline-git-cache. Each status line invocation runs as a new process, so process-based identifiers like $$, os.getpid(), or process.pid produce a different value every time and the cache is never reused.
Each script checks if the cache file is missing or older than 5 seconds before running git commands:
Tips
- Test with mock input:
echo '{"model":{"display_name":"Opus"},"context_window":{"used_percentage":25}}' | ./statusline.sh - Keep output short: the status bar has limited width, so long output may get truncated or wrap awkwardly
- Cache slow operations: your script runs frequently during active sessions, so commands like
git statuscan cause lag. See the caching example for how to handle this.
Troubleshooting
Status line not appearing- Verify your script is executable:
chmod +x ~/.claude/statusline.sh - Check that your script outputs to stdout, not stderr
- Run your script manually to verify it produces output
- If
disableAllHooksis set totruein your settings, the status line is also disabled. Remove this setting or set it tofalseto re-enable.
-- or empty values
- Fields may be
nullbefore the first API response completes - Handle null values in your script with fallbacks such as
// 0in jq - Restart Claude Code if values remain empty after multiple messages
- Use
used_percentagefor accurate context state rather than cumulative totals - The
total_input_tokensandtotal_output_tokensare cumulative across the session and may exceed the context window size - Context percentage may differ from
/contextoutput due to when each is calculated
- Verify your terminal supports OSC 8 hyperlinks (iTerm2, Kitty, WezTerm)
- Terminal.app does not support clickable links
- SSH and tmux sessions may strip OSC sequences depending on configuration
- If escape sequences appear as literal text like
\e]8;;, useprintf '%b'instead ofecho -efor more reliable escape handling
- Complex escape sequences (ANSI colors, OSC 8 links) can occasionally cause garbled output if they overlap with other UI updates
- If you see corrupted text, try simplifying your script to plain text output
- Multi-line status lines with escape codes are more prone to rendering issues than single-line plain text
- Scripts that exit with non-zero codes or produce no output cause the status line to go blank
- Slow scripts block the status line from updating until they complete. Keep scripts fast to avoid stale output.
- If a new update triggers while a slow script is running, the in-flight script is cancelled
- Test your script independently with mock input before configuring it
- System notifications like MCP server errors, auto-updates, and token warnings display on the right side of the same row as your status line
- Enabling verbose mode adds a token counter to this area
- On narrow terminals, these notifications may truncate your status line output