|
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* feat(core): add Agent Team foundation (experimental, flag-gated) First stage of re-porting the Agent Team feature (originally PR #2886) onto current main. The branch had diverged 362 commits behind a parallel rewrite of the agent runtime, so the feature is being re-applied stage by stage rather than merged. This stage lands the self-contained agents/team/ subsystem (TeamManager, mailbox, identity, tasks, leader permission bridge, test-utils) plus the team_create/team_delete and task_create/task_update/task_list tools, with the additive plumbing they need: - Config: TeamManager/TeamContext accessors, cleanupTeamRuntime, and isAgentTeamEnabled (settings or QWEN_CODE_ENABLE_AGENT_TEAM=1). - Tool registry: team/task tools registered lazily, gated on the flag. - Runtime hooks: completeOnIdle for one-shot teammates; args on the agent approval event; teammate-aware tool exclusion sets. - Backend types: TeamAgentHandle, optional getAgent, completeOnIdle. - New experimental.agentTeam setting. Everything is gated behind the experimental flag and inert by default. Build is green; team unit tests and all touched-file regressions pass. * feat(core): add send_message team routing (experimental) Stage 1 of the Agent Team re-port. Extends the send_message tool so it can route to a teammate (or "*" for broadcast) via TeamManager in addition to its existing background-task path, and supports the shutdown_request control message (leader-only). Recipient selection is a oneOf over `to`/`task_id`. Layered on top of main's classifier integration: send_message keeps its 'ask' default permission and forwards the routing fields + message to the AUTO classifier, since the message is an instruction the recipient executes. Re-adds the team-lifecycle E2E test, which now passes end to end (create -> tasks -> messages -> list -> update -> delete). * feat(core): let the Agent tool spawn named teammates (experimental) Stage 3 of the Agent Team re-port. Adds the `name` parameter to the Agent tool: when a team is active and a name is given, the call routes through TeamManager.spawnTeammate instead of launching a one-shot subagent. Without a team the call is rejected up front rather than silently falling back. The tool description advertises team coordination only when the experimental flag is on. Ported onto main's rewritten Agent tool. * feat(cli): render team_result/task_list tool displays (experimental) Stage 5 (partial) of the Agent Team re-port. Teaches the ToolMessage result renderer about the TeamResultDisplay and TaskListResultDisplay shapes so the team/task tools' output is shown via their returnDisplay text instead of a stringified object, and adds a JSON.stringify safeguard for any other non-string display object. The remaining CLI wiring (nonInteractiveCli + useGeminiStream team drivers, permissionController.handleTeammateApproval) is coupled to the turn-loop Teammate handling and will land together with Stage 4. * feat(core): treat teammate messages as top-level turns (experimental) Stage 4 of the Agent Team re-port. Adds SendMessageType.Teammate and includes it in isTopLevelInteraction so that a teammate message delivered to the leader resets the loop detector and opens an interaction span, the same as a user/cron/notification turn. Per the agreed minimal integration, teammate turns deliberately do NOT run the UserQuery/Cron block — they don't bump commit attribution, aren't recorded as user messages, and don't trigger auto-memory prefetch. That keeps the edit to main's restructured turn loop to a single condition. * feat(cli): drive teammates from the headless run loop (experimental) Stage 5 of the Agent Team re-port. Wires the non-interactive/headless run loop to the active team: it subscribes to TeamManager changes, drains teammate messages into the leader's conversation as SendMessageType.Teammate turns, waits for teammate activity when the leader has no pending tool calls, and routes teammate tool-approval requests through the session's permission channel (SDK in stream-json mode; YOLO/cancel fallback otherwise). Adds PermissionController.handleTeammateApproval and exposes it on the ControlService permission facade. Ported onto main's restructured run loop (which added its own cron/notification drain mechanism). * feat(cli): drive teammates from the interactive turn loop (experimental) Final Stage 5 piece of the Agent Team re-port. Wires the interactive (TUI) useGeminiStream hook to the active team: it subscribes to TeamManager, queues teammate messages, and drains them into the conversation as SendMessageType.Teammate turns when idle, guarded against racing the notification drain. Treats teammate turns like user/cron for image-format checks and new-prompt stats. Ported onto main's rewritten hook. * fix(core): declare proper-lockfile dependency for the team subsystem The Agent Team mailbox and task files import proper-lockfile, but the dependency was never declared in package.json, so a clean `npm ci` (as CI runs) failed to resolve the module — cascading into implicit-any and possibly-undefined errors in the same files. It built locally only because the working tree's node_modules already had the package from an earlier install. Adds proper-lockfile to packages/core dependencies and @types/proper-lockfile to root devDependencies (matching the original feature branch), and regenerates the lockfile. Build and typecheck are clean. * fix(team): address review findings on the agent-team subsystem (experimental) Triaged the unresolved review threads from the superseded PR #2886 against the re-ported code and applied the valid fixes: - task_update(status:'deleted') now enforces the same ownership guard as updateTask, so a teammate cannot delete another teammate's task. - Completing a task and adding a blocks edge in the same call no longer leaves the dependent permanently blocked by the just-completed task. - listTasks treats a momentarily-empty (mid-create) task file as a create in flight and skips it instead of quarantining and losing the task. - Fire-and-forget coordination calls (flush, auto-claim, unassign, poll) log rejections instead of surfacing as unhandled rejections. - pollLeaderInbox re-checks the leader callback after the awaited read so a detach during the read cannot throw or drop the batch. - scanIdleAgentsForTasks skips teammates with a pending shutdown. - broadcast uses allSettled so one terminated recipient does not fail the whole broadcast. - Hybrid tool-response+teammate turns reset the loop detector, preventing a false LoopDetected when a polling leader merges teammate messages. - useGeminiStream drains its teammate queue on a manager swap; the join event carries the teammate model for the UI tab label. - Removed dead consumeUnreadByType and an unreachable ENOENT branch. Verified: core unit tests (incl. new regressions for the delete guard, the complete+addBlocks re-block, and the empty-file create race) plus live L3 (3-agent) and L4 (4-agent) E2E, both clean. * fix(core): close ownership TOCTOU and lock-ordering hazard in agent-team tasks deleteTask checked ownership against a pre-lock read, so a concurrent claimTask/updateTask could reassign the owner between the check and the unlink — silently destroying another teammate's task. Acquire the lock first, then re-read and re-check ownership inside it before unlinking, mirroring updateTask. Reciprocal edge cleanup now runs after the lock is released (never holding two per-task locks at once) but before the single tasks-updated notification, so no listener observes a phantom blocker. blockTask issued its two updateTask writes via Promise.all; two calls over the same pair in opposite directions could deadlock on per-task locks. Serialize the writes to remove the lock-ordering hazard. * fix(core): harden agent-team message handling and auto-claim - Cap per-agent pending messages (MAX_PENDING_MESSAGES). The queue only drains when its recipient goes IDLE, so an unbounded queue let a single looping teammate balloon a busy teammate's memory; sendMessage now applies backpressure once the cap is reached. - Wrap auto-claimed task content (subject/description, authored by another agent) in a <task_content> envelope with a defensive instruction so it is treated as data, not as instructions to obey. - Surface fire-and-forget coordination failures (flush, auto-claim, unassign) to the leader's conversation. They were only logged via a namespaced debug logger, i.e. invisible in production, despite mapping to silent stuck-teammate / stuck-task symptoms. * fix(core): require approval for agent-team task_create/task_update A task's subject/description becomes the prompt an idle teammate auto-claims and executes with full tool access — the same privileged-sink shape as send_message. Both tools inherited the base default 'allow', which short-circuits the classifier in AUTO mode. Override getDefaultPermission to 'ask' so that injection path stays under the classifier / human-in-the-loop, matching send_message. * docs(core): correct completeOnIdle JSDoc for team teammates The JSDoc cited team teammates as the use-case for completeOnIdle:true, but teammates set it to false so they settle to IDLE (not COMPLETED) and stay alive for follow-up messages and auto-claim. Document the actual semantics and the invariant the leader's wait loop relies on. * fix(core): harden agent-team leader callback and task envelope - fireAndForget: wrap leaderMessageCallback in try/catch so a throwing callback cannot re-introduce the unhandled rejection the wrapper exists to prevent (enforces the documented 'must not throw from this catch'). - tryAutoClaimTask: nonce-tag the <task_content> envelope with the per-session envelopeNonce (same pattern as formatLeaderEnvelope) so a teammate-authored description cannot forge the closing tag and break out of the protected zone via a </task_content> payload. * fix(core): make deleteTask edge cleanup resilient to partial failure Use Promise.allSettled (was Promise.all) for post-unlink edge cleanup so a single failing dependent (corrupt JSON, EACCES, lock exhaustion) no longer skips notifyTasksUpdated for the dependents that were cleaned. Without this their blockedBy is cleared but scanIdleAgentsForTasks never re-runs, leaving them stuck idle with no recovery (the task file is already unlinked, so a retry returns false). Per-failure warnings are logged. * fix(cli): mount useTeamInProcess so teammate tabs render The hook bridging team TEAMMATE_JOINED events to agent-tab registration (useTeamInProcess) was authored but never mounted in AgentViewProvider — only useArenaInProcess was. As a result teammate tabs never registered and the teammate tab bar never appeared during in-process team runs. Mount useTeamInProcess alongside useArenaInProcess, and label teammate tabs by name rather than model (teammates inherit the leader's model, so a model label collapses to a generic "teammate" and is identical across the team). Add a regression test asserting the provider mounts the team bridge. * test(terminal-capture): add agent-team feature demo + capture fixes Add a standalone streaming demo of the agent-team feature that captures the full lifecycle and the teammate tab navigation into a single GIF (scenarios/agent-team-demo.ts). Supporting engine fixes: - capture(): scroll the xterm viewport to the live bottom before screenshotting, so a capture taken after an idle period shows the current state instead of stale top-of-buffer scrollback. - scenario-runner: skip scenarios/*.ts files with no default export (driver scripts that guard their own entrypoint), so batch runs don't choke. * fix(core): serialize in-process mailbox writers to fix Windows lock flakiness The concurrent-write test fired 10 writeMessage() calls at one inbox, each contending for the same proper-lockfile lock with a fixed, non-randomized backoff. On Windows, slower fs syscalls let the tail writers exhaust the retry budget before winning the lock, throwing ELOCKED ("Lock file is already being held") — a flaky failure that alternated pass/fail across CI runs. Add a per-inbox in-process Mutex (async-mutex, the pattern already used in jsonl-utils and writeContextFile) so same-process writers serialize in memory and only one reaches for the file lock at a time. The proper-lockfile lock stays inside the mutex to preserve cross-process safety between agent processes. Also randomize the lock backoff to de-synchronize genuine cross-process contenders. * feat(team): render teammate reports as a compact notification line A teammate's report was injected into the leader's conversation as a raw <teammate_message_<nonce>> envelope and rendered verbatim as a user bubble — a large, scaffolding-heavy block on screen for what is often the biggest payload in the feature. Adopt the two-text split the notification queue already uses: the full nonce-tagged envelope still goes to the leader's model, but the user now sees a compact "● <name> reported back" line in its place. The verbatim USER bubble is suppressed for SendMessageType.Teammate exactly as it is for Cron, and coordination-error notices get the same treatment. The leader callback now delivers both the model text and a display string built in TeamManager (where the structured sender/summary live), so the UI never parses the envelope. Headless is unchanged — it ignores the extra arg. * fix(terminal-capture): widen agent-team-demo Phase C budget so the GIF doesn't cut off The leader sits idle (no Main-view output) while teammates read their files, so Phase C captures no frames until a report lands — making maxPolls the real wall-clock budget. At 80 polls (~112s) a slow second scout could exhaust it before reporting, ending the GIF mid-run. Bump to 200 polls (~5min) so the capture outlasts the slowest scout plus the combined summary and delete; the loop still exits early on `deleted` and idle polls capture no frames, so the GIF doesn't bloat. * fix(core): separate task-content nonce; forward send_message summary Address two review findings on the agent-team messaging path: - The <task_content_…> envelope reused envelopeNonce — the per-session nonce the leader trusts to authenticate <teammate_message_…> blocks. Because the task-content prompt is delivered to the claiming teammate, a teammate could learn the nonce and forge a leader-trusted envelope. Use a dedicated taskContentNonce so the leader-trust nonce stays secret from teammates. - The SendMessage 'summary' param was dropped between the tool and the mailbox, so the leader UI always showed the '{name} reported back' fallback. Thread summary through sendMessage → writeMessage so it reaches formatLeaderDisplay. Adds regression tests for both. * fix(core,cli): harden agent-team messaging per review round 4 - task-content envelope uses a fresh per-claim nonce instead of a shared per-session one, so a teammate that learns one task's nonce can't forge a later task's closing tag to inject the next claimant. - team_delete wraps manager.cleanup() in try/catch and always resets the Config team state, so a cleanup failure no longer permanently wedges team_create for the rest of the session. - unassignTeammateTasks uses Promise.allSettled so one corrupt/locked task file no longer strands the remaining tasks on a terminated teammate; the caller's re-scan still fires. - non-interactive teammate-approval responses .catch() rejections to avoid an unhandledRejection if the teammate terminates mid-approval. - setupEventBridge warns when the backend can't provide an agent handle or event emitter instead of returning silently. * fix(core): don't let a failed dependent unblock abort task completion unblockDependents used Promise.all, so a single dependent failing (corrupt JSON, EACCES, lock exhaustion) rejected out of updateTask before the completed status was persisted — the task stayed in_progress on disk while already-processed dependents were unblocked, leaving the dependency graph inconsistent. Switch to Promise.allSettled with a debug warning per failure, mirroring the best-effort edge cleanup in deleteTask and unassignTeammateTasks. * fix(core): quarantine corrupt teammate inboxes; skip task scan when no agent is idle Review round 7. A corrupt teammate inbox previously made every writeMessage/consumeUnread re-throw on the same file, so the teammate could never receive another message (including shutdown requests) — while the leader inbox already self-healed via quarantine. readInboxRaw now renames the corrupt file to .corrupt-{ts} and continues on a fresh inbox; the leader-side offset clamps to 0 if the inbox shrank behind the poller so messages are re-surfaced rather than silently skipped. scanIdleAgentsForTasks now checks for idle members before reading the task board, avoiding a full tasks-directory scan on every task update while all agents are busy. Also document the restrictsOwnership field enumeration hazard and the intentional metadata/activeForm exclusion. * fix(core): re-check task ownership under the lock when unassigning a terminated teammate Review round 8. unassignTeammateTasks snapshotted in_progress tasks and then blind-wrote {status: pending, owner: null} per task, so a leader reassignment (or the dying teammate's final completion) landing between the snapshot and the per-task lock was silently reverted. Releases now go through an in-lock compare-and-set that skips the task when its owner or status no longer matches the snapshot. Also isolate task-update listeners (one throwing listener no longer starves the rest) and drop the lone const enum for subsystem consistency. * fix(core): harden team task file layer against partial writes and transient I/O - createTask claims the ID with an empty O_EXCL placeholder and fills it via temp-file + rename, so concurrent readers never see partial JSON (which the quarantine would have destroyed mid-create) - listTasks quarantines only on parse failures; transient read errors (EMFILE/EIO/EACCES) skip the file for one round instead of renaming a healthy task away, and the read fan-out is capped at 16 - updateTask / claimTask / releaseOwnedTask guard the in-lock readFile against ENOENT (resetTaskList and the quarantine rename run without per-task locks), mirroring deleteTask - cover releaseOwnedTask's three defensive branches with tests * fix(core): drain messages enqueued during the IDLE transition; settle abort on idle agents - a message enqueued from inside the synchronous IDLE STATUS_CHANGE emit (TeamManager's flush) landed after the run loop's final empty check while `processing` was still true — enqueueMessage would not restart the loop and the message stranded in a dead queue; the loop now re-checks the queue after `processing` flips false - abort() on an idle/initializing agent only set the signal: no loop was running to observe it, so the agent never reached a terminal status and allTeammatesTerminated()-style gates never fired; abort now settles CANCELLED directly when no loop is in flight - regression tests drive the real AgentInteractive (stub model, real loop) through send-during-idle-emit and abort-while-idle * test(core): align FakeAgent queue and abort semantics with AgentInteractive FakeAgent modeled a friendlier runtime than the one that ships: enqueueMessage processed inline (no queue, no processing flag, resurrecting terminal agents via unconditional RUNNING), which is exactly what masked the flush-into-dead-queue bug. It now queues while a round is in flight, drains before settling IDLE, drops messages after abort()/shutdown() like the real drained queue, and never resurrects a terminal agent. * fix(core): surface spawn failures, handle shutdown_rejected, envelope peer messages - spawnTeammate now checks the agent's status after spawnAgent resolves: start() reports chat-creation failure via FAILED without throwing, so the leader was told the teammate joined while sends were accepted into a queue that could never flush; a failed spawn now rolls back and surfaces the reason (with a terminal-status replay in setupEventBridge for the attach race) - shutdown_rejected now clears _shutdownPending: a teammate that declined once stayed excluded from auto-claim and kill-armed on any later "shutdown_approved" mention - peer-to-peer deliveries get a fresh-nonce envelope like leader deliveries, closing inline leader impersonation between teammates (deliberately not the leader-trust nonce, which must never reach teammate context) * fix(core): exclude workflow tool from teammates The teammate ALS identity propagates into anything a teammate spawns, so prepareTools() keeps choosing the teammate exclusion set for nested agents — without WORKFLOW in it, a teammate-launched workflow re-arms the O(k^n) recursive fan-out the subagent exclusion set prevents. * fix(core): make task tools visible to permission review; reject dependency cycles - task_create / task_update now project their content (subject, description, status, owner, edges) to the AUTO classifier — the base '' sentinel projected to an empty object, so the classifier ruled on task_create({}) and the 'ask' override was blind; the interactive confirmation now shows the description (truncated), since that text is what a claiming teammate executes - task_update rejects self-edges and dependency cycles instead of silently persisting a graph that auto-claim can never unblock - regression tests pin the 'ask' default and a non-empty classifier projection for both tools * fix(core): reclaim stale teams on team_create instead of wedging the name Nothing deletes team dirs on normal exit (only an explicit team_delete does), so every Ctrl+C, completed headless run, or crash permanently wedged the team name behind createTeamFile's wx-exclusive create, with manual rm -rf as the default recovery. team_create now records the owner identity (leadSessionId + leadPid) and, on EEXIST, reclaims the team when the recorded lead process is gone (or is this process); only a live concurrent owner keeps the name refused. * fix(cli): pass teammate envelopes straight to the model, skipping shell/@/slash preprocessing Teammate envelopes are model-authored text already rendered as a notification line by the teammate drain, but they still flowed through the user-input preprocessing: with shell mode active a teammate report was EXECUTED as a shell command, and a leading / or an @path was reinterpreted against the leader's session. They now early-return like Notification. * fix(core): exclude Teammate from UserPromptSubmit hooks and record it in chat history Teammate envelopes are machine-driven re-entries like Cron and Notification: user-authored UserPromptSubmit hooks must not fire on (or block) internal coordination traffic. They also never reached any chat-recording path — record them like notifications so a resumed session restores the same compact info line the live UI rendered. * fix(cli): stop teammate-approval rejections from escaping as unhandled rejections The stream-json listener voided handleTeammateApproval's promise while the handler's own error path re-issues a respond() that can reject (teammate terminated mid-request) — an unhandledRejection that can take down an SDK session. The call site now catches like its headless siblings, and the controller's catch-path respond(Cancel) is wrapped so the method never rejects out of its own error path. * test(core): add getSessionId to team-lifecycle mock config |
||
|---|---|---|
| .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 | ||
| CHANGELOG.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
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
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 22 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 five primary ways:
- Interactive mode (terminal UI)
- Headless mode (scripts, CI)
- IDE integration (VS Code, Zed)
- SDKs (TypeScript, Python, Java)
- Daemon mode —
qwen serveexposes ACP over HTTP+SSE so multiple clients share one agent (experimental)
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):
Daemon mode (qwen serve, experimental)
cd your-project/
qwen serve
# → qwen serve listening on http://127.0.0.1:4170 (mode=http-bridge)
Run Qwen Code as a local HTTP daemon so IDE plugins, web UIs, CI scripts and custom CLIs all share one agent session over HTTP+SSE — instead of each spawning their own subprocess. Loopback bind has no auth by default (set QWEN_SERVER_TOKEN to enable bearer auth even on loopback); remote binds (--hostname 0.0.0.0) require a token — boot refuses without one. See:
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.
