refactor(status-line): redesign JSON input schema and add context fields

Restructure the status line stdin JSON for clarity and accuracy:
- Rename model.id → model.display_name, cwd → workspace.current_dir
- Replace raw context_window size/count with used_percentage,
  remaining_percentage, current_usage, context_window_size, and
  total_input_tokens/total_output_tokens
- Add version field from cfg.getCliVersion()
- Add git.branch, metrics.models, metrics.files
- Remove upstream-only fields: tokens.tool (never populated),
  session (start_time/elapsed_time not live-updating),
  streaming_state, approval_mode, terminal, metrics.tools
- Rename tokens.candidates → tokens.completion (Qwen API convention)
- Fix template string escaping in builtin-agents to avoid
  templateString() placeholder collision

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
克竟 2026-04-08 17:52:07 +08:00
parent 7902806e63
commit 24a28d5fb0
4 changed files with 240 additions and 47 deletions

View file

@ -40,7 +40,7 @@ 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.id'); tokens=$(echo \"$input\" | jq -r '.context_window.last_prompt_token_count'); echo \"$model ctx:$tokens\"",
"command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.display_name'); pct=$(echo \"$input\" | jq -r '.context_window.used_percentage'); echo \"$model ctx:${pct}%\"",
"padding": 0
}
}
@ -60,13 +60,45 @@ The command receives a JSON object via stdin with the following fields:
```json
{
"session_id": "abc-123",
"cwd": "/home/user/project",
"version": "0.14.1",
"model": {
"id": "qwen-3-235b"
"display_name": "qwen-3-235b"
},
"context_window": {
"context_window_size": 131072,
"last_prompt_token_count": 45000
"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"
@ -74,14 +106,24 @@ The command receives a JSON object via stdin with the following fields:
}
```
| Field | Type | Description |
| ---------------------------------------- | ---------------- | ---------------------------------------------------------------------------------- |
| `session_id` | string | Unique session identifier |
| `cwd` | string | Current working directory |
| `model.id` | string | Current model identifier |
| `context_window.context_window_size` | number | Total context window size |
| `context_window.last_prompt_token_count` | number | Tokens used in the last prompt |
| `vim` | object \| absent | Present only when vim mode is enabled. Contains `mode` (`"INSERT"` or `"NORMAL"`). |
| 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)`.
@ -94,7 +136,7 @@ The command receives a JSON object via stdin with the following fields:
"ui": {
"statusLine": {
"type": "command",
"command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.id'); tokens=$(echo \"$input\" | jq -r '.context_window.last_prompt_token_count'); size=$(echo \"$input\" | jq -r '.context_window.context_window_size'); pct=$((tokens * 100 / (size > 0 ? size : 1))); echo \"$model ctx:${pct}%\""
"command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.display_name'); pct=$(echo \"$input\" | jq -r '.context_window.used_percentage'); echo \"$model ctx:${pct}%\""
}
}
}
@ -109,7 +151,7 @@ Output: `qwen-3-235b ctx:34%`
"ui": {
"statusLine": {
"type": "command",
"command": "branch=$(git branch --show-current 2>/dev/null); dir=$(basename \"$PWD\"); echo \"$dir${branch:+ ($branch)}\""
"command": "input=$(cat); branch=$(echo \"$input\" | jq -r '.git.branch // empty'); dir=$(basename \"$(echo \"$input\" | jq -r '.workspace.current_dir')\"); echo \"$dir${branch:+ ($branch)}\""
}
}
}
@ -117,7 +159,22 @@ Output: `qwen-3-235b ctx:34%`
Output: `my-project (main)`
> Note: `git` and `pwd` run in the workspace directory automatically.
> 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`
### Script file for complex commands
@ -126,18 +183,17 @@ For longer commands, save a script file at `~/.qwen/statusline-command.sh`:
```bash
#!/bin/bash
input=$(cat)
model=$(echo "$input" | jq -r '.model.id')
tokens=$(echo "$input" | jq -r '.context_window.last_prompt_token_count')
size=$(echo "$input" | jq -r '.context_window.context_window_size')
branch=$(git branch --show-current 2>/dev/null)
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)")
if [ "$tokens" -gt 0 ] && [ "$size" -gt 0 ] 2>/dev/null; then
pct=$((tokens * 100 / size))
parts+=("ctx:${pct}%")
fi
[ "$pct" != "0" ] 2>/dev/null && parts+=("ctx:${pct}%")
([ "$added" -gt 0 ] || [ "$removed" -gt 0 ]) 2>/dev/null && parts+=("+${added}/-${removed}")
echo "${parts[*]}"
```
@ -157,7 +213,7 @@ Then reference it in settings:
## Behavior
- **Update triggers**: The status line updates when the model changes, a new message is sent (token count changes), or vim mode is toggled. Updates are debounced (300ms).
- **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**: Only the first line of stdout is used. The text is rendered with dimmed colors and truncated to terminal width.
- **Hot reload**: Changes to `ui.statusLine` in settings take effect immediately — no restart required.
@ -166,9 +222,9 @@ Then reference it in settings:
## 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 '{"model":{"id":"test"},"cwd":"/tmp","context_window":{"context_window_size":1,"last_prompt_token_count":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 |
| 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 |