-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat(ui): add customizable status line with /statusline command #2923
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
6784f0c
feat(ui): add customizable status line
wenshao 8d85492
feat(ui): rewrite customizable status line
wenshao 959690b
fix: regenerate settings.schema.json via generate:settings-schema
wenshao be13adb
fix: add SettingsContext to Footer tests
wenshao c219f7c
fix: address review feedback from Copilot
wenshao 24251db
fix: track vimEnabled changes in status line triggers
wenshao 1a985bb
fix: exec cwd, output trimming, and status line alignment
wenshao 4c4e638
fix: kill child process when statusLine config is removed
wenshao 9bba05b
fix: add ASK_USER_QUESTION to statusline-setup agent, clear debounce …
wenshao b1af941
docs: add status line user documentation
wenshao 12e1ef4
docs: add prerequisites, hot-reload note, fix troubleshooting test JSON
wenshao 813d863
docs: guard division by zero in script example
wenshao 5b9c94b
docs: fix jsonc trailing commas that break settings parser
wenshao 3aa246a
docs: quote $input in inline command examples
wenshao e4e3c21
fix: handle PS1 newlines in statusline-setup agent prompt
wenshao f67c9c5
fix: clarify footer comment and add Windows shell note to docs
wenshao f807118
docs: use sh -c in troubleshooting test command
wenshao 0e9c361
fix: use explicit Agent tool wording in /statusline prompt
wenshao 51964fa
Merge remote-tracking branch 'origin/main' into feature/status-line-c…
wenshao 7902806
docs: add ui.statusLine entry to settings reference
wenshao 24a28d5
refactor(status-line): redesign JSON input schema and add context fields
BZ-D c369538
fix(test): add missing metrics and model fields to Footer test mock
wenshao 520ed4e
fix: address audit findings across status-line and verbose-mode features
wenshao 0be4d32
Merge remote-tracking branch 'origin/main' into feature/status-line-c…
wenshao 55b1ab1
fix(status-line): derive remaining_percentage from used and reject em…
wenshao fc7ac2a
fix(statusline-setup): clarify agent prompt for script execution and …
wenshao 841eb3c
fix: address reviewer feedback — stdin error logging, JSON schema, i18n
wenshao 7804946
refactor(footer): inline status line in footer left section
wenshao a1c33cd
refactor(status-line): remove padding config
wenshao eaaa553
fix(footer): prevent status line from pushing right items off screen
wenshao f9b88c8
fix(footer): use wrap instead of truncate for status line text
wenshao cf879f0
refactor(footer): match upstream layout — status line + hints coexist
wenshao 50bf5cc
fix(footer): truncate hints/mode row to prevent extra lines
wenshao 63a14ae
fix(footer): remove Box wrapper from indicators for proper truncation
wenshao 2a28132
fix(footer): suppress hint when status line active, hide on exit prompts
wenshao bcd0b5e
docs: update status line documentation to reflect inline footer layout
wenshao 55116f8
fix: revert verbose mode hint to original descriptive text
wenshao 36aadd7
fix: restore i18n keys for 'Press Ctrl+O to show full tool output'
wenshao f25fc04
test: add comprehensive tests for useStatusLine hook and statuslineCo…
wenshao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| # 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. | ||
|
|
||
| ``` | ||
| With status line (default approval mode — 1 row): | ||
| ┌─────────────────────────────────────────────────────────────────┐ | ||
| │ user@host ~/project (main) ctx:34% 🔒 docker | Debug | 67% │ ← status line | ||
| └─────────────────────────────────────────────────────────────────┘ | ||
|
|
||
| With status line + non-default mode (2 rows): | ||
| ┌─────────────────────────────────────────────────────────────────┐ | ||
| │ user@host ~/project (main) ctx:34% 🔒 docker | Debug | 67% │ ← status line | ||
| │ 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`](https://jqlang.github.io/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}%\"" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| | Field | Type | Required | Description | | ||
| | --------- | ----------- | -------- | ------------------------------------------------------------------------------------- | | ||
| | `type` | `"command"` | Yes | Must be `"command"` | | ||
| | `command` | string | Yes | Shell command to execute. Receives JSON via stdin, first line of stdout is displayed. | | ||
|
|
||
| ## 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" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| | 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 (0–100) | | ||
| | `context_window.remaining_percentage` | number | Context window remaining as percentage (0–100) | | ||
| | `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 | ||
|
|
||
| ```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}%\"" | ||
| } | ||
wenshao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| ``` | ||
|
|
||
| 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` | ||
|
|
||
| ### 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). | ||
| - **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 in the footer's left section and truncated if it exceeds the available width. | ||
| - **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 | | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.