* fix(core): headless hook trust requires the folder-trust feature The reporter PoC runs headless (--output-format stream-json with a one-shot prompt), so the unconditional headless exemption left the reported execution path open on a fresh default environment. Require the operator to have enabled security.folderTrust.enabled before the workspace choice counts as implicit trust; with the feature off, project hooks are withheld in every mode. Embedders enable the feature in their managed home settings and keep working via their trust store. Verified locally against the reporter scenario: default fresh home -> marker absent; feature on + trust-store grant -> marker written. Refs #14746 Co-authored-by: Qwen-Coder <[email protected]> * refactor(core): unify the project-hook gate on explicit trust The headless exemption branch never changed an observable outcome: the CLI settings layer already drops workspace settings for an undecided folder once the feature is enabled, and with the feature off the explicit predicate is false anyway. Replace both branches with the single explicit-trust rule so interactive and non-interactive callers share one contract. Behavior is unchanged; verified against the reporter PoC (default fresh home withheld, trusted path executes). Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): address review findings on the unified hook trust gate R1-1: the headless feature-off test wrote folderTrust into workspace settings, where WORKSPACE_RESTRICTED_SETTINGS strips it — the rig user scope stayed enabled and the test never reached the named gate. Write the feature-off decision into the user-scope home file instead. R1-2: the docs sentence overclaimed; IDE-granted trust loads hooks with the feature off. State the predicate as shipped. R1-3: headless withholding was silent for the newly gated population. Emit one stderr line from the non-interactive entry and a debug-log warn from the registry when the trust gate is why the scope is absent (getTrustWithheldProjectHooks, diagnostics only). Verified: reporter PoC withheld with the stderr line present; core 1052 passed; tool-hook integration 14 passed. Co-authored-by: Qwen-Coder <[email protected]> * feat(web-shell): surface trust-withheld hooks in the workspace overview The hooks listing already marks why a project hook is disabled, but the ACP status serializer dropped the reason, so the web-shell overview only showed a count while trust-withheld hooks silently did not run. Carry disabledReason through ServeHookEntry/DaemonHookEntry (optional, older daemons unaffected), count untrusted hooks in the summary, flag the facet, and name the remedy in the tooltip (en + zh). Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): pin the trust-withheld hooks tooltip wording Component-level witness for the untrusted-hooks facet: the row shows the withheld count and the title names the remedy. The sidebar hooks facet is opt-in for embedding hosts, so the jsdom suite is the reachability-independent pin. Co-authored-by: Qwen-Coder <[email protected]> * test(cli): align fixtures with the surfaced hook state The workspace-hooks status test now expects the disabledReason the serializer carries, and the headless-warnings config stub provides the new getTrustWithheldProjectHooks accessor. Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): tolerate partial config stubs in the headless hook notice The resumed-transcript and stream-json housekeeping tests use minimal config stubs; optional-chain the two accessors the notice reads, and provide the accessor on the remaining isInteractive stubs. All 124 llm.test.tsx cases pass. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): make the withheld-hooks surfaces reachable and equivalent Review findings on the status surfaces: the listing never carried disabledReason untrusted for settings project hooks (the trust gate keeps them out of the registry, and only agent/session rows were trustGated), so the web-shell hint was unreachable; buildHooksListing now appends the diagnostics block as disabled (untrusted) rows, with a real-listing test. The headless stderr notice now mirrors the TUI predicate (raw workspace hooks non-empty, exclude disableAllHooks and safe/bare), covering the feature-on undecided case the Config accessor cannot see. The facet value stays language-neutral; the localized withheld count and remedy live in the tooltip. Core 1035, llm 124, web-shell sidebar 51 tests pass. Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): browser witness for the withheld-hooks status surface Real Chromium e2e: open Daemon Status via the rail More popover, switch to Diagnostics, and assert the workspace.hooks section renders with the withheld count from a report shaped like the real daemon output. Screenshot retained as test output. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): fold the deferred approval follow-ups into this PR Per review direction, land the #16295 items here instead of a follow-up: - Explicit projectHooksApproved state: a stored approval keeps execution authority inside a command-line --yolo session (previously the trust gate withheld it because the pending flag is undefined while exempt). The flag never doubles as nothing-to-approve; mid-session decisions update it. Tests pin yolo+approved executes, yolo+unapproved withholds. - Display-only raw workspace block: with the feature on and the folder untrusted the settings layer drops the workspace scope, so the listing could not show the withheld hooks. projectHooksDisplayOnly feeds only getTrustWithheldProjectHooks, never the registry; test pins both halves. - Variable-source wording: the dialog hint now says environment (project .env/settings.env or shell), not only the approved project env. Per-hook approval granularity stays out: it is a feature iteration, not a correctness fix. Core 1017+1053, cli config 565, approvals 50, llm 127 pass. Co-authored-by: Qwen-Coder <[email protected]> * style: prettier the reworded dialog hint Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): /hooks list shows withheld project hooks in ACP/web-shell The non-interactive /hooks list read only the registry, so an ACP session (web-shell) with unapproved project hooks answered \"No hooks configured\" — the user could not see what was withheld or why. The list now includes the approval/trust diagnostics blocks, marked disabled (not approved / rejected / folder not trusted). Verified against a real ACP session: the withheld SessionStart hook is listed with its reason. Tests pin all three reasons. Co-authored-by: Qwen-Coder <[email protected]> * feat(web-shell): explicit project-hooks approval prompt for withheld hooks Web Shell previously had no interactive surface for the project-hooks approval gate: users had to run /hooks manually. The daemon now reports the pending decision (with sanitized hook lines) on /workspace/hooks, including on a cold daemon and when folder trust withholds the workspace scope, and a dedicated POST /workspace/hooks/approve route (plus the workspace-qualified twin) records the same hash-bound decision the TUI dialog writes. The Web Shell renders an Approve / Keep-disabled banner from that state. This is deliberately not session/request_permission: automation clients auto-answer permission prompts, a trust decision must be an explicit user act, and approving never grants folder trust. Co-authored-by: Qwen-Coder <[email protected]> * refactor(web-shell): render hooks approval with the shared ToolApproval card Replace the custom full-width alert banner with the existing ToolApproval card in the standard approval slot above the composer, so the hooks trust decision looks and behaves like every other approval: same panel, option list, keyboard handling, and safe-default focus. The card supplies a project_hooks request with decision-specific option labels; ToolApproval localizes its heading and question for that tool name. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): hooks approval probe reads once and polls only while pending The approval card polled /workspace/hooks unconditionally, adding background metadata reads the workspace-overview quiet-period contract forbids. Read once per mount/workspace/settings change, poll only while a decision is pending, and let the E2E quiet-period test settle the initial probe before counting sidebar traffic. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): apply the persisted decision once and to every live session Local review + ponytail over the PR diff: - The ACP child no longer re-derives and re-writes the approval hash on the approve control. The daemon persists the decision for the exact block the user reviewed; a second write could bind it to a block that changed since the review. The child only applies the decision, to every live session config, not just the bootstrap one. - The raw-workspace review subject hashes the project env files regardless of current folder trust, so a decision recorded while untrusted still matches after the folder is trusted. - The headless trust warning no longer doubles with the approval-gate warning for a pending block; the registry debug-log duplicate and its interface member are removed (the stderr line and listings already report the withheld state). - Drop dead test stubs for an accessor llm.tsx never reads, a redundant type member, and a stray blank-line deletion. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): scope the approve decision to the admission-bound workspace The per-session fan-out matched every live config in the child. Key it on the admission-bound workspace root (the same root the settings handlers use) against the request's cwd instead: session/cd moves the live target dir, and a decision recorded for one workspace must not clear or set the gate on a session admitted under another. Regression test covers same-workspace, cd-moved, and foreign-workspace configs. Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): bind the project-hooks approval to the reviewed block The daemon's approve route re-read .qwen/settings.json at click time and persisted the hash of that fresh read, while the ACP child applied the decision to the block it rendered at startup. A file change inside that window (git pull, another session, the workspace's own agent) recorded an approval for hooks the user never saw, and left the card pending because the stored hash matched neither side. The child now persists against the same read that produced the status cell it rendered, so the reviewed block and the recorded block are the same object. The daemon only falls back to its own read when no child answered, which is the cold-daemon case where that read is what the client saw. Also unbreaks the web-shell lane: App.test.tsx's daemon-react-sdk mock had no useOptionalWorkspace export, so every App render threw and 1320 tests failed; and the workspace-overview facet counts no longer include the per-mount /hooks probe, which broke the exact facet-set assertion. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw2kfno0dl * fix(cli): explicit folder trust alone runs project hooks The startup gate demanded a stored approval even when the folder was explicitly trusted, contradicting the documented either-or authority: core's readProjectHooks accepts approval OR explicit trust, but the pending flag short-circuited the accessor before trust was consulted. A platform that pre-trusts its managed directories (folderTrust enabled plus a TRUST_FOLDER/TRUST_PARENT entry) saw every project hook withheld headless. Clear the pending gate for a trusted folder unless the exact block was explicitly rejected — a recorded 'no' still wins over trust. Verified headless on a real fixture: fresh home withholds; trust config alone fires the hook; a rejected block stays withheld in the same trusted folder. Gate unit tests cover pending/rejected/approved under explicit trust. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): bind the approval decision to the rendered block hash end to end The status cell now publishes pendingApprovalHash, the Web Shell card echoes it with the decision, and both the daemon route and the ACP child verify it: the daemon rejects a decision whose hash names a block other than the current file (409 project_hooks_changed, nothing persisted), and the child applies a hash-bound decision only to configs holding that exact startup subject (projectHooksApprovalSubject), leaving any config with a different block pending. Closes the cold-daemon re-read window: an approval can no longer bind commands the user never saw. Verified on a real daemon: render hash -> edit the hook file -> POST with the rendered hash returns 409 and persists nothing; re-render -> approve with the fresh hash persists and enables the hook; a real browser session then fires it. Card tests 5/5 (including hash echo), approve-control tests 2/2, gate tests 20/20, build + bundle clean. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): close the remaining approval-gate review findings - Escape on the Web Shell trust card defers without recording a decision (TUI semantics): ToolApproval gains escapeDismisses/onDismiss, and the card hides until the block hash changes instead of persisting a rejection for a habitual keypress. - The card's options are *_once kinds so the shared safe-default logic pre-selects the reject side; a stray Enter on the security gate can never grant hook execution. Decision-specific labels stay via the option-id i18n keys. - The hooks-trust card reports into approvalOverlayActive, so the composer is dormant while the card owns the keyboard, exactly like a tool approval. - Under command-line YOLO, replacing the hooks block drops the old approval authority: an edited block cannot execute on the previous block's approval while the gate is exempt. - The trust-withheld listing surfaces the display-only block even when the empty execution block reads the gate as cleared (feature on, folder untrusted) — the state the copy exists for. Tests: core config + hooks-listing 1055/1055, hooks reload 38/38, ToolApproval + card 99/99 including Escape-defer and reject-default regressions; build + bundle clean. Co-authored-by: Qwen-Coder <[email protected]> * chore: record merge provenance The main merge (952e0633) refreshed the lint gate; this marker commit carries the required co-author trailer for the batch. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): propagate trust authority to every gate re-application path Review round at2a00fb4d: the startup gate honored explicit trust, but the auxiliary paths re-read only the approval store and re-pended hooks that trust already authorized. - applyProjectHooksApproval (the /hooks reload and YOLO-exit path) now applies the same either-or rule as startup: explicit trust clears a pending block; only an explicit rejection of the exact block wins. - The startup gate mirrors Config.isExplicitlyTrustedFolder(): an IDE-reported trusted workspace counts even with the feature off. - The headless trust warning stays quiet when the execution accessor actually serves the block (approved or trust-authorized), matching the TUI predicate; the test mock now carries workspace.settings.hooks. - The live status cell and the cold-daemon fallback stop prompting when the hooks are not actually withheld (explicit trust / served block), so a trusted workspace never sees a card whose reject would stop running hooks. - The card's Escape defer is keyed by workspace identity read at dismiss time (no reset effect can race it), clears on workspace switch, and a failed decision POST renders an explicit retry notice. - The withheld /hooks listing filters non-event keys and null entries like the registry path; the YOLO replacement comment now states the fail-closed behavior instead of promising a re-apply that never runs. Tests: daemon facade covers cold-daemon pending+hash, trusted no-prompt, hash-match persist, 409 changed-block persists nothing, not-pending; gate re-apply under trust; IDE trust with feature off; trusted reload keeps hooks / untrusted edit withheld. cli 1879/1879, core 1055/1055, web-shell card+ToolApproval 99/99. Real browser: trusted dir runs hooks with no card and no approval record; untrusted dir card -> Escape defer (nothing persisted) -> approve -> hook fires. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]>
The open-source AI coding agent for your terminal, editor, desktop, browser, and chat.
中文 | 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, Web UI, SDKs, and chat integrations (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
Open a terminal in your project and start Qwen Code:
cd /path/to/your-project
qwen
Inside the session, run /auth to configure your provider and API key. Then try:
Explain this repository and show me where to start.
See the Authentication Guide and Settings Reference for detailed setup.
How to Use Qwen Code
Choose the interface that fits your workflow:
| Interface | Get started |
|---|---|
| Terminal | Run qwen in your project — see Quick Start. |
| Desktop | Download Qwen Code Desktop for macOS, Windows, and Linux. |
| Web | Run qwen serve --open to open the Web UI in your browser. (experimental) |
| Editor | Set up VS Code, Zed, or JetBrains. |
| Chat | Connect Telegram, DingTalk, WeChat, or Feishu, then run qwen channel start. |
For automation and custom integrations:
- Headless — Run
qwen -p "..."in scripts, CI/CD, or batch jobs. - SDKs — Build with TypeScript, Python, or Java.
- Daemon — Run
qwen serveto connect clients over HTTP + SSE (ACP). (experimental) Daemon guide. - Computer Use — Set up native MCP tools or Node REPL + SDK in Qwen Code, Claude Code, or Codex.
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())
Daemon Mode
Run Qwen Code as a long-lived service when you want browser, editor, SDK, and automation clients to share agent sessions without each starting a separate process. The daemon provides the built-in Web Shell and an HTTP + SSE API, supports multiple workspaces, and lets clients reconnect to active sessions.
Start it in the foreground and open the Web Shell:
qwen serve --open
The default address is http://127.0.0.1:4170. To run and manage it in the background instead:
qwen daemon start
qwen daemon status
qwen daemon logs -f
qwen daemon stop
By default, the daemon listens on loopback without bearer authentication; any local process that can reach it can use the full API, including code execution. On shared or untrusted hosts, set QWEN_SERVER_TOKEN and use --require-auth. See the Daemon guide for workspaces, authentication, remote access, and the API.
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) | ✓ | — |
Qwen Code Evaluation
On SWE-bench Verified, seven Qwen Code versions scored 76.40%–77.80% on average using the same Qwen 3.7 Max model (500 tasks, three trials per version).
Full results and evaluation configuration →
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-audio-agent — Realtime full-duplex voice frontend for Qwen Code over ACP — talk hands-free with barge-in and a local wake word
-
🦞 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.
- Aliyun Model Studio CLI — Official CLI for Aliyun's AI platform (
bailian-cli). Extends Qwen Code with image/video generation, knowledge retrieval, app orchestration, and model deployment
Privacy
See the Terms of Service and Privacy Notice, including Chrome extension and Browser Use data handling.
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.

