Customizing the Claude Code Status Line — How to Always Display Session Info in Your Terminal
Honestly, when I first used Claude Code, what made me most anxious was things like "How much context am I using right now?" and "How much is this costing me?" Constantly typing the /usage command interrupted my workflow, and I thought it would be nice to have something always visible like a dashboard. That's when I discovered the Status Line feature.
The Claude Code status line is an info bar that always sits at the bottom of your terminal, and the key point is that by registering just one shell script, you can see information like the model name, context usage, cumulative cost, and Git status in real time. It doesn't consume any API tokens, and since it runs locally, there's no need to worry about additional costs. Follow this guide and in 5 minutes you can create your own dashboard showing model name, cost, and context in real time at the bottom of your terminal.
This article is written for macOS/Linux environments. Windows users can apply the same steps in a WSL environment.
Here's what the finished status line looks like:
📦 Claude Sonnet 4.6 | 📊 42% | 💰 $1.37 | 📁 my-projectCore Concepts
How It Works: The stdin → Processing → stdout Pipeline
The architecture of the status line is surprisingly simple. You register a shell command in ~/.claude/settings.json, and every time a specific event occurs (assistant message completion, permission mode change, tool call completion, etc.), Claude Code executes that command while piping the current session state as JSON to stdin. The string your script outputs to stdout is rendered directly at the bottom of the terminal.
For those unfamiliar with shell scripting, here's a brief summary — stdin is the channel through which a program receives input, and stdout is the channel through which it sends output. Here, Claude Code feeds JSON data into stdin, and the script processes it and outputs the result to stdout. It's the same concept as Linux pipes (
|).
Let's first look at where statusLine sits within the overall structure of the settings file:
{
"permissions": {
"allow": ["Read"],
"deny": []
},
"statusLine": {
"type": "command",
"command": "sh ~/.claude/statusline.sh",
"refreshInterval": 5,
"padding": 0
}
}statusLine is a top-level key in settings.json. Here's a summary of each option:
| Option | Description |
|---|---|
type |
Currently only "command" is supported |
command |
The shell command or script path to execute |
refreshInterval |
Periodic refresh interval in seconds. If not set, updates are event-driven only |
padding |
Number of spaces added to the left and right of the status line text. 0 means full width; increasing the number adds margins |
refreshInterval forces the status line to refresh every N seconds regardless of events. If you want to keep information up to date even during long-running tool executions, set it to around 5–10 seconds. When not set, updates only occur when events like tool call completion, assistant response completion, or permission changes happen.
JSON Structure Received via stdin
The JSON your script receives contains surprisingly rich information.
| Field | Content |
|---|---|
model.display_name |
Name of the currently active model |
context_window.used_percentage |
Context window usage (%) |
context_window.remaining_percentage |
Remaining context percentage |
cost.total_cost_usd |
Cumulative session cost (USD) |
cost.total_lines_added / removed |
Number of lines added/removed |
workspace.current_dir |
Current working directory |
workspace.git_worktree |
Git worktree path |
rate_limits.five_hour.used_percentage |
API usage over 5-hour window (%) |
rate_limits.seven_day.used_percentage |
API usage over 7-day window (%) |
rate_limits.*.resets_at |
Reset time (ISO 8601 format, e.g., 2026-04-20T15:30:00Z) |
session_id |
Current session identifier |
version |
Claude Code version |
Try It Yourself First
Before writing a full script, it's much easier to understand by seeing what JSON actually comes in. You can dump the stdin contents to a file with the following configuration:
{
"statusLine": {
"type": "command",
"command": "tee /tmp/claude-status.json | cat"
}
}After configuring, restart Claude Code, have a couple of conversations, then check with cat /tmp/claude-status.json to see the actual JSON structure. Once you've seen it firsthand, you'll have a much better sense of which fields to extract and how when writing your script.
/statusline — Generate Instantly with Natural Language
If writing a script feels daunting, you can use the /statusline command within Claude Code. Describe the information you want in natural language, and it will automatically handle both script generation and settings.json registration.
/statusline Show me the model name, context usage progress bar, cost, and Git branchThat single line creates a working status line right away. When you need fine-tuning later, you can directly edit the generated script, making this the perfect entry point.
Practical Applications
Example 1: Essential Info in One Line with a Bash Script
This is the most basic yet practical configuration. All you need is jq, a JSON parsing tool, installed and you're ready to go. (jq is a lightweight tool for filtering and extracting JSON data in the terminal, installable via brew install jq or apt install jq.)
#!/bin/bash
# ~/.claude/statusline.sh
INPUT=$(cat)
# jq를 한 번만 호출해서 필요한 값을 모두 추출
read -r MODEL CTX COST DIR <<< $(echo "$INPUT" | jq -r '[
.model.display_name,
(.context_window.used_percentage | tostring),
(.cost.total_cost_usd | tostring),
(.workspace.current_dir | split("/") | last)
] | @tsv')
printf "📦 %s | 📊 %.0f%% | 💰 $%.2f | 📁 %s" "$MODEL" "$CTX" "$COST" "$DIR"| Line | Purpose |
|---|---|
INPUT=$(cat) |
Stores the entire JSON from stdin into a variable |
jq -r '[...] | @tsv' |
Extracts needed fields in a single jq call, tab-separated |
read -r ... <<< $(...) |
Assigns the extracted values to individual variables |
printf |
Outputs the formatted string with emojis to stdout |
I initially wrote it calling
jqseparately for each field, but later realized that parsing the JSON every time was wasteful. Extracting multiple values in a singlejqcall as shown above is both more performant and cleaner.
After setting up, don't forget to run chmod +x ~/.claude/statusline.sh (I once wasted 10 minutes because I forgot this).
Example 2: Rate Limit Warning Visualization
On Max or Team plans, it can be alarming when you approach API limits — but you can detect this in advance through the status line.
#!/bin/bash
INPUT=$(cat)
read -r FIVE_HR RESETS <<< $(echo "$INPUT" | jq -r '[
(.rate_limits.five_hour.used_percentage | tostring),
.rate_limits.five_hour.resets_at
] | @tsv')
if (( $(echo "$FIVE_HR > 80" | bc -l) )); then
ICON="🔴"
elif (( $(echo "$FIVE_HR > 60" | bc -l) )); then
ICON="🟡"
else
ICON="🟢"
fi
printf "%s Rate: %.0f%% | Reset: %s" "$ICON" "$FIVE_HR" "$RESETS"This script depends on the
bccommand. It's included by default on macOS, but on some minimal Linux images (Alpine, etc.), you may need to install it separately with something likeapk add bc.
When it exceeds 80%, the icon turns to a red circle, giving you an intuitive signal to slow down or take a break. I used emojis here instead of ANSI escape codes because whether ANSI color codes render in the Claude Code status line can vary depending on the terminal environment. The emoji approach is safer in terms of compatibility.
Example 3: Powerline-Style with ccstatusline
If you don't want to manage scripts yourself and want a polished UI, community tools are a great option.
{
"statusLine": {
"type": "command",
"command": "ccstatusline"
}
}ccstatusline supports Powerline arrow separators, built-in themes, and a token speed widget. You can configure it interactively with the ccstatusline config command, making it especially recommended for those who enjoy customizing their terminal.
Note: If you set it up with
npx -y ccstatusline@latest, it checks for package downloads on every execution, which can become noticeably slow when combined withrefreshInterval. It's much faster to install globally withnpm install -g ccstatuslineand then call it directly as"command": "ccstatusline"as shown above.
Powerline fonts are special fonts for rendering arrow-shaped separators (, ) in the terminal. They're supported by installing fonts like Nerd Fonts or MesloLGS NF.
Example 4: Per-Project Configuration Separation
Global settings go in ~/.claude/settings.json, and project-specific settings go in .claude/settings.json at the project root. This is a situation commonly encountered in practice — for example, you might emphasize worktree information in a monorepo while focusing on cost in personal projects.
my-project/
├── .claude/
│ └── settings.json # Project-specific status line
│ └── statusline.sh # Project-specific script
├── src/
└── ...
~/.claude/
├── settings.json # Global default status line
└── statusline.sh # Global default scriptWhen .claude/settings.json exists at the project root, that configuration overrides the global settings. When working across multiple worktrees in a monorepo, the relevant information automatically switches for each project, significantly reducing context-switching costs.
Pros and Cons Analysis
Pros
| Item | Details |
|---|---|
| No API token consumption | Runs locally with zero additional cost |
| Complete flexibility | Can display anything executable in a shell — system resources, external service status, etc. |
| Low barrier to entry | A single natural language line with the /statusline command creates one instantly |
| Rich session data | Provides model, context, cost, rate limit, and workspace information via JSON |
| Community ecosystem | Ready-to-use implementations in Bash, Go, Rust, Node.js, Python, and more |
Cons and Considerations
| Item | Details | Mitigation |
|---|---|---|
| Stale data | Without refreshInterval, data won't update during long-running tasks |
Set "refreshInterval": 5 |
| External dependencies | Requires jq, Node.js, bc, etc. to be installed | Add check logic like command -v jq >/dev/null at the top of the script |
| Terminal compatibility | ANSI code and Powerline font support varies across terminals | Prepare emoji-based fallbacks |
| Performance issues | Delays when calling heavy external commands | Cache results in a temporary file and increase refreshInterval to 30 seconds or more, or use ccstatusline's block timer cache |
In practice, here's how caching specifically works: you save external API call results to /tmp/claude-cache.txt, and if the file's modification time is within N seconds, you use the cached value. This lets you separate the status line refresh cycle from the heavy computation cycle.
Most Common Mistakes in Practice
- Forgetting
chmod +x— If you don't give the script execution permission after writing it, nothing will be displayed. There's no error message either, making the cause surprisingly hard to find. - Running a Bash script without jq installed — It may not be installed by default even on macOS, so you'll need
brew install jq. On Linux, you can install it withapt install jqoryum install jq. - Wondering "Why isn't it updating?" without refreshInterval — The default is event-driven updates, so there are no updates while a tool is running. If you need periodic updates, be sure to add it to your configuration.
Wrapping Up
The Claude Code status line follows the simple formula of "one shell script = your own real-time dashboard," greatly enhancing the visibility of your session state. It won't prevent cost overruns by itself, but just being able to always see where things stand provides a surprising amount of peace of mind.
Here are 3 steps to get started right now:
- Open Claude Code and type
/statusline Show me the model name, context usage, and cost. This is the fastest way to experience the status line. - Open the generated
~/.claude/statusline.shfile and add information relevant to your needs, like the Rate Limit example in this article. It's also a good idea to add"refreshInterval": 5to~/.claude/settings.json. - Once you're comfortable with the basic script, try community tools like ccstatusline, or set up per-project status lines with
.claude/settings.jsonfor situation-specific configurations.
References
Core Documentation
- Customize your status line — Claude Code Official Docs
- Creating The Perfect Claude Code Status Line | AI Hero
- ccstatusline GitHub
Additional References
- Building a Custom Claude Code Statusline to Track Worktrees and Usage | Dan Does Code
- ClaudeCodeStatusLine GitHub
- claude-statusline (Go) Blog
- claude-code-status-line (Rust) — crates.io
- I Replaced /usage and /context with a Single Statusline — DEV Community
- Custom status lines for Claude Code | PhotoStructure
- How to Customize Your Claude Code Status Line | alexop.dev