qwen-code/docs/users/features/status-line.md
Shaojin Wen b004450d7f
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
feat(cli): support multi-line status line output (#3311)
* feat(cli): support multi-line status line output (#3211)

Remove the single-line hard limit (.split('\n')[0]) from the status line
hook so user scripts can output multiple rows. Footer renders each line
as a separate <Text wrap="truncate"> element, preserving per-line
horizontal truncation. Ink's virtual DOM handles re-rendering without
manual ANSI cursor management.

* feat(cli): cap status line output at 3 lines

Prevent runaway scripts from flooding the footer — lines beyond the
third are silently discarded.

* docs: mention 3-line cap in status line docs and agent prompt

* fix(cli): cap status line at 2 lines to keep footer within 3 rows

Footer has a fixed bottom row (hint/mode indicator), so status line
gets at most 2 lines to keep the total footer height at 3 rows max.

* test(cli): improve useStatusLine coverage to 100% lines

Add tests for: per-model metrics payload, contextWindowSize/version/
model fallbacks, config removal with pending debounce, command change
cancelling pending debounce.

* docs: update status line ASCII diagram for multi-line layouts

Also fix TS error in test (null → null as never for mock return).

* refactor(cli): return string[] from useStatusLine, filter empty lines

Address review feedback:
- Hook returns `lines: string[]` instead of `text: string | null`,
  eliminating the join/split round-trip with Footer.
- Filter empty lines before slicing so leading blanks don't eat real
  content (e.g. "\n\nreal content" no longer yields ["", ""]).
- Export MAX_STATUS_LINES with comment explaining the 3-row constraint.
- Use `status-line-${i}` as React key for clarity.

* test(cli): add Footer multi-line rendering, \r\n, and pure-newline tests

Address remaining review feedback:
- Footer test: mock useStatusLine, verify multi-line rendering and
  hint suppression.
- useStatusLine test: add \r\n line ending and pure-newline edge case.

* fix(cli): align right footer indicators to top

When the status line has multiple rows, the left column becomes taller
than the right section. The outer Box defaults to `alignItems: stretch`
which caused the indicators to visually center; add `alignItems="flex-start"`
on the right Box so they stay anchored to the top row.

Reported via e2e test in #3311.
2026-04-17 12:44:30 +08:00

13 KiB
Raw Blame History

Status Line

Display custom information in the footer using a shell command.

The status line lets you run a shell command whose output is displayed in the footer's left section. The command receives structured JSON context via stdin, so it can show session-aware information like the current model, token usage, git branch, or anything else you can script.

Single-line status (default approval mode — 1 row):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line
└─────────────────────────────────────────────────────────────────┘

Multi-line status (up to 2 lines — 2 rows):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line 1
│  ████████░░░░░░░░░░ 34% context                                │  ← status line 2
└─────────────────────────────────────────────────────────────────┘

Multi-line status + non-default mode (3 rows max):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line 1
│  ████████░░░░░░░░░░ 34% context                                │  ← status line 2
│  auto-accept edits (shift + tab to cycle)                       │  ← mode indicator
└─────────────────────────────────────────────────────────────────┘

When configured, the status line replaces the default "? for shortcuts" hint. High-priority messages (Ctrl+C/D exit prompts, Esc, vim INSERT mode) temporarily override the status line. The status line text is truncated to fit within the available width.

Prerequisites

  • jq is recommended for parsing the JSON input (install via brew install jq, apt install jq, etc.)
  • Simple commands that don't need JSON data (e.g. git branch --show-current) work without jq

Quick setup

The easiest way to configure a status line is the /statusline command. It launches a setup agent that reads your shell PS1 configuration and generates a matching status line:

/statusline

You can also give it specific instructions:

/statusline show model name and context usage percentage

Manual configuration

Add a statusLine object under the ui key in ~/.qwen/settings.json:

{
  "ui": {
    "statusLine": {
      "type": "command",
      "command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.display_name'); pct=$(echo \"$input\" | jq -r '.context_window.used_percentage'); echo \"$model  ctx:${pct}%\""
    }
  }
}
Field Type Required Description
type "command" Yes Must be "command"
command string Yes Shell command to execute. Receives JSON via stdin, stdout is displayed (up to 2 lines).

JSON input

The command receives a JSON object via stdin with the following fields:

{
  "session_id": "abc-123",
  "version": "0.14.1",
  "model": {
    "display_name": "qwen-3-235b"
  },
  "context_window": {
    "context_window_size": 131072,
    "used_percentage": 34.3,
    "remaining_percentage": 65.7,
    "current_usage": 45000,
    "total_input_tokens": 30000,
    "total_output_tokens": 5000
  },
  "workspace": {
    "current_dir": "/home/user/project"
  },
  "git": {
    "branch": "main"
  },
  "metrics": {
    "models": {
      "qwen-3-235b": {
        "api": {
          "total_requests": 10,
          "total_errors": 0,
          "total_latency_ms": 5000
        },
        "tokens": {
          "prompt": 30000,
          "completion": 5000,
          "total": 35000,
          "cached": 10000,
          "thoughts": 2000
        }
      }
    },
    "files": {
      "total_lines_added": 120,
      "total_lines_removed": 30
    }
  },
  "vim": {
    "mode": "INSERT"
  }
}
Field Type Description
session_id string Unique session identifier
version string Qwen Code version
model.display_name string Current model name
context_window.context_window_size number Total context window size in tokens
context_window.used_percentage number Context window usage as percentage (0100)
context_window.remaining_percentage number Context window remaining as percentage (0100)
context_window.current_usage number Token count from the last API call (current context size)
context_window.total_input_tokens number Total input tokens consumed this session
context_window.total_output_tokens number Total output tokens consumed this session
workspace.current_dir string Current working directory
git object | absent Present only inside a git repository.
git.branch string Current branch name
metrics.models.<id>.api object Per-model API stats: total_requests, total_errors, total_latency_ms
metrics.models.<id>.tokens object Per-model token usage: prompt, completion, total, cached, thoughts
metrics.files object File change stats: total_lines_added, total_lines_removed
vim object | absent Present only when vim mode is enabled. Contains mode ("INSERT" or "NORMAL").

Important: stdin can only be read once. Always store it in a variable first: input=$(cat).

Examples

Model and token usage

{
  "ui": {
    "statusLine": {
      "type": "command",
      "command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.display_name'); pct=$(echo \"$input\" | jq -r '.context_window.used_percentage'); echo \"$model  ctx:${pct}%\""
    }
  }
}

Output: qwen-3-235b ctx:34%

Git branch + directory

{
  "ui": {
    "statusLine": {
      "type": "command",
      "command": "input=$(cat); branch=$(echo \"$input\" | jq -r '.git.branch // empty'); dir=$(basename \"$(echo \"$input\" | jq -r '.workspace.current_dir')\"); echo \"$dir${branch:+ ($branch)}\""
    }
  }
}

Output: my-project (main)

Note: The git.branch field is provided directly in the JSON input — no need to shell out to git.

File change stats

{
  "ui": {
    "statusLine": {
      "type": "command",
      "command": "input=$(cat); added=$(echo \"$input\" | jq -r '.metrics.files.total_lines_added'); removed=$(echo \"$input\" | jq -r '.metrics.files.total_lines_removed'); echo \"+$added/-$removed lines\""
    }
  }
}

Output: +120/-30 lines

Script file for complex commands

For longer commands, save a script file at ~/.qwen/statusline-command.sh:

#!/bin/bash
input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name')
pct=$(echo "$input" | jq -r '.context_window.used_percentage')
branch=$(echo "$input" | jq -r '.git.branch // empty')
added=$(echo "$input" | jq -r '.metrics.files.total_lines_added')
removed=$(echo "$input" | jq -r '.metrics.files.total_lines_removed')

parts=()
[ -n "$model" ] && parts+=("$model")
[ -n "$branch" ] && parts+=("($branch)")
[ "$pct" != "0" ] 2>/dev/null && parts+=("ctx:${pct}%")
([ "$added" -gt 0 ] || [ "$removed" -gt 0 ]) 2>/dev/null && parts+=("+${added}/-${removed}")

echo "${parts[*]}"

Then reference it in settings:

{
  "ui": {
    "statusLine": {
      "type": "command",
      "command": "bash ~/.qwen/statusline-command.sh"
    }
  }
}

Behavior

  • Update triggers: The status line updates when the model changes, a new message is sent (token count changes), vim mode is toggled, git branch changes, tool calls complete, or file changes occur. Updates are debounced (300ms).
  • Timeout: Commands that take longer than 5 seconds are killed. The status line clears on failure.
  • Output: Multi-line output is supported (up to 2 lines; extra lines are discarded). Each line is rendered as a separate row with dimmed colors in the footer's left section. Lines that exceed the available width are truncated.
  • Hot reload: Changes to ui.statusLine in settings take effect immediately — no restart required.
  • Shell: Commands run via /bin/sh on macOS/Linux. On Windows, cmd.exe is used by default — wrap POSIX commands with bash -c "..." or point to a bash script (e.g. bash ~/.qwen/statusline-command.sh).
  • Removal: Delete the ui.statusLine key from settings to disable. The "? for shortcuts" hint returns.

Troubleshooting

Problem Cause Fix
Status line not showing Config at wrong path Must be under ui.statusLine, not root-level statusLine
Empty output Command fails silently Test manually: echo '{"session_id":"test","version":"0.14.1","model":{"display_name":"test"},"context_window":{"context_window_size":0,"used_percentage":0,"remaining_percentage":100,"current_usage":0,"total_input_tokens":0,"total_output_tokens":0},"workspace":{"current_dir":"/tmp"},"metrics":{"models":{},"files":{"total_lines_added":0,"total_lines_removed":0}}}' | sh -c 'your_command'
Stale data No trigger fired Send a message or switch models to trigger an update
Command too slow Complex script Optimize the script or move heavy work to a background cache