* fix(cli): stream long responses into scrollback to stop scroll-to-top lock ## Problem In non-VP (default) mode, scrolling up while the model streams a long reply — especially one containing a markdown table — jumps the viewport to the very top and locks it there until the response finishes (issue #5941). Root cause: when the live (below-`<Static>`) frame grows taller than the terminal, ink can no longer do its incremental cursor-up redraw and falls back to clearing + repainting the whole frame from the top on every token. A markdown table renders ~2 rows per data row (TableRenderer draws a separator between every row), so #6081's source-line budget under-counted the rendered height and the frame still overflowed for tables / wide CJK text. ## Fix Incremental scrollback streaming + a rendered-height safety net: - useGeminiStream: commit finished chunks of the streaming reply into `<Static>` (scrollback) so the pending live item stays short. The commit is rendered-height-aware (tables count double, wide/CJK lines wrap) and bounded by the live content-area height (threaded via `availableTerminalHeightRef`), with a reserve so it fires before the render-side clip. It commits in a `while` loop and splits only at `findLastSafeSplitPoint` boundaries (never inside a fenced code block). - MarkdownDisplay: a rendered-height-aware slice of the pending preview as a last line of defence — it guarantees the live frame never exceeds the viewport regardless of how the stream is chunked (tables charged at ~2x; non-table lines charged their wrapped height). A completed table renders in full; a table still being written renders live and is clamped by TableRenderer's new `maxHeight`. Result: long replies (and tables) flow smoothly into scrollback, tables draw live, and the viewport never locks to the top. Refs #5941, #6081 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): address review — share rendered-height estimator + guard edges Follow-up to review feedback on the incremental-scrollback streaming fix: - Extract a shared rendered-height estimator (`pendingRenderedHeight.ts`: `fitPendingSlice` / `estimateWrappedRows` / `isTableStart`) and use it from BOTH the useGeminiStream commit and the MarkdownDisplay safety-net slice, so the two agree on table (block: 2*dataRows + chrome) and wrap accounting instead of diverging. - useGeminiStream: use a conservative content-area fallback (terminalHeight minus a composer reserve) when `availableTerminalHeightRef` is not yet populated, so a short terminal never commits with an over-large budget. - MarkdownDisplay: allow the pending slice to keep 0 lines — a single very wide / CJK line that wraps past the budget now renders only the "generating more" cue instead of one oversized row that would bypass the height bound. - Add missing `useCallback` deps (terminalWidth / terminalHeight / availableTerminalHeightRef) — fixes the CI ESLint failure. - Tests: unit tests for the shared estimator (table detection, zero/negative width, CJK wrapping, cut-before / clamp / keptLines=0 boundaries) and for TableRenderer's `maxHeight` clamp (fit, clip+cue, vertical fallback, undefined passthrough). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): rename shared util to kebab-case to satisfy check-file lint The new pendingRenderedHeight.{ts,test.ts} tripped the check-file/filename-naming-convention (KEBAB_CASE) ESLint rule on new files in packages/cli/src. Rename to pending-rendered-height.{ts,test.ts} and update imports. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): point imports at renamed pending-rendered-height module The previous rename commit landed the file rename but not the importer edits (a stale pathspec aborted the git add), leaving MarkdownDisplay, useGeminiStream and the test importing the old ./pendingRenderedHeight.js path — a module-not- found in CI. Update the imports to the kebab-case path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): drop unused eslint-disable directive on while(true) reportUnusedDisableDirectives + --max-warnings 0 flags the no-constant-condition disable as an unused directive (the rule doesn't flag while(true) here). Remove it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): unify table parsing in shared module + cover commit edge cases Address the second review pass: - Move splitMarkdownTableRow and the table regexes (TABLE_ROW_RE / TABLE_SEPARATOR_RE) into pending-rendered-height.ts as the single source of truth; MarkdownDisplay now imports them instead of keeping duplicate copies. - isTableStart now also checks the separator's column count matches the header (mirroring the renderer's table detection) so the height estimator and the renderer agree on what is a table. - Tests: shared-module coverage for splitMarkdownTableRow and the isTableStart column-count check; tighten the incremental-commit assertion (budget-relative, requires multiple commits); add coverage for the splitPoint<=0 loop-break guard and for the populated-availableTerminalHeightRef production path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): commit streaming chunks only at block boundaries (no split tables) The incremental scrollback commit could cut a markdown table mid-way (e.g. when the tail row was still streaming): the committed chunk kept the header+rows and rendered as a table, but the continuation started with headerless `| ... |` rows that render as raw text (visible orphaned rows below a table). Only commit at a blank-line block boundary. A table (or list / code block) has no internal blank line, so it is never split into a headerless continuation; a still-streaming table stays pending — bounded in view by MarkdownDisplay's clamp — until it is complete, then commits whole. Tests: the oversized-commit test now uses blank-line-separated content and asserts every committed chunk ends at a block boundary; add a regression test that a streaming table taller than the budget is never committed as a headerless fragment (its header stays with its rows in the pending item). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): don't treat code-fence content as a table in the height estimator fitPendingSlice called isTableStart on every line regardless of fenced-code- block state, so table-like lines inside a ``` block were charged as a table (2*dataRows + chrome) while MarkdownDisplay renders them as code (one row each). Track the code fence and charge fenced lines individually. Share CODE_FENCE_RE from the module (MarkdownDisplay now imports it too) to keep a single source of truth. Adds a unit test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): account for vertical-format table height + tilde code fences Address the third review pass: - (Critical) fitPendingSlice charged the horizontal table height (2*dataRows+5) only, but TableRenderer falls back to the vertical key-value format on a narrow terminal / when cells wrap tall, which is much taller for 3+ column tables. Charge the larger of the horizontal and vertical estimates (dataRows*colCount + separators + marginY), still capped by the clamp — under-charging could let a vertical-format table overflow the viewport and re-introduce the scroll lock. - findLastSafeSplitPoint only recognised triple-backtick fences while the estimator's CODE_FENCE_RE also matches ~~~; a ~~~ block with an internal blank line could be split mid-block. isIndexInsideCodeBlock / findEnclosingCodeBlockStart now track both fence types (matching by fence character). - Tests: vertical-format table cost, ~~~ fence tracking, inline math and multi-backtick spans in splitMarkdownTableRow, and a ~~~ split-safety case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): only charge vertical-format table height on a narrow terminal The prior fix charged the max of the horizontal and vertical table estimates unconditionally, which over-estimated a table's height on a wide terminal (where it actually renders in the shorter horizontal format) and clipped small tables early with a premature "generating more". Mirror TableRenderer's width-based vertical decision (contentWidth < max(24, 6*colCount + 5)) and charge the format it will actually render: horizontal when the terminal is wide enough, vertical only when narrow — so a narrow-terminal vertical render still can't overflow and lock, but a small table on a wide terminal is no longer clipped prematurely. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): fix phantom code fences + read commit width from a live ref Address the fourth review pass: - (Critical) isIndexInsideCodeBlock / findEnclosingCodeBlockStart used indexOf('```', ...) which matches only the first three characters of a longer fence run, so a 4+ backtick/tilde fence was miscounted as two delimiters (phantom close-then-reopen). That could mark a blank line inside a code block as outside it, letting findLastSafeSplitPoint split mid-block and commit an unclosed code block to scrollback. findNextFence now returns the full run length, callers advance past the whole run, and a fence only closes a block opened with the same character and a run at least as long. - The commit loop read height live from availableTerminalHeightRef but width from the render-time closure, so a mid-stream resize handled the two inconsistently. Pair a terminalWidthRef with the height ref and read both live. Tests: a 6-backtick fenced block is not split at its internal blank line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .github | ||
| .husky | ||
| .qwen | ||
| .vscode | ||
| docs | ||
| docs-site | ||
| eslint-rules | ||
| integration-tests | ||
| packages | ||
| patches | ||
| scripts | ||
| .dockerignore | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .npmrc | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc.json | ||
| .yamllint.yml | ||
| AGENTS.md | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| Dockerfile | ||
| esbuild.config.js | ||
| eslint.config.js | ||
| eslint.legacy-filenames.mjs | ||
| LICENSE | ||
| Makefile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SECURITY.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
The open-source AI coding agent that lives in your terminal.
中文 | Deutsch | français | 日本語 | Русский | Português (Brasil)
Why Qwen Code?
- Agentic out of the box — Auto-Memory, Auto-Skills, SubAgents, Agent Teams, and MCP. Dynamic workflows, zero setup.
- Open-source, inside and out — The framework and the Qwen models are open-source. They evolve together. No vendor lock-in.
- Multi-protocol — Supports OpenAI, Anthropic, Gemini, and Qwen APIs. Any third-party provider or local model (Ollama / vLLM). Switch at runtime.
- Beyond the terminal — IDE plugins, Desktop app, daemon mode, SDKs, and IM bots (Telegram / DingTalk / WeChat / Feishu).
Tip
Qwen Code is actively iterating on itself — using its own agent and models to file issues, submit PRs, review code, and run tests. Powered by the community, driven by AI.
Installation
Linux / macOS:
curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.sh | bash
Windows:
irm https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.ps1 | iex
Restart your terminal after installation to ensure environment variables take effect.
NPM / Homebrew
NPM (requires Node.js 22+):
npm install -g @qwen-code/qwen-code@latest
Homebrew (macOS / Linux):
brew install qwen-code
Quick Start
qwen # Launch interactive terminal UI
# Inside the session:
/auth # Configure your provider and API key
See the Authentication Guide and Settings Reference for detailed setup.
How to Use Qwen Code
| Mode | Command | Use Case |
|---|---|---|
| Interactive | qwen |
Terminal UI with rich rendering, @file references, slash commands |
| Headless | qwen -p "..." |
Scripts, CI/CD, batch processing — no UI |
| IDE | — | VS Code, Zed, JetBrains |
| Desktop | — | Qwen Code Desktop — GUI for macOS, Windows, Linux |
| Daemon | qwen serve |
Shared agent session over HTTP+SSE (ACP). Multiple clients, one agent. (experimental) Docs |
| SDK | — | TypeScript, Python, Java |
| IM Bot | qwen channel |
Connect to Telegram, DingTalk, WeChat, or Feishu |
SDK example (Python)
import asyncio
from qwen_code_sdk import is_sdk_result_message, query
async def main() -> None:
result = query(
"Summarize the repository layout.",
{
"cwd": "/path/to/project",
"path_to_qwen_executable": "qwen",
},
)
async for message in result:
if is_sdk_result_message(message):
print(message["result"])
asyncio.run(main())
Capabilities
If you know Claude Code, you already know Qwen Code — and then some. We've put significant effort into bringing Qwen Code to feature parity with Claude Code, improving both breadth and reliability across the board.
| Feature | Qwen Code | Claude Code |
|---|---|---|
| SubAgents, Agent Teams, Dynamic Workflows | ✓ | ✓ |
| Auto-Memory, Auto-Skills, Hooks | ✓ | ✓ |
| Built-in Skills (/review, /batch, /loop, /bugfix…) | ✓ | ✓ |
| MCP, Plan Mode, LSP Integration | ✓ | ✓ |
| Auto Mode, Sandbox, Git Worktrees | ✓ | ✓ |
| Computer Use (desktop automation) | ✓ | ✓ |
| IDE Plugins (VS Code / JetBrains / Zed) | ✓ | ✓ |
| SDK | ✓ | ✓ |
| Headless Mode, Session Management | ✓ | ✓ |
| Open-source — model and framework | ✓ | — |
| Multi-protocol (OpenAI / Anthropic / Gemini / Qwen + any provider) | ✓ | — |
| Agent Arena (multi-model head-to-head on same task) | ✓ | — |
Daemon Mode — qwen serve (multi-client shared agent) |
✓ | — |
| IM Channels (Telegram / DingTalk / WeChat / Feishu) | ✓ | — |
Ecosystem
-
Qwen Code Desktop — Official desktop app for macOS, Windows, and Linux
-
AionUi — A modern GUI for command-line AI tools including Qwen Code
-
Gemini CLI Desktop — A cross-platform desktop/web/mobile UI for Qwen Code
-
🦞 Qwen Code Claw — Let other agents (Claude, Codex, etc.) delegate coding tasks to Qwen Code via ACP. Paste this prompt into your agent:
Use Qwen Code as a coding agent. First, fetch and read this skill reference:
https://raw.githubusercontent.com/QwenLM/qwen-code/refs/heads/main/.qwen/skills/qwen-code-claw/SKILL.md
Then follow the instructions inside to install, authenticate, and use Qwen Code via acpx for all coding tasks.
Contributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
Acknowledgments
This project was originally based on Google Gemini CLI v0.8.2. We gratefully acknowledge the Gemini CLI team's excellent work. Starting from Qwen Code v0.1, we stopped syncing with upstream and began independent development as a multi-protocol, multi-platform agent framework with deep integrations for Qwen models and beyond.
