| name | statusline |
| description | Configure a custom status line in the CLI. Use when the user mentions status line, statusline, statusLine, CLI status bar, prompt footer customization, or wants to add session context above the prompt. |
CLI Status Line
The CLI supports a user-configurable status line rendered above the prompt. A command is spawned on each conversation update, receives a JSON payload on stdin describing the session, and its stdout is displayed as the status line. The spec is aligned with Claude Code's status line.
Configuration
Add a statusLine entry to ~/.cursor/cli-config.json:
{
"statusLine": {
"type": "command",
"command": "~/.cursor/statusline.sh",
"padding": 2
}
}
The command field supports full paths, ~ expansion, and shell-style argument splitting. You can point it at a script file or use an inline command like jq -r '...'.
| Field | Required | Default | Description |
|---|
type | yes | — | Must be "command" |
command | yes | — | Path to an executable or inline command. ~ is expanded. |
padding | no | 0 | Horizontal inset (in characters) for the status line container. |
updateIntervalMs | no | 300 | Minimum interval between invocations. Clamped to >= 300ms. |
timeoutMs | no | 2000 | Maximum time the command may run before it is killed. |
Stdin payload
The command receives a JSON object on stdin. The TypeScript interface is StatusLinePayload in packages/agent-cli/src/hooks/use-status-line.ts.
Full JSON schema
{
"session_id": "abc123",
"session_name": "my session",
"transcript_path": "/path/to/transcript.jsonl",
"render_width_chars": 120,
"cwd": "/Users/me/project",
"model": {
"id": "claude-4-opus",
"display_name": "Claude 4 Opus",
"param_summary": "(Thinking)",
"max_mode": true
},
"workspace": {
"current_dir": "/Users/me/project",
"project_dir": "/Users/me/project/.cursor/transcripts",
"added_dirs": []
},
"version": "1.2.3",
"output_style": {
"name": "default"
},
"context_window": {
"total_input_tokens": 15234,
"total_output_tokens": null,
"context_window_size": 200000,
"used_percentage": 34.5,
"remaining_percentage": 65.5,
"current_usage": null
},
"vim": {
"mode": "NORMAL"
},
"worktree": {
"name": "my-feature",
"path": "/Users/me/.cursor/worktrees/repo/my-feature"
}
}
Available fields
| Field | Description |
|---|
session_id | Unique session identifier |
session_name | Custom session name. Absent if no name has been set |
transcript_path | Path to conversation transcript file |
render_width_chars | Usable terminal columns minus built-in padding |
cwd, workspace.current_dir | Current working directory (both contain the same value) |
workspace.project_dir | Directory where transcripts are stored |
workspace.added_dirs | Additional directories (empty array for now) |
model.id, model.display_name | Current model identifier and display name |
model.param_summary | Formatted parameter summary (e.g. "(Thinking)", "High"). Absent when empty |
model.max_mode | true when max mode is enabled. Absent otherwise |
version | CLI version string |
output_style.name | "default" or "compact" |
context_window.total_input_tokens | Estimated input tokens (derived from used_percentage) |
context_window.total_output_tokens | Cumulative output tokens (null when not tracked) |
context_window.context_window_size | Maximum context window size in tokens |
context_window.used_percentage | Percentage of context window used |
context_window.remaining_percentage | Percentage of context window remaining |
context_window.current_usage | Token counts from the last API call (null before first call) |
vim.mode | "NORMAL" or "INSERT" when vim mode is enabled |
worktree.name | Worktree name when running inside a worktree |
worktree.path | Absolute path to the worktree directory |
Fields that may be absent
session_name — only present when a custom name has been set
model.param_summary — only present when model has non-default parameters
model.max_mode — only present when max mode is enabled
vim — only present when vim mode is enabled
worktree — only present when running in a worktree
Fields that may be null
context_window.current_usage — null before the first API call
context_window.used_percentage, context_window.remaining_percentage — may be null early in the session
Stdout / rendering
- Multiple lines are supported: each line of stdout renders as a separate row in the status area.
- ANSI color codes are supported (use chalk, tput,
\033[32m, etc.).
- If the command exits non-zero with empty stdout, the status line is not updated (previous text is kept).
- If the command times out or a new update arrives while the script is running, the in-flight process is killed.
- The status line runs locally and does not consume API tokens.
Examples
Basic: model + context usage
#!/usr/bin/env bash
payload=$(cat)
model=$(echo "$payload" | jq -r '.model.display_name')
pct=$(echo "$payload" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
printf "\033[90m%s ctx %s%%\033[0m" "$model" "$pct"
Context progress bar
#!/usr/bin/env bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
BAR_WIDTH=10
FILLED=$((PCT * BAR_WIDTH / 100))
EMPTY=$((BAR_WIDTH - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
[ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"
echo "[$MODEL] $BAR $PCT%"
Multi-line with git info
#!/usr/bin/env 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)
BRANCH=""
git rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | 🌿 $(git branch --show-current 2>/dev/null)"
echo -e "\033[36m[$MODEL]\033[0m 📁 ${DIR##*/}$BRANCH"
echo -e "ctx $PCT%"
Inline jq command (no script file)
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}
Testing
Test a script with mock input:
echo '{"model":{"display_name":"Opus"},"context_window":{"used_percentage":25}}' | ./statusline.sh
The command is spawned with child_process.spawn (no shell on Unix, shell: true on Windows for .cmd/.bat compatibility). Updates are debounced at the configured interval. If a new update triggers while a script is running, the in-flight process is killed via AbortController and the new invocation starts immediately.