* feat(cli): replace inline AgentExecutionDisplay with always-on LiveAgentPanel
Surface running subagents in a borderless, always-on roster anchored
beneath the input footer (mirrors Claude Code's CoordinatorTaskPanel)
and retire the verbose inline `AgentExecutionDisplay` frame whose
per-tool-call mutations caused scrollback flicker. Detail / cancel /
resume keep flowing through the existing BackgroundTasksDialog.
LiveAgentPanel:
- Two-column row: status icon + (optional) type + description +
activity on the left (truncate-end), elapsed + tokens on the right
in a flex-shrink:0 column so the cost/time fields are never hidden
by long descriptions.
- Re-pulls each agent from BackgroundTaskRegistry on every wall-clock
tick so `recentActivities` stays fresh — the snapshot from
`useBackgroundTaskView` only refreshes on `statusChange` to keep
the footer pill / AppContainer quiet under heavy tool traffic.
- Reaches for Config via raw ConfigContext (not useConfig) so the
panel degrades to snapshot-only when no provider is mounted (test
isolation).
- Hides when any dialog is visible (auth / permission / bg tasks)
and self-hides when no agent entries are live.
- Drops `subagentType` from the row when it is the default
`general-purpose` builtin to keep the line uncluttered; specialized
types still bold-anchor the row.
- Keeps terminal entries on screen for 8s so the user gets feedback
when an agent finishes, then they fall off (BackgroundTasksDialog
retains them long-term).
Inline frame retirement:
- ToolMessage's SubagentExecutionRenderer collapses to the focus-
routed approval surfaces only (focus-holder banner + queued
marker). All other agent state is owned by the panel + dialog.
- AgentExecutionDisplay.tsx + test removed (-918 lines); the
subagents/index export is dropped with a pointer to the new
surfaces.
Net diff: +97 / -1069.
* fix(cli): move elapsed + tokens to the front of LiveAgentPanel rows
The two-column layout used `flex-grow:1` on the left description
column, which puffed it out to fill the row even when content was
short — leaving a visible gap between the description tail and the
right-pinned elapsed/tokens whenever the terminal was wider than the
content. Worse, the gap made it look like display space was being
wasted while the description still got truncated.
Move elapsed + tokens to the front of the row (right after the status
icon) so:
- Time and cost are pinned at a stable left position and are NEVER at
risk of being truncated, regardless of description / activity length.
- The row reads as one tight left-to-right line — no flex-grow, no
internal gap. On wide terminals the unused width sits at the row
tail (invisible), where it belongs.
- Description + activity become the truncatable tail; `truncate-end`
cuts only when the line genuinely overflows the panel width.
Also wrap the icon-plus-spaces span in a template literal so the two
spaces of breathing room after the glyph survive a prettier pass.
Verified at 60 / 100 / 200 cols: at 200 cols the row renders flush
with no trailing ellipsis and no internal gap; at 60 cols the time +
tokens stay at the front and the description tail truncates with `…`.
* fix(cli): switch LiveAgentPanel row to layout C (right-pinned, no flex-grow)
Iterating on the row layout based on visual review:
- Layout A (time first, single Text) put numbers ahead of identity,
which broke the natural left-to-right reading order.
- Layout B (right-pinned with flex-grow:1 on left) puffed the left
column out to fill the row, leaving a visible gap between the
description tail and the right-pinned elapsed when the terminal
was wider than the content.
Layout C keeps the right column flex-shrink:0 so elapsed + tokens are
never clipped, but DROPS flex-grow on the left so the two columns sit
side-by-side: empty slack falls off the row tail (invisible) instead
of opening a gap inside the row. Identity (type) and intent
(description / activity) read first, cost reads last — matching the
natural visual hierarchy. When the row overflows the panel width the
left column truncates with `…` mid-row, while elapsed + tokens stay
intact.
Verified at 60 / 100 / 200 cols — at 200 cols there is no internal
gap and no trailing ellipsis; at 60 cols time + tokens stay visible
on the right and the description / activity tail truncates with `…`.
* fix(cli): port Claude Code's bullet + arrow visual to LiveAgentPanel rows
Adopt the leaked CoordinatorTaskPanel visual conventions:
- `○` replaces `⊷` for live (running) slots — matches Claude Code's
use of `figures.circle` for the active-agent bullet, gives a
uniform list look across the running roster. Terminal states
keep distinct check / cross marks (✔ / ✖) so they're easy to
scan at a glance.
- `▶` separates the description from elapsed / tokens, mirroring
Claude's `PLAY_ICON` suffix marker.
- Activity is wrapped in `( ... )` so it reads as an annotation on
the description rather than a sibling field, and the type prefix
switches from ` · ` to `: ` (e.g. `editor: tighten import order`)
to match Claude's `name: description` pattern.
The two-column flex layout from layout C is preserved — left column
flex-shrink:1 with truncate-end, right column (` ▶ Ns · Nk tokens`)
flex-shrink:0 so elapsed + tokens are never clipped, regardless of
how long the description / activity grows. This is the one
intentional divergence from Claude's literal pattern, which puts
elapsed at the row tail without pinning and lets it disappear off
narrow terminals.
Verified at 60 / 100 / 200 cols: at 200 cols the row is flush with
no internal gap; at 60 cols the description / activity tail
truncates with `…` while elapsed + tokens stay visible on the right.
* fix(cli): widen LiveAgentPanel, drop [in turn] marker, point overflow at dialog
Three usability fixes from review:
1. Use `terminalWidth` instead of `mainAreaWidth`. The latter is
capped at 100 cols (intended for markdown / code where soft-wrap
matters), which on a 200-col terminal left half the screen empty
to the right of an already-truncating row. Live progress lines
have nothing to soft-wrap, so the panel wants the full width.
2. Drop the `[in turn]` foreground marker. The flavor distinction
matters in BackgroundTasksDialog (cancel semantics differ for
foreground vs background entries) but in the glance panel the
marker reads as cryptic noise — users asked what it meant. Keep
the dialog as the surface that surfaces it.
3. Annotate the overflow callout with `(↓ to view all)`. The panel
is intentionally read-only (it has no keyboard focus so it can't
steal input from the composer), so when the roster outgrows the
row budget we point users at the existing dialog — same keystroke
the footer pill uses, kept in sync so users only learn one
gesture.
* fix(cli): make Down on focused BackgroundTasksPill open the dialog
The focus chain Composer → AgentTabBar → BackgroundTasksPill is
walked with the Down arrow, but Down dead-ended at the pill — the
pill only opened the dialog on Enter. Users who followed the
LiveAgentPanel's "(↓ to view all)" overflow callout reached the
highlighted pill and got stuck there, defeating the hint.
Route Down on the focused pill into openDialog so the chain
completes naturally: Composer ↓ → AgentTabBar ↓ → Pill ↓ → Dialog.
Enter still works, so existing muscle memory keeps functioning.
* fix(cli): address Copilot review on LiveAgentPanel — interval gate, ghost rows, dead doc
Three findings from Copilot's PR review:
1. **1s interval kept ticking forever after expiry.** The gate was
`entries.some(isAgentEntry)`, but `BackgroundTaskRegistry.getAll()`
retains terminal entries indefinitely — once the last visible row
passed its 8s window the panel returned null but the interval kept
firing setNow each second, churning re-renders for nothing on
screen. The gate now considers visibility (running / paused OR
terminal-within-window) and the interval clears itself once the
condition flips false. New entries restart the interval via the
`entries` dep.
2. **Ghost rows when registry forgets an entry.** The live re-pull
fell back to the snapshot when `registry.get()` returned
undefined. The canonical case is a foreground subagent that
unregisters silently after its statusChange fires
(`unregisterForeground` deletes without emitting a follow-up
transition) — the snapshot still says `running`, so the row
would never clear. Trust the registry: when it says the entry
is gone, drop the row. The snapshot-only fallback is preserved
for the no-Config case (test fixtures).
3. **Dead doc reference.** The trailing comment in `subagents/index.ts`
pointed at `docs/comparison/subagent-display-deep-dive.md`, which
doesn't exist in this repo (it lives in the codeagents knowledge
base, not qwen-code). Dropped the dead pointer; the in-tree
pointers to `LiveAgentPanel` and `BackgroundTasksDialog` already
tell readers where to look.
Coverage delta: +2 cases on `LiveAgentPanel.test.tsx` — `drops
snapshot rows the live registry no longer knows about` (issue #2)
and `still shows the snapshot when no Config is mounted` (locks in
the test-fixture fallback that issue #2's stricter rule would
otherwise have broken).
* fix(cli): address Copilot second-pass review — dialogOpen tick gate, isFocused doc
Two further findings from Copilot:
1. **Interval kept ticking while bg-tasks dialog was open.** The
first-pass fix already torn the tick down once all rows expired
from the visibility window, but `dialogOpen` was a separate
reason the panel returned null and was missed by the gate. Add
`dialogOpen` to the useEffect deps and short-circuit when true,
so the dialog's tenure is interval-free.
2. **Stale `isFocused` doc comment in `ToolMessageProps`.** The
comment claimed the prop controlled the now-retired `Ctrl+E /
Ctrl+F` display shortcuts (those died with the inline
`AgentExecutionDisplay` frame). Rewrite the comment to describe
the only remaining behavior — the focus-routed approval surface
(focus-holder banner vs. queued-sibling marker).
Coverage delta: +1 case on `LiveAgentPanel.test.tsx` — `tears the
1s tick down when the bg-tasks dialog opens` advances 60s of fake
time with `dialogOpen=true` and asserts no panel state drift.
* fix(cli): reconcile registry-missing snapshots as just-finished + address review nits
Hot-fix: the previous round's "drop the row when registry.get()
returns undefined" was too aggressive. `unregisterForeground` calls
`emitStatusChange(entry)` BEFORE it deletes the entry, so the
snapshot useBackgroundTaskView captures still says "running" while
the very next render's registry.get sees nothing. Dropping the row
outright made foreground subagents disappear from the panel the
instant they finished — users saw "SubAgents 不显示了" on tasks that
ran-and-immediately-completed.
Reconciliation now has three branches:
1. live found → use live (newest recentActivities).
2. snap says still-live but registry forgot → synthesize a
terminal version with endTime pinned to the FIRST observation
so the 8s visibility window gives the user a "the agent
finished" beat then evicts cleanly. The first-seen-missing
timestamp is held in a useRef map (without it, each tick
resets endTime to `now` and the row never expires).
3. snap is already terminal but registry forgot → drop (no
useful state to keep showing).
Also addresses three smaller review notes (deepseek-v4-pro via
/review):
- DEFAULT_SUBAGENT_TYPE is now imported from
@qwen-code/qwen-code-core (a new DEFAULT_BUILTIN_SUBAGENT_TYPE
export referenced by both BuiltinAgentRegistry's seed entry and
the panel's default-type elision). A backend rename now propagates
instead of silently re-introducing the redundant
`general-purpose:` prefix on every row.
- The useMemo body now reads `now` (as `reconcileAt`) so the
dependency is semantically honest — a future "remove dead dep"
cleanup can no longer silently freeze the panel on the first
tool-call after a snapshot refresh.
Coverage delta: +5 cases on LiveAgentPanel.test.tsx — token rendering
on completed entries, status-icon routing for paused / failed /
cancelled (parametrized), case-insensitive prefix stripping in
descriptionWithoutPrefix, plus the rewritten ghost-row case
(synthesized terminal lingers 8s then evicts) and a sibling case
asserting already-terminal snapshots with empty registry still drop.
17 LiveAgentPanel tests pass.
* refactor(cli): split LiveAgentPanelBody + drop dead isPending/isWaitingForOtherApproval props
Two structural reviews from deepseek-v4-pro via /review:
1. **Hook-order footgun.** The `if (dialogOpen) return null` guard
sat between the hook block and a substantial block of pure-render
code; an extension that added `useMemo`/`useRef`/`useCallback`
below the guard would crash with "Rendered fewer hooks than
expected" the next time `dialogOpen` toggled. Extract the pure-
render logic into `LiveAgentPanelBody` so the guard becomes the
parent's last statement, and any future "add a hook to the body"
refactor naturally lands in the inner component (hook-free
today, hook-free for as long as it stays a presentational FC).
2. **Dead pass-through removed.** `isPending` and
`isWaitingForOtherApproval` were dead in the subagent renderer
(LiveAgentPanel + BackgroundTasksDialog own that surface) but
ToolGroupMessage still computed `isWaitingForOtherApproval` and
forwarded both into ToolMessage. Drop them from
`ToolMessageProps`, drop the computation + forwarding in
ToolGroupMessage, drop the test factory references. ToolGroupMessage
keeps `isPending` on its own props for upstream caller compatibility
(HistoryItemDisplay et al. forward it) but stops destructuring
it; the doc comment now points at the surfaces that own that
gating today.
Coverage: existing 115 cases across LiveAgentPanel /
BackgroundTasksDialog / BackgroundTasksPill / ToolMessage /
ToolGroupMessage all green; the panel split is purely structural
and the dead-prop removal was verified TS-clean before commit.
* fix(cli): map internal tool names to user-facing display names in LiveAgentPanel rows
`recentActivities[].name` carries the internal tool name from
AgentToolCallEvent (e.g. `run_shell_command`, `glob`). Rendering it
verbatim surfaced raw identifiers in the panel
(`run_shell_command rg TODO`) while BackgroundTasksDialog already
mapped through ToolDisplayNames to show user-facing names (`Shell rg
TODO`) — the two surfaces' vocabularies drifted on the same data.
Mirror the dialog's `TOOL_DISPLAY_BY_NAME` lookup so the panel and
dialog speak the same vocabulary. Added a test asserting
`run_shell_command` renders as `Shell` and the raw name is not
surfaced.
Coverage delta: +1 case on LiveAgentPanel.test.tsx (18 total).
* fix(cli): keep already-terminal snapshots visible until TTL + close 4 test gaps
1. **Terminal snap + missing registry now follows the visibility
window.** Cancelled / failed foreground subagents go through
`cancel`/`fail` (which stamp `endTime` and emit statusChange)
followed by `unregisterForeground` (which deletes silently). The
snap captures the real `endTime`, so the previous "drop on
missing registry" branch made cancelled / failed foreground
tasks disappear instantly — contradicting the panel's "brief
terminal visibility" contract that the synthesized-completion
path also relies on. Keep the snap as-is when `endTime` is set;
the visibleAgents filter evicts it after `TERMINAL_VISIBLE_MS`
like any other terminal entry. Defensive fallback drops the
pathological "terminal status with no endTime" shape.
(Copilot finding on PRRT_kwDOPB-92c6AS4z9.)
2. **Close 4 test gaps flagged by tanzhenxin's review:**
- `elides the default 'general-purpose' subagent type from the row`
locks in DEFAULT_BUILTIN_SUBAGENT_TYPE comparison.
- `truncates the description tail when the panel width is too
narrow` exercises the `width` prop (existing cases ignored it)
and anchors on the right-pinned `▶ 3s` tail staying intact.
- `clears the 1s tick interval when unmounted with live work in
flight` spies on setInterval / clearInterval so a discarded
fiber can't keep firing setNow.
- `keeps terminal snapshots visible until the TTL even when the
registry forgot them` covers the cancelled / failed foreground
reconciliation path with a `✖` glyph + 9s eviction assertion.
Coverage: 22 LiveAgentPanel tests pass (was 18). All TS clean.
* refactor(cli): rename FOREGROUND_ROW_PREFIX from `[in turn]` to `[blocking]`
User feedback: `[in turn]` reads as "queued / sequential" — the
opposite of what it actually means (the row is blocking the user's
current turn). Even maintainers had to chase the source comment to
confirm the semantics, which is a strong signal that end users are
not getting the warning the prefix is supposed to deliver.
`[blocking]` reads more directly: "this row is what's holding up
your input", which is what cancelling it actually unblocks. Update
the in-row prefix and the cancel-confirmation hint in lockstep
(`x again to confirm stop · ends the blocking turn`) so the two
surfaces share vocabulary.
LiveAgentPanel still suppresses the marker in its row (the panel
has no cancel surface, so the warning has nothing actionable to
pair with); update the historical comment + reinforce the test
guard so neither the legacy `[in turn]` nor the new `[blocking]`
bleeds into the glance roster.
* fix(cli): address 4 review findings — height budget, neutral synthesis glyph, stale comments
1. **`availableTerminalHeight` regression** (claude-opus-4-7 via
/qreview, DefaultAppLayout.tsx:127). LiveAgentPanel rendered
OUTSIDE the `mainControlsRef` Box, so its 2-7 rows were not
measured by `controlsHeight` and not subtracted from
`availableTerminalHeight = terminalHeight - controlsHeight -
staticExtraHeight - 2 - tabBarHeight` in AppContainer. Pending
tool results in MainContent could render past the visible area
and push the composer / panel off-screen — a regression vs PR
#3768 which suppressed the inline frame in the live phase. Move
the panel INSIDE the `mainControlsRef` Box so `measureElement`
picks up its rows automatically; no new infrastructure needed.
2. **Neutral glyph for synthesized terminal rows** (claude-opus-4-7,
LiveAgentPanel.tsx:278). The synthesis branch hardcoded
`status: 'completed'` regardless of the actual outcome. Foreground
subagents that errored or were cancelled were rendered with the
green ✔ for 8s — directly contradicting the inline tool result
the user just saw (`Subagent execution failed.` /
`Agent was cancelled by the user.`). Add a `synthesized` flag
on the synthesized rows; `statusIcon` checks it first and
returns a neutral `·` + secondary color, so the panel never lies
about an outcome it cannot determine. Status stays `'completed'`
purely so the visibility-window filter treats the row as
terminal.
3. **PR description claim retired** (Copilot, ToolMessage.tsx:411).
The body's Reviewer Notes section claimed `isPending` /
`isWaitingForOtherApproval` were kept on `ToolMessageProps` as
vestigial pass-through; the props were actually removed in
commit
|
||
|---|---|---|
| .github | ||
| .husky | ||
| .qwen | ||
| .vscode | ||
| docs | ||
| docs-site | ||
| eslint-rules | ||
| integration-tests | ||
| packages | ||
| scripts | ||
| .dockerignore | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .npmrc | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc.json | ||
| .yamllint.yml | ||
| AGENTS.md | ||
| CONTRIBUTING.md | ||
| Dockerfile | ||
| esbuild.config.js | ||
| eslint.config.js | ||
| LICENSE | ||
| Makefile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SECURITY.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
An open-source AI agent that lives in your terminal.
中文 | Deutsch | français | 日本語 | Русский | Português (Brasil)
🎉 News
-
2026-04-15: Qwen OAuth free tier has been discontinued. To continue using Qwen Code, switch to Alibaba Cloud Coding Plan, OpenRouter, Fireworks AI, or bring your own API key. Run
qwen authto configure. -
2026-04-13: Qwen OAuth free tier policy update: daily quota adjusted to 100 requests/day (from 1,000).
-
2026-04-02: Qwen3.6-Plus is now live! Get an API key from Alibaba Cloud ModelStudio to access it through the OpenAI-compatible API.
-
2026-02-16: Qwen3.5-Plus is now live!
Why Qwen Code?
Qwen Code is an open-source AI agent for the terminal, optimized for Qwen series models. It helps you understand large codebases, automate tedious work, and ship faster.
- Multi-protocol, flexible providers: use OpenAI / Anthropic / Gemini-compatible APIs, Alibaba Cloud Coding Plan, OpenRouter, Fireworks AI, or bring your own API key.
- Open-source, co-evolving: both the framework and the Qwen3-Coder model are open-source—and they ship and evolve together.
- Agentic workflow, feature-rich: rich built-in tools (Skills, SubAgents) for a full agentic workflow and a Claude Code-like experience.
- Terminal-first, IDE-friendly: built for developers who live in the command line, with optional integration for VS Code, Zed, and JetBrains IDEs.
Installation
Quick Install (Recommended)
Linux / macOS
bash -c "$(curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh)"
Windows (Run as Administrator)
Works in both Command Prompt and PowerShell:
powershell -Command "Invoke-WebRequest 'https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.bat' -OutFile (Join-Path $env:TEMP 'install-qwen.bat'); & (Join-Path $env:TEMP 'install-qwen.bat')"
Note
: It's recommended to restart your terminal after installation to ensure environment variables take effect.
Manual Installation
Prerequisites
Make sure you have Node.js 20 or later installed. Download it from nodejs.org.
NPM
npm install -g @qwen-code/qwen-code@latest
Homebrew (macOS, Linux)
brew install qwen-code
Quick Start
# Start Qwen Code (interactive)
qwen
# Then, in the session:
/help
/auth
On first use, you'll be prompted to sign in. You can run /auth anytime to switch authentication methods.
Example prompts:
What does this project do?
Explain the codebase structure.
Help me refactor this function.
Generate unit tests for this module.
Click to watch a demo video
🦞 Use Qwen Code for Coding Tasks in Claw
Copy the prompt below and paste it 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.
Authentication
Qwen Code supports the following authentication methods:
- API Key (recommended): use an API key from Alibaba Cloud Model Studio (Beijing / intl) or any supported provider (OpenAI, Anthropic, Google GenAI, and other compatible endpoints).
- Coding Plan: subscribe to the Alibaba Cloud Coding Plan (Beijing / intl) for a fixed monthly fee with higher quotas.
⚠️ Qwen OAuth was discontinued on April 15, 2026. If you were previously using Qwen OAuth, please switch to one of the methods above. Run
qwenand then/authto reconfigure.
API Key (recommended)
Use an API key to connect to Alibaba Cloud Model Studio or any supported provider. Supports multiple protocols:
- OpenAI-compatible: Alibaba Cloud ModelStudio, ModelScope, OpenAI, OpenRouter, and other OpenAI-compatible providers
- Anthropic: Claude models
- Google GenAI: Gemini models
The recommended way to configure models and providers is by editing ~/.qwen/settings.json (create it if it doesn't exist). This file lets you define all available models, API keys, and default settings in one place.
Quick Setup in 3 Steps
Step 1: Create or edit ~/.qwen/settings.json
Here is a complete example:
{
"modelProviders": {
"openai": [
{
"id": "qwen3.6-plus",
"name": "qwen3.6-plus",
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"description": "Qwen3-Coder via Dashscope",
"envKey": "DASHSCOPE_API_KEY"
}
]
},
"env": {
"DASHSCOPE_API_KEY": "sk-xxxxxxxxxxxxx"
},
"security": {
"auth": {
"selectedType": "openai"
}
},
"model": {
"name": "qwen3.6-plus"
}
}
Step 2: Understand each field
| Field | What it does |
|---|---|
modelProviders |
Declares which models are available and how to connect to them. Keys like openai, anthropic, gemini represent the API protocol. |
modelProviders[].id |
The model ID sent to the API (e.g. qwen3.6-plus, gpt-4o). |
modelProviders[].envKey |
The name of the environment variable that holds your API key. |
modelProviders[].baseUrl |
The API endpoint URL (required for non-default endpoints). |
env |
A fallback place to store API keys (lowest priority; prefer .env files or export for sensitive keys). |
security.auth.selectedType |
The protocol to use on startup (openai, anthropic, gemini, vertex-ai). |
model.name |
The default model to use when Qwen Code starts. |
Step 3: Start Qwen Code — your configuration takes effect automatically:
qwen
Use the /model command at any time to switch between all configured models.
More Examples
Coding Plan (Alibaba Cloud ModelStudio) — fixed monthly fee, higher quotas
{
"modelProviders": {
"openai": [
{
"id": "qwen3.6-plus",
"name": "qwen3.6-plus (Coding Plan)",
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"description": "qwen3.6-plus from ModelStudio Coding Plan",
"envKey": "BAILIAN_CODING_PLAN_API_KEY"
},
{
"id": "qwen3.5-plus",
"name": "qwen3.5-plus (Coding Plan)",
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"description": "qwen3.5-plus with thinking enabled from ModelStudio Coding Plan",
"envKey": "BAILIAN_CODING_PLAN_API_KEY",
"generationConfig": {
"extra_body": {
"enable_thinking": true
}
}
},
{
"id": "glm-4.7",
"name": "glm-4.7 (Coding Plan)",
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"description": "glm-4.7 with thinking enabled from ModelStudio Coding Plan",
"envKey": "BAILIAN_CODING_PLAN_API_KEY",
"generationConfig": {
"extra_body": {
"enable_thinking": true
}
}
},
{
"id": "kimi-k2.5",
"name": "kimi-k2.5 (Coding Plan)",
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"description": "kimi-k2.5 with thinking enabled from ModelStudio Coding Plan",
"envKey": "BAILIAN_CODING_PLAN_API_KEY",
"generationConfig": {
"extra_body": {
"enable_thinking": true
}
}
}
]
},
"env": {
"BAILIAN_CODING_PLAN_API_KEY": "sk-xxxxxxxxxxxxx"
},
"security": {
"auth": {
"selectedType": "openai"
}
},
"model": {
"name": "qwen3.6-plus"
}
}
Subscribe to the Coding Plan and get your API key at Alibaba Cloud ModelStudio(Beijing) or Alibaba Cloud ModelStudio(intl).
Multiple providers (OpenAI + Anthropic + Gemini)
{
"modelProviders": {
"openai": [
{
"id": "gpt-4o",
"name": "GPT-4o",
"envKey": "OPENAI_API_KEY",
"baseUrl": "https://api.openai.com/v1"
}
],
"anthropic": [
{
"id": "claude-sonnet-4-20250514",
"name": "Claude Sonnet 4",
"envKey": "ANTHROPIC_API_KEY"
}
],
"gemini": [
{
"id": "gemini-2.5-pro",
"name": "Gemini 2.5 Pro",
"envKey": "GEMINI_API_KEY"
}
]
},
"env": {
"OPENAI_API_KEY": "sk-xxxxxxxxxxxxx",
"ANTHROPIC_API_KEY": "sk-ant-xxxxxxxxxxxxx",
"GEMINI_API_KEY": "AIzaxxxxxxxxxxxxx"
},
"security": {
"auth": {
"selectedType": "openai"
}
},
"model": {
"name": "gpt-4o"
}
}
Enable thinking mode (for supported models like qwen3.5-plus)
{
"modelProviders": {
"openai": [
{
"id": "qwen3.5-plus",
"name": "qwen3.5-plus (thinking)",
"envKey": "DASHSCOPE_API_KEY",
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"generationConfig": {
"extra_body": {
"enable_thinking": true
}
}
}
]
},
"env": {
"DASHSCOPE_API_KEY": "sk-xxxxxxxxxxxxx"
},
"security": {
"auth": {
"selectedType": "openai"
}
},
"model": {
"name": "qwen3.5-plus"
}
}
Tip: You can also set API keys via
exportin your shell or.envfiles, which take higher priority thansettings.json→env. See the authentication guide for full details.
Security note: Never commit API keys to version control. The
~/.qwen/settings.jsonfile is in your home directory and should stay private.
Local Model Setup (Ollama / vLLM)
You can also run models locally — no API key or cloud account needed. This is not an authentication method; instead, configure your local model endpoint in ~/.qwen/settings.json using the modelProviders field.
Set generationConfig.contextWindowSize inside the matching provider entry
and adjust it to the context length configured on your local server.
Ollama setup
- Install Ollama from ollama.com
- Pull a model:
ollama pull qwen3:32b - Configure
~/.qwen/settings.json:
{
"modelProviders": {
"openai": [
{
"id": "qwen3:32b",
"name": "Qwen3 32B (Ollama)",
"baseUrl": "http://localhost:11434/v1",
"description": "Qwen3 32B running locally via Ollama",
"generationConfig": {
"contextWindowSize": 131072
}
}
]
},
"security": {
"auth": {
"selectedType": "openai"
}
},
"model": {
"name": "qwen3:32b"
}
}
vLLM setup
- Install vLLM:
pip install vllm - Start the server:
vllm serve Qwen/Qwen3-32B - Configure
~/.qwen/settings.json:
{
"modelProviders": {
"openai": [
{
"id": "Qwen/Qwen3-32B",
"name": "Qwen3 32B (vLLM)",
"baseUrl": "http://localhost:8000/v1",
"description": "Qwen3 32B running locally via vLLM",
"generationConfig": {
"contextWindowSize": 131072
}
}
]
},
"security": {
"auth": {
"selectedType": "openai"
}
},
"model": {
"name": "Qwen/Qwen3-32B"
}
}
Usage
As an open-source terminal agent, you can use Qwen Code in four primary ways:
- Interactive mode (terminal UI)
- Headless mode (scripts, CI)
- IDE integration (VS Code, Zed)
- SDKs (TypeScript, Python, Java)
Interactive mode
cd your-project/
qwen
Run qwen in your project folder to launch the interactive terminal UI. Use @ to reference local files (for example @src/main.ts).
Headless mode
cd your-project/
qwen -p "your question"
Use -p to run Qwen Code without the interactive UI—ideal for scripts, automation, and CI/CD. Learn more: Headless mode.
IDE integration
Use Qwen Code inside your editor (VS Code, Zed, and JetBrains IDEs):
SDKs
Build on top of Qwen Code with the available SDKs:
- TypeScript: Use the Qwen Code SDK
- Python: Use the Python SDK
- Java: Use the Java SDK
Python SDK example:
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())
Commands & Shortcuts
Session Commands
/help- Display available commands/clear- Clear conversation history/compress- Compress history to save tokens/stats- Show current session information/bug- Submit a bug report/exitor/quit- Exit Qwen Code
Keyboard Shortcuts
Ctrl+C- Cancel current operationCtrl+D- Exit (on empty line)Up/Down- Navigate command history
Learn more about Commands
Tip: In YOLO mode (
--yolo), vision switching happens automatically without prompts when images are detected. Learn more about Approval Mode
Configuration
Qwen Code can be configured via settings.json, environment variables, and CLI flags.
| File | Scope | Description |
|---|---|---|
~/.qwen/settings.json |
User (global) | Applies to all your Qwen Code sessions. Recommended for modelProviders and env. |
.qwen/settings.json |
Project | Applies only when running Qwen Code in this project. Overrides user settings. |
The most commonly used top-level fields in settings.json:
| Field | Description |
|---|---|
modelProviders |
Define available models per protocol (openai, anthropic, gemini, vertex-ai). |
env |
Fallback environment variables (e.g. API keys). Lower priority than shell export and .env files. |
security.auth.selectedType |
The protocol to use on startup (e.g. openai). |
model.name |
The default model to use when Qwen Code starts. |
See the Authentication section above for complete
settings.jsonexamples, and the settings reference for all available options.
Benchmark Results
Terminal-Bench Performance
| Agent | Model | Accuracy |
|---|---|---|
| Qwen Code | Qwen3-Coder-480A35 | 37.5% |
| Qwen Code | Qwen3-Coder-30BA3B | 31.3% |
Ecosystem
Looking for a graphical interface?
- 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
Troubleshooting
If you encounter issues, check the troubleshooting guide.
Common issues:
Qwen OAuth free tier was discontinued on 2026-04-15: Qwen OAuth is no longer available. Runqwen→/authand switch to API Key or Coding Plan. See the Authentication section above for setup instructions.
To report a bug from within the CLI, run /bug and include a short title and repro steps.
Connect with Us
- Discord: https://discord.gg/RN7tqZCeDK
- Dingtalk: https://qr.dingtalk.com/action/joingroup?code=v1,k1,+FX6Gf/ZDlTahTIRi8AEQhIaBlqykA0j+eBKKdhLeAE=&_dt_no_comment=1&origin=1
Acknowledgments
This project is based on Google Gemini CLI. We acknowledge and appreciate the excellent work of the Gemini CLI team. Our main contribution focuses on parser-level adaptations to better support Qwen-Coder models.
