Back to Qwen Code

Status Line

docs/users/features/status-line.md

0.15.614.2 KB
Original Source

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:

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}%\""
    }
  }
}
FieldTypeRequiredDescription
type"command"YesMust be "command"
commandstringYesShell command to execute. Receives JSON via stdin, stdout is displayed (up to 2 lines).
refreshIntervalnumberNoRe-run the command every N seconds (minimum 1). Useful for data that changes without an Agent state event (clock, quota, uptime).

JSON input

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

json
{
  "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"
  }
}
FieldTypeDescription
session_idstringUnique session identifier
versionstringQwen Code version
model.display_namestringCurrent model name
context_window.context_window_sizenumberTotal context window size in tokens
context_window.used_percentagenumberContext window usage as percentage (0–100)
context_window.remaining_percentagenumberContext window remaining as percentage (0–100)
context_window.current_usagenumberToken count from the last API call (current context size)
context_window.total_input_tokensnumberTotal input tokens consumed this session
context_window.total_output_tokensnumberTotal output tokens consumed this session
workspace.current_dirstringCurrent working directory
gitobject | absentPresent only inside a git repository.
git.branchstringCurrent branch name
metrics.models.<id>.apiobjectPer-model API stats: total_requests, total_errors, total_latency_ms
metrics.models.<id>.tokensobjectPer-model token usage: prompt, completion, total, cached, thoughts
metrics.filesobjectFile change stats: total_lines_added, total_lines_removed
vimobject | absentPresent 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

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}%\""
    }
  }
}

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

Git branch + directory

json
{
  "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

json
{
  "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

Live clock and git branch

Use refreshInterval when the statusline shows data that changes without an Agent event (e.g. the clock, uptime, or rate-limit counters):

json
{
  "ui": {
    "statusLine": {
      "type": "command",
      "command": "input=$(cat); branch=$(echo \"$input\" | jq -r '.git.branch // \"no-git\"'); echo \"$(date +%H:%M:%S)  ($branch)\"",
      "refreshInterval": 1
    }
  }
}

Output (refreshed every second): 14:32:07 (main)

Script file for complex commands

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

bash
#!/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:

json
{
  "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). Set refreshInterval (seconds) to additionally re-run the command on a timer — useful for data that changes without an Agent event (clock, rate limits, build status).
  • 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

ProblemCauseFix
Status line not showingConfig at wrong pathMust be under ui.statusLine, not root-level statusLine
Empty outputCommand fails silentlyTest 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 dataNo trigger firedSend a message or switch models to trigger an update — or set refreshInterval to re-run the command on a timer
Command too slowComplex scriptOptimize the script or move heavy work to a background cache