<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Qwen Code Documentation</title>
<link>https://qwenlm.github.io/qwen-code-docs/en/users/overview/</link>
<description>Qwen Code AI coding agent documentation</description>
<language>en</language>
<lastBuildDate>Mon, 18 May 2026 03:18:42 +0000</lastBuildDate>
<generator>ForgeRSS</generator>
<atom:link href="https://qwenlm.github.io/qwen-code-docs/en/users/overview/" rel="self" type="application/rss+xml"/>
<image>
  <url>https://qwenlm.github.io/qwen-code-docs/favicon.ico</url>
  <title>Qwen Code Documentation</title>
  <link>https://qwenlm.github.io/qwen-code-docs/en/users/overview/</link>
</image>
<item>
  <title>Run Prompts on a Schedule</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/scheduled-tasks/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/scheduled-tasks/</guid>
  <pubDate>Thu, 26 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Scheduled Tasks
Run Prompts on a Schedule
Use
/loop
and the cron scheduling tools to run prompts repeatedly, poll for status, or set one-time reminders within a Qwen Code session.
Scheduled tasks let Qwen Code re-run a prompt automatically on an interval. Use them to poll a deployment, babysit a PR, check back on a long-running build, or remind yourself to do something later in the session.
Tasks are session-scoped: they live in the current Qwen Code process and are gone when...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Scheduled Tasks
Run Prompts on a Schedule
Use
/loop
and the cron scheduling tools to run prompts repeatedly, poll for status, or set one-time reminders within a Qwen Code session.
Scheduled tasks let Qwen Code re-run a prompt automatically on an interval. Use them to poll a deployment, babysit a PR, check back on a long-running build, or remind yourself to do something later in the session.
Tasks are session-scoped: they live in the current Qwen Code process and are gone when you exit. Nothing is written to disk.
Note:
Scheduled tasks are an experimental feature. Enable them with
experimental.cron: true
in your
settings
, or set
QWEN_CODE_ENABLE_CRON=1
in your environment.
Schedule a recurring prompt with /loop
The
/loop
bundled skill
is the quickest way to schedule a recurring prompt. Pass an optional interval and a prompt, and Qwen Code sets up a cron job that fires in the background while the session stays open.
/loop 5m check if the deployment finished and tell me what happened
Qwen Code parses the interval, converts it to a cron expression, schedules the job, and confirms the cadence and job ID. It then immediately executes the prompt once — you don’t have to wait for the first cron fire.
Interval syntax
Intervals are optional. You can lead with them, trail with them, or leave them out entirely.
Form
Example
Parsed interval
Leading token
/loop 30m check the build
every 30 minutes
Trailing
every
clause
/loop check the build every 2 hours
every 2 hours
No interval
/loop check the build
defaults to every 10 minutes
Supported units are
s
for seconds,
m
for minutes,
h
for hours, and
d
for days. Seconds are rounded up to the nearest minute since cron has one-minute granularity. Intervals that don’t divide evenly into their unit, such as
7m
or
90m
, are rounded to the nearest clean interval and Qwen Code tells you what it picked.
Loop over another command
The scheduled prompt can itself be a command or skill invocation. This is useful for re-running a workflow you’ve already packaged.
/loop 20m /review-pr 1234
Each time the job fires, Qwen Code runs
/review-pr 1234
as if you had typed it.
Manage loops
/loop
also supports two subcommands for managing existing jobs:
/loop list
Lists all scheduled jobs with their IDs and cron expressions.
/loop clear
Cancels all scheduled jobs at once.
Set a one-time reminder
For one-shot reminders, describe what you want in natural language instead of using
/loop
. Qwen Code schedules a single-fire task that deletes itself after running.
remind me at 3pm to push the release branch
in 45 minutes, check whether the integration tests passed
Qwen Code pins the fire time to a specific minute and hour using a cron expression and confirms when it will fire.
Manage scheduled tasks
Ask Qwen Code in natural language to list or cancel tasks, or reference the underlying tools directly.
what scheduled tasks do I have?
cancel the deploy check job
Under the hood, Qwen Code uses these tools:
Tool
Purpose
CronCreate
Schedule a new task. Accepts a 5-field cron expression, the prompt to run, and whether it recurs or fires once.
CronList
List all scheduled tasks with their IDs, schedules, and prompts.
CronDelete
Cancel a task by ID.
Each scheduled task has an 8-character ID you can pass to
CronDelete
. A session can hold up to 50 scheduled tasks at once.
How scheduled tasks run
The scheduler checks every second for due tasks and enqueues them when the session is idle. A scheduled prompt fires between your turns, not while Qwen Code is mid-response. If Qwen Code is busy when a task comes due, the prompt waits until the current turn ends.
All times are interpreted in your local timezone. A cron expression like
0 9 * * *
means 9am wherever you’re running Qwen Code, not UTC.
Jitter
To avoid every session hitting the API at the same wall-clock moment, the scheduler adds a small deterministic offset to fire times:
Recurring tasks
fire up to 10% of their period late, capped at 15 minutes. An hourly job might fire anywhere from
:00
to
:06
.
One-shot tasks
scheduled for the top or bottom of the hour (minute
:00
or
:30
) fire up to 90 seconds early.
The offset is derived from the task ID, so the same task always gets the same offset. If exact timing matters, pick a minute that is not
:00
or
:30
, for example
3 9 * * *
instead of
0 9 * * *
, and the one-shot jitter will not apply.
Three-day expiry
Recurring tasks automatically expire 3 days after creation. The task fires one final time, then deletes itself. This bounds how long a forgotten loop can run. If you need a recurring task to last longer, cancel and recreate it before it expires.
One-shot tasks do not expire on a timer — they simply delete themselves after firing once.
Cron expression reference
CronCreate
accepts standard 5-field cron expressions:
minute hour day-of-month month day-of-week
. All fields support wildcards (
*
), single values (
5
), steps (
*/15
), ranges (
1-5
), and comma-separated lists (
1,15,30
).
Example
Meaning
*/5 * * * *
Every 5 minutes
0 * * * *
Every hour on the hour
7 * * * *
Every hour at 7 minutes past
0 9 * * *
Every day at 9am local
0 9 * * 1-5
Weekdays at 9am local
30 14 15 3 *
March 15 at 2:30pm local
Day-of-week uses
0
or
7
for Sunday through
6
for Saturday. When both day-of-month and day-of-week are constrained (neither is
*
), a date matches if either field matches — this follows standard vixie-cron semantics.
Extended syntax like
L
,
W
,
?
, and name aliases such as
MON
or
JAN
is not supported.
Limitations
Session-scoped scheduling has inherent constraints:
Tasks only fire while Qwen Code is running and idle. Closing the terminal or letting the session exit cancels everything.
No catch-up for missed fires. If a task’s scheduled time passes while Qwen Code is busy on a long-running request, it fires once when Qwen Code becomes idle, not once per missed interval.
No persistence across restarts. Restarting Qwen Code clears all session-scoped tasks.
Last updated on
May 18, 2026
Status Line
Contextual Tips</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/scheduled-tasks/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Scheduled Tasks
Run Prompts on a Schedule
Use
/loop
and the cron scheduling tools to run prompts repeatedly, poll for status, or set one-time reminders within a Qwen Code session.
Scheduled tasks let Qwen Code re-run a prompt automatically on an interval. Use them to poll a deployment, babysit a PR, check back on a long-running build, or remind yourself to do something later in the session.
Tasks are session-scoped: they live in the current Qwen Code process and are gone when...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Scheduled Tasks
Run Prompts on a Schedule
Use
/loop
and the cron scheduling tools to run prompts repeatedly, poll for status, or set one-time reminders within a Qwen Code session.
Scheduled tasks let Qwen Code re-run a prompt automatically on an interval. Use them to poll a deployment, babysit a PR, check back on a long-running build, or remind yourself to do something later in the session.
Tasks are session-scoped: they live in the current Qwen Code process and are gone when you exit. Nothing is written to disk.
Note:
Scheduled tasks are an experimental feature. Enable them with
experimental.cron: true
in your
settings
, or set
QWEN_CODE_ENABLE_CRON=1
in your environment.
Schedule a recurring prompt with /loop
The
/loop
bundled skill
is the quickest way to schedule a recurring prompt. Pass an optional interval and a prompt, and Qwen Code sets up a cron job that fires in the background while the session stays open.
/loop 5m check if the deployment finished and tell me what happened
Qwen Code parses the interval, converts it to a cron expression, schedules the job, and confirms the cadence and job ID. It then immediately executes the prompt once — you don’t have to wait for the first cron fire.
Interval syntax
Intervals are optional. You can lead with them, trail with them, or leave them out entirely.
Form
Example
Parsed interval
Leading token
/loop 30m check the build
every 30 minutes
Trailing
every
clause
/loop check the build every 2 hours
every 2 hours
No interval
/loop check the build
defaults to every 10 minutes
Supported units are
s
for seconds,
m
for minutes,
h
for hours, and
d
for days. Seconds are rounded up to the nearest minute since cron has one-minute granularity. Intervals that don’t divide evenly into their unit, such as
7m
or
90m
, are rounded to the nearest clean interval and Qwen Code tells you what it picked.
Loop over another command
The scheduled prompt can itself be a command or skill invocation. This is useful for re-running a workflow you’ve already packaged.
/loop 20m /review-pr 1234
Each time the job fires, Qwen Code runs
/review-pr 1234
as if you had typed it.
Manage loops
/loop
also supports two subcommands for managing existing jobs:
/loop list
Lists all scheduled jobs with their IDs and cron expressions.
/loop clear
Cancels all scheduled jobs at once.
Set a one-time reminder
For one-shot reminders, describe what you want in natural language instead of using
/loop
. Qwen Code schedules a single-fire task that deletes itself after running.
remind me at 3pm to push the release branch
in 45 minutes, check whether the integration tests passed
Qwen Code pins the fire time to a specific minute and hour using a cron expression and confirms when it will fire.
Manage scheduled tasks
Ask Qwen Code in natural language to list or cancel tasks, or reference the underlying tools directly.
what scheduled tasks do I have?
cancel the deploy check job
Under the hood, Qwen Code uses these tools:
Tool
Purpose
CronCreate
Schedule a new task. Accepts a 5-field cron expression, the prompt to run, and whether it recurs or fires once.
CronList
List all scheduled tasks with their IDs, schedules, and prompts.
CronDelete
Cancel a task by ID.
Each scheduled task has an 8-character ID you can pass to
CronDelete
. A session can hold up to 50 scheduled tasks at once.
How scheduled tasks run
The scheduler checks every second for due tasks and enqueues them when the session is idle. A scheduled prompt fires between your turns, not while Qwen Code is mid-response. If Qwen Code is busy when a task comes due, the prompt waits until the current turn ends.
All times are interpreted in your local timezone. A cron expression like
0 9 * * *
means 9am wherever you’re running Qwen Code, not UTC.
Jitter
To avoid every session hitting the API at the same wall-clock moment, the scheduler adds a small deterministic offset to fire times:
Recurring tasks
fire up to 10% of their period late, capped at 15 minutes. An hourly job might fire anywhere from
:00
to
:06
.
One-shot tasks
scheduled for the top or bottom of the hour (minute
:00
or
:30
) fire up to 90 seconds early.
The offset is derived from the task ID, so the same task always gets the same offset. If exact timing matters, pick a minute that is not
:00
or
:30
, for example
3 9 * * *
instead of
0 9 * * *
, and the one-shot jitter will not apply.
Three-day expiry
Recurring tasks automatically expire 3 days after creation. The task fires one final time, then deletes itself. This bounds how long a forgotten loop can run. If you need a recurring task to last longer, cancel and recreate it before it expires.
One-shot tasks do not expire on a timer — they simply delete themselves after firing once.
Cron expression reference
CronCreate
accepts standard 5-field cron expressions:
minute hour day-of-month month day-of-week
. All fields support wildcards (
*
), single values (
5
), steps (
*/15
), ranges (
1-5
), and comma-separated lists (
1,15,30
).
Example
Meaning
*/5 * * * *
Every 5 minutes
0 * * * *
Every hour on the hour
7 * * * *
Every hour at 7 minutes past
0 9 * * *
Every day at 9am local
0 9 * * 1-5
Weekdays at 9am local
30 14 15 3 *
March 15 at 2:30pm local
Day-of-week uses
0
or
7
for Sunday through
6
for Saturday. When both day-of-month and day-of-week are constrained (neither is
*
), a date matches if either field matches — this follows standard vixie-cron semantics.
Extended syntax like
L
,
W
,
?
, and name aliases such as
MON
or
JAN
is not supported.
Limitations
Session-scoped scheduling has inherent constraints:
Tasks only fire while Qwen Code is running and idle. Closing the terminal or letting the session exit cancels everything.
No catch-up for missed fires. If a task’s scheduled time passes while Qwen Code is busy on a long-running request, it fires once when Qwen Code becomes idle, not once per missed interval.
No persistence across restarts. Restarting Qwen Code clears all session-scoped tasks.
Last updated on
May 18, 2026
Status Line
Contextual Tips</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/scheduled-tasks/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>DingTalk (Dingtalk)</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/dingtalk/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/dingtalk/</guid>
  <pubDate>Mon, 23 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
DingTalk
DingTalk (Dingtalk)
This guide covers setting up a Qwen Code channel on DingTalk (钉钉).
Prerequisites
A DingTalk organization account
A DingTalk bot application with AppKey and AppSecret (see below)
Creating a Bot
Go to the
DingTalk Developer Portal
Create a new application (or use an existing one)
Under the application, enable the
Robot
capability
In Robot settings, enable
Stream Mode
(机器人协议 → Stream 模式)
Note the
AppKey
(Client ID) and
AppSecret
(Client Secr...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
DingTalk
DingTalk (Dingtalk)
This guide covers setting up a Qwen Code channel on DingTalk (钉钉).
Prerequisites
A DingTalk organization account
A DingTalk bot application with AppKey and AppSecret (see below)
Creating a Bot
Go to the
DingTalk Developer Portal
Create a new application (or use an existing one)
Under the application, enable the
Robot
capability
In Robot settings, enable
Stream Mode
(机器人协议 → Stream 模式)
Note the
AppKey
(Client ID) and
AppSecret
(Client Secret) from the application credentials page
Stream Mode
DingTalk Stream mode uses an outbound WebSocket connection — no public URL or server is needed. The bot connects to DingTalk’s servers, which push messages through the WebSocket. This is the simplest deployment model.
Configuration
Add the channel to
~/.qwen/settings.json
:
{
"channels"
: {
"my-dingtalk"
: {
"type"
:
"dingtalk"
,
"clientId"
:
"$DINGTALK_CLIENT_ID"
,
"clientSecret"
:
"$DINGTALK_CLIENT_SECRET"
,
"senderPolicy"
:
"open"
,
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/your/project"
,
"instructions"
:
"You are a concise coding assistant responding via DingTalk."
,
"groupPolicy"
:
"open"
,
"groups"
: {
"*"
: {
"requireMention"
:
true
}
}
}
}
}
Set the credentials as environment variables:
export
DINGTALK_CLIENT_ID
=<
your-app-key
>
export
DINGTALK_CLIENT_SECRET
=<
your-app-secret
>
Or define them in the
env
section of
settings.json
:
{
"env"
: {
"DINGTALK_CLIENT_ID"
:
"your-app-key"
,
"DINGTALK_CLIENT_SECRET"
:
"your-app-secret"
}
}
Running
# Start only the DingTalk channel
qwen
channel
start
my-dingtalk
# Or start all configured channels together
qwen
channel
start
Open DingTalk and send a message to the bot. You should see a 👀 emoji reaction appear while the agent processes, followed by the response.
Group Chats
DingTalk bots work in both DM and group conversations. To enable group support:
Set
groupPolicy
to
"allowlist"
or
"open"
in your channel config
Add the bot to a DingTalk group
@mention the bot in the group to trigger a response
By default, the bot requires an @mention in group chats (
requireMention: true
). Set
"requireMention": false
for a specific group to make it respond to all messages. See
Group Chats
for full details.
Finding a Group’s Conversation ID
DingTalk uses
conversationId
to identify groups. You can find it in the channel service logs when someone sends a message in the group — look for the
conversationId
field in the log output.
Images and Files
You can send photos and documents to the bot, not just text.
Photos:
Send an image (screenshot, diagram, etc.) and the agent will analyze it using its vision capabilities. This requires a multimodal model — add
"model": "qwen3.5-plus"
(or another vision-capable model) to your channel config. DingTalk supports sending images directly or as part of rich text messages (mixed text + images).
Files:
Send a PDF, code file, or any document. The bot downloads it from DingTalk’s servers and saves it locally so the agent can read it with its file tools. Audio and video files are also supported. This works with any model.
Key Differences from Telegram
Authentication:
AppKey + AppSecret instead of a static bot token. The SDK manages access token refresh automatically.
Connection:
WebSocket stream instead of polling — no public IP or webhook URL needed.
Formatting:
Responses use DingTalk’s markdown dialect (a limited subset). Tables are automatically converted to plain text since DingTalk doesn’t render them. Long messages are split into chunks at ~3800 characters.
Working indicator:
A 👀 emoji reaction is added to the user’s message while processing, then removed when the response is sent.
Media download:
Two-step process — a
downloadCode
from the message is exchanged for a temporary download URL via DingTalk’s API.
Groups:
DingTalk uses
isInAtList
for @mention detection instead of parsing message entities.
Tips
Use DingTalk markdown-aware instructions
— DingTalk supports a limited markdown subset (headers, bold, links, code blocks, but not tables). Adding instructions like “Use DingTalk markdown. Avoid tables.” helps the agent format responses correctly.
Restrict access
— In an organization context,
senderPolicy: "open"
may be acceptable. For tighter control, use
"allowlist"
or
"pairing"
. See
DM Pairing
for details.
Referenced messages
— Quoting (replying to) a user message includes the quoted text as context for the agent. Quoting bot responses is not yet supported.
Troubleshooting
Bot doesn’t connect
Verify your AppKey and AppSecret are correct
Check that the environment variables are set before running
qwen channel start
Make sure
Stream Mode
is enabled in the bot’s settings on the DingTalk Developer Portal
Check the terminal output for connection errors
Bot doesn’t respond in groups
Check that
groupPolicy
is set to
"allowlist"
or
"open"
(default is
"disabled"
)
Make sure you @mention the bot in the group message
Verify the bot has been added to the group
”No sessionWebhook in message”
This means DingTalk didn’t include a reply endpoint in the message callback. This can happen if the bot’s permissions are misconfigured. Check the bot’s settings in the Developer Portal.
”Sorry, something went wrong processing your message”
This usually means the agent encountered an error. Check the terminal output for details.
Last updated on
May 18, 2026
WeChat
Plugins</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/dingtalk/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
DingTalk
DingTalk (Dingtalk)
This guide covers setting up a Qwen Code channel on DingTalk (钉钉).
Prerequisites
A DingTalk organization account
A DingTalk bot application with AppKey and AppSecret (see below)
Creating a Bot
Go to the
DingTalk Developer Portal
Create a new application (or use an existing one)
Under the application, enable the
Robot
capability
In Robot settings, enable
Stream Mode
(机器人协议 → Stream 模式)
Note the
AppKey
(Client ID) and
AppSecret
(Client Secr...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
DingTalk
DingTalk (Dingtalk)
This guide covers setting up a Qwen Code channel on DingTalk (钉钉).
Prerequisites
A DingTalk organization account
A DingTalk bot application with AppKey and AppSecret (see below)
Creating a Bot
Go to the
DingTalk Developer Portal
Create a new application (or use an existing one)
Under the application, enable the
Robot
capability
In Robot settings, enable
Stream Mode
(机器人协议 → Stream 模式)
Note the
AppKey
(Client ID) and
AppSecret
(Client Secret) from the application credentials page
Stream Mode
DingTalk Stream mode uses an outbound WebSocket connection — no public URL or server is needed. The bot connects to DingTalk’s servers, which push messages through the WebSocket. This is the simplest deployment model.
Configuration
Add the channel to
~/.qwen/settings.json
:
{
"channels"
: {
"my-dingtalk"
: {
"type"
:
"dingtalk"
,
"clientId"
:
"$DINGTALK_CLIENT_ID"
,
"clientSecret"
:
"$DINGTALK_CLIENT_SECRET"
,
"senderPolicy"
:
"open"
,
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/your/project"
,
"instructions"
:
"You are a concise coding assistant responding via DingTalk."
,
"groupPolicy"
:
"open"
,
"groups"
: {
"*"
: {
"requireMention"
:
true
}
}
}
}
}
Set the credentials as environment variables:
export
DINGTALK_CLIENT_ID
=<
your-app-key
>
export
DINGTALK_CLIENT_SECRET
=<
your-app-secret
>
Or define them in the
env
section of
settings.json
:
{
"env"
: {
"DINGTALK_CLIENT_ID"
:
"your-app-key"
,
"DINGTALK_CLIENT_SECRET"
:
"your-app-secret"
}
}
Running
# Start only the DingTalk channel
qwen
channel
start
my-dingtalk
# Or start all configured channels together
qwen
channel
start
Open DingTalk and send a message to the bot. You should see a 👀 emoji reaction appear while the agent processes, followed by the response.
Group Chats
DingTalk bots work in both DM and group conversations. To enable group support:
Set
groupPolicy
to
"allowlist"
or
"open"
in your channel config
Add the bot to a DingTalk group
@mention the bot in the group to trigger a response
By default, the bot requires an @mention in group chats (
requireMention: true
). Set
"requireMention": false
for a specific group to make it respond to all messages. See
Group Chats
for full details.
Finding a Group’s Conversation ID
DingTalk uses
conversationId
to identify groups. You can find it in the channel service logs when someone sends a message in the group — look for the
conversationId
field in the log output.
Images and Files
You can send photos and documents to the bot, not just text.
Photos:
Send an image (screenshot, diagram, etc.) and the agent will analyze it using its vision capabilities. This requires a multimodal model — add
"model": "qwen3.5-plus"
(or another vision-capable model) to your channel config. DingTalk supports sending images directly or as part of rich text messages (mixed text + images).
Files:
Send a PDF, code file, or any document. The bot downloads it from DingTalk’s servers and saves it locally so the agent can read it with its file tools. Audio and video files are also supported. This works with any model.
Key Differences from Telegram
Authentication:
AppKey + AppSecret instead of a static bot token. The SDK manages access token refresh automatically.
Connection:
WebSocket stream instead of polling — no public IP or webhook URL needed.
Formatting:
Responses use DingTalk’s markdown dialect (a limited subset). Tables are automatically converted to plain text since DingTalk doesn’t render them. Long messages are split into chunks at ~3800 characters.
Working indicator:
A 👀 emoji reaction is added to the user’s message while processing, then removed when the response is sent.
Media download:
Two-step process — a
downloadCode
from the message is exchanged for a temporary download URL via DingTalk’s API.
Groups:
DingTalk uses
isInAtList
for @mention detection instead of parsing message entities.
Tips
Use DingTalk markdown-aware instructions
— DingTalk supports a limited markdown subset (headers, bold, links, code blocks, but not tables). Adding instructions like “Use DingTalk markdown. Avoid tables.” helps the agent format responses correctly.
Restrict access
— In an organization context,
senderPolicy: "open"
may be acceptable. For tighter control, use
"allowlist"
or
"pairing"
. See
DM Pairing
for details.
Referenced messages
— Quoting (replying to) a user message includes the quoted text as context for the agent. Quoting bot responses is not yet supported.
Troubleshooting
Bot doesn’t connect
Verify your AppKey and AppSecret are correct
Check that the environment variables are set before running
qwen channel start
Make sure
Stream Mode
is enabled in the bot’s settings on the DingTalk Developer Portal
Check the terminal output for connection errors
Bot doesn’t respond in groups
Check that
groupPolicy
is set to
"allowlist"
or
"open"
(default is
"disabled"
)
Make sure you @mention the bot in the group message
Verify the bot has been added to the group
”No sessionWebhook in message”
This means DingTalk didn’t include a reply endpoint in the message callback. This can happen if the bot’s permissions are misconfigured. Check the bot’s settings in the Developer Portal.
”Sorry, something went wrong processing your message”
This usually means the agent encountered an error. Check the terminal output for details.
Last updated on
May 18, 2026
WeChat
Plugins</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/dingtalk/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Phase 1 技术设计文档：基础设施重建</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase1-technical-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase1-technical-design/</guid>
  <pubDate>Tue, 17 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Phase 1 技术设计文档：基础设施重建
Phase 1 技术设计文档：基础设施重建
1. 设计目标与约束
1.1 目标
建立统一的命令元数据模型，覆盖来源（source）、执行类型（commandType）、模式能力（supportedModes）、可见性（userInvocable / modelInvocable）四个维度
用 capability-based 过滤替换 non-interactive/acp 中的硬编码白名单
为 Phase 2/3 的能力扩展提供稳定的底层接口
1.2 硬性约束
零行为变化
：non-interactive 和 acp 模式下现有可用命令集保持不变（例外：修复 MCP_PROMPT 被错误拦截，属于 bug fix）
向后兼容
：
SlashCommand
接口的新增字段全部为可选或有合理默认值，现有命令代码无需立即修改
不新增执行器
：不创建 ModeAdapter / CommandExecutor 等新执行架构，只扩展现有 CommandService 和过滤逻辑
不改变现有命令能力
：不为任何...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Phase 1 技术设计文档：基础设施重建
Phase 1 技术设计文档：基础设施重建
1. 设计目标与约束
1.1 目标
建立统一的命令元数据模型，覆盖来源（source）、执行类型（commandType）、模式能力（supportedModes）、可见性（userInvocable / modelInvocable）四个维度
用 capability-based 过滤替换 non-interactive/acp 中的硬编码白名单
为 Phase 2/3 的能力扩展提供稳定的底层接口
1.2 硬性约束
零行为变化
：non-interactive 和 acp 模式下现有可用命令集保持不变（例外：修复 MCP_PROMPT 被错误拦截，属于 bug fix）
向后兼容
：
SlashCommand
接口的新增字段全部为可选或有合理默认值，现有命令代码无需立即修改
不新增执行器
：不创建 ModeAdapter / CommandExecutor 等新执行架构，只扩展现有 CommandService 和过滤逻辑
不改变现有命令能力
：不为任何命令新增 local 子命令，不修改任何命令的 action 实现
2. 新增类型定义
2.1 文件位置
所有新增类型定义在
packages/cli/src/ui/commands/types.ts
，与现有
SlashCommand
接口共文件。
2.2
ExecutionMode
/**
* 运行模式枚举。
* - interactive：React/Ink UI 模式（终端交互）
* - non_interactive：无交互 CLI 模式（文本/JSON 输出）
* - acp：ACP/Zed 集成模式
*/
export
type
ExecutionMode
=
'interactive'
|
'non_interactive'
|
'acp'
;
2.3
CommandSource
/**
* 命令来源枚举，用于 Help 分组、补全 badge、ACP available commands。
*
* 与 CommandKind 的区别：
* - CommandKind 是内部加载器分类（4 种），影响加载逻辑
* - CommandSource 是面向用户的来源分类（9 种），影响展示和心智模型
*
* 两者可能重叠，但职责不同，不合并。
*/
export
type
CommandSource
=
|
'builtin-command'
// 内置命令（BuiltinCommandLoader）
|
'bundled-skill'
// 随包分发的 skill（BundledSkillLoader）
|
'skill-dir-command'
// 用户/项目 .qwen/commands/ 下的文件命令（FileCommandLoader，非插件）
|
'plugin-command'
// 插件提供的命令（FileCommandLoader，extensionName 不为空）
|
'mcp-prompt'
;
// MCP server 提供的 prompt（McpPromptLoader）
// 以下来源预留，Phase 1 不实现对应 Loader，但 schema 先定义：
// | 'workflow-command'
// | 'plugin-skill'
// | 'dynamic-skill'
// | 'builtin-plugin-skill'
// | 'mcp-skill'
2.4
CommandType
/**
* 命令执行类型，描述命令"怎么执行"。
*
* - prompt：产生 submit_prompt，将内容提交给模型。适用于 skill、file command、MCP prompt。
*   默认 supportedModes 为所有模式，默认 modelInvocable 为 true。
*
* - local：在本地执行逻辑，不依赖 React/Ink UI。可返回 message、stream_messages、
*   submit_prompt、tool 等类型。适用于查询类、配置类、状态类 built-in 命令。
*   默认 supportedModes 为 ['interactive']，需显式声明 supportedModes 才能开放给其他模式。
*   这与 Claude Code 的 supportsNonInteractive: true 语义一致——非交互支持需要显式声明，而非自动推断。
*
* - local-jsx：依赖 React/Ink UI 的命令（打开 dialog、渲染 JSX 组件等）。
*   默认 supportedModes 仅为 ['interactive']。
*/
export
type
CommandType
=
'prompt'
|
'local'
|
'local-jsx'
;
2.5 扩展
SlashCommand
接口
在现有接口上追加新字段，
全部为可选
以保证向后兼容：
export
interface
SlashCommand
{
// ── 现有字段（保持不变） ──────────────────────────────────────────────
name
:
string
;
altNames
?:
string
[];
description
:
string
;
hidden
?:
boolean
;
completionPriority
?:
number
;
kind
:
CommandKind
;
extensionName
?:
string
;
action
?:
(...)
=>
...;
completion
?:
(...)
=>
...;
subCommands
?:
SlashCommand
[];
// ── Phase 1 新增：来源与执行类型 ──────────────────────────────────────
/**
* 命令来源，用于 Help 分组、补全 badge、ACP available commands 展示。
* 由各 Loader 填充，不由命令自身声明。
* 未来废弃 CommandKind 时，source 将成为唯一来源标识。
*/
source
?:
CommandSource
;
/**
* 展示用的来源标签，面向用户。
* - builtin-command → "Built-in"
* - bundled-skill → "Skill"
* - skill-dir-command → "Custom"
* - plugin-command → "Plugin: <extensionName>"
* - mcp-prompt → "MCP: <serverName>"
* 由各 Loader 填充，可被命令自身覆盖。
*/
sourceLabel
?:
string
;
/**
* 命令执行类型。
* - 由各 Loader 填充默认值（prompt/local-jsx）
* - built-in 命令由各命令文件自身声明（local 或 local-jsx）
* 未声明时的默认策略见 getEffectiveCommandType()。
*/
commandType
?:
CommandType
;
// ── Phase 1 新增：模式能力 ──────────────────────────────────────────
/**
* 此命令在哪些运行模式下可用。
* 未声明时根据 commandType 推断默认值（见 getEffectiveSupportedModes()）。
* 显式声明优先于推断值。
*/
supportedModes
?:
ExecutionMode
[];
// ── Phase 1 新增：可见性 ──────────────────────────────────────────────
/**
* 用户是否可通过 slash command 调用此命令。
* 默认 true（几乎所有命令都是 userInvocable）。
*/
userInvocable
?:
boolean
;
/**
* 模型是否可通过 tool call 调用此命令。
* 默认 false。prompt 类型的命令（skill、file command、MCP prompt）应设为 true。
* built-in commands 不允许模型调用（始终为 false）。
*/
modelInvocable
?:
boolean
;
// ── Phase 3 预留：体验元数据（Phase 1 仅定义，不使用）──────────────────
/**
* 参数提示，显示在补全菜单命令名后。
* 示例："<model-id>" / "show|list|set <id>" / "[--fast] [<model-id>]"
*/
argumentHint
?:
string
;
/**
* 供模型理解何时调用此命令的说明。
* 将被注入 modelInvocable 命令的 description 中。
*/
whenToUse
?:
string
;
/**
* 使用示例，供 Help 目录和补全展示。
*/
examples
?:
string
[];
}
3. 各 Loader 的字段填充规范
3.1 填充原则
source
和
sourceLabel
由 Loader 在构建
SlashCommand
时填充，命令自身不声明
commandType
：Loader 填充默认值；built-in 命令由命令文件自身声明
supportedModes
：通过
getEffectiveSupportedModes()
推断，不需要显式填充（除非需要覆盖默认值）
modelInvocable
：Loader 填充，built-in 命令始终为
false
，prompt 类型命令为
true
3.2
BuiltinCommandLoader
// 不填充 source/sourceLabel/commandType — 由各命令文件自声明
// 因为 built-in 命令的 commandType 是 local 或 local-jsx，需要逐个标注
// 注入 source 和 sourceLabel：
for
(
const
cmd
of
rawCommands) {
enrichedCommands.
push
({
...
cmd,
source:
'builtin-command'
,
sourceLabel:
'Built-in'
,
userInvocable: cmd.userInvocable
??
true
,
modelInvocable:
false
,
// built-in 命令不允许模型调用
});
}
3.3
BundledSkillLoader
return
skills.
map
((
skill
)
=>
({
name: skill.name,
description: skill.description,
kind: CommandKind.
SKILL
,
source:
'bundled-skill'
as
CommandSource
,
sourceLabel:
'Skill'
,
commandType:
'prompt'
as
CommandType
,
userInvocable:
true
,
modelInvocable:
true
,
action:
async
(
...
)
=>
{
...
},
}));
3.4
FileCommandLoader
// 在 createSlashCommandFromDefinition 中：
return
{
name: baseCommandName,
description,
kind: CommandKind.
FILE
,
extensionName,
// source 根据 extensionName 决定：
source: extensionName
?
'plugin-command'
:
'skill-dir-command'
,
sourceLabel: extensionName
?
`Plugin: ${
extensionName
}`
:
'Custom'
,
commandType:
'prompt'
,
userInvocable:
true
,
modelInvocable:
!
extensionName,
// 插件命令暂不允许模型调用，用户/项目命令允许
action:
async
(
...
)
=>
{
...
},
};
注
：插件命令（plugin-command）暂不标记为
modelInvocable
，避免安全隐患。后续 Phase 可以按需开放，由用户通过配置控制。
3.5
McpPromptLoader
const
newPromptCommand
:
SlashCommand
=
{
name: commandName,
description: prompt.description
||
`Invoke prompt ${
prompt
.
name
}`
,
kind: CommandKind.
MCP_PROMPT
,
source:
'mcp-prompt'
,
sourceLabel:
`MCP: ${
serverName
}`
,
commandType:
'prompt'
,
userInvocable:
true
,
modelInvocable:
true
,
// ... 其余现有字段
};
4. Built-in 命令的
commandType
声明规范
4.1 分类标准
commandType
判断标准
local
action 只使用
ui.addItem
（文本类型）、返回
message
/
stream_messages
/
submit_prompt
/
tool
，不依赖 React 组件渲染
local-jsx
action 返回
dialog
，或 action 中调用
ui.addItem
时传入含 JSX 的复杂类型（如
HistoryItemHelp
、
HistoryItemStats
），或依赖
confirm_action
/
load_history
/
quit
注意
：
ui.addItem(message/error/info 类型)
是
local
；
ui.addItem(help/stats/tools/about 等复杂 UI 类型)
是
local-jsx
。
4.2 Built-in 命令分类表
local
类
（声明
commandType: 'local'
，
supportedModes
推断为 all modes）：
命令文件
命令名
说明
btwCommand.ts
btw
返回
submit_prompt
或
stream_messages
bugCommand.ts
bug
返回
submit_prompt
或
stream_messages
compressCommand.ts
compress
已有 executionMode 适配，返回
message
/
submit_prompt
contextCommand.ts
context
返回
message
（含 UI 渲染但文本可替代）
exportCommand.ts
export
文件 I/O，返回
message
initCommand.ts
init
返回
submit_prompt
/
message
/
confirm_action
memoryCommand.ts
memory
子命令返回
message
（文件 I/O）
planCommand.ts
plan
返回
submit_prompt
summaryCommand.ts
summary
已有 executionMode 适配，返回
submit_prompt
/
message
insightCommand.ts
insight
返回
stream_messages
注意
：
contextCommand
和
insightCommand
虽然当前返回
addItem
调用，但其本质是文本内容，属于
local
。
local-jsx
类
（声明
commandType: 'local-jsx'
，
supportedModes
推断为
['interactive']
）：
命令文件
命令名
不能 headless 的原因
aboutCommand.ts
about
addItem(HistoryItemAbout)
— 复杂 UI 组件
agentsCommand.ts
agents
dialog: subagent_create/subagent_list
approvalModeCommand.ts
approval-mode
dialog: approval-mode
arenaCommand.ts
arena
dialog: arena_*
authCommand.ts
auth
dialog: auth
clearCommand.ts
clear
ui.clear()
直接操作终端
copyCommand.ts
copy
剪贴板操作，无 headless 路径
directoryCommand.tsx
directory
JSX 组件
docsCommand.ts
docs
打开浏览器
editorCommand.ts
editor
dialog: editor
extensionsCommand.ts
extensions
dialog: extensions_manage
helpCommand.ts
help
addItem(HistoryItemHelp)
— 复杂 Help UI
hooksCommand.ts
hooks
dialog: hooks
ideCommand.ts
ide
IDE 进程检测与交互
languageCommand.ts
language
dialog
+
reloadCommands
mcpCommand.ts
mcp
dialog: mcp
modelCommand.ts
model
dialog: model/fast-model
permissionsCommand.ts
permissions
dialog: permissions
quitCommand.ts
quit
quit
result 类型
restoreCommand.ts
restore
load_history
result 类型
resumeCommand.ts
resume
dialog: resume
settingsCommand.ts
settings
dialog: settings
setupGithubCommand.ts
setup-github
confirm_shell_commands
+ 交互式操作
skillsCommand.ts
skills
addItem(HistoryItemSkillsList)
— 复杂 UI
statsCommand.ts
stats
addItem(HistoryItemStats)
— 复杂 UI
statuslineCommand.ts
statusline
UI 状态配置
terminalSetupCommand.ts
terminal-setup
终端配置向导
themeCommand.ts
theme
dialog: theme
toolsCommand.ts
tools
addItem(HistoryItemTools)
— 复杂 UI
trustCommand.ts
trust
dialog: trust
vimCommand.ts
vim
toggleVimEnabled()
— UI 状态
5.
getEffectiveSupportedModes
推断规则
此函数是 Phase 1 的核心逻辑，替代原有白名单，将被
filterCommandsForMode
调用。
/**
* 获取命令的实际支持模式列表。
*
* 推断优先级（从高到低）：
* 1. 命令显式声明的 supportedModes（最高优先级）
* 2. 基于 commandType 的推断
* 3. 基于 CommandKind 的兜底（向后兼容）
*/
export
function
getEffectiveSupportedModes
(
cmd
:
SlashCommand
)
:
ExecutionMode
[] {
// 优先级 1：显式声明
if
(cmd.supportedModes
!==
undefined
) {
return
cmd.supportedModes;
}
// 优先级 2：基于 commandType 推断
if
(cmd.commandType
!==
undefined
) {
switch
(cmd.commandType) {
case
'prompt'
:
// prompt 类型无 UI 依赖，天然全模式可用
return
[
'interactive'
,
'non_interactive'
,
'acp'
];
case
'local'
:
// local 类型保守默认：仅 interactive。
// 需要非交互支持的命令须显式声明 supportedModes（对应 Claude Code 的 supportsNonInteractive: true）。
// Phase 2 中逐个验证并解锁，防止未适配的命令意外暴露给 headless 调用者。
return
[
'interactive'
];
case
'local-jsx'
:
return
[
'interactive'
];
}
}
// 优先级 3：兜底（基于 CommandKind，向后兼容旧代码）
switch
(cmd.kind) {
case
CommandKind.
BUILT_IN
:
// built-in 命令未声明 commandType 时保守默认（interactive only）
// 这个分支在 Phase 1 完成后应不再被命中（所有 built-in 都有 commandType）
return
[
'interactive'
];
case
CommandKind.
FILE
:
case
CommandKind.
SKILL
:
case
CommandKind.
MCP_PROMPT
:
// 这三类命令的 action 天然无 UI 依赖，历史行为也是全模式可用
return
[
'interactive'
,
'non_interactive'
,
'acp'
];
default
:
return
[
'interactive'
];
}
}
/**
* 根据 supportedModes 过滤适合当前模式的命令。
* 替代原 filterCommandsForNonInteractive 函数。
*/
export
function
filterCommandsForMode
(
commands
:
readonly
SlashCommand
[],
mode
:
ExecutionMode
,
)
:
SlashCommand
[] {
return
commands.
filter
((
cmd
)
=>
getEffectiveSupportedModes
(cmd).
includes
(mode),
);
}
6.
CommandService
接口扩展
在
packages/cli/src/services/CommandService.ts
中新增两个方法：
export
class
CommandService
{
// ── 现有方法（保持不变）────────────────────────────────────────────────
getCommands
()
:
readonly
SlashCommand
[] {
return
this
.commands;
}
// ── Phase 1 新增方法 ──────────────────────────────────────────────────
/**
* 返回在指定执行模式下可用的命令列表。
* 替代原有白名单 + filterCommandsForNonInteractive 的组合。
*
*
@param
mode
目标运行模式
*
@returns
适合该模式的命令列表（不含 hidden 命令）
*/
getCommandsForMode
(
mode
:
ExecutionMode
)
:
readonly
SlashCommand
[] {
return
this
.commands.
filter
((
cmd
)
=>
{
if
(cmd.hidden)
return
false
;
return
getEffectiveSupportedModes
(cmd).
includes
(mode);
});
}
/**
* 返回所有 modelInvocable 为 true 的命令。
* Phase 2 中 SkillTool 将消费此方法；Phase 1 仅提供接口。
*
*
@returns
模型可调用的命令列表
*/
getModelInvocableCommands
()
:
readonly
SlashCommand
[] {
return
this
.commands.
filter
(
(
cmd
)
=>
!
cmd.hidden
&&
cmd.modelInvocable
===
true
,
);
}
}
注意
：
getEffectiveSupportedModes
和
filterCommandsForMode
应作为
CommandService
内部使用的工具函数，或提取到独立的
packages/cli/src/services/commandUtils.ts
文件并导出，以便测试和复用。
7.
nonInteractiveCliCommands.ts
重构
7.1 删除内容
// ❌ 删除
export
const
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
=
[
'init'
,
'summary'
,
'compress'
,
'btw'
,
'bug'
,
'context'
,
]
as
const
;
// ❌ 删除
function
filterCommandsForNonInteractive
(
commands
:
readonly
SlashCommand
[],
allowedBuiltinCommandNames
:
Set
<
string
>,
)
:
SlashCommand
[] {
...
}
7.2 新增内容
// ✅ 新增（或从 commandUtils 导入）
import
{ filterCommandsForMode }
from
'../services/commandUtils.js'
;
7.3
handleSlashCommand
函数签名变更
// ❌ 旧签名
export
const
handleSlashCommand
=
async
(
rawQuery
:
string
,
abortController
:
AbortController
,
config
:
Config
,
settings
:
LoadedSettings
,
allowedBuiltinCommandNames
:
string
[]
=
[
...
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
],
):
Promise
<
NonInteractiveSlashCommandResult
>
// ✅ 新签名（移除 allowedBuiltinCommandNames）
export
const
handleSlashCommand
=
async
(
rawQuery
:
string
,
abortController
:
AbortController
,
config
:
Config
,
settings
:
LoadedSettings
,
):
Promise
<
NonInteractiveSlashCommandResult
>
7.4 内部实现变更
// 旧：
const
filteredCommands
=
filterCommandsForNonInteractive
(
allCommands,
allowedBuiltinSet,
);
// 新：
const
executionMode
=
isAcpMode
?
'acp'
:
'non_interactive'
;
const
filteredCommands
=
filterCommandsForMode
(allCommands, executionMode);
7.5
getAvailableCommands
函数签名变更
// ❌ 旧签名
export
const
getAvailableCommands
=
async
(
config
:
Config
,
abortSignal
:
AbortSignal
,
allowedBuiltinCommandNames
:
string
[]
=
[
...
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
],
):
Promise
<
SlashCommand[]
>
// ✅ 新签名
export
const
getAvailableCommands
=
async
(
config
:
Config
,
abortSignal
:
AbortSignal
,
mode
:
ExecutionMode
=
'acp'
,
):
Promise
<
SlashCommand[]
>
新增
mode
参数替代原来的白名单参数，ACP Session 调用时可明确指定
'acp'
，non-interactive 调用时指定
'non_interactive'
。
8.
Session.ts
（ACP）调用变更
// ❌ 旧调用
const
slashCommandResult
=
await
handleSlashCommand
(
inputText,
abortController,
this
.config,
this
.settings,
// 不传，使用默认白名单
);
// ✅ 新调用（无变化，移除了不再存在的默认参数）
const
slashCommandResult
=
await
handleSlashCommand
(
inputText,
abortController,
this
.config,
this
.settings,
);
// ─────────────────────────────────────────
// ❌ 旧调用
const
slashCommands
=
await
getAvailableCommands
(
this
.config,
abortController.signal,
);
// ✅ 新调用（明确指定 mode）
const
slashCommands
=
await
getAvailableCommands
(
this
.config,
abortController.signal,
'acp'
,
);
9. 文件变更总览
9.1 修改的文件
文件
修改内容
packages/cli/src/ui/commands/types.ts
新增
ExecutionMode
、
CommandSource
、
CommandType
类型；扩展
SlashCommand
接口
packages/cli/src/services/CommandService.ts
新增
getCommandsForMode()
、
getModelInvocableCommands()
方法
packages/cli/src/nonInteractiveCliCommands.ts
删除白名单常量和旧过滤函数；更新两个导出函数的签名；引入
filterCommandsForMode
packages/cli/src/acp-integration/session/Session.ts
更新
handleSlashCommand
和
getAvailableCommands
调用
packages/cli/src/services/BuiltinCommandLoader.ts
在构建命令时注入
source: 'builtin-command'
、
sourceLabel: 'Built-in'
、
modelInvocable: false
packages/cli/src/services/BundledSkillLoader.ts
注入
source: 'bundled-skill'
、
commandType: 'prompt'
、
modelInvocable: true
packages/cli/src/services/FileCommandLoader.ts
/
command-factory.ts
注入
source
、
commandType: 'prompt'
、
modelInvocable
（根据 extensionName）
packages/cli/src/services/McpPromptLoader.ts
注入
source: 'mcp-prompt'
、
commandType: 'prompt'
、
modelInvocable: true
各 built-in 命令文件（10 个 local + 27 个 local-jsx）
声明
commandType: 'local'
或
commandType: 'local-jsx'
9.2 新增的文件
文件
内容
packages/cli/src/services/commandUtils.ts
getEffectiveSupportedModes()
、
filterCommandsForMode()
工具函数及其导出
9.3 不变的文件
packages/cli/src/utils/commands.ts
（
parseSlashCommand
无需修改）
packages/cli/src/ui/hooks/slashCommandProcessor.ts
（interactive 路径无需修改）
packages/cli/src/ui/noninteractive/nonInteractiveUi.ts
（stub UI 无需修改）
所有命令的
action
实现（Phase 1 不修改任何命令行为）
10. 行为影响分析
10.1 变化汇总
场景
旧行为
新行为
性质
non-interactive 下执行
/init
✅ 允许（白名单）
✅ 允许（
commandType: local
）
无变化
non-interactive 下执行
/summary
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/compress
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/btw
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/bug
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/context
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/model
❌ unsupported
❌ unsupported（
commandType: local-jsx
）
无变化
non-interactive 下执行 file command
✅ 允许（CommandKind.FILE）
✅ 允许（
commandType: prompt
）
无变化
non-interactive 下执行 bundled skill
✅ 允许（CommandKind.SKILL）
✅ 允许（
commandType: prompt
）
无变化
non-interactive 下执行 MCP prompt
❌ 被 CommandKind 拦截
✅ 允许（
commandType: prompt
）
Bug fix
non-interactive 下执行
/export
❌ 不在白名单
❌ 不允许（
commandType: local
，默认 interactive only）
无变化
non-interactive 下执行
/memory
❌ 不在白名单
❌ 不允许（
commandType: local
，默认 interactive only）
无变化
non-interactive 下执行
/plan
❌ 不在白名单
❌ 不允许（
commandType: local
，默认 interactive only）
无变化
关于
local
命令的保守默认策略
：
commandType: 'local'
的默认
supportedModes
为
['interactive']
，这与 Claude Code 的设计一致——
local
类型命令需要显式声明
supportsNonInteractive: true
才能在非交互模式下运行。Phase 1 中白名单内的 6 个命令（
init
、
summary
、
compress
、
btw
、
bug
、
context
）通过显式声明
supportedModes: ['interactive', 'non_interactive', 'acp']
来等价替换原白名单效果。Phase 2 中需要扩展的命令（如
/export
、
/memory
、
/plan
）在验证 action 实现 headless-friendly 之后，再逐个解锁。
10.2 Phase 2 模式差异命令：双注册模式
对于 Phase 2 中需要”交互模式有 UI，非交互模式有文本输出”的命令（如
/model
），应采用
双注册模式
，而非在单个命令的
action
内部分支。
这是 Claude Code 的标准模式，以
/context
为例（参见
src/commands/context/index.ts
）：两个同名
Command
对象，一个
local-jsx
仅 interactive，另一个
local
仅 non-interactive，通过
isEnabled()
互斥。
Qwen Code 在 Phase 2 中应采用等价方式，以
supportedModes
替代
isEnabled()
实现互斥：
// ① 交互模式版：local-jsx，仅 interactive
export
const
modelCommandInteractive
:
SlashCommand
=
{
name:
'model'
,
kind: CommandKind.
BUILT_IN
,
commandType:
'local-jsx'
,
supportedModes: [
'interactive'
],
// 显式限定
// action: 打开 dialog 选择 model
};
// ② 非交互/acp 版：local，显式开放给 headless 调用者
export
const
modelCommandHeadless
:
SlashCommand
=
{
name:
'model'
,
kind: CommandKind.
BUILT_IN
,
commandType:
'local'
,
supportedModes: [
'non_interactive'
,
'acp'
],
// 显式限定
// action: 读取/设置 model，返回 message（纯文本）
};
两个对象同名，
supportedModes
互斥，
filterCommandsForMode
自动选择正确版本。与 Claude Code 的
isEnabled()
互斥相比，
supportedModes
过滤更显式、更易测试，且不需要运行时环境检测。
Phase 1 不实现任何双注册命令
，该模式仅作为 Phase 2 的实施规范预留在此。
11. 测试策略
11.1 新增工具函数测试
在
packages/cli/src/services/commandUtils.test.ts
（新文件）中：
describe
(
'getEffectiveSupportedModes'
, ()
=>
{
it
(
'显式 supportedModes 优先于 commandType 推断'
, ()
=>
{
const
cmd
:
SlashCommand
=
{
name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
,
commandType:
'local'
,
supportedModes: [
'interactive'
],
// 显式限制
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
]);
});
it
(
'commandType: local 推断为 all modes'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
, commandType:
'local'
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
it
(
'commandType: local-jsx 推断为 interactive only'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
, commandType:
'local-jsx'
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
]);
});
it
(
'commandType: prompt 推断为 all modes'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
SKILL
, commandType:
'prompt'
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
it
(
'未声明 commandType 且 CommandKind.BUILT_IN，兜底为 interactive'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
]);
});
it
(
'未声明 commandType 且 CommandKind.FILE，兜底为 all modes'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
FILE
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
it
(
'未声明 commandType 且 CommandKind.MCP_PROMPT，兜底为 all modes（修复原有限制）'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
MCP_PROMPT
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
});
describe
(
'filterCommandsForMode'
, ()
=>
{
it
(
'正确过滤 non_interactive 模式下的命令'
, ()
=>
{
...
});
it
(
'正确过滤 acp 模式下的命令'
, ()
=>
{
...
});
it
(
'不过滤 hidden 命令（filterCommandsForMode 不处理 hidden，CommandService 处理）'
, ()
=>
{
...
});
});
11.2 更新
nonInteractiveCliCommands.test.ts
删除对
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
的所有引用
删除对
allowedBuiltinCommandNames
参数的测试用例
新增：验证 commandType: local 的命令在 non-interactive 下通过过滤
新增：验证 commandType: local-jsx 的命令在 non-interactive 下被过滤
保留：验证 file command / skill command 在 non-interactive 下通过过滤
11.3 更新
CommandService.test.ts
新增
getCommandsForMode
的测试用例
新增
getModelInvocableCommands
的测试用例
11.4 各 Loader 测试
BuiltinCommandLoader.test.ts
：验证所有命令都有
source: 'builtin-command'
BundledSkillLoader.test.ts
：验证
source: 'bundled-skill'
和
modelInvocable: true
FileCommandLoader.test.ts
：验证用户命令有
source: 'skill-dir-command'
，插件命令有
source: 'plugin-command'
McpPromptLoader.test.ts
：验证
source: 'mcp-prompt'
和
modelInvocable: true
12. 实施顺序
建议按以下顺序实施，每步可独立 commit 和 review：
Step 1
（~30min）：修改
types.ts
，新增
ExecutionMode
、
CommandSource
、
CommandType
和
SlashCommand
新字段
→ 纯类型变更，TypeScript 编译检查
Step 2
（~1h）：新建
commandUtils.ts
，实现
getEffectiveSupportedModes
和
filterCommandsForMode
，同步新建
commandUtils.test.ts
→ 单元测试覆盖核心逻辑
Step 3
（~1h）：重构
nonInteractiveCliCommands.ts
，删除白名单，引入
filterCommandsForMode
，更新函数签名
→ 行为等价（Phase 1 保守策略：local 类命令显式写
supportedModes: ['interactive']
）
Step 4
（~30min）：更新
CommandService.ts
，新增两个方法
Step 5
（~2h）：为所有 built-in 命令文件添加
commandType
声明
→ 逐个确认分类正确性
Step 6
（~1.5h）：更新所有 Loader，注入
source
、
sourceLabel
、
commandType
、
modelInvocable
Step 7
（~30min）：更新
Session.ts
的调用签名
Step 8
（~1h）：运行所有测试，修复失败用例，更新快照
Step 9
（~30min）：CR 自查：确认白名单已完全移除，无遗漏调用
13. 验收 Checklist
TypeScript 编译无错误（
npm run typecheck
）
npm run lint
无新增 lint 错误
所有现有测试通过（
cd packages/cli && npx vitest run
）
commandUtils.test.ts
新增测试全部通过
getEffectiveSupportedModes
覆盖所有 7 种 case
filterCommandsForMode
覆盖 interactive / non_interactive / acp 三种模式
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
在整个代码库中无任何引用（
grep
验证）
filterCommandsForNonInteractive
函数在整个代码库中无任何引用
所有 built-in 命令有
commandType
字段
所有 Loader 输出的命令有
source
和
sourceLabel
字段
BundledSkillLoader
/
FileCommandLoader
（用户命令）/
McpPromptLoader
输出的命令
modelInvocable: true
BuiltinCommandLoader
输出的命令
modelInvocable: false
CommandService.getCommandsForMode('non_interactive')
返回与重构前等价的命令集
MCP prompt 命令在 non-interactive 模式下不再被错误拦截
Last updated on
May 18, 2026
Qwen Code Command 模块重构方案
Phase 2 技术设计文档：能力扩展</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase1-technical-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Phase 1 技术设计文档：基础设施重建
Phase 1 技术设计文档：基础设施重建
1. 设计目标与约束
1.1 目标
建立统一的命令元数据模型，覆盖来源（source）、执行类型（commandType）、模式能力（supportedModes）、可见性（userInvocable / modelInvocable）四个维度
用 capability-based 过滤替换 non-interactive/acp 中的硬编码白名单
为 Phase 2/3 的能力扩展提供稳定的底层接口
1.2 硬性约束
零行为变化
：non-interactive 和 acp 模式下现有可用命令集保持不变（例外：修复 MCP_PROMPT 被错误拦截，属于 bug fix）
向后兼容
：
SlashCommand
接口的新增字段全部为可选或有合理默认值，现有命令代码无需立即修改
不新增执行器
：不创建 ModeAdapter / CommandExecutor 等新执行架构，只扩展现有 CommandService 和过滤逻辑
不改变现有命令能力
：不为任何...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Phase 1 技术设计文档：基础设施重建
Phase 1 技术设计文档：基础设施重建
1. 设计目标与约束
1.1 目标
建立统一的命令元数据模型，覆盖来源（source）、执行类型（commandType）、模式能力（supportedModes）、可见性（userInvocable / modelInvocable）四个维度
用 capability-based 过滤替换 non-interactive/acp 中的硬编码白名单
为 Phase 2/3 的能力扩展提供稳定的底层接口
1.2 硬性约束
零行为变化
：non-interactive 和 acp 模式下现有可用命令集保持不变（例外：修复 MCP_PROMPT 被错误拦截，属于 bug fix）
向后兼容
：
SlashCommand
接口的新增字段全部为可选或有合理默认值，现有命令代码无需立即修改
不新增执行器
：不创建 ModeAdapter / CommandExecutor 等新执行架构，只扩展现有 CommandService 和过滤逻辑
不改变现有命令能力
：不为任何命令新增 local 子命令，不修改任何命令的 action 实现
2. 新增类型定义
2.1 文件位置
所有新增类型定义在
packages/cli/src/ui/commands/types.ts
，与现有
SlashCommand
接口共文件。
2.2
ExecutionMode
/**
* 运行模式枚举。
* - interactive：React/Ink UI 模式（终端交互）
* - non_interactive：无交互 CLI 模式（文本/JSON 输出）
* - acp：ACP/Zed 集成模式
*/
export
type
ExecutionMode
=
'interactive'
|
'non_interactive'
|
'acp'
;
2.3
CommandSource
/**
* 命令来源枚举，用于 Help 分组、补全 badge、ACP available commands。
*
* 与 CommandKind 的区别：
* - CommandKind 是内部加载器分类（4 种），影响加载逻辑
* - CommandSource 是面向用户的来源分类（9 种），影响展示和心智模型
*
* 两者可能重叠，但职责不同，不合并。
*/
export
type
CommandSource
=
|
'builtin-command'
// 内置命令（BuiltinCommandLoader）
|
'bundled-skill'
// 随包分发的 skill（BundledSkillLoader）
|
'skill-dir-command'
// 用户/项目 .qwen/commands/ 下的文件命令（FileCommandLoader，非插件）
|
'plugin-command'
// 插件提供的命令（FileCommandLoader，extensionName 不为空）
|
'mcp-prompt'
;
// MCP server 提供的 prompt（McpPromptLoader）
// 以下来源预留，Phase 1 不实现对应 Loader，但 schema 先定义：
// | 'workflow-command'
// | 'plugin-skill'
// | 'dynamic-skill'
// | 'builtin-plugin-skill'
// | 'mcp-skill'
2.4
CommandType
/**
* 命令执行类型，描述命令"怎么执行"。
*
* - prompt：产生 submit_prompt，将内容提交给模型。适用于 skill、file command、MCP prompt。
*   默认 supportedModes 为所有模式，默认 modelInvocable 为 true。
*
* - local：在本地执行逻辑，不依赖 React/Ink UI。可返回 message、stream_messages、
*   submit_prompt、tool 等类型。适用于查询类、配置类、状态类 built-in 命令。
*   默认 supportedModes 为 ['interactive']，需显式声明 supportedModes 才能开放给其他模式。
*   这与 Claude Code 的 supportsNonInteractive: true 语义一致——非交互支持需要显式声明，而非自动推断。
*
* - local-jsx：依赖 React/Ink UI 的命令（打开 dialog、渲染 JSX 组件等）。
*   默认 supportedModes 仅为 ['interactive']。
*/
export
type
CommandType
=
'prompt'
|
'local'
|
'local-jsx'
;
2.5 扩展
SlashCommand
接口
在现有接口上追加新字段，
全部为可选
以保证向后兼容：
export
interface
SlashCommand
{
// ── 现有字段（保持不变） ──────────────────────────────────────────────
name
:
string
;
altNames
?:
string
[];
description
:
string
;
hidden
?:
boolean
;
completionPriority
?:
number
;
kind
:
CommandKind
;
extensionName
?:
string
;
action
?:
(...)
=>
...;
completion
?:
(...)
=>
...;
subCommands
?:
SlashCommand
[];
// ── Phase 1 新增：来源与执行类型 ──────────────────────────────────────
/**
* 命令来源，用于 Help 分组、补全 badge、ACP available commands 展示。
* 由各 Loader 填充，不由命令自身声明。
* 未来废弃 CommandKind 时，source 将成为唯一来源标识。
*/
source
?:
CommandSource
;
/**
* 展示用的来源标签，面向用户。
* - builtin-command → "Built-in"
* - bundled-skill → "Skill"
* - skill-dir-command → "Custom"
* - plugin-command → "Plugin: <extensionName>"
* - mcp-prompt → "MCP: <serverName>"
* 由各 Loader 填充，可被命令自身覆盖。
*/
sourceLabel
?:
string
;
/**
* 命令执行类型。
* - 由各 Loader 填充默认值（prompt/local-jsx）
* - built-in 命令由各命令文件自身声明（local 或 local-jsx）
* 未声明时的默认策略见 getEffectiveCommandType()。
*/
commandType
?:
CommandType
;
// ── Phase 1 新增：模式能力 ──────────────────────────────────────────
/**
* 此命令在哪些运行模式下可用。
* 未声明时根据 commandType 推断默认值（见 getEffectiveSupportedModes()）。
* 显式声明优先于推断值。
*/
supportedModes
?:
ExecutionMode
[];
// ── Phase 1 新增：可见性 ──────────────────────────────────────────────
/**
* 用户是否可通过 slash command 调用此命令。
* 默认 true（几乎所有命令都是 userInvocable）。
*/
userInvocable
?:
boolean
;
/**
* 模型是否可通过 tool call 调用此命令。
* 默认 false。prompt 类型的命令（skill、file command、MCP prompt）应设为 true。
* built-in commands 不允许模型调用（始终为 false）。
*/
modelInvocable
?:
boolean
;
// ── Phase 3 预留：体验元数据（Phase 1 仅定义，不使用）──────────────────
/**
* 参数提示，显示在补全菜单命令名后。
* 示例："<model-id>" / "show|list|set <id>" / "[--fast] [<model-id>]"
*/
argumentHint
?:
string
;
/**
* 供模型理解何时调用此命令的说明。
* 将被注入 modelInvocable 命令的 description 中。
*/
whenToUse
?:
string
;
/**
* 使用示例，供 Help 目录和补全展示。
*/
examples
?:
string
[];
}
3. 各 Loader 的字段填充规范
3.1 填充原则
source
和
sourceLabel
由 Loader 在构建
SlashCommand
时填充，命令自身不声明
commandType
：Loader 填充默认值；built-in 命令由命令文件自身声明
supportedModes
：通过
getEffectiveSupportedModes()
推断，不需要显式填充（除非需要覆盖默认值）
modelInvocable
：Loader 填充，built-in 命令始终为
false
，prompt 类型命令为
true
3.2
BuiltinCommandLoader
// 不填充 source/sourceLabel/commandType — 由各命令文件自声明
// 因为 built-in 命令的 commandType 是 local 或 local-jsx，需要逐个标注
// 注入 source 和 sourceLabel：
for
(
const
cmd
of
rawCommands) {
enrichedCommands.
push
({
...
cmd,
source:
'builtin-command'
,
sourceLabel:
'Built-in'
,
userInvocable: cmd.userInvocable
??
true
,
modelInvocable:
false
,
// built-in 命令不允许模型调用
});
}
3.3
BundledSkillLoader
return
skills.
map
((
skill
)
=>
({
name: skill.name,
description: skill.description,
kind: CommandKind.
SKILL
,
source:
'bundled-skill'
as
CommandSource
,
sourceLabel:
'Skill'
,
commandType:
'prompt'
as
CommandType
,
userInvocable:
true
,
modelInvocable:
true
,
action:
async
(
...
)
=>
{
...
},
}));
3.4
FileCommandLoader
// 在 createSlashCommandFromDefinition 中：
return
{
name: baseCommandName,
description,
kind: CommandKind.
FILE
,
extensionName,
// source 根据 extensionName 决定：
source: extensionName
?
'plugin-command'
:
'skill-dir-command'
,
sourceLabel: extensionName
?
`Plugin: ${
extensionName
}`
:
'Custom'
,
commandType:
'prompt'
,
userInvocable:
true
,
modelInvocable:
!
extensionName,
// 插件命令暂不允许模型调用，用户/项目命令允许
action:
async
(
...
)
=>
{
...
},
};
注
：插件命令（plugin-command）暂不标记为
modelInvocable
，避免安全隐患。后续 Phase 可以按需开放，由用户通过配置控制。
3.5
McpPromptLoader
const
newPromptCommand
:
SlashCommand
=
{
name: commandName,
description: prompt.description
||
`Invoke prompt ${
prompt
.
name
}`
,
kind: CommandKind.
MCP_PROMPT
,
source:
'mcp-prompt'
,
sourceLabel:
`MCP: ${
serverName
}`
,
commandType:
'prompt'
,
userInvocable:
true
,
modelInvocable:
true
,
// ... 其余现有字段
};
4. Built-in 命令的
commandType
声明规范
4.1 分类标准
commandType
判断标准
local
action 只使用
ui.addItem
（文本类型）、返回
message
/
stream_messages
/
submit_prompt
/
tool
，不依赖 React 组件渲染
local-jsx
action 返回
dialog
，或 action 中调用
ui.addItem
时传入含 JSX 的复杂类型（如
HistoryItemHelp
、
HistoryItemStats
），或依赖
confirm_action
/
load_history
/
quit
注意
：
ui.addItem(message/error/info 类型)
是
local
；
ui.addItem(help/stats/tools/about 等复杂 UI 类型)
是
local-jsx
。
4.2 Built-in 命令分类表
local
类
（声明
commandType: 'local'
，
supportedModes
推断为 all modes）：
命令文件
命令名
说明
btwCommand.ts
btw
返回
submit_prompt
或
stream_messages
bugCommand.ts
bug
返回
submit_prompt
或
stream_messages
compressCommand.ts
compress
已有 executionMode 适配，返回
message
/
submit_prompt
contextCommand.ts
context
返回
message
（含 UI 渲染但文本可替代）
exportCommand.ts
export
文件 I/O，返回
message
initCommand.ts
init
返回
submit_prompt
/
message
/
confirm_action
memoryCommand.ts
memory
子命令返回
message
（文件 I/O）
planCommand.ts
plan
返回
submit_prompt
summaryCommand.ts
summary
已有 executionMode 适配，返回
submit_prompt
/
message
insightCommand.ts
insight
返回
stream_messages
注意
：
contextCommand
和
insightCommand
虽然当前返回
addItem
调用，但其本质是文本内容，属于
local
。
local-jsx
类
（声明
commandType: 'local-jsx'
，
supportedModes
推断为
['interactive']
）：
命令文件
命令名
不能 headless 的原因
aboutCommand.ts
about
addItem(HistoryItemAbout)
— 复杂 UI 组件
agentsCommand.ts
agents
dialog: subagent_create/subagent_list
approvalModeCommand.ts
approval-mode
dialog: approval-mode
arenaCommand.ts
arena
dialog: arena_*
authCommand.ts
auth
dialog: auth
clearCommand.ts
clear
ui.clear()
直接操作终端
copyCommand.ts
copy
剪贴板操作，无 headless 路径
directoryCommand.tsx
directory
JSX 组件
docsCommand.ts
docs
打开浏览器
editorCommand.ts
editor
dialog: editor
extensionsCommand.ts
extensions
dialog: extensions_manage
helpCommand.ts
help
addItem(HistoryItemHelp)
— 复杂 Help UI
hooksCommand.ts
hooks
dialog: hooks
ideCommand.ts
ide
IDE 进程检测与交互
languageCommand.ts
language
dialog
+
reloadCommands
mcpCommand.ts
mcp
dialog: mcp
modelCommand.ts
model
dialog: model/fast-model
permissionsCommand.ts
permissions
dialog: permissions
quitCommand.ts
quit
quit
result 类型
restoreCommand.ts
restore
load_history
result 类型
resumeCommand.ts
resume
dialog: resume
settingsCommand.ts
settings
dialog: settings
setupGithubCommand.ts
setup-github
confirm_shell_commands
+ 交互式操作
skillsCommand.ts
skills
addItem(HistoryItemSkillsList)
— 复杂 UI
statsCommand.ts
stats
addItem(HistoryItemStats)
— 复杂 UI
statuslineCommand.ts
statusline
UI 状态配置
terminalSetupCommand.ts
terminal-setup
终端配置向导
themeCommand.ts
theme
dialog: theme
toolsCommand.ts
tools
addItem(HistoryItemTools)
— 复杂 UI
trustCommand.ts
trust
dialog: trust
vimCommand.ts
vim
toggleVimEnabled()
— UI 状态
5.
getEffectiveSupportedModes
推断规则
此函数是 Phase 1 的核心逻辑，替代原有白名单，将被
filterCommandsForMode
调用。
/**
* 获取命令的实际支持模式列表。
*
* 推断优先级（从高到低）：
* 1. 命令显式声明的 supportedModes（最高优先级）
* 2. 基于 commandType 的推断
* 3. 基于 CommandKind 的兜底（向后兼容）
*/
export
function
getEffectiveSupportedModes
(
cmd
:
SlashCommand
)
:
ExecutionMode
[] {
// 优先级 1：显式声明
if
(cmd.supportedModes
!==
undefined
) {
return
cmd.supportedModes;
}
// 优先级 2：基于 commandType 推断
if
(cmd.commandType
!==
undefined
) {
switch
(cmd.commandType) {
case
'prompt'
:
// prompt 类型无 UI 依赖，天然全模式可用
return
[
'interactive'
,
'non_interactive'
,
'acp'
];
case
'local'
:
// local 类型保守默认：仅 interactive。
// 需要非交互支持的命令须显式声明 supportedModes（对应 Claude Code 的 supportsNonInteractive: true）。
// Phase 2 中逐个验证并解锁，防止未适配的命令意外暴露给 headless 调用者。
return
[
'interactive'
];
case
'local-jsx'
:
return
[
'interactive'
];
}
}
// 优先级 3：兜底（基于 CommandKind，向后兼容旧代码）
switch
(cmd.kind) {
case
CommandKind.
BUILT_IN
:
// built-in 命令未声明 commandType 时保守默认（interactive only）
// 这个分支在 Phase 1 完成后应不再被命中（所有 built-in 都有 commandType）
return
[
'interactive'
];
case
CommandKind.
FILE
:
case
CommandKind.
SKILL
:
case
CommandKind.
MCP_PROMPT
:
// 这三类命令的 action 天然无 UI 依赖，历史行为也是全模式可用
return
[
'interactive'
,
'non_interactive'
,
'acp'
];
default
:
return
[
'interactive'
];
}
}
/**
* 根据 supportedModes 过滤适合当前模式的命令。
* 替代原 filterCommandsForNonInteractive 函数。
*/
export
function
filterCommandsForMode
(
commands
:
readonly
SlashCommand
[],
mode
:
ExecutionMode
,
)
:
SlashCommand
[] {
return
commands.
filter
((
cmd
)
=>
getEffectiveSupportedModes
(cmd).
includes
(mode),
);
}
6.
CommandService
接口扩展
在
packages/cli/src/services/CommandService.ts
中新增两个方法：
export
class
CommandService
{
// ── 现有方法（保持不变）────────────────────────────────────────────────
getCommands
()
:
readonly
SlashCommand
[] {
return
this
.commands;
}
// ── Phase 1 新增方法 ──────────────────────────────────────────────────
/**
* 返回在指定执行模式下可用的命令列表。
* 替代原有白名单 + filterCommandsForNonInteractive 的组合。
*
*
@param
mode
目标运行模式
*
@returns
适合该模式的命令列表（不含 hidden 命令）
*/
getCommandsForMode
(
mode
:
ExecutionMode
)
:
readonly
SlashCommand
[] {
return
this
.commands.
filter
((
cmd
)
=>
{
if
(cmd.hidden)
return
false
;
return
getEffectiveSupportedModes
(cmd).
includes
(mode);
});
}
/**
* 返回所有 modelInvocable 为 true 的命令。
* Phase 2 中 SkillTool 将消费此方法；Phase 1 仅提供接口。
*
*
@returns
模型可调用的命令列表
*/
getModelInvocableCommands
()
:
readonly
SlashCommand
[] {
return
this
.commands.
filter
(
(
cmd
)
=>
!
cmd.hidden
&&
cmd.modelInvocable
===
true
,
);
}
}
注意
：
getEffectiveSupportedModes
和
filterCommandsForMode
应作为
CommandService
内部使用的工具函数，或提取到独立的
packages/cli/src/services/commandUtils.ts
文件并导出，以便测试和复用。
7.
nonInteractiveCliCommands.ts
重构
7.1 删除内容
// ❌ 删除
export
const
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
=
[
'init'
,
'summary'
,
'compress'
,
'btw'
,
'bug'
,
'context'
,
]
as
const
;
// ❌ 删除
function
filterCommandsForNonInteractive
(
commands
:
readonly
SlashCommand
[],
allowedBuiltinCommandNames
:
Set
<
string
>,
)
:
SlashCommand
[] {
...
}
7.2 新增内容
// ✅ 新增（或从 commandUtils 导入）
import
{ filterCommandsForMode }
from
'../services/commandUtils.js'
;
7.3
handleSlashCommand
函数签名变更
// ❌ 旧签名
export
const
handleSlashCommand
=
async
(
rawQuery
:
string
,
abortController
:
AbortController
,
config
:
Config
,
settings
:
LoadedSettings
,
allowedBuiltinCommandNames
:
string
[]
=
[
...
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
],
):
Promise
<
NonInteractiveSlashCommandResult
>
// ✅ 新签名（移除 allowedBuiltinCommandNames）
export
const
handleSlashCommand
=
async
(
rawQuery
:
string
,
abortController
:
AbortController
,
config
:
Config
,
settings
:
LoadedSettings
,
):
Promise
<
NonInteractiveSlashCommandResult
>
7.4 内部实现变更
// 旧：
const
filteredCommands
=
filterCommandsForNonInteractive
(
allCommands,
allowedBuiltinSet,
);
// 新：
const
executionMode
=
isAcpMode
?
'acp'
:
'non_interactive'
;
const
filteredCommands
=
filterCommandsForMode
(allCommands, executionMode);
7.5
getAvailableCommands
函数签名变更
// ❌ 旧签名
export
const
getAvailableCommands
=
async
(
config
:
Config
,
abortSignal
:
AbortSignal
,
allowedBuiltinCommandNames
:
string
[]
=
[
...
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
],
):
Promise
<
SlashCommand[]
>
// ✅ 新签名
export
const
getAvailableCommands
=
async
(
config
:
Config
,
abortSignal
:
AbortSignal
,
mode
:
ExecutionMode
=
'acp'
,
):
Promise
<
SlashCommand[]
>
新增
mode
参数替代原来的白名单参数，ACP Session 调用时可明确指定
'acp'
，non-interactive 调用时指定
'non_interactive'
。
8.
Session.ts
（ACP）调用变更
// ❌ 旧调用
const
slashCommandResult
=
await
handleSlashCommand
(
inputText,
abortController,
this
.config,
this
.settings,
// 不传，使用默认白名单
);
// ✅ 新调用（无变化，移除了不再存在的默认参数）
const
slashCommandResult
=
await
handleSlashCommand
(
inputText,
abortController,
this
.config,
this
.settings,
);
// ─────────────────────────────────────────
// ❌ 旧调用
const
slashCommands
=
await
getAvailableCommands
(
this
.config,
abortController.signal,
);
// ✅ 新调用（明确指定 mode）
const
slashCommands
=
await
getAvailableCommands
(
this
.config,
abortController.signal,
'acp'
,
);
9. 文件变更总览
9.1 修改的文件
文件
修改内容
packages/cli/src/ui/commands/types.ts
新增
ExecutionMode
、
CommandSource
、
CommandType
类型；扩展
SlashCommand
接口
packages/cli/src/services/CommandService.ts
新增
getCommandsForMode()
、
getModelInvocableCommands()
方法
packages/cli/src/nonInteractiveCliCommands.ts
删除白名单常量和旧过滤函数；更新两个导出函数的签名；引入
filterCommandsForMode
packages/cli/src/acp-integration/session/Session.ts
更新
handleSlashCommand
和
getAvailableCommands
调用
packages/cli/src/services/BuiltinCommandLoader.ts
在构建命令时注入
source: 'builtin-command'
、
sourceLabel: 'Built-in'
、
modelInvocable: false
packages/cli/src/services/BundledSkillLoader.ts
注入
source: 'bundled-skill'
、
commandType: 'prompt'
、
modelInvocable: true
packages/cli/src/services/FileCommandLoader.ts
/
command-factory.ts
注入
source
、
commandType: 'prompt'
、
modelInvocable
（根据 extensionName）
packages/cli/src/services/McpPromptLoader.ts
注入
source: 'mcp-prompt'
、
commandType: 'prompt'
、
modelInvocable: true
各 built-in 命令文件（10 个 local + 27 个 local-jsx）
声明
commandType: 'local'
或
commandType: 'local-jsx'
9.2 新增的文件
文件
内容
packages/cli/src/services/commandUtils.ts
getEffectiveSupportedModes()
、
filterCommandsForMode()
工具函数及其导出
9.3 不变的文件
packages/cli/src/utils/commands.ts
（
parseSlashCommand
无需修改）
packages/cli/src/ui/hooks/slashCommandProcessor.ts
（interactive 路径无需修改）
packages/cli/src/ui/noninteractive/nonInteractiveUi.ts
（stub UI 无需修改）
所有命令的
action
实现（Phase 1 不修改任何命令行为）
10. 行为影响分析
10.1 变化汇总
场景
旧行为
新行为
性质
non-interactive 下执行
/init
✅ 允许（白名单）
✅ 允许（
commandType: local
）
无变化
non-interactive 下执行
/summary
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/compress
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/btw
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/bug
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/context
✅ 允许
✅ 允许
无变化
non-interactive 下执行
/model
❌ unsupported
❌ unsupported（
commandType: local-jsx
）
无变化
non-interactive 下执行 file command
✅ 允许（CommandKind.FILE）
✅ 允许（
commandType: prompt
）
无变化
non-interactive 下执行 bundled skill
✅ 允许（CommandKind.SKILL）
✅ 允许（
commandType: prompt
）
无变化
non-interactive 下执行 MCP prompt
❌ 被 CommandKind 拦截
✅ 允许（
commandType: prompt
）
Bug fix
non-interactive 下执行
/export
❌ 不在白名单
❌ 不允许（
commandType: local
，默认 interactive only）
无变化
non-interactive 下执行
/memory
❌ 不在白名单
❌ 不允许（
commandType: local
，默认 interactive only）
无变化
non-interactive 下执行
/plan
❌ 不在白名单
❌ 不允许（
commandType: local
，默认 interactive only）
无变化
关于
local
命令的保守默认策略
：
commandType: 'local'
的默认
supportedModes
为
['interactive']
，这与 Claude Code 的设计一致——
local
类型命令需要显式声明
supportsNonInteractive: true
才能在非交互模式下运行。Phase 1 中白名单内的 6 个命令（
init
、
summary
、
compress
、
btw
、
bug
、
context
）通过显式声明
supportedModes: ['interactive', 'non_interactive', 'acp']
来等价替换原白名单效果。Phase 2 中需要扩展的命令（如
/export
、
/memory
、
/plan
）在验证 action 实现 headless-friendly 之后，再逐个解锁。
10.2 Phase 2 模式差异命令：双注册模式
对于 Phase 2 中需要”交互模式有 UI，非交互模式有文本输出”的命令（如
/model
），应采用
双注册模式
，而非在单个命令的
action
内部分支。
这是 Claude Code 的标准模式，以
/context
为例（参见
src/commands/context/index.ts
）：两个同名
Command
对象，一个
local-jsx
仅 interactive，另一个
local
仅 non-interactive，通过
isEnabled()
互斥。
Qwen Code 在 Phase 2 中应采用等价方式，以
supportedModes
替代
isEnabled()
实现互斥：
// ① 交互模式版：local-jsx，仅 interactive
export
const
modelCommandInteractive
:
SlashCommand
=
{
name:
'model'
,
kind: CommandKind.
BUILT_IN
,
commandType:
'local-jsx'
,
supportedModes: [
'interactive'
],
// 显式限定
// action: 打开 dialog 选择 model
};
// ② 非交互/acp 版：local，显式开放给 headless 调用者
export
const
modelCommandHeadless
:
SlashCommand
=
{
name:
'model'
,
kind: CommandKind.
BUILT_IN
,
commandType:
'local'
,
supportedModes: [
'non_interactive'
,
'acp'
],
// 显式限定
// action: 读取/设置 model，返回 message（纯文本）
};
两个对象同名，
supportedModes
互斥，
filterCommandsForMode
自动选择正确版本。与 Claude Code 的
isEnabled()
互斥相比，
supportedModes
过滤更显式、更易测试，且不需要运行时环境检测。
Phase 1 不实现任何双注册命令
，该模式仅作为 Phase 2 的实施规范预留在此。
11. 测试策略
11.1 新增工具函数测试
在
packages/cli/src/services/commandUtils.test.ts
（新文件）中：
describe
(
'getEffectiveSupportedModes'
, ()
=>
{
it
(
'显式 supportedModes 优先于 commandType 推断'
, ()
=>
{
const
cmd
:
SlashCommand
=
{
name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
,
commandType:
'local'
,
supportedModes: [
'interactive'
],
// 显式限制
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
]);
});
it
(
'commandType: local 推断为 all modes'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
, commandType:
'local'
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
it
(
'commandType: local-jsx 推断为 interactive only'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
, commandType:
'local-jsx'
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
]);
});
it
(
'commandType: prompt 推断为 all modes'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
SKILL
, commandType:
'prompt'
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
it
(
'未声明 commandType 且 CommandKind.BUILT_IN，兜底为 interactive'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
BUILT_IN
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
]);
});
it
(
'未声明 commandType 且 CommandKind.FILE，兜底为 all modes'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
FILE
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
it
(
'未声明 commandType 且 CommandKind.MCP_PROMPT，兜底为 all modes（修复原有限制）'
, ()
=>
{
const
cmd
:
SlashCommand
=
{ name:
'test'
, description:
''
, kind: CommandKind.
MCP_PROMPT
};
expect
(
getEffectiveSupportedModes
(cmd)).
toEqual
([
'interactive'
,
'non_interactive'
,
'acp'
]);
});
});
describe
(
'filterCommandsForMode'
, ()
=>
{
it
(
'正确过滤 non_interactive 模式下的命令'
, ()
=>
{
...
});
it
(
'正确过滤 acp 模式下的命令'
, ()
=>
{
...
});
it
(
'不过滤 hidden 命令（filterCommandsForMode 不处理 hidden，CommandService 处理）'
, ()
=>
{
...
});
});
11.2 更新
nonInteractiveCliCommands.test.ts
删除对
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
的所有引用
删除对
allowedBuiltinCommandNames
参数的测试用例
新增：验证 commandType: local 的命令在 non-interactive 下通过过滤
新增：验证 commandType: local-jsx 的命令在 non-interactive 下被过滤
保留：验证 file command / skill command 在 non-interactive 下通过过滤
11.3 更新
CommandService.test.ts
新增
getCommandsForMode
的测试用例
新增
getModelInvocableCommands
的测试用例
11.4 各 Loader 测试
BuiltinCommandLoader.test.ts
：验证所有命令都有
source: 'builtin-command'
BundledSkillLoader.test.ts
：验证
source: 'bundled-skill'
和
modelInvocable: true
FileCommandLoader.test.ts
：验证用户命令有
source: 'skill-dir-command'
，插件命令有
source: 'plugin-command'
McpPromptLoader.test.ts
：验证
source: 'mcp-prompt'
和
modelInvocable: true
12. 实施顺序
建议按以下顺序实施，每步可独立 commit 和 review：
Step 1
（~30min）：修改
types.ts
，新增
ExecutionMode
、
CommandSource
、
CommandType
和
SlashCommand
新字段
→ 纯类型变更，TypeScript 编译检查
Step 2
（~1h）：新建
commandUtils.ts
，实现
getEffectiveSupportedModes
和
filterCommandsForMode
，同步新建
commandUtils.test.ts
→ 单元测试覆盖核心逻辑
Step 3
（~1h）：重构
nonInteractiveCliCommands.ts
，删除白名单，引入
filterCommandsForMode
，更新函数签名
→ 行为等价（Phase 1 保守策略：local 类命令显式写
supportedModes: ['interactive']
）
Step 4
（~30min）：更新
CommandService.ts
，新增两个方法
Step 5
（~2h）：为所有 built-in 命令文件添加
commandType
声明
→ 逐个确认分类正确性
Step 6
（~1.5h）：更新所有 Loader，注入
source
、
sourceLabel
、
commandType
、
modelInvocable
Step 7
（~30min）：更新
Session.ts
的调用签名
Step 8
（~1h）：运行所有测试，修复失败用例，更新快照
Step 9
（~30min）：CR 自查：确认白名单已完全移除，无遗漏调用
13. 验收 Checklist
TypeScript 编译无错误（
npm run typecheck
）
npm run lint
无新增 lint 错误
所有现有测试通过（
cd packages/cli && npx vitest run
）
commandUtils.test.ts
新增测试全部通过
getEffectiveSupportedModes
覆盖所有 7 种 case
filterCommandsForMode
覆盖 interactive / non_interactive / acp 三种模式
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
在整个代码库中无任何引用（
grep
验证）
filterCommandsForNonInteractive
函数在整个代码库中无任何引用
所有 built-in 命令有
commandType
字段
所有 Loader 输出的命令有
source
和
sourceLabel
字段
BundledSkillLoader
/
FileCommandLoader
（用户命令）/
McpPromptLoader
输出的命令
modelInvocable: true
BuiltinCommandLoader
输出的命令
modelInvocable: false
CommandService.getCommandsForMode('non_interactive')
返回与重构前等价的命令集
MCP prompt 命令在 non-interactive 模式下不再被错误拦截
Last updated on
May 18, 2026
Qwen Code Command 模块重构方案
Phase 2 技术设计文档：能力扩展</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase1-technical-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Multi File Read Tool ( read_many_files )</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/multi-file/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/multi-file/</guid>
  <pubDate>Thu, 12 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Multi-File Read
Multi File Read Tool (
read_many_files
)
This document describes the
read_many_files
tool for Qwen Code.
Description
Use
read_many_files
to read content from multiple files specified by paths or glob patterns. The behavior of this tool depends on the provided files:
For text files, this tool concatenates their content into a single string.
For image (e.g., PNG, JPEG), PDF, audio (MP3, WAV), and video (MP4, MOV) files, it reads and returns them as base64-enco...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Multi-File Read
Multi File Read Tool (
read_many_files
)
This document describes the
read_many_files
tool for Qwen Code.
Description
Use
read_many_files
to read content from multiple files specified by paths or glob patterns. The behavior of this tool depends on the provided files:
For text files, this tool concatenates their content into a single string.
For image (e.g., PNG, JPEG), PDF, audio (MP3, WAV), and video (MP4, MOV) files, it reads and returns them as base64-encoded data, provided they are explicitly requested by name or extension.
read_many_files
can be used to perform tasks such as getting an overview of a codebase, finding where specific functionality is implemented, reviewing documentation, or gathering context from multiple configuration files.
Note:
read_many_files
looks for files following the provided paths or glob patterns. A directory path such as
"/docs"
will return an empty result; the tool requires a pattern such as
"/docs/*"
or
"/docs/*.md"
to identify the relevant files.
Arguments
read_many_files
takes the following arguments:
paths
(list[string], required): An array of glob patterns or paths relative to the tool’s target directory (e.g.,
["src/**/*.ts"]
,
["README.md", "docs/*", "assets/logo.png"]
).
exclude
(list[string], optional): Glob patterns for files/directories to exclude (e.g.,
["**/*.log", "temp/"]
). These are added to default excludes if
useDefaultExcludes
is true.
include
(list[string], optional): Additional glob patterns to include. These are merged with
paths
(e.g.,
["*.test.ts"]
to specifically add test files if they were broadly excluded, or
["images/*.jpg"]
to include specific image types).
recursive
(boolean, optional): Whether to search recursively. This is primarily controlled by
**
in glob patterns. Defaults to
true
.
useDefaultExcludes
(boolean, optional): Whether to apply a list of default exclusion patterns (e.g.,
node_modules
,
.git
, non image/pdf binary files). Defaults to
true
.
respect_git_ignore
(boolean, optional): Whether to respect .gitignore patterns when finding files. Defaults to true.
How to use
read_many_files
with Qwen Code
read_many_files
searches for files matching the provided
paths
and
include
patterns, while respecting
exclude
patterns and default excludes (if enabled).
For text files: it reads the content of each matched file (attempting to skip binary files not explicitly requested as image/PDF) and concatenates it into a single string, with a separator
--- {filePath} ---
between the content of each file. Uses UTF-8 encoding by default.
The tool inserts a
--- End of content ---
after the last file.
For image and PDF files: if explicitly requested by name or extension (e.g.,
paths: ["logo.png"]
or
include: ["*.pdf"]
), the tool reads the file and returns its content as a base64 encoded string.
The tool attempts to detect and skip other binary files (those not matching common image/PDF types or not explicitly requested) by checking for null bytes in their initial content.
Usage:
read_many_files(paths=["Your files or paths here."], include=["Additional files to include."], exclude=["Files to exclude."], recursive=False, useDefaultExcludes=false, respect_git_ignore=true)
read_many_files
examples
Read all TypeScript files in the
src
directory:
read_many_files(paths=["src/**/*.ts"])
Read the main README, all Markdown files in the
docs
directory, and a specific logo image, excluding a specific file:
read_many_files(paths=["README.md", "docs/**/*.md", "assets/logo.png"], exclude=["docs/OLD_README.md"])
Read all JavaScript files but explicitly include test files and all JPEGs in an
images
folder:
read_many_files(paths=["**/*.js"], include=["**/*.test.js", "images/**/*.jpg"], useDefaultExcludes=False)
Important notes
Binary file handling:
Image/PDF/Audio/Video files:
The tool can read common image types (PNG, JPEG, etc.), PDF, audio (mp3, wav), and video (mp4, mov) files, returning them as base64 encoded data. These files
must
be explicitly targeted by the
paths
or
include
patterns (e.g., by specifying the exact filename like
video.mp4
or a pattern like
*.mov
).
Other binary files:
The tool attempts to detect and skip other types of binary files by examining their initial content for null bytes. The tool excludes these files from its output.
Performance:
Reading a very large number of files or very large individual files can be resource-intensive.
Path specificity:
Ensure paths and glob patterns are correctly specified relative to the tool’s target directory. For image/PDF files, ensure the patterns are specific enough to include them.
Default excludes:
Be aware of the default exclusion patterns (like
node_modules
,
.git
) and use
useDefaultExcludes=False
if you need to override them, but do so cautiously.
Last updated on
May 18, 2026
File System
Shell</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/multi-file/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Multi-File Read
Multi File Read Tool (
read_many_files
)
This document describes the
read_many_files
tool for Qwen Code.
Description
Use
read_many_files
to read content from multiple files specified by paths or glob patterns. The behavior of this tool depends on the provided files:
For text files, this tool concatenates their content into a single string.
For image (e.g., PNG, JPEG), PDF, audio (MP3, WAV), and video (MP4, MOV) files, it reads and returns them as base64-enco...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Multi-File Read
Multi File Read Tool (
read_many_files
)
This document describes the
read_many_files
tool for Qwen Code.
Description
Use
read_many_files
to read content from multiple files specified by paths or glob patterns. The behavior of this tool depends on the provided files:
For text files, this tool concatenates their content into a single string.
For image (e.g., PNG, JPEG), PDF, audio (MP3, WAV), and video (MP4, MOV) files, it reads and returns them as base64-encoded data, provided they are explicitly requested by name or extension.
read_many_files
can be used to perform tasks such as getting an overview of a codebase, finding where specific functionality is implemented, reviewing documentation, or gathering context from multiple configuration files.
Note:
read_many_files
looks for files following the provided paths or glob patterns. A directory path such as
"/docs"
will return an empty result; the tool requires a pattern such as
"/docs/*"
or
"/docs/*.md"
to identify the relevant files.
Arguments
read_many_files
takes the following arguments:
paths
(list[string], required): An array of glob patterns or paths relative to the tool’s target directory (e.g.,
["src/**/*.ts"]
,
["README.md", "docs/*", "assets/logo.png"]
).
exclude
(list[string], optional): Glob patterns for files/directories to exclude (e.g.,
["**/*.log", "temp/"]
). These are added to default excludes if
useDefaultExcludes
is true.
include
(list[string], optional): Additional glob patterns to include. These are merged with
paths
(e.g.,
["*.test.ts"]
to specifically add test files if they were broadly excluded, or
["images/*.jpg"]
to include specific image types).
recursive
(boolean, optional): Whether to search recursively. This is primarily controlled by
**
in glob patterns. Defaults to
true
.
useDefaultExcludes
(boolean, optional): Whether to apply a list of default exclusion patterns (e.g.,
node_modules
,
.git
, non image/pdf binary files). Defaults to
true
.
respect_git_ignore
(boolean, optional): Whether to respect .gitignore patterns when finding files. Defaults to true.
How to use
read_many_files
with Qwen Code
read_many_files
searches for files matching the provided
paths
and
include
patterns, while respecting
exclude
patterns and default excludes (if enabled).
For text files: it reads the content of each matched file (attempting to skip binary files not explicitly requested as image/PDF) and concatenates it into a single string, with a separator
--- {filePath} ---
between the content of each file. Uses UTF-8 encoding by default.
The tool inserts a
--- End of content ---
after the last file.
For image and PDF files: if explicitly requested by name or extension (e.g.,
paths: ["logo.png"]
or
include: ["*.pdf"]
), the tool reads the file and returns its content as a base64 encoded string.
The tool attempts to detect and skip other binary files (those not matching common image/PDF types or not explicitly requested) by checking for null bytes in their initial content.
Usage:
read_many_files(paths=["Your files or paths here."], include=["Additional files to include."], exclude=["Files to exclude."], recursive=False, useDefaultExcludes=false, respect_git_ignore=true)
read_many_files
examples
Read all TypeScript files in the
src
directory:
read_many_files(paths=["src/**/*.ts"])
Read the main README, all Markdown files in the
docs
directory, and a specific logo image, excluding a specific file:
read_many_files(paths=["README.md", "docs/**/*.md", "assets/logo.png"], exclude=["docs/OLD_README.md"])
Read all JavaScript files but explicitly include test files and all JPEGs in an
images
folder:
read_many_files(paths=["**/*.js"], include=["**/*.test.js", "images/**/*.jpg"], useDefaultExcludes=False)
Important notes
Binary file handling:
Image/PDF/Audio/Video files:
The tool can read common image types (PNG, JPEG, etc.), PDF, audio (mp3, wav), and video (mp4, mov) files, returning them as base64 encoded data. These files
must
be explicitly targeted by the
paths
or
include
patterns (e.g., by specifying the exact filename like
video.mp4
or a pattern like
*.mov
).
Other binary files:
The tool attempts to detect and skip other types of binary files by examining their initial content for null bytes. The tool excludes these files from its output.
Performance:
Reading a very large number of files or very large individual files can be resource-intensive.
Path specificity:
Ensure paths and glob patterns are correctly specified relative to the tool’s target directory. For image/PDF files, ensure the patterns are specific enough to include them.
Default excludes:
Be aware of the default exclusion patterns (like
node_modules
,
.git
) and use
useDefaultExcludes=False
if you need to override them, but do so cautiously.
Last updated on
May 18, 2026
File System
Shell</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/multi-file/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>MCP servers with Qwen Code</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/mcp-server/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/mcp-server/</guid>
  <pubDate>Tue, 10 Dec 2024 00:00:00 +0000</pubDate>
  <category>MCP</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
MCP Servers
MCP servers with Qwen Code
This document provides a guide to configuring and using Model Context Protocol (MCP) servers with Qwen Code.
What is an MCP server?
An MCP server is an application that exposes tools and resources to the CLI through the Model Context Protocol, allowing it to interact with external systems and data sources. MCP servers act as a bridge between the model and your local environment or other services like APIs.
An MCP server enables the CLI...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
MCP Servers
MCP servers with Qwen Code
This document provides a guide to configuring and using Model Context Protocol (MCP) servers with Qwen Code.
What is an MCP server?
An MCP server is an application that exposes tools and resources to the CLI through the Model Context Protocol, allowing it to interact with external systems and data sources. MCP servers act as a bridge between the model and your local environment or other services like APIs.
An MCP server enables the CLI to:
Discover tools:
List available tools, their descriptions, and parameters through standardized schema definitions.
Execute tools:
Call specific tools with defined arguments and receive structured responses.
Access resources:
Read data from specific resources (though the CLI primarily focuses on tool execution).
With an MCP server, you can extend the CLI’s capabilities to perform actions beyond its built-in features, such as interacting with databases, APIs, custom scripts, or specialized workflows.
Core Integration Architecture
Qwen Code integrates with MCP servers through a sophisticated discovery and execution system built into the core package (
packages/core/src/tools/
):
Discovery Layer (
mcp-client.ts
)
The discovery process is orchestrated by
discoverMcpTools()
, which:
Iterates through configured servers
from your
settings.json
mcpServers
configuration
Establishes connections
using appropriate transport mechanisms (Stdio, SSE, or Streamable HTTP)
Fetches tool definitions
from each server using the MCP protocol
Sanitizes and validates
tool schemas for compatibility with the Qwen API
Registers tools
in the global tool registry with conflict resolution
Execution Layer (
mcp-tool.ts
)
Each discovered MCP tool is wrapped in a
DiscoveredMCPTool
instance that:
Handles confirmation logic
based on server trust settings and user preferences
Manages tool execution
by calling the MCP server with proper parameters
Processes responses
for both the LLM context and user display
Maintains connection state
and handles timeouts
Transport Mechanisms
The CLI supports three MCP transport types:
Stdio Transport:
Spawns a subprocess and communicates via stdin/stdout
SSE Transport:
Connects to Server-Sent Events endpoints
Streamable HTTP Transport:
Uses HTTP streaming for communication
How to set up your MCP server
Qwen Code uses the
mcpServers
configuration in your
settings.json
file to locate and connect to MCP servers. This configuration supports multiple servers with different transport mechanisms.
Configure the MCP server in settings.json
You can configure MCP servers in your
settings.json
file in two main ways: through the top-level
mcpServers
object for specific server definitions, and through the
mcp
object for global settings that control server discovery and execution.
Global MCP Settings (
mcp
)
The
mcp
object in your
settings.json
allows you to define global rules for all MCP servers.
mcp.serverCommand
(string): A global command to start an MCP server.
mcp.allowed
(array of strings): A list of MCP server names to allow. If this is set, only servers from this list (matching the keys in the
mcpServers
object) will be connected to.
mcp.excluded
(array of strings): A list of MCP server names to exclude. Servers in this list will not be connected to.
Example:
{
"mcp"
: {
"allowed"
: [
"my-trusted-server"
],
"excluded"
: [
"experimental-server"
]
}
}
Server-Specific Configuration (
mcpServers
)
The
mcpServers
object is where you define each individual MCP server you want the CLI to connect to.
Configuration Structure
Add an
mcpServers
object to your
settings.json
file:
{
...file
contains
other
config
objects
"mcpServers"
: {
"serverName"
: {
"command"
:
"path/to/server"
,
"args"
: [
"--arg1"
,
"value1"
],
"env"
: {
"API_KEY"
:
"$MY_API_TOKEN"
},
"cwd"
:
"./server-directory"
,
"timeout"
:
30000
,
"trust"
:
false
}
}
}
Configuration Properties
Each server configuration supports the following properties:
Required (one of the following)
command
(string): Path to the executable for Stdio transport
url
(string): SSE endpoint URL (e.g.,
"http://localhost:8080/sse"
)
httpUrl
(string): HTTP streaming endpoint URL
Optional
args
(string[]): Command-line arguments for Stdio transport
headers
(object): Custom HTTP headers when using
url
or
httpUrl
env
(object): Environment variables for the server process. Values can reference environment variables using
$VAR_NAME
or
${VAR_NAME}
syntax
cwd
(string): Working directory for Stdio transport
timeout
(number): Request timeout in milliseconds (default: 600,000ms = 10 minutes)
trust
(boolean): When
true
, bypasses all tool call confirmations for this server (default:
false
)
includeTools
(string[]): List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.
excludeTools
(string[]): List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server.
Note:
excludeTools
takes precedence over
includeTools
- if a tool is in both lists, it will be excluded.
targetAudience
(string): The OAuth Client ID allowlisted on the IAP-protected application you are trying to access. Used with
authProviderType: 'service_account_impersonation'
.
targetServiceAccount
(string): The email address of the Google Cloud Service Account to impersonate. Used with
authProviderType: 'service_account_impersonation'
.
OAuth Support for Remote MCP Servers
Qwen Code supports OAuth 2.0 authentication for remote MCP servers using SSE or HTTP transports. This enables secure access to MCP servers that require authentication.
Automatic OAuth Discovery
For servers that support OAuth discovery, you can omit the OAuth configuration and let the CLI discover it automatically:
{
"mcpServers"
: {
"discoveredServer"
: {
"url"
:
"https://api.example.com/sse"
}
}
}
The CLI will automatically:
Detect when a server requires OAuth authentication (401 responses)
Discover OAuth endpoints from server metadata
Perform dynamic client registration if supported
Handle the OAuth flow and token management
Authentication Flow
When connecting to an OAuth-enabled server:
Initial connection attempt
fails with 401 Unauthorized
OAuth discovery
finds authorization and token endpoints
Browser opens
for user authentication (requires local browser access)
Authorization code
is exchanged for access tokens
Tokens are stored
securely for future use
Connection retry
succeeds with valid tokens
Browser Redirect Requirements
Important:
OAuth authentication requires that the redirect URI is accessible:
Default behavior
: Redirects to
http://localhost:7777/oauth/callback
(works for local setups)
Custom redirect URI
: Use
--oauth-redirect-uri
or configure
redirectUri
in settings.json to specify a different URL
For
remote/cloud server deployments
(e.g., web terminals, SSH sessions, cloud IDEs):
The default
localhost
redirect will NOT work
You MUST configure a custom
redirectUri
pointing to a publicly accessible URL
The user’s browser must be able to reach this URL and redirect back to the server
Example for remote servers:
qwen
mcp
add
--transport
sse
remote-server
https://api.example.com/sse/
\
--oauth-redirect-uri
https://your-remote-server.example.com/oauth/callback
OAuth will not work in:
Headless environments without browser access
Environments where the configured
redirectUri
is unreachable from the user’s browser
Managing OAuth Authentication
Use the
/mcp auth
command to manage OAuth authentication:
# List servers requiring authentication
/mcp
auth
# Authenticate with a specific server
/mcp
auth
serverName
# Re-authenticate if tokens expire
/mcp
auth
serverName
OAuth Configuration Properties
enabled
(boolean): Enable OAuth for this server
clientId
(string): OAuth client identifier (optional with dynamic registration)
clientSecret
(string): OAuth client secret (optional for public clients)
authorizationUrl
(string): OAuth authorization endpoint (auto-discovered if omitted)
tokenUrl
(string): OAuth token endpoint (auto-discovered if omitted)
scopes
(string[]): Required OAuth scopes
redirectUri
(string): Custom redirect URI.
Critical for remote deployments
: Defaults to
http://localhost:7777/oauth/callback
. When running Qwen Code on remote/cloud servers, set this to a publicly accessible URL (e.g.,
https://your-server.com/oauth/callback
). Can be configured via
qwen mcp add --oauth-redirect-uri
or directly in settings.json.
tokenParamName
(string): Query parameter name for tokens in SSE URLs
audiences
(string[]): Audiences the token is valid for
Token Management
OAuth tokens are automatically:
Stored securely
in
~/.qwen/mcp-oauth-tokens.json
Refreshed
when expired (if refresh tokens are available)
Validated
before each connection attempt
Cleaned up
when invalid or expired
Authentication Provider Type
You can specify the authentication provider type using the
authProviderType
property:
authProviderType
(string): Specifies the authentication provider. Can be one of the following:
dynamic_discovery
(default): The CLI will automatically discover the OAuth configuration from the server.
google_credentials
: The CLI will use the Google Application Default Credentials (ADC) to authenticate with the server. When using this provider, you must specify the required scopes.
service_account_impersonation
: The CLI will impersonate a Google Cloud Service Account to authenticate with the server. This is useful for accessing IAP-protected services (this was specifically designed for Cloud Run services).
Google Credentials
{
"mcpServers"
: {
"googleCloudServer"
: {
"httpUrl"
:
"https://my-gcp-service.run.app/mcp"
,
"authProviderType"
:
"google_credentials"
,
"oauth"
: {
"scopes"
: [
"https://www.googleapis.com/auth/userinfo.email"
]
}
}
}
}
Service Account Impersonation
To authenticate with a server using Service Account Impersonation, you must set the
authProviderType
to
service_account_impersonation
and provide the following properties:
targetAudience
(string): The OAuth Client ID allowslisted on the IAP-protected application you are trying to access.
targetServiceAccount
(string): The email address of the Google Cloud Service Account to impersonate.
The CLI will use your local Application Default Credentials (ADC) to generate an OIDC ID token for the specified service account and audience. This token will then be used to authenticate with the MCP server.
Setup Instructions
Create
or use an existing OAuth 2.0 client ID.
To use an existing OAuth 2.0 client ID, follow the steps in
How to share OAuth Clients
.
Add the OAuth ID to the allowlist for
programmatic access
for the application.
Since Cloud Run is not yet a supported resource type in gcloud iap, you must allowlist the Client ID on the project.
Create a service account.
Documentation
,
Cloud Console Link
Add both the service account and users to the IAP Policy
in the “Security” tab of the Cloud Run service itself or via gcloud.
Grant all users and groups
who will access the MCP Server the necessary permissions to
impersonate the service account
(i.e.,
roles/iam.serviceAccountTokenCreator
).
Enable
the IAM Credentials API
for your project.
Example Configurations
Python MCP Server (Stdio)
{
"mcpServers"
: {
"pythonTools"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
,
"--port"
,
"8080"
],
"cwd"
:
"./mcp-servers/python"
,
"env"
: {
"DATABASE_URL"
:
"$DB_CONNECTION_STRING"
,
"API_KEY"
:
"${EXTERNAL_API_KEY}"
},
"timeout"
:
15000
}
}
}
Node.js MCP Server (Stdio)
{
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"dist/server.js"
,
"--verbose"
],
"cwd"
:
"./mcp-servers/node"
,
"trust"
:
true
}
}
}
Docker-based MCP Server
{
"mcpServers"
: {
"dockerizedServer"
: {
"command"
:
"docker"
,
"args"
: [
"run"
,
"-i"
,
"--rm"
,
"-e"
,
"API_KEY"
,
"-v"
,
"${PWD}:/workspace"
,
"my-mcp-server:latest"
],
"env"
: {
"API_KEY"
:
"$EXTERNAL_SERVICE_TOKEN"
}
}
}
}
HTTP-based MCP Server
{
"mcpServers"
: {
"httpServer"
: {
"httpUrl"
:
"http://localhost:3000/mcp"
,
"timeout"
:
5000
}
}
}
HTTP-based MCP Server with Custom Headers
{
"mcpServers"
: {
"httpServerWithAuth"
: {
"httpUrl"
:
"http://localhost:3000/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer your-api-token"
,
"X-Custom-Header"
:
"custom-value"
,
"Content-Type"
:
"application/json"
},
"timeout"
:
5000
}
}
}
MCP Server with Tool Filtering
{
"mcpServers"
: {
"filteredServer"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
],
"includeTools"
: [
"safe_tool"
,
"file_reader"
,
"data_processor"
],
// "excludeTools": ["dangerous_tool", "file_deleter"],
"timeout"
:
30000
}
}
}
SSE MCP Server with SA Impersonation
{
"mcpServers"
: {
"myIapProtectedServer"
: {
"url"
:
"https://my-iap-service.run.app/sse"
,
"authProviderType"
:
"service_account_impersonation"
,
"targetAudience"
:
"YOUR_IAP_CLIENT_ID.apps.googleusercontent.com"
,
"targetServiceAccount"
:
"your-sa@your-project.iam.gserviceaccount.com"
}
}
}
Discovery Process Deep Dive
When Qwen Code starts, it performs MCP server discovery through the following detailed process:
1. Server Iteration and Connection
For each configured server in
mcpServers
:
Status tracking begins:
Server status is set to
CONNECTING
Transport selection:
Based on configuration properties:
httpUrl
→
StreamableHTTPClientTransport
url
→
SSEClientTransport
command
→
StdioClientTransport
Connection establishment:
The MCP client attempts to connect with the configured timeout
Error handling:
Connection failures are logged and the server status is set to
DISCONNECTED
2. Tool Discovery
Upon successful connection:
Tool listing:
The client calls the MCP server’s tool listing endpoint
Schema validation:
Each tool’s function declaration is validated
Tool filtering:
Tools are filtered based on
includeTools
and
excludeTools
configuration
Name sanitization:
Tool names are cleaned to meet Qwen API requirements:
Invalid characters (non-alphanumeric, underscore, dot, hyphen) are replaced with underscores
Names longer than 63 characters are truncated with middle replacement (
___
)
3. Conflict Resolution
When multiple servers expose tools with the same name:
First registration wins:
The first server to register a tool name gets the unprefixed name
Automatic prefixing:
Subsequent servers get prefixed names:
serverName__toolName
Registry tracking:
The tool registry maintains mappings between server names and their tools
4. Schema Processing
Tool parameter schemas undergo sanitization for API compatibility:
$schema
properties
are removed
additionalProperties
are stripped
anyOf
with
default
have their default values removed (Vertex AI compatibility)
Recursive processing
applies to nested schemas
5. Connection Management
After discovery:
Persistent connections:
Servers that successfully register tools maintain their connections
Cleanup:
Servers that provide no usable tools have their connections closed
Status updates:
Final server statuses are set to
CONNECTED
or
DISCONNECTED
Tool Execution Flow
When the model decides to use an MCP tool, the following execution flow occurs:
1. Tool Invocation
The model generates a
FunctionCall
with:
Tool name:
The registered name (potentially prefixed)
Arguments:
JSON object matching the tool’s parameter schema
2. Confirmation Process
Each
DiscoveredMCPTool
implements sophisticated confirmation logic:
Trust-based Bypass
if
(
this
.trust) {
return
false
;
// No confirmation needed
}
Dynamic Allow-listing
The system maintains internal allow-lists for:
Server-level:
serverName
→ All tools from this server are trusted
Tool-level:
serverName.toolName
→ This specific tool is trusted
User Choice Handling
When confirmation is required, users can choose:
Proceed once:
Execute this time only
Always allow this tool:
Add to tool-level allow-list
Always allow this server:
Add to server-level allow-list
Cancel:
Abort execution
3. Execution
Upon confirmation (or trust bypass):
Parameter preparation:
Arguments are validated against the tool’s schema
MCP call:
The underlying
CallableTool
invokes the server with:
const
functionCalls
=
[
{
name:
this
.serverToolName,
// Original server tool name
args: params,
},
];
Response processing:
Results are formatted for both LLM context and user display
4. Response Handling
The execution result contains:
llmContent
:
Raw response parts for the language model’s context
returnDisplay
:
Formatted output for user display (often JSON in markdown code blocks)
How to interact with your MCP server
Using the
/mcp
Command
The
/mcp
command provides comprehensive information about your MCP server setup:
/mcp
This displays:
Server list:
All configured MCP servers
Connection status:
CONNECTED
,
CONNECTING
, or
DISCONNECTED
Server details:
Configuration summary (excluding sensitive data)
Available tools:
List of tools from each server with descriptions
Discovery state:
Overall discovery process status
Example
/mcp
Output
MCP Servers Status:
📡 pythonTools (CONNECTED)
Command: python -m my_mcp_server --port 8080
Working Directory: ./mcp-servers/python
Timeout: 15000ms
Tools: calculate_sum, file_analyzer, data_processor
🔌 nodeServer (DISCONNECTED)
Command: node dist/server.js --verbose
Error: Connection refused
🐳 dockerizedServer (CONNECTED)
Command: docker run -i --rm -e API_KEY my-mcp-server:latest
Tools: docker__deploy, docker__status
Discovery State: COMPLETED
Tool Usage
Once discovered, MCP tools are available to the Qwen model like built-in tools. The model will automatically:
Select appropriate tools
based on your requests
Present confirmation dialogs
(unless the server is trusted)
Execute tools
with proper parameters
Display results
in a user-friendly format
Status Monitoring and Troubleshooting
Connection States
The MCP integration tracks several states:
Server Status (
MCPServerStatus
)
DISCONNECTED
:
Server is not connected or has errors
CONNECTING
:
Connection attempt in progress
CONNECTED
:
Server is connected and ready
Discovery State (
MCPDiscoveryState
)
NOT_STARTED
:
Discovery hasn’t begun
IN_PROGRESS
:
Currently discovering servers
COMPLETED
:
Discovery finished (with or without errors)
Common Issues and Solutions
Server Won’t Connect
Symptoms:
Server shows
DISCONNECTED
status
Troubleshooting:
Check configuration:
Verify
command
,
args
, and
cwd
are correct
Test manually:
Run the server command directly to ensure it works
Check dependencies:
Ensure all required packages are installed
Review logs:
Look for error messages in the CLI output
Verify permissions:
Ensure the CLI can execute the server command
No Tools Discovered
Symptoms:
Server connects but no tools are available
Troubleshooting:
Verify tool registration:
Ensure your server actually registers tools
Check MCP protocol:
Confirm your server implements the MCP tool listing correctly
Review server logs:
Check stderr output for server-side errors
Test tool listing:
Manually test your server’s tool discovery endpoint
Tools Not Executing
Symptoms:
Tools are discovered but fail during execution
Troubleshooting:
Parameter validation:
Ensure your tool accepts the expected parameters
Schema compatibility:
Verify your input schemas are valid JSON Schema
Error handling:
Check if your tool is throwing unhandled exceptions
Timeout issues:
Consider increasing the
timeout
setting
Sandbox Compatibility
Symptoms:
MCP servers fail when sandboxing is enabled
Solutions:
Docker-based servers:
Use Docker containers that include all dependencies
Path accessibility:
Ensure server executables are available in the sandbox
Network access:
Configure sandbox to allow necessary network connections
Environment variables:
Verify required environment variables are passed through
Debugging Tips
Enable debug mode:
Run the CLI with
--debug
for verbose output
Check stderr:
MCP server stderr is captured and logged (INFO messages filtered)
Test isolation:
Test your MCP server independently before integrating
Incremental setup:
Start with simple tools before adding complex functionality
Use
/mcp
frequently:
Monitor server status during development
Important Notes
Security Considerations
Trust settings:
The
trust
option bypasses all confirmation dialogs. Use cautiously and only for servers you completely control
Access tokens:
Be security-aware when configuring environment variables containing API keys or tokens
Sandbox compatibility:
When using sandboxing, ensure MCP servers are available within the sandbox environment
Private data:
Using broadly scoped personal access tokens can lead to information leakage between repositories
Performance and Resource Management
Connection persistence:
The CLI maintains persistent connections to servers that successfully register tools
Automatic cleanup:
Connections to servers providing no tools are automatically closed
Timeout management:
Configure appropriate timeouts based on your server’s response characteristics
Resource monitoring:
MCP servers run as separate processes and consume system resources
Schema Compatibility
Schema compliance mode:
By default (
schemaCompliance: "auto"
), tool schemas are passed through as-is. Set
"model": { "generationConfig": { "schemaCompliance": "openapi_30" } }
in your
settings.json
to convert models to Strict OpenAPI 3.0 format.
OpenAPI 3.0 transformations:
When
openapi_30
mode is enabled, the system handles:
Nullable types:
["string", "null"]
->
type: "string", nullable: true
Const values:
const: "foo"
->
enum: ["foo"]
Exclusive limits: numeric
exclusiveMinimum
-> boolean form with
minimum
Keyword removal:
$schema
,
$id
,
dependencies
,
patternProperties
Name sanitization:
Tool names are automatically sanitized to meet API requirements
Conflict resolution:
Tool name conflicts between servers are resolved through automatic prefixing
This comprehensive integration makes MCP servers a powerful way to extend the CLI’s capabilities while maintaining security, reliability, and ease of use.
Returning Rich Content from Tools
MCP tools are not limited to returning simple text. You can return rich, multi-part content, including text, images, audio, and other binary data in a single tool response. This allows you to build powerful tools that can provide diverse information to the model in a single turn.
All data returned from the tool is processed and sent to the model as context for its next generation, enabling it to reason about or summarize the provided information.
How It Works
To return rich content, your tool’s response must adhere to the MCP specification for a
CallToolResult
. The
content
field of the result should be an array of
ContentBlock
objects. The CLI will correctly process this array, separating text from binary data and packaging it for the model.
You can mix and match different content block types in the
content
array. The supported block types include:
text
image
audio
resource
(embedded content)
resource_link
Example: Returning Text and an Image
Here is an example of a valid JSON response from an MCP tool that returns both a text description and an image:
{
"content"
: [
{
"type"
:
"text"
,
"text"
:
"Here is the logo you requested."
},
{
"type"
:
"image"
,
"data"
:
"BASE64_ENCODED_IMAGE_DATA_HERE"
,
"mimeType"
:
"image/png"
},
{
"type"
:
"text"
,
"text"
:
"The logo was created in 2025."
}
]
}
When Qwen Code receives this response, it will:
Extract all the text and combine it into a single
functionResponse
part for the model.
Present the image data as a separate
inlineData
part.
Provide a clean, user-friendly summary in the CLI, indicating that both text and an image were received.
This enables you to build sophisticated tools that can provide rich, multi-modal context to the Qwen model.
MCP Prompts as Slash Commands
In addition to tools, MCP servers can expose predefined prompts that can be executed as slash commands within Qwen Code. This allows you to create shortcuts for common or complex queries that can be easily invoked by name.
Defining Prompts on the Server
Here’s a small example of a stdio MCP server that defines prompts:
import
{ McpServer }
from
'@modelcontextprotocol/sdk/server/mcp.js'
;
import
{ StdioServerTransport }
from
'@modelcontextprotocol/sdk/server/stdio.js'
;
import
{ z }
from
'zod'
;
const
server
=
new
McpServer
({
name:
'prompt-server'
,
version:
'1.0.0'
,
});
server.
registerPrompt
(
'poem-writer'
,
{
title:
'Poem Writer'
,
description:
'Write a nice haiku'
,
argsSchema: { title: z.
string
(), mood: z.
string
().
optional
() },
},
({
title
,
mood
})
=>
({
messages: [
{
role:
'user'
,
content: {
type:
'text'
,
text:
`Write a haiku${
mood
?
` with the mood ${
mood
}`
:
''} called ${
title
}. Note that a haiku is 5 syllables followed by 7 syllables followed by 5 syllables `
,
},
},
],
}),
);
const
transport
=
new
StdioServerTransport
();
await
server.
connect
(transport);
This can be included in
settings.json
under
mcpServers
with:
{
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"filename.ts"
]
}
}
}
Invoking Prompts
Once a prompt is discovered, you can invoke it using its name as a slash command. The CLI will automatically handle parsing arguments.
/poem-writer
--title=
"Qwen Code"
--mood=
"reverent"
or, using positional arguments:
/poem-writer
"Qwen Code"
reverent
When you run this command, the CLI executes the
prompts/get
method on the MCP server with the provided arguments. The server is responsible for substituting the arguments into the prompt template and returning the final prompt text. The CLI then sends this prompt to the model for execution. This provides a convenient way to automate and share common workflows.
Managing MCP Servers with
qwen mcp
While you can always configure MCP servers by manually editing your
settings.json
file, the CLI provides a convenient set of commands to manage your server configurations programmatically. These commands streamline the process of adding, listing, and removing MCP servers without needing to directly edit JSON files.
Adding a Server (
qwen mcp add
)
The
add
command configures a new MCP server in your
settings.json
. Based on the scope (
-s, --scope
), it will be added to either the user config
~/.qwen/settings.json
or the project config
.qwen/settings.json
file.
Command:
qwen
mcp
add
[options]
<
name
>
<
commandOrUrl
>
[args...]
<name>
: A unique name for the server.
<commandOrUrl>
: The command to execute (for
stdio
) or the URL (for
http
/
sse
).
[args...]
: Optional arguments for a
stdio
command.
Options (Flags):
-s, --scope
: Configuration scope (user or project). [default: “project”]
-t, --transport
: Transport type (stdio, sse, http). [default: “stdio”]
-e, --env
: Set environment variables (e.g. -e KEY=value).
-H, --header
: Set HTTP headers for SSE and HTTP transports (e.g. -H “X-Api-Key: abc123” -H “Authorization: Bearer abc123”).
--timeout
: Set connection timeout in milliseconds.
--trust
: Trust the server (bypass all tool call confirmation prompts).
--description
: Set the description for the server.
--include-tools
: A comma-separated list of tools to include.
--exclude-tools
: A comma-separated list of tools to exclude.
--oauth-client-id
: OAuth client ID for MCP server authentication.
--oauth-client-secret
: OAuth client secret for MCP server authentication.
--oauth-redirect-uri
: OAuth redirect URI (e.g.,
https://your-server.com/oauth/callback
). Defaults to
http://localhost:7777/oauth/callback
for local setups.
Important for remote deployments
: When running Qwen Code on remote/cloud servers, set this to a publicly accessible URL.
--oauth-authorization-url
: OAuth authorization URL.
--oauth-token-url
: OAuth token URL.
--oauth-scopes
: OAuth scopes (comma-separated).
Adding an stdio server
This is the default transport for running local servers.
# Basic syntax
qwen
mcp
add
<
nam
e
>
<
comman
d
>
[args...]
# Example: Adding a local server
qwen
mcp
add
my-stdio-server
-e
API_KEY=
123
/path/to/server
arg1
arg2
arg3
# Example: Adding a local python server
qwen
mcp
add
python-server
python
server.py
--port
8080
Adding an HTTP server
This transport is for servers that use the streamable HTTP transport.
# Basic syntax
qwen
mcp
add
--transport
http
<
nam
e
>
<
ur
l
>
# Example: Adding an HTTP server
qwen
mcp
add
--transport
http
http-server
https://api.example.com/mcp/
# Example: Adding an HTTP server with an authentication header
qwen
mcp
add
--transport
http
secure-http
https://api.example.com/mcp/
--header
"Authorization: Bearer abc123"
Adding an SSE server
This transport is for servers that use Server-Sent Events (SSE).
# Basic syntax
qwen
mcp
add
--transport
sse
<
nam
e
>
<
ur
l
>
# Example: Adding an SSE server
qwen
mcp
add
--transport
sse
sse-server
https://api.example.com/sse/
# Example: Adding an SSE server with an authentication header
qwen
mcp
add
--transport
sse
secure-sse
https://api.example.com/sse/
--header
"Authorization: Bearer abc123"
# Example: Adding an OAuth-enabled SSE server
qwen
mcp
add
--transport
sse
oauth-server
https://api.example.com/sse/
\
--oauth-client-id
your-client-id
\
--oauth-redirect-uri
https://your-server.com/oauth/callback
\
--oauth-authorization-url
https://provider.example.com/authorize
\
--oauth-token-url
https://provider.example.com/token
Managing Servers (
qwen mcp
)
To view and manage all MCP servers currently configured, use the
manage
command or simply
qwen mcp
. This opens an interactive TUI dialog where you can:
View all MCP servers with their connection status
Enable/disable servers
Reconnect to disconnected servers
View tools and prompts provided by each server
View server logs
Command:
qwen
mcp
# or
qwen
mcp
manage
The management dialog provides a visual interface showing each server’s name, configuration details, connection status, and available tools/prompts.
Removing a Server (
qwen mcp remove
)
To delete a server from your configuration, use the
remove
command with the server’s name.
Command:
qwen
mcp
remove
<
nam
e
>
Example:
qwen
mcp
remove
my-server
This will find and delete the “my-server” entry from the
mcpServers
object in the appropriate
settings.json
file based on the scope (
-s, --scope
).
Last updated on
May 18, 2026
Web Search
Sandboxing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/mcp-server/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
MCP Servers
MCP servers with Qwen Code
This document provides a guide to configuring and using Model Context Protocol (MCP) servers with Qwen Code.
What is an MCP server?
An MCP server is an application that exposes tools and resources to the CLI through the Model Context Protocol, allowing it to interact with external systems and data sources. MCP servers act as a bridge between the model and your local environment or other services like APIs.
An MCP server enables the CLI...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
MCP Servers
MCP servers with Qwen Code
This document provides a guide to configuring and using Model Context Protocol (MCP) servers with Qwen Code.
What is an MCP server?
An MCP server is an application that exposes tools and resources to the CLI through the Model Context Protocol, allowing it to interact with external systems and data sources. MCP servers act as a bridge between the model and your local environment or other services like APIs.
An MCP server enables the CLI to:
Discover tools:
List available tools, their descriptions, and parameters through standardized schema definitions.
Execute tools:
Call specific tools with defined arguments and receive structured responses.
Access resources:
Read data from specific resources (though the CLI primarily focuses on tool execution).
With an MCP server, you can extend the CLI’s capabilities to perform actions beyond its built-in features, such as interacting with databases, APIs, custom scripts, or specialized workflows.
Core Integration Architecture
Qwen Code integrates with MCP servers through a sophisticated discovery and execution system built into the core package (
packages/core/src/tools/
):
Discovery Layer (
mcp-client.ts
)
The discovery process is orchestrated by
discoverMcpTools()
, which:
Iterates through configured servers
from your
settings.json
mcpServers
configuration
Establishes connections
using appropriate transport mechanisms (Stdio, SSE, or Streamable HTTP)
Fetches tool definitions
from each server using the MCP protocol
Sanitizes and validates
tool schemas for compatibility with the Qwen API
Registers tools
in the global tool registry with conflict resolution
Execution Layer (
mcp-tool.ts
)
Each discovered MCP tool is wrapped in a
DiscoveredMCPTool
instance that:
Handles confirmation logic
based on server trust settings and user preferences
Manages tool execution
by calling the MCP server with proper parameters
Processes responses
for both the LLM context and user display
Maintains connection state
and handles timeouts
Transport Mechanisms
The CLI supports three MCP transport types:
Stdio Transport:
Spawns a subprocess and communicates via stdin/stdout
SSE Transport:
Connects to Server-Sent Events endpoints
Streamable HTTP Transport:
Uses HTTP streaming for communication
How to set up your MCP server
Qwen Code uses the
mcpServers
configuration in your
settings.json
file to locate and connect to MCP servers. This configuration supports multiple servers with different transport mechanisms.
Configure the MCP server in settings.json
You can configure MCP servers in your
settings.json
file in two main ways: through the top-level
mcpServers
object for specific server definitions, and through the
mcp
object for global settings that control server discovery and execution.
Global MCP Settings (
mcp
)
The
mcp
object in your
settings.json
allows you to define global rules for all MCP servers.
mcp.serverCommand
(string): A global command to start an MCP server.
mcp.allowed
(array of strings): A list of MCP server names to allow. If this is set, only servers from this list (matching the keys in the
mcpServers
object) will be connected to.
mcp.excluded
(array of strings): A list of MCP server names to exclude. Servers in this list will not be connected to.
Example:
{
"mcp"
: {
"allowed"
: [
"my-trusted-server"
],
"excluded"
: [
"experimental-server"
]
}
}
Server-Specific Configuration (
mcpServers
)
The
mcpServers
object is where you define each individual MCP server you want the CLI to connect to.
Configuration Structure
Add an
mcpServers
object to your
settings.json
file:
{
...file
contains
other
config
objects
"mcpServers"
: {
"serverName"
: {
"command"
:
"path/to/server"
,
"args"
: [
"--arg1"
,
"value1"
],
"env"
: {
"API_KEY"
:
"$MY_API_TOKEN"
},
"cwd"
:
"./server-directory"
,
"timeout"
:
30000
,
"trust"
:
false
}
}
}
Configuration Properties
Each server configuration supports the following properties:
Required (one of the following)
command
(string): Path to the executable for Stdio transport
url
(string): SSE endpoint URL (e.g.,
"http://localhost:8080/sse"
)
httpUrl
(string): HTTP streaming endpoint URL
Optional
args
(string[]): Command-line arguments for Stdio transport
headers
(object): Custom HTTP headers when using
url
or
httpUrl
env
(object): Environment variables for the server process. Values can reference environment variables using
$VAR_NAME
or
${VAR_NAME}
syntax
cwd
(string): Working directory for Stdio transport
timeout
(number): Request timeout in milliseconds (default: 600,000ms = 10 minutes)
trust
(boolean): When
true
, bypasses all tool call confirmations for this server (default:
false
)
includeTools
(string[]): List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.
excludeTools
(string[]): List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server.
Note:
excludeTools
takes precedence over
includeTools
- if a tool is in both lists, it will be excluded.
targetAudience
(string): The OAuth Client ID allowlisted on the IAP-protected application you are trying to access. Used with
authProviderType: 'service_account_impersonation'
.
targetServiceAccount
(string): The email address of the Google Cloud Service Account to impersonate. Used with
authProviderType: 'service_account_impersonation'
.
OAuth Support for Remote MCP Servers
Qwen Code supports OAuth 2.0 authentication for remote MCP servers using SSE or HTTP transports. This enables secure access to MCP servers that require authentication.
Automatic OAuth Discovery
For servers that support OAuth discovery, you can omit the OAuth configuration and let the CLI discover it automatically:
{
"mcpServers"
: {
"discoveredServer"
: {
"url"
:
"https://api.example.com/sse"
}
}
}
The CLI will automatically:
Detect when a server requires OAuth authentication (401 responses)
Discover OAuth endpoints from server metadata
Perform dynamic client registration if supported
Handle the OAuth flow and token management
Authentication Flow
When connecting to an OAuth-enabled server:
Initial connection attempt
fails with 401 Unauthorized
OAuth discovery
finds authorization and token endpoints
Browser opens
for user authentication (requires local browser access)
Authorization code
is exchanged for access tokens
Tokens are stored
securely for future use
Connection retry
succeeds with valid tokens
Browser Redirect Requirements
Important:
OAuth authentication requires that the redirect URI is accessible:
Default behavior
: Redirects to
http://localhost:7777/oauth/callback
(works for local setups)
Custom redirect URI
: Use
--oauth-redirect-uri
or configure
redirectUri
in settings.json to specify a different URL
For
remote/cloud server deployments
(e.g., web terminals, SSH sessions, cloud IDEs):
The default
localhost
redirect will NOT work
You MUST configure a custom
redirectUri
pointing to a publicly accessible URL
The user’s browser must be able to reach this URL and redirect back to the server
Example for remote servers:
qwen
mcp
add
--transport
sse
remote-server
https://api.example.com/sse/
\
--oauth-redirect-uri
https://your-remote-server.example.com/oauth/callback
OAuth will not work in:
Headless environments without browser access
Environments where the configured
redirectUri
is unreachable from the user’s browser
Managing OAuth Authentication
Use the
/mcp auth
command to manage OAuth authentication:
# List servers requiring authentication
/mcp
auth
# Authenticate with a specific server
/mcp
auth
serverName
# Re-authenticate if tokens expire
/mcp
auth
serverName
OAuth Configuration Properties
enabled
(boolean): Enable OAuth for this server
clientId
(string): OAuth client identifier (optional with dynamic registration)
clientSecret
(string): OAuth client secret (optional for public clients)
authorizationUrl
(string): OAuth authorization endpoint (auto-discovered if omitted)
tokenUrl
(string): OAuth token endpoint (auto-discovered if omitted)
scopes
(string[]): Required OAuth scopes
redirectUri
(string): Custom redirect URI.
Critical for remote deployments
: Defaults to
http://localhost:7777/oauth/callback
. When running Qwen Code on remote/cloud servers, set this to a publicly accessible URL (e.g.,
https://your-server.com/oauth/callback
). Can be configured via
qwen mcp add --oauth-redirect-uri
or directly in settings.json.
tokenParamName
(string): Query parameter name for tokens in SSE URLs
audiences
(string[]): Audiences the token is valid for
Token Management
OAuth tokens are automatically:
Stored securely
in
~/.qwen/mcp-oauth-tokens.json
Refreshed
when expired (if refresh tokens are available)
Validated
before each connection attempt
Cleaned up
when invalid or expired
Authentication Provider Type
You can specify the authentication provider type using the
authProviderType
property:
authProviderType
(string): Specifies the authentication provider. Can be one of the following:
dynamic_discovery
(default): The CLI will automatically discover the OAuth configuration from the server.
google_credentials
: The CLI will use the Google Application Default Credentials (ADC) to authenticate with the server. When using this provider, you must specify the required scopes.
service_account_impersonation
: The CLI will impersonate a Google Cloud Service Account to authenticate with the server. This is useful for accessing IAP-protected services (this was specifically designed for Cloud Run services).
Google Credentials
{
"mcpServers"
: {
"googleCloudServer"
: {
"httpUrl"
:
"https://my-gcp-service.run.app/mcp"
,
"authProviderType"
:
"google_credentials"
,
"oauth"
: {
"scopes"
: [
"https://www.googleapis.com/auth/userinfo.email"
]
}
}
}
}
Service Account Impersonation
To authenticate with a server using Service Account Impersonation, you must set the
authProviderType
to
service_account_impersonation
and provide the following properties:
targetAudience
(string): The OAuth Client ID allowslisted on the IAP-protected application you are trying to access.
targetServiceAccount
(string): The email address of the Google Cloud Service Account to impersonate.
The CLI will use your local Application Default Credentials (ADC) to generate an OIDC ID token for the specified service account and audience. This token will then be used to authenticate with the MCP server.
Setup Instructions
Create
or use an existing OAuth 2.0 client ID.
To use an existing OAuth 2.0 client ID, follow the steps in
How to share OAuth Clients
.
Add the OAuth ID to the allowlist for
programmatic access
for the application.
Since Cloud Run is not yet a supported resource type in gcloud iap, you must allowlist the Client ID on the project.
Create a service account.
Documentation
,
Cloud Console Link
Add both the service account and users to the IAP Policy
in the “Security” tab of the Cloud Run service itself or via gcloud.
Grant all users and groups
who will access the MCP Server the necessary permissions to
impersonate the service account
(i.e.,
roles/iam.serviceAccountTokenCreator
).
Enable
the IAM Credentials API
for your project.
Example Configurations
Python MCP Server (Stdio)
{
"mcpServers"
: {
"pythonTools"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
,
"--port"
,
"8080"
],
"cwd"
:
"./mcp-servers/python"
,
"env"
: {
"DATABASE_URL"
:
"$DB_CONNECTION_STRING"
,
"API_KEY"
:
"${EXTERNAL_API_KEY}"
},
"timeout"
:
15000
}
}
}
Node.js MCP Server (Stdio)
{
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"dist/server.js"
,
"--verbose"
],
"cwd"
:
"./mcp-servers/node"
,
"trust"
:
true
}
}
}
Docker-based MCP Server
{
"mcpServers"
: {
"dockerizedServer"
: {
"command"
:
"docker"
,
"args"
: [
"run"
,
"-i"
,
"--rm"
,
"-e"
,
"API_KEY"
,
"-v"
,
"${PWD}:/workspace"
,
"my-mcp-server:latest"
],
"env"
: {
"API_KEY"
:
"$EXTERNAL_SERVICE_TOKEN"
}
}
}
}
HTTP-based MCP Server
{
"mcpServers"
: {
"httpServer"
: {
"httpUrl"
:
"http://localhost:3000/mcp"
,
"timeout"
:
5000
}
}
}
HTTP-based MCP Server with Custom Headers
{
"mcpServers"
: {
"httpServerWithAuth"
: {
"httpUrl"
:
"http://localhost:3000/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer your-api-token"
,
"X-Custom-Header"
:
"custom-value"
,
"Content-Type"
:
"application/json"
},
"timeout"
:
5000
}
}
}
MCP Server with Tool Filtering
{
"mcpServers"
: {
"filteredServer"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
],
"includeTools"
: [
"safe_tool"
,
"file_reader"
,
"data_processor"
],
// "excludeTools": ["dangerous_tool", "file_deleter"],
"timeout"
:
30000
}
}
}
SSE MCP Server with SA Impersonation
{
"mcpServers"
: {
"myIapProtectedServer"
: {
"url"
:
"https://my-iap-service.run.app/sse"
,
"authProviderType"
:
"service_account_impersonation"
,
"targetAudience"
:
"YOUR_IAP_CLIENT_ID.apps.googleusercontent.com"
,
"targetServiceAccount"
:
"your-sa@your-project.iam.gserviceaccount.com"
}
}
}
Discovery Process Deep Dive
When Qwen Code starts, it performs MCP server discovery through the following detailed process:
1. Server Iteration and Connection
For each configured server in
mcpServers
:
Status tracking begins:
Server status is set to
CONNECTING
Transport selection:
Based on configuration properties:
httpUrl
→
StreamableHTTPClientTransport
url
→
SSEClientTransport
command
→
StdioClientTransport
Connection establishment:
The MCP client attempts to connect with the configured timeout
Error handling:
Connection failures are logged and the server status is set to
DISCONNECTED
2. Tool Discovery
Upon successful connection:
Tool listing:
The client calls the MCP server’s tool listing endpoint
Schema validation:
Each tool’s function declaration is validated
Tool filtering:
Tools are filtered based on
includeTools
and
excludeTools
configuration
Name sanitization:
Tool names are cleaned to meet Qwen API requirements:
Invalid characters (non-alphanumeric, underscore, dot, hyphen) are replaced with underscores
Names longer than 63 characters are truncated with middle replacement (
___
)
3. Conflict Resolution
When multiple servers expose tools with the same name:
First registration wins:
The first server to register a tool name gets the unprefixed name
Automatic prefixing:
Subsequent servers get prefixed names:
serverName__toolName
Registry tracking:
The tool registry maintains mappings between server names and their tools
4. Schema Processing
Tool parameter schemas undergo sanitization for API compatibility:
$schema
properties
are removed
additionalProperties
are stripped
anyOf
with
default
have their default values removed (Vertex AI compatibility)
Recursive processing
applies to nested schemas
5. Connection Management
After discovery:
Persistent connections:
Servers that successfully register tools maintain their connections
Cleanup:
Servers that provide no usable tools have their connections closed
Status updates:
Final server statuses are set to
CONNECTED
or
DISCONNECTED
Tool Execution Flow
When the model decides to use an MCP tool, the following execution flow occurs:
1. Tool Invocation
The model generates a
FunctionCall
with:
Tool name:
The registered name (potentially prefixed)
Arguments:
JSON object matching the tool’s parameter schema
2. Confirmation Process
Each
DiscoveredMCPTool
implements sophisticated confirmation logic:
Trust-based Bypass
if
(
this
.trust) {
return
false
;
// No confirmation needed
}
Dynamic Allow-listing
The system maintains internal allow-lists for:
Server-level:
serverName
→ All tools from this server are trusted
Tool-level:
serverName.toolName
→ This specific tool is trusted
User Choice Handling
When confirmation is required, users can choose:
Proceed once:
Execute this time only
Always allow this tool:
Add to tool-level allow-list
Always allow this server:
Add to server-level allow-list
Cancel:
Abort execution
3. Execution
Upon confirmation (or trust bypass):
Parameter preparation:
Arguments are validated against the tool’s schema
MCP call:
The underlying
CallableTool
invokes the server with:
const
functionCalls
=
[
{
name:
this
.serverToolName,
// Original server tool name
args: params,
},
];
Response processing:
Results are formatted for both LLM context and user display
4. Response Handling
The execution result contains:
llmContent
:
Raw response parts for the language model’s context
returnDisplay
:
Formatted output for user display (often JSON in markdown code blocks)
How to interact with your MCP server
Using the
/mcp
Command
The
/mcp
command provides comprehensive information about your MCP server setup:
/mcp
This displays:
Server list:
All configured MCP servers
Connection status:
CONNECTED
,
CONNECTING
, or
DISCONNECTED
Server details:
Configuration summary (excluding sensitive data)
Available tools:
List of tools from each server with descriptions
Discovery state:
Overall discovery process status
Example
/mcp
Output
MCP Servers Status:
📡 pythonTools (CONNECTED)
Command: python -m my_mcp_server --port 8080
Working Directory: ./mcp-servers/python
Timeout: 15000ms
Tools: calculate_sum, file_analyzer, data_processor
🔌 nodeServer (DISCONNECTED)
Command: node dist/server.js --verbose
Error: Connection refused
🐳 dockerizedServer (CONNECTED)
Command: docker run -i --rm -e API_KEY my-mcp-server:latest
Tools: docker__deploy, docker__status
Discovery State: COMPLETED
Tool Usage
Once discovered, MCP tools are available to the Qwen model like built-in tools. The model will automatically:
Select appropriate tools
based on your requests
Present confirmation dialogs
(unless the server is trusted)
Execute tools
with proper parameters
Display results
in a user-friendly format
Status Monitoring and Troubleshooting
Connection States
The MCP integration tracks several states:
Server Status (
MCPServerStatus
)
DISCONNECTED
:
Server is not connected or has errors
CONNECTING
:
Connection attempt in progress
CONNECTED
:
Server is connected and ready
Discovery State (
MCPDiscoveryState
)
NOT_STARTED
:
Discovery hasn’t begun
IN_PROGRESS
:
Currently discovering servers
COMPLETED
:
Discovery finished (with or without errors)
Common Issues and Solutions
Server Won’t Connect
Symptoms:
Server shows
DISCONNECTED
status
Troubleshooting:
Check configuration:
Verify
command
,
args
, and
cwd
are correct
Test manually:
Run the server command directly to ensure it works
Check dependencies:
Ensure all required packages are installed
Review logs:
Look for error messages in the CLI output
Verify permissions:
Ensure the CLI can execute the server command
No Tools Discovered
Symptoms:
Server connects but no tools are available
Troubleshooting:
Verify tool registration:
Ensure your server actually registers tools
Check MCP protocol:
Confirm your server implements the MCP tool listing correctly
Review server logs:
Check stderr output for server-side errors
Test tool listing:
Manually test your server’s tool discovery endpoint
Tools Not Executing
Symptoms:
Tools are discovered but fail during execution
Troubleshooting:
Parameter validation:
Ensure your tool accepts the expected parameters
Schema compatibility:
Verify your input schemas are valid JSON Schema
Error handling:
Check if your tool is throwing unhandled exceptions
Timeout issues:
Consider increasing the
timeout
setting
Sandbox Compatibility
Symptoms:
MCP servers fail when sandboxing is enabled
Solutions:
Docker-based servers:
Use Docker containers that include all dependencies
Path accessibility:
Ensure server executables are available in the sandbox
Network access:
Configure sandbox to allow necessary network connections
Environment variables:
Verify required environment variables are passed through
Debugging Tips
Enable debug mode:
Run the CLI with
--debug
for verbose output
Check stderr:
MCP server stderr is captured and logged (INFO messages filtered)
Test isolation:
Test your MCP server independently before integrating
Incremental setup:
Start with simple tools before adding complex functionality
Use
/mcp
frequently:
Monitor server status during development
Important Notes
Security Considerations
Trust settings:
The
trust
option bypasses all confirmation dialogs. Use cautiously and only for servers you completely control
Access tokens:
Be security-aware when configuring environment variables containing API keys or tokens
Sandbox compatibility:
When using sandboxing, ensure MCP servers are available within the sandbox environment
Private data:
Using broadly scoped personal access tokens can lead to information leakage between repositories
Performance and Resource Management
Connection persistence:
The CLI maintains persistent connections to servers that successfully register tools
Automatic cleanup:
Connections to servers providing no tools are automatically closed
Timeout management:
Configure appropriate timeouts based on your server’s response characteristics
Resource monitoring:
MCP servers run as separate processes and consume system resources
Schema Compatibility
Schema compliance mode:
By default (
schemaCompliance: "auto"
), tool schemas are passed through as-is. Set
"model": { "generationConfig": { "schemaCompliance": "openapi_30" } }
in your
settings.json
to convert models to Strict OpenAPI 3.0 format.
OpenAPI 3.0 transformations:
When
openapi_30
mode is enabled, the system handles:
Nullable types:
["string", "null"]
->
type: "string", nullable: true
Const values:
const: "foo"
->
enum: ["foo"]
Exclusive limits: numeric
exclusiveMinimum
-> boolean form with
minimum
Keyword removal:
$schema
,
$id
,
dependencies
,
patternProperties
Name sanitization:
Tool names are automatically sanitized to meet API requirements
Conflict resolution:
Tool name conflicts between servers are resolved through automatic prefixing
This comprehensive integration makes MCP servers a powerful way to extend the CLI’s capabilities while maintaining security, reliability, and ease of use.
Returning Rich Content from Tools
MCP tools are not limited to returning simple text. You can return rich, multi-part content, including text, images, audio, and other binary data in a single tool response. This allows you to build powerful tools that can provide diverse information to the model in a single turn.
All data returned from the tool is processed and sent to the model as context for its next generation, enabling it to reason about or summarize the provided information.
How It Works
To return rich content, your tool’s response must adhere to the MCP specification for a
CallToolResult
. The
content
field of the result should be an array of
ContentBlock
objects. The CLI will correctly process this array, separating text from binary data and packaging it for the model.
You can mix and match different content block types in the
content
array. The supported block types include:
text
image
audio
resource
(embedded content)
resource_link
Example: Returning Text and an Image
Here is an example of a valid JSON response from an MCP tool that returns both a text description and an image:
{
"content"
: [
{
"type"
:
"text"
,
"text"
:
"Here is the logo you requested."
},
{
"type"
:
"image"
,
"data"
:
"BASE64_ENCODED_IMAGE_DATA_HERE"
,
"mimeType"
:
"image/png"
},
{
"type"
:
"text"
,
"text"
:
"The logo was created in 2025."
}
]
}
When Qwen Code receives this response, it will:
Extract all the text and combine it into a single
functionResponse
part for the model.
Present the image data as a separate
inlineData
part.
Provide a clean, user-friendly summary in the CLI, indicating that both text and an image were received.
This enables you to build sophisticated tools that can provide rich, multi-modal context to the Qwen model.
MCP Prompts as Slash Commands
In addition to tools, MCP servers can expose predefined prompts that can be executed as slash commands within Qwen Code. This allows you to create shortcuts for common or complex queries that can be easily invoked by name.
Defining Prompts on the Server
Here’s a small example of a stdio MCP server that defines prompts:
import
{ McpServer }
from
'@modelcontextprotocol/sdk/server/mcp.js'
;
import
{ StdioServerTransport }
from
'@modelcontextprotocol/sdk/server/stdio.js'
;
import
{ z }
from
'zod'
;
const
server
=
new
McpServer
({
name:
'prompt-server'
,
version:
'1.0.0'
,
});
server.
registerPrompt
(
'poem-writer'
,
{
title:
'Poem Writer'
,
description:
'Write a nice haiku'
,
argsSchema: { title: z.
string
(), mood: z.
string
().
optional
() },
},
({
title
,
mood
})
=>
({
messages: [
{
role:
'user'
,
content: {
type:
'text'
,
text:
`Write a haiku${
mood
?
` with the mood ${
mood
}`
:
''} called ${
title
}. Note that a haiku is 5 syllables followed by 7 syllables followed by 5 syllables `
,
},
},
],
}),
);
const
transport
=
new
StdioServerTransport
();
await
server.
connect
(transport);
This can be included in
settings.json
under
mcpServers
with:
{
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"filename.ts"
]
}
}
}
Invoking Prompts
Once a prompt is discovered, you can invoke it using its name as a slash command. The CLI will automatically handle parsing arguments.
/poem-writer
--title=
"Qwen Code"
--mood=
"reverent"
or, using positional arguments:
/poem-writer
"Qwen Code"
reverent
When you run this command, the CLI executes the
prompts/get
method on the MCP server with the provided arguments. The server is responsible for substituting the arguments into the prompt template and returning the final prompt text. The CLI then sends this prompt to the model for execution. This provides a convenient way to automate and share common workflows.
Managing MCP Servers with
qwen mcp
While you can always configure MCP servers by manually editing your
settings.json
file, the CLI provides a convenient set of commands to manage your server configurations programmatically. These commands streamline the process of adding, listing, and removing MCP servers without needing to directly edit JSON files.
Adding a Server (
qwen mcp add
)
The
add
command configures a new MCP server in your
settings.json
. Based on the scope (
-s, --scope
), it will be added to either the user config
~/.qwen/settings.json
or the project config
.qwen/settings.json
file.
Command:
qwen
mcp
add
[options]
<
name
>
<
commandOrUrl
>
[args...]
<name>
: A unique name for the server.
<commandOrUrl>
: The command to execute (for
stdio
) or the URL (for
http
/
sse
).
[args...]
: Optional arguments for a
stdio
command.
Options (Flags):
-s, --scope
: Configuration scope (user or project). [default: “project”]
-t, --transport
: Transport type (stdio, sse, http). [default: “stdio”]
-e, --env
: Set environment variables (e.g. -e KEY=value).
-H, --header
: Set HTTP headers for SSE and HTTP transports (e.g. -H “X-Api-Key: abc123” -H “Authorization: Bearer abc123”).
--timeout
: Set connection timeout in milliseconds.
--trust
: Trust the server (bypass all tool call confirmation prompts).
--description
: Set the description for the server.
--include-tools
: A comma-separated list of tools to include.
--exclude-tools
: A comma-separated list of tools to exclude.
--oauth-client-id
: OAuth client ID for MCP server authentication.
--oauth-client-secret
: OAuth client secret for MCP server authentication.
--oauth-redirect-uri
: OAuth redirect URI (e.g.,
https://your-server.com/oauth/callback
). Defaults to
http://localhost:7777/oauth/callback
for local setups.
Important for remote deployments
: When running Qwen Code on remote/cloud servers, set this to a publicly accessible URL.
--oauth-authorization-url
: OAuth authorization URL.
--oauth-token-url
: OAuth token URL.
--oauth-scopes
: OAuth scopes (comma-separated).
Adding an stdio server
This is the default transport for running local servers.
# Basic syntax
qwen
mcp
add
<
nam
e
>
<
comman
d
>
[args...]
# Example: Adding a local server
qwen
mcp
add
my-stdio-server
-e
API_KEY=
123
/path/to/server
arg1
arg2
arg3
# Example: Adding a local python server
qwen
mcp
add
python-server
python
server.py
--port
8080
Adding an HTTP server
This transport is for servers that use the streamable HTTP transport.
# Basic syntax
qwen
mcp
add
--transport
http
<
nam
e
>
<
ur
l
>
# Example: Adding an HTTP server
qwen
mcp
add
--transport
http
http-server
https://api.example.com/mcp/
# Example: Adding an HTTP server with an authentication header
qwen
mcp
add
--transport
http
secure-http
https://api.example.com/mcp/
--header
"Authorization: Bearer abc123"
Adding an SSE server
This transport is for servers that use Server-Sent Events (SSE).
# Basic syntax
qwen
mcp
add
--transport
sse
<
nam
e
>
<
ur
l
>
# Example: Adding an SSE server
qwen
mcp
add
--transport
sse
sse-server
https://api.example.com/sse/
# Example: Adding an SSE server with an authentication header
qwen
mcp
add
--transport
sse
secure-sse
https://api.example.com/sse/
--header
"Authorization: Bearer abc123"
# Example: Adding an OAuth-enabled SSE server
qwen
mcp
add
--transport
sse
oauth-server
https://api.example.com/sse/
\
--oauth-client-id
your-client-id
\
--oauth-redirect-uri
https://your-server.com/oauth/callback
\
--oauth-authorization-url
https://provider.example.com/authorize
\
--oauth-token-url
https://provider.example.com/token
Managing Servers (
qwen mcp
)
To view and manage all MCP servers currently configured, use the
manage
command or simply
qwen mcp
. This opens an interactive TUI dialog where you can:
View all MCP servers with their connection status
Enable/disable servers
Reconnect to disconnected servers
View tools and prompts provided by each server
View server logs
Command:
qwen
mcp
# or
qwen
mcp
manage
The management dialog provides a visual interface showing each server’s name, configuration details, connection status, and available tools/prompts.
Removing a Server (
qwen mcp remove
)
To delete a server from your configuration, use the
remove
command with the server’s name.
Command:
qwen
mcp
remove
<
nam
e
>
Example:
qwen
mcp
remove
my-server
This will find and delete the “my-server” entry from the
mcpServers
object in the appropriate
settings.json
file based on the scope (
-s, --scope
).
Last updated on
May 18, 2026
Web Search
Sandboxing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/mcp-server/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code RoadMap</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/roadmap/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/roadmap/</guid>
  <pubDate>Mon, 09 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Roadmap
Qwen Code RoadMap
Objective
: Catch up with Claude Code’s product functionality, continuously refine details, and enhance user experience.
Category
Phase 1
Phase 2
User Experience
✅ Terminal UI
✅ Support OpenAI Protocol
✅ Settings
✅ OAuth
✅ Cache Control
✅ Memory
✅ Compress
✅ Theme
Better UI
OnBoarding
LogView
✅ Session
Permission
🔄 Cross-platform Compatibility
✅ Coding Plan
✅ Anthropic Provider
✅ Multimodal Input
✅ Unified WebUI
Coding Workflow
✅ Slash Commands
✅ MCP
✅ P...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Roadmap
Qwen Code RoadMap
Objective
: Catch up with Claude Code’s product functionality, continuously refine details, and enhance user experience.
Category
Phase 1
Phase 2
User Experience
✅ Terminal UI
✅ Support OpenAI Protocol
✅ Settings
✅ OAuth
✅ Cache Control
✅ Memory
✅ Compress
✅ Theme
Better UI
OnBoarding
LogView
✅ Session
Permission
🔄 Cross-platform Compatibility
✅ Coding Plan
✅ Anthropic Provider
✅ Multimodal Input
✅ Unified WebUI
Coding Workflow
✅ Slash Commands
✅ MCP
✅ PlanMode
✅ TodoWrite
✅ SubAgent
✅ Multi Model
✅ Chat Management
✅ Tools (WebFetch, Bash, TextSearch, FileReadFile, EditFile)
🔄 Hooks
✅ Skill
✅ Headless Mode
✅ Tools (WebSearch)
✅ LSP Support
✅ Concurrent Runner
Building Open Capabilities
✅ Custom Commands
✅ QwenCode SDK
✅ Extension System
Integrating Community Ecosystem
✅ VSCode Plugin
✅ ACP/Zed
✅ GHA
Administrative Capabilities
✅ Stats
✅ Feedback
Costs
Dashboard
✅ User Feedback Dialog
For more details, please see the list below.
Features
Completed Features
Feature
Version
Description
Category
Phase
Coding Plan
V0.10.0
Alibaba Cloud Coding Plan authentication & models
User Experience
2
Unified WebUI
V0.9.0
Shared WebUI component library for VSCode/CLI
User Experience
2
Export Chat
V0.8.0
Export sessions to Markdown/HTML/JSON/JSONL
User Experience
2
Extension System
V0.8.0
Full extension management with slash commands
Building Open Capabilities
2
LSP Support
V0.7.0
Experimental LSP service (
--experimental-lsp
)
Coding Workflow
2
Anthropic Provider
V0.7.0
Anthropic API provider support
User Experience
2
User Feedback Dialog
V0.7.0
In-app feedback collection with fatigue mechanism
Administrative Capabilities
2
Concurrent Runner
V0.6.0
Batch CLI execution with Git integration
Coding Workflow
2
Multimodal Input
V0.6.0
Image, PDF, audio, video input support
User Experience
2
Skill
V0.6.0
Extensible custom AI skills (experimental)
Coding Workflow
2
Github Actions
V0.5.0
qwen-code-action and automation
Integrating Community Ecosystem
1
VSCode Plugin
V0.5.0
VSCode extension plugin
Integrating Community Ecosystem
1
QwenCode SDK
V0.4.0
Open SDK for third-party integration
Building Open Capabilities
1
Session
V0.4.0
Enhanced session management
User Experience
1
i18n
V0.3.0
Internationalization and multilingual support
User Experience
1
Headless Mode
V0.3.0
Headless mode (non-interactive)
Coding Workflow
1
ACP/Zed
V0.2.0
ACP and Zed editor integration
Integrating Community Ecosystem
1
Terminal UI
V0.1.0+
Interactive terminal user interface
User Experience
1
Settings
V0.1.0+
Configuration management system
User Experience
1
Theme
V0.1.0+
Multi-theme support
User Experience
1
Support OpenAI Protocol
V0.1.0+
Support for OpenAI API protocol
User Experience
1
Chat Management
V0.1.0+
Session management (save, restore, browse)
Coding Workflow
1
MCP
V0.1.0+
Model Context Protocol integration
Coding Workflow
1
Multi Model
V0.1.0+
Multi-model support and switching
Coding Workflow
1
Slash Commands
V0.1.0+
Slash command system
Coding Workflow
1
Tool: Bash
V0.1.0+
Shell command execution tool (with is_background param)
Coding Workflow
1
Tool: FileRead/EditFile
V0.1.0+
File read/write and edit tools
Coding Workflow
1
Custom Commands
V0.1.0+
Custom command loading
Building Open Capabilities
1
Feedback
V0.1.0+
Feedback mechanism (/bug command)
Administrative Capabilities
1
Stats
V0.1.0+
Usage statistics and quota display
Administrative Capabilities
1
Memory
V0.0.9+
Project-level and global memory management
User Experience
1
Cache Control
V0.0.9+
Prompt caching control (Anthropic, DashScope)
User Experience
1
PlanMode
V0.0.14
Task planning mode
Coding Workflow
1
Compress
V0.0.11
Chat compression mechanism
User Experience
1
SubAgent
V0.0.11
Dedicated sub-agent system
Coding Workflow
1
TodoWrite
V0.0.10
Task management and progress tracking
Coding Workflow
1
Tool: TextSearch
V0.0.8+
Text search tool (grep, supports .qwenignore)
Coding Workflow
1
Tool: WebFetch
V0.0.7+
Web content fetching tool
Coding Workflow
1
Tool: WebSearch
V0.0.7+
Web search tool (using Tavily API)
Coding Workflow
1
OAuth
V0.0.5+
OAuth login authentication (Qwen OAuth)
User Experience
1
Features to Develop
Feature
Priority
Status
Description
Category
Better UI
P1
Planned
Optimized terminal UI interaction
User Experience
OnBoarding
P1
Planned
New user onboarding flow
User Experience
Permission
P1
Planned
Permission system optimization
User Experience
Cross-platform Compatibility
P1
In Progress
Windows/Linux/macOS compatibility
User Experience
LogView
P2
Planned
Log viewing and debugging feature
User Experience
Hooks
P2
In Progress
Extension hooks system
Coding Workflow
Costs
P2
Planned
Cost tracking and analysis
Administrative Capabilities
Dashboard
P2
Planned
Management dashboard
Administrative Capabilities
Distinctive Features to Discuss
Feature
Status
Description
Home Spotlight
Research
Project discovery and quick launch
Competitive Mode
Research
Competitive mode
Pulse
Research
User activity pulse analysis (OpenAI Pulse reference)
Code Wiki
Research
Project codebase wiki/documentation system
Last updated on
May 18, 2026
Architecture
Contributing Guide</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/roadmap/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Roadmap
Qwen Code RoadMap
Objective
: Catch up with Claude Code’s product functionality, continuously refine details, and enhance user experience.
Category
Phase 1
Phase 2
User Experience
✅ Terminal UI
✅ Support OpenAI Protocol
✅ Settings
✅ OAuth
✅ Cache Control
✅ Memory
✅ Compress
✅ Theme
Better UI
OnBoarding
LogView
✅ Session
Permission
🔄 Cross-platform Compatibility
✅ Coding Plan
✅ Anthropic Provider
✅ Multimodal Input
✅ Unified WebUI
Coding Workflow
✅ Slash Commands
✅ MCP
✅ P...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Roadmap
Qwen Code RoadMap
Objective
: Catch up with Claude Code’s product functionality, continuously refine details, and enhance user experience.
Category
Phase 1
Phase 2
User Experience
✅ Terminal UI
✅ Support OpenAI Protocol
✅ Settings
✅ OAuth
✅ Cache Control
✅ Memory
✅ Compress
✅ Theme
Better UI
OnBoarding
LogView
✅ Session
Permission
🔄 Cross-platform Compatibility
✅ Coding Plan
✅ Anthropic Provider
✅ Multimodal Input
✅ Unified WebUI
Coding Workflow
✅ Slash Commands
✅ MCP
✅ PlanMode
✅ TodoWrite
✅ SubAgent
✅ Multi Model
✅ Chat Management
✅ Tools (WebFetch, Bash, TextSearch, FileReadFile, EditFile)
🔄 Hooks
✅ Skill
✅ Headless Mode
✅ Tools (WebSearch)
✅ LSP Support
✅ Concurrent Runner
Building Open Capabilities
✅ Custom Commands
✅ QwenCode SDK
✅ Extension System
Integrating Community Ecosystem
✅ VSCode Plugin
✅ ACP/Zed
✅ GHA
Administrative Capabilities
✅ Stats
✅ Feedback
Costs
Dashboard
✅ User Feedback Dialog
For more details, please see the list below.
Features
Completed Features
Feature
Version
Description
Category
Phase
Coding Plan
V0.10.0
Alibaba Cloud Coding Plan authentication & models
User Experience
2
Unified WebUI
V0.9.0
Shared WebUI component library for VSCode/CLI
User Experience
2
Export Chat
V0.8.0
Export sessions to Markdown/HTML/JSON/JSONL
User Experience
2
Extension System
V0.8.0
Full extension management with slash commands
Building Open Capabilities
2
LSP Support
V0.7.0
Experimental LSP service (
--experimental-lsp
)
Coding Workflow
2
Anthropic Provider
V0.7.0
Anthropic API provider support
User Experience
2
User Feedback Dialog
V0.7.0
In-app feedback collection with fatigue mechanism
Administrative Capabilities
2
Concurrent Runner
V0.6.0
Batch CLI execution with Git integration
Coding Workflow
2
Multimodal Input
V0.6.0
Image, PDF, audio, video input support
User Experience
2
Skill
V0.6.0
Extensible custom AI skills (experimental)
Coding Workflow
2
Github Actions
V0.5.0
qwen-code-action and automation
Integrating Community Ecosystem
1
VSCode Plugin
V0.5.0
VSCode extension plugin
Integrating Community Ecosystem
1
QwenCode SDK
V0.4.0
Open SDK for third-party integration
Building Open Capabilities
1
Session
V0.4.0
Enhanced session management
User Experience
1
i18n
V0.3.0
Internationalization and multilingual support
User Experience
1
Headless Mode
V0.3.0
Headless mode (non-interactive)
Coding Workflow
1
ACP/Zed
V0.2.0
ACP and Zed editor integration
Integrating Community Ecosystem
1
Terminal UI
V0.1.0+
Interactive terminal user interface
User Experience
1
Settings
V0.1.0+
Configuration management system
User Experience
1
Theme
V0.1.0+
Multi-theme support
User Experience
1
Support OpenAI Protocol
V0.1.0+
Support for OpenAI API protocol
User Experience
1
Chat Management
V0.1.0+
Session management (save, restore, browse)
Coding Workflow
1
MCP
V0.1.0+
Model Context Protocol integration
Coding Workflow
1
Multi Model
V0.1.0+
Multi-model support and switching
Coding Workflow
1
Slash Commands
V0.1.0+
Slash command system
Coding Workflow
1
Tool: Bash
V0.1.0+
Shell command execution tool (with is_background param)
Coding Workflow
1
Tool: FileRead/EditFile
V0.1.0+
File read/write and edit tools
Coding Workflow
1
Custom Commands
V0.1.0+
Custom command loading
Building Open Capabilities
1
Feedback
V0.1.0+
Feedback mechanism (/bug command)
Administrative Capabilities
1
Stats
V0.1.0+
Usage statistics and quota display
Administrative Capabilities
1
Memory
V0.0.9+
Project-level and global memory management
User Experience
1
Cache Control
V0.0.9+
Prompt caching control (Anthropic, DashScope)
User Experience
1
PlanMode
V0.0.14
Task planning mode
Coding Workflow
1
Compress
V0.0.11
Chat compression mechanism
User Experience
1
SubAgent
V0.0.11
Dedicated sub-agent system
Coding Workflow
1
TodoWrite
V0.0.10
Task management and progress tracking
Coding Workflow
1
Tool: TextSearch
V0.0.8+
Text search tool (grep, supports .qwenignore)
Coding Workflow
1
Tool: WebFetch
V0.0.7+
Web content fetching tool
Coding Workflow
1
Tool: WebSearch
V0.0.7+
Web search tool (using Tavily API)
Coding Workflow
1
OAuth
V0.0.5+
OAuth login authentication (Qwen OAuth)
User Experience
1
Features to Develop
Feature
Priority
Status
Description
Category
Better UI
P1
Planned
Optimized terminal UI interaction
User Experience
OnBoarding
P1
Planned
New user onboarding flow
User Experience
Permission
P1
Planned
Permission system optimization
User Experience
Cross-platform Compatibility
P1
In Progress
Windows/Linux/macOS compatibility
User Experience
LogView
P2
Planned
Log viewing and debugging feature
User Experience
Hooks
P2
In Progress
Extension hooks system
Coding Workflow
Costs
P2
Planned
Cost tracking and analysis
Administrative Capabilities
Dashboard
P2
Planned
Management dashboard
Administrative Capabilities
Distinctive Features to Discuss
Feature
Status
Description
Home Spotlight
Research
Project discovery and quick launch
Competitive Mode
Research
Competitive mode
Pulse
Research
User activity pulse analysis (OpenAI Pulse reference)
Code Wiki
Research
Project codebase wiki/documentation system
Last updated on
May 18, 2026
Architecture
Contributing Guide</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/roadmap/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Memory 记忆管理系统</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/auto-memory/memory-system/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/auto-memory/memory-system/</guid>
  <pubDate>Sun, 08 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Auto Memory
Memory 记忆管理系统
Memory 记忆管理系统
本文介绍 Qwen Code 中
Managed Auto-Memory
（托管自动记忆）的记忆管理机制、触发时机和实现细节。
目录
概述
存储结构
记忆类型
记忆条目格式
核心生命周期
Extract — 提取
Dream — 整合
Recall — 召回
Forget — 遗忘
索引重建
遥测埋点
概述
Managed Auto-Memory 是一套在 AI 会话过程中
自动
积累、整合和检索用户相关知识的持久化记忆系统。它通过四个核心操作维护记忆的生命周期：
操作
英文
触发方式
作用
提取
Extract
自动（每轮对话后）
从对话记录中提炼新知识写入记忆文件
整合
Dream
自动（周期性后台任务）
对记忆文件去重、合并，保持整洁
召回
Recall
自动（每轮对话前）
检索与当前请求相关的记忆注入到系统提示
遗忘
Forget
手动（用户命令
/forget
）
精确删除指定的记忆条目
存储结构
目录布局
~/.qwen/                              ...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Auto Memory
Memory 记忆管理系统
Memory 记忆管理系统
本文介绍 Qwen Code 中
Managed Auto-Memory
（托管自动记忆）的记忆管理机制、触发时机和实现细节。
目录
概述
存储结构
记忆类型
记忆条目格式
核心生命周期
Extract — 提取
Dream — 整合
Recall — 召回
Forget — 遗忘
索引重建
遥测埋点
概述
Managed Auto-Memory 是一套在 AI 会话过程中
自动
积累、整合和检索用户相关知识的持久化记忆系统。它通过四个核心操作维护记忆的生命周期：
操作
英文
触发方式
作用
提取
Extract
自动（每轮对话后）
从对话记录中提炼新知识写入记忆文件
整合
Dream
自动（周期性后台任务）
对记忆文件去重、合并，保持整洁
召回
Recall
自动（每轮对话前）
检索与当前请求相关的记忆注入到系统提示
遗忘
Forget
手动（用户命令
/forget
）
精确删除指定的记忆条目
存储结构
目录布局
~/.qwen/                                      ← 全局基础目录（默认）
└── projects/
└── <sanitized-git-root>/                 ← 项目标识（基于 Git 根路径）
├── meta.json                         ← 元数据（提取/整合时间戳、状态）
├── extract-cursor.json               ← 提取游标（已处理的对话偏移量）
├── consolidation.lock                ← Dream 进程互斥锁
└── memory/                           ← 记忆主目录
├── MEMORY.md                     ← 索引文件（自动生成，汇总所有条目）
├── user.md                       ← 用户偏好记忆（示例）
├── feedback.md                   ← 反馈规范记忆（示例）
├── project/
│   └── milestone.md              ← 项目记忆（支持子目录）
└── reference/
└── grafana.md                ← 外部资源记忆
环境变量覆盖
：
QWEN_CODE_MEMORY_BASE_DIR
：替换全局基础目录
QWEN_CODE_MEMORY_LOCAL=1
：改用项目内路径
.qwen/memory/
关键文件说明
文件
说明
meta.json
记录最后一次 Extract / Dream 的时间、会话 ID、涉及的记忆类型、执行状态
extract-cursor.json
记录当前会话已处理到对话历史的哪个偏移量，避免重复提取
consolidation.lock
Dream 运行时的文件锁，内容为持有者 PID，超过 1 小时自动失效
MEMORY.md
所有主题文件的索引，每次 Extract/Dream 后重建，格式为 Markdown 列表
记忆类型
系统支持四种内置记忆类型，每种对应不同的信息维度：
类型
存储内容
何时写入
何时读取
user
用户的角色、技能背景、工作习惯
了解到用户角色/偏好/知识背景时
回答需要根据用户背景定制时
feedback
用户对 AI 行为的指导：避免什么、继续什么
用户纠正 AI 或确认某种非显而易见的做法时
影响 AI 行为方式时
project
项目进展、目标、决策、截止日期、Bug 追踪
了解到谁在做什么、为什么、截止何时时
帮助 AI 理解工作背景和动机时
reference
外部系统资源指针（Dashboard、工单系统、Slack 频道等）
得知某种外部资源及其用途时
用户提及外部系统或相关信息时
不应该存入记忆的内容
：代码模式/约定、Git 历史、调试方案、临时任务状态、已在 QWEN.md/AGENTS.md 中记录的内容。
记忆条目格式
每个主题文件使用
YAML frontmatter + Markdown body
格式：
---
name
:
记忆名称
description
:
一句话描述（用于判断召回相关性，要具体）
type
:
user|feedback|project|reference
---
记忆主体内容（summary 行）
Why: 背后原因（让 AI 能理解边界情况而不是盲目遵守规则）
How to apply: 适用场景和使用方式
对于
feedback
和
project
类型，强烈建议填写
Why
和
How to apply
，使记忆在边界情况下仍能正确应用。
核心生命周期
Extract — 提取
触发时机
每次 AI 完成一轮响应后，由
scheduleAutoMemoryExtract
自动触发（后台非阻塞）。
调度逻辑（
extractScheduler.ts
）
跳过原因说明
：
原因
含义
memory_tool
本轮主 Agent 已直接写了记忆文件，跳过以避免冲突
already_running
提取正在进行且无法入队
queued
已有提取在运行，本次请求已入队
核心提取流程（
extract.ts
）
提取游标（Cursor）
：
字段：
{ sessionId, processedOffset, updatedAt }
每次提取后更新
processedOffset
为当前历史长度
下次提取时，只处理
offset >= processedOffset
的消息
跨会话时（
sessionId
变化）从偏移量 0 重新开始
Patch 过滤规则
：
摘要长度 < 12 字符 → 丢弃
摘要以
?
结尾 → 丢弃（疑问句）
包含临时性关键词（today/now/currently/temporary 等）→ 丢弃
相同
topic:summary
组合 → 去重
Dream — 整合
触发时机
每次 AI 完成一轮响应后，由
scheduleManagedAutoMemoryDream
自动触发（后台非阻塞）。但受多个门控条件保护，大多数情况下会被跳过。
调度门控（
dreamScheduler.ts
）
门控参数
：
参数
默认值
说明
minHoursBetweenDreams
24 小时
两次 Dream 之间的最小时间间隔
minSessionsBetweenDreams
5 个会话
触发 Dream 所需的最小新会话数
SESSION_SCAN_INTERVAL_MS
10 分钟
会话文件扫描的节流间隔
DREAM_LOCK_STALE_MS
1 小时
lock 文件被视为过期的时间阈值
锁机制
：
lock 文件位于
<project-state-dir>/consolidation.lock
内容为持有进程的 PID
检查时：若 PID 进程已不存在（
kill(pid, 0)
失败）或 lock 超过 1 小时 → 视为过期，自动清除
整合执行流程（
dream.ts
）
机械去重逻辑
：
对每个主题文件内部：按
summary.toLowerCase()
去重，合并
why
/
howToApply
字段
按 summary 字母顺序重新排序
跨文件：相同
type:summary
的条目合并到最先发现的文件，删除重复文件
Recall — 召回
触发时机
每轮 AI 处理用户请求之前，由
resolveRelevantAutoMemoryPromptForQuery
自动触发，将相关记忆注入系统提示词。
召回流程（
recall.ts
）
评分规则（启发式）
：
条件
加分
query token 出现在文档内容中
+2（每个 token）
query token 是该类型的特征关键词
+1（每个 token）
文档 body 非空
+1
每种类型的特征关键词
：
user
：user, preference, background, role, terse
feedback
：feedback, rule, avoid, style, summary
project
：project, goal, incident, deadline, release
reference
：reference, dashboard, ticket, docs, link
Prompt 构建规则
：
最多注入 5 篇文档（
MAX_RELEVANT_DOCS
）
每篇文档 body 截断至 1200 字符（
MAX_DOC_BODY_CHARS
）
超出截断时追加提示：“NOTE: Relevant memory truncated for prompt budget.”
包含文档的新鲜度信息（基于文件 mtime）
Forget — 遗忘
触发时机
由用户手动执行
/forget <query>
命令触发。
遗忘流程（
forget.ts
）
Entry ID 设计
：
单条目文件（常见情况）：
relativePath
（如
feedback/no-summary.md
）
多条目文件：
relativePath:index
（如
feedback/style.md:2
）
使用稳定 ID 使模型可以精确定位条目而不影响同文件的其他条目
索引重建
MEMORY.md
是所有主题文件的导航索引，每次 Extract 或 Dream 后调用
rebuildManagedAutoMemoryIndex
重建：
- [用户偏好](user/preferences.md) — 用户是资深 Go 工程师，第一次接触 React
- [反馈规范](feedback/style.md) — 保持回复简洁，不要尾部总结
- [项目里程碑](project/milestone.md) — 移动端发布切分支前的合并冻结窗口
索引限制
：
每行最多 150 字符（超出用
…
截断）
最多 200 行
总大小不超过 25,000 字节
遥测埋点
系统内置三类遥测事件，用于监控记忆操作的性能和效果：
Extract 遥测
字段
类型
说明
trigger
'auto'
触发方式（当前仅自动）
status
'completed'
|
'failed'
执行结果
patches_count
number
提取到的有效 patch 数量
touched_topics
string[]
被写入的记忆类型列表
duration_ms
number
总耗时（毫秒）
Dream 遥测
字段
类型
说明
trigger
'auto'
触发方式
status
'updated'
|
'noop'
|
'failed'
执行结果
deduped_entries
number
机械路径去重的条目数量
touched_topics
string[]
被修改的记忆类型列表
duration_ms
number
总耗时（毫秒）
Recall 遥测
字段
类型
说明
query_length
number
查询字符串长度
docs_scanned
number
扫描的文档总数
docs_selected
number
最终注入的文档数
strategy
'none'
|
'heuristic'
|
'model'
选择策略
duration_ms
number
总耗时（毫秒）
相关源文件索引
文件
职责
packages/core/src/memory/types.ts
类型定义：
AutoMemoryType
、
AutoMemoryMetadata
、
AutoMemoryExtractCursor
packages/core/src/memory/paths.ts
路径计算：
getAutoMemoryRoot
、
isAutoMemPath
、各类文件路径 helpers
packages/core/src/memory/store.ts
脚手架初始化：
ensureAutoMemoryScaffold
，索引/元数据读写
packages/core/src/memory/scan.ts
扫描主题文件：
scanAutoMemoryTopicDocuments
，解析 frontmatter
packages/core/src/memory/entries.ts
条目解析和渲染：
parseAutoMemoryEntries
、
renderAutoMemoryBody
packages/core/src/memory/extract.ts
提取核心逻辑：
runAutoMemoryExtract
，游标管理，patch 去重
packages/core/src/memory/extractScheduler.ts
提取调度器：
ManagedAutoMemoryExtractRuntime
，队列/运行状态机
packages/core/src/memory/extractionAgentPlanner.ts
提取 Agent：
runAutoMemoryExtractionByAgent
packages/core/src/memory/dream.ts
整合核心逻辑：
runManagedAutoMemoryDream
，Agent 路径 + 机械去重
packages/core/src/memory/dreamScheduler.ts
整合调度器：
ManagedAutoMemoryDreamRuntime
，门控检查，锁管理
packages/core/src/memory/dreamAgentPlanner.ts
整合 Agent：
planManagedAutoMemoryDreamByAgent
packages/core/src/memory/recall.ts
召回逻辑：
resolveRelevantAutoMemoryPromptForQuery
，启发式+模型双路径
packages/core/src/memory/forget.ts
遗忘逻辑：
forgetManagedAutoMemoryEntries
，候选生成+精确删除
packages/core/src/memory/indexer.ts
索引重建：
rebuildManagedAutoMemoryIndex
，
buildManagedAutoMemoryIndex
packages/core/src/memory/prompt.ts
系统提示模板：记忆类型说明、格式示例、使用规范
packages/core/src/memory/governance.ts
治理建议类型：
AutoMemoryGovernanceSuggestionType
packages/core/src/memory/state.ts
提取运行状态：
isExtractRunning
、
markExtractRunning
、
clearExtractRunning
packages/core/src/memory/memoryAge.ts
新鲜度描述：
memoryAge
、
memoryFreshnessText
Last updated on
May 18, 2026
Adaptive Output Token Escalation Design
Channels Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/auto-memory/memory-system/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Auto Memory
Memory 记忆管理系统
Memory 记忆管理系统
本文介绍 Qwen Code 中
Managed Auto-Memory
（托管自动记忆）的记忆管理机制、触发时机和实现细节。
目录
概述
存储结构
记忆类型
记忆条目格式
核心生命周期
Extract — 提取
Dream — 整合
Recall — 召回
Forget — 遗忘
索引重建
遥测埋点
概述
Managed Auto-Memory 是一套在 AI 会话过程中
自动
积累、整合和检索用户相关知识的持久化记忆系统。它通过四个核心操作维护记忆的生命周期：
操作
英文
触发方式
作用
提取
Extract
自动（每轮对话后）
从对话记录中提炼新知识写入记忆文件
整合
Dream
自动（周期性后台任务）
对记忆文件去重、合并，保持整洁
召回
Recall
自动（每轮对话前）
检索与当前请求相关的记忆注入到系统提示
遗忘
Forget
手动（用户命令
/forget
）
精确删除指定的记忆条目
存储结构
目录布局
~/.qwen/                              ...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Auto Memory
Memory 记忆管理系统
Memory 记忆管理系统
本文介绍 Qwen Code 中
Managed Auto-Memory
（托管自动记忆）的记忆管理机制、触发时机和实现细节。
目录
概述
存储结构
记忆类型
记忆条目格式
核心生命周期
Extract — 提取
Dream — 整合
Recall — 召回
Forget — 遗忘
索引重建
遥测埋点
概述
Managed Auto-Memory 是一套在 AI 会话过程中
自动
积累、整合和检索用户相关知识的持久化记忆系统。它通过四个核心操作维护记忆的生命周期：
操作
英文
触发方式
作用
提取
Extract
自动（每轮对话后）
从对话记录中提炼新知识写入记忆文件
整合
Dream
自动（周期性后台任务）
对记忆文件去重、合并，保持整洁
召回
Recall
自动（每轮对话前）
检索与当前请求相关的记忆注入到系统提示
遗忘
Forget
手动（用户命令
/forget
）
精确删除指定的记忆条目
存储结构
目录布局
~/.qwen/                                      ← 全局基础目录（默认）
└── projects/
└── <sanitized-git-root>/                 ← 项目标识（基于 Git 根路径）
├── meta.json                         ← 元数据（提取/整合时间戳、状态）
├── extract-cursor.json               ← 提取游标（已处理的对话偏移量）
├── consolidation.lock                ← Dream 进程互斥锁
└── memory/                           ← 记忆主目录
├── MEMORY.md                     ← 索引文件（自动生成，汇总所有条目）
├── user.md                       ← 用户偏好记忆（示例）
├── feedback.md                   ← 反馈规范记忆（示例）
├── project/
│   └── milestone.md              ← 项目记忆（支持子目录）
└── reference/
└── grafana.md                ← 外部资源记忆
环境变量覆盖
：
QWEN_CODE_MEMORY_BASE_DIR
：替换全局基础目录
QWEN_CODE_MEMORY_LOCAL=1
：改用项目内路径
.qwen/memory/
关键文件说明
文件
说明
meta.json
记录最后一次 Extract / Dream 的时间、会话 ID、涉及的记忆类型、执行状态
extract-cursor.json
记录当前会话已处理到对话历史的哪个偏移量，避免重复提取
consolidation.lock
Dream 运行时的文件锁，内容为持有者 PID，超过 1 小时自动失效
MEMORY.md
所有主题文件的索引，每次 Extract/Dream 后重建，格式为 Markdown 列表
记忆类型
系统支持四种内置记忆类型，每种对应不同的信息维度：
类型
存储内容
何时写入
何时读取
user
用户的角色、技能背景、工作习惯
了解到用户角色/偏好/知识背景时
回答需要根据用户背景定制时
feedback
用户对 AI 行为的指导：避免什么、继续什么
用户纠正 AI 或确认某种非显而易见的做法时
影响 AI 行为方式时
project
项目进展、目标、决策、截止日期、Bug 追踪
了解到谁在做什么、为什么、截止何时时
帮助 AI 理解工作背景和动机时
reference
外部系统资源指针（Dashboard、工单系统、Slack 频道等）
得知某种外部资源及其用途时
用户提及外部系统或相关信息时
不应该存入记忆的内容
：代码模式/约定、Git 历史、调试方案、临时任务状态、已在 QWEN.md/AGENTS.md 中记录的内容。
记忆条目格式
每个主题文件使用
YAML frontmatter + Markdown body
格式：
---
name
:
记忆名称
description
:
一句话描述（用于判断召回相关性，要具体）
type
:
user|feedback|project|reference
---
记忆主体内容（summary 行）
Why: 背后原因（让 AI 能理解边界情况而不是盲目遵守规则）
How to apply: 适用场景和使用方式
对于
feedback
和
project
类型，强烈建议填写
Why
和
How to apply
，使记忆在边界情况下仍能正确应用。
核心生命周期
Extract — 提取
触发时机
每次 AI 完成一轮响应后，由
scheduleAutoMemoryExtract
自动触发（后台非阻塞）。
调度逻辑（
extractScheduler.ts
）
跳过原因说明
：
原因
含义
memory_tool
本轮主 Agent 已直接写了记忆文件，跳过以避免冲突
already_running
提取正在进行且无法入队
queued
已有提取在运行，本次请求已入队
核心提取流程（
extract.ts
）
提取游标（Cursor）
：
字段：
{ sessionId, processedOffset, updatedAt }
每次提取后更新
processedOffset
为当前历史长度
下次提取时，只处理
offset >= processedOffset
的消息
跨会话时（
sessionId
变化）从偏移量 0 重新开始
Patch 过滤规则
：
摘要长度 < 12 字符 → 丢弃
摘要以
?
结尾 → 丢弃（疑问句）
包含临时性关键词（today/now/currently/temporary 等）→ 丢弃
相同
topic:summary
组合 → 去重
Dream — 整合
触发时机
每次 AI 完成一轮响应后，由
scheduleManagedAutoMemoryDream
自动触发（后台非阻塞）。但受多个门控条件保护，大多数情况下会被跳过。
调度门控（
dreamScheduler.ts
）
门控参数
：
参数
默认值
说明
minHoursBetweenDreams
24 小时
两次 Dream 之间的最小时间间隔
minSessionsBetweenDreams
5 个会话
触发 Dream 所需的最小新会话数
SESSION_SCAN_INTERVAL_MS
10 分钟
会话文件扫描的节流间隔
DREAM_LOCK_STALE_MS
1 小时
lock 文件被视为过期的时间阈值
锁机制
：
lock 文件位于
<project-state-dir>/consolidation.lock
内容为持有进程的 PID
检查时：若 PID 进程已不存在（
kill(pid, 0)
失败）或 lock 超过 1 小时 → 视为过期，自动清除
整合执行流程（
dream.ts
）
机械去重逻辑
：
对每个主题文件内部：按
summary.toLowerCase()
去重，合并
why
/
howToApply
字段
按 summary 字母顺序重新排序
跨文件：相同
type:summary
的条目合并到最先发现的文件，删除重复文件
Recall — 召回
触发时机
每轮 AI 处理用户请求之前，由
resolveRelevantAutoMemoryPromptForQuery
自动触发，将相关记忆注入系统提示词。
召回流程（
recall.ts
）
评分规则（启发式）
：
条件
加分
query token 出现在文档内容中
+2（每个 token）
query token 是该类型的特征关键词
+1（每个 token）
文档 body 非空
+1
每种类型的特征关键词
：
user
：user, preference, background, role, terse
feedback
：feedback, rule, avoid, style, summary
project
：project, goal, incident, deadline, release
reference
：reference, dashboard, ticket, docs, link
Prompt 构建规则
：
最多注入 5 篇文档（
MAX_RELEVANT_DOCS
）
每篇文档 body 截断至 1200 字符（
MAX_DOC_BODY_CHARS
）
超出截断时追加提示：“NOTE: Relevant memory truncated for prompt budget.”
包含文档的新鲜度信息（基于文件 mtime）
Forget — 遗忘
触发时机
由用户手动执行
/forget <query>
命令触发。
遗忘流程（
forget.ts
）
Entry ID 设计
：
单条目文件（常见情况）：
relativePath
（如
feedback/no-summary.md
）
多条目文件：
relativePath:index
（如
feedback/style.md:2
）
使用稳定 ID 使模型可以精确定位条目而不影响同文件的其他条目
索引重建
MEMORY.md
是所有主题文件的导航索引，每次 Extract 或 Dream 后调用
rebuildManagedAutoMemoryIndex
重建：
- [用户偏好](user/preferences.md) — 用户是资深 Go 工程师，第一次接触 React
- [反馈规范](feedback/style.md) — 保持回复简洁，不要尾部总结
- [项目里程碑](project/milestone.md) — 移动端发布切分支前的合并冻结窗口
索引限制
：
每行最多 150 字符（超出用
…
截断）
最多 200 行
总大小不超过 25,000 字节
遥测埋点
系统内置三类遥测事件，用于监控记忆操作的性能和效果：
Extract 遥测
字段
类型
说明
trigger
'auto'
触发方式（当前仅自动）
status
'completed'
|
'failed'
执行结果
patches_count
number
提取到的有效 patch 数量
touched_topics
string[]
被写入的记忆类型列表
duration_ms
number
总耗时（毫秒）
Dream 遥测
字段
类型
说明
trigger
'auto'
触发方式
status
'updated'
|
'noop'
|
'failed'
执行结果
deduped_entries
number
机械路径去重的条目数量
touched_topics
string[]
被修改的记忆类型列表
duration_ms
number
总耗时（毫秒）
Recall 遥测
字段
类型
说明
query_length
number
查询字符串长度
docs_scanned
number
扫描的文档总数
docs_selected
number
最终注入的文档数
strategy
'none'
|
'heuristic'
|
'model'
选择策略
duration_ms
number
总耗时（毫秒）
相关源文件索引
文件
职责
packages/core/src/memory/types.ts
类型定义：
AutoMemoryType
、
AutoMemoryMetadata
、
AutoMemoryExtractCursor
packages/core/src/memory/paths.ts
路径计算：
getAutoMemoryRoot
、
isAutoMemPath
、各类文件路径 helpers
packages/core/src/memory/store.ts
脚手架初始化：
ensureAutoMemoryScaffold
，索引/元数据读写
packages/core/src/memory/scan.ts
扫描主题文件：
scanAutoMemoryTopicDocuments
，解析 frontmatter
packages/core/src/memory/entries.ts
条目解析和渲染：
parseAutoMemoryEntries
、
renderAutoMemoryBody
packages/core/src/memory/extract.ts
提取核心逻辑：
runAutoMemoryExtract
，游标管理，patch 去重
packages/core/src/memory/extractScheduler.ts
提取调度器：
ManagedAutoMemoryExtractRuntime
，队列/运行状态机
packages/core/src/memory/extractionAgentPlanner.ts
提取 Agent：
runAutoMemoryExtractionByAgent
packages/core/src/memory/dream.ts
整合核心逻辑：
runManagedAutoMemoryDream
，Agent 路径 + 机械去重
packages/core/src/memory/dreamScheduler.ts
整合调度器：
ManagedAutoMemoryDreamRuntime
，门控检查，锁管理
packages/core/src/memory/dreamAgentPlanner.ts
整合 Agent：
planManagedAutoMemoryDreamByAgent
packages/core/src/memory/recall.ts
召回逻辑：
resolveRelevantAutoMemoryPromptForQuery
，启发式+模型双路径
packages/core/src/memory/forget.ts
遗忘逻辑：
forgetManagedAutoMemoryEntries
，候选生成+精确删除
packages/core/src/memory/indexer.ts
索引重建：
rebuildManagedAutoMemoryIndex
，
buildManagedAutoMemoryIndex
packages/core/src/memory/prompt.ts
系统提示模板：记忆类型说明、格式示例、使用规范
packages/core/src/memory/governance.ts
治理建议类型：
AutoMemoryGovernanceSuggestionType
packages/core/src/memory/state.ts
提取运行状态：
isExtractRunning
、
markExtractRunning
、
clearExtractRunning
packages/core/src/memory/memoryAge.ts
新鲜度描述：
memoryAge
、
memoryFreshnessText
Last updated on
May 18, 2026
Adaptive Output Token Escalation Design
Channels Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/auto-memory/memory-system/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Uninstall</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/support/Uninstall/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/support/Uninstall/</guid>
  <pubDate>Thu, 05 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Support
Uninstall
Uninstall
Your uninstall method depends on how you ran the CLI. Follow the instructions for either npx or a global npm installation.
Method 1: Using npx
npx runs packages from a temporary cache without a permanent installation. To “uninstall” the CLI, you must clear this cache, which will remove qwen-code and any other packages previously executed with npx.
The npx cache is a directory named
_npx
inside your main npm cache folder. You can find your npm cache path by ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Support
Uninstall
Uninstall
Your uninstall method depends on how you ran the CLI. Follow the instructions for either npx or a global npm installation.
Method 1: Using npx
npx runs packages from a temporary cache without a permanent installation. To “uninstall” the CLI, you must clear this cache, which will remove qwen-code and any other packages previously executed with npx.
The npx cache is a directory named
_npx
inside your main npm cache folder. You can find your npm cache path by running
npm config get cache
.
For macOS / Linux
# The path is typically ~/.npm/_npx
rm
-rf
"$(
npm
config get cache)/_npx"
For Windows
Command Prompt
:: The path is typically
%
LocalAppData
%
\npm
-
cache\_npx
rmdir /s /q
"%LocalAppData%\npm-cache\_npx"
PowerShell
# The path is typically $env:LocalAppData\npm-cache\_npx
Remove-Item
-
Path (
Join-Path
$
env:
LocalAppData
"npm-cache\_npx"
)
-
Recurse
-
Force
Method 2: Using npm (Global Install)
If you installed the CLI globally (e.g.
npm install -g @qwen-code/qwen-code
), use the
npm uninstall
command with the
-g
flag to remove it.
npm
uninstall
-g
@qwen-code/qwen-code
This command completely removes the package from your system.
Last updated on
May 18, 2026
Terms of Service
Introduction</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/support/Uninstall/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Support
Uninstall
Uninstall
Your uninstall method depends on how you ran the CLI. Follow the instructions for either npx or a global npm installation.
Method 1: Using npx
npx runs packages from a temporary cache without a permanent installation. To “uninstall” the CLI, you must clear this cache, which will remove qwen-code and any other packages previously executed with npx.
The npx cache is a directory named
_npx
inside your main npm cache folder. You can find your npm cache path by ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Support
Uninstall
Uninstall
Your uninstall method depends on how you ran the CLI. Follow the instructions for either npx or a global npm installation.
Method 1: Using npx
npx runs packages from a temporary cache without a permanent installation. To “uninstall” the CLI, you must clear this cache, which will remove qwen-code and any other packages previously executed with npx.
The npx cache is a directory named
_npx
inside your main npm cache folder. You can find your npm cache path by running
npm config get cache
.
For macOS / Linux
# The path is typically ~/.npm/_npx
rm
-rf
"$(
npm
config get cache)/_npx"
For Windows
Command Prompt
:: The path is typically
%
LocalAppData
%
\npm
-
cache\_npx
rmdir /s /q
"%LocalAppData%\npm-cache\_npx"
PowerShell
# The path is typically $env:LocalAppData\npm-cache\_npx
Remove-Item
-
Path (
Join-Path
$
env:
LocalAppData
"npm-cache\_npx"
)
-
Recurse
-
Force
Method 2: Using npm (Global Install)
If you installed the CLI globally (e.g.
npm install -g @qwen-code/qwen-code
), use the
npm uninstall
command with the
-g
flag to remove it.
npm
uninstall
-g
@qwen-code/qwen-code
This command completely removes the package from your system.
Last updated on
May 18, 2026
Terms of Service
Introduction</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/support/Uninstall/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Channels Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/channels/channels-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/channels/channels-design/</guid>
  <pubDate>Mon, 02 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Channels
Channels Design
Channels Design
External messaging integrations for Qwen Code — interact with an agent from Telegram, WeChat, and more.
User documentation:
Channels Overview
.
Overview
A
channel
connects an external messaging platform to a Qwen Code agent. Configured in
settings.json
, managed via
qwen channel
subcommands, multi-user (each user gets an isolated ACP session).
Architecture
┌──────────┐                        ┌─────────────────────────────────────┐
│ Telegram │    P...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Channels
Channels Design
Channels Design
External messaging integrations for Qwen Code — interact with an agent from Telegram, WeChat, and more.
User documentation:
Channels Overview
.
Overview
A
channel
connects an external messaging platform to a Qwen Code agent. Configured in
settings.json
, managed via
qwen channel
subcommands, multi-user (each user gets an isolated ACP session).
Architecture
┌──────────┐                        ┌─────────────────────────────────────┐
│ Telegram │    Platform API        │        Channel Service              │
│ User A   │◄──────────────────────►│                                     │
├──────────┤  (WebSocket/polling)   │  ┌───────────┐    ┌──────────────┐  │
│ WeChat   │◄──────────────────────►│  │ Platform   │    │  ACP Bridge  │  │
│ User B   │                        │  │ Adapter    │    │  (shared)    │  │
└──────────┘                        │  │            │    │              │  │
│  │ - connect  │    │  - spawns    │  │
│  │ - receive  │    │    qwen-code │  │
│  │ - send     │    │  - manages   │  │
│  │            │    │    sessions  │  │
│  └─────┬──────┘    └──────┬───────┘  │
│        │                  │          │
│        ▼                  ▼          │
│  ┌─────────────────────────────────┐ │
│  │  SenderGate · GroupGate         │ │
│  │  SessionRouter · ChannelBase    │ │
│  └─────────────────────────────────┘ │
└─────────────────────────────────────┘
│
│ stdio (ACP ndjson)
▼
┌─────────────────────────────────────┐
│        qwen-code --acp              │
│   Session A (user alice, id: "abc") │
│   Session B (user bob,   id: "def") │
└─────────────────────────────────────┘
Platform Adapter
— connects to external API, translates messages to/from Envelopes.
ACP Bridge
— spawns
qwen-code --acp
, manages sessions, emits
textChunk
/
toolCall
/
disconnected
events.
Session Router
— maps senders to ACP sessions via namespaced keys (
<channel>:<sender>
).
Sender Gate
/
Group Gate
— access control (allowlist / pairing / open) and mention gating.
Channel Base
— abstract base with Template Method pattern: plugins override
connect
,
sendMessage
,
disconnect
.
Channel Registry
—
Map<string, ChannelPlugin>
with collision detection.
Envelope
Normalized message format all platforms convert to:
Identity
:
senderId
,
senderName
,
chatId
,
channelName
Content
:
text
, optional
imageBase64
/
imageMimeType
, optional
referencedText
Context
:
isGroup
,
isMentioned
,
isReplyToBot
, optional
threadId
Plugin responsibilities:
senderId
must be stable/unique;
chatId
must distinguish DMs from groups; boolean flags must be accurate for gate logic; @mentions stripped from
text
.
Message Flow
Inbound:  User message → Adapter → GroupGate → SenderGate → Slash commands → SessionRouter → AcpBridge → Agent
Outbound: Agent response → AcpBridge → SessionRouter → Adapter → User
Slash commands (
/clear
,
/help
,
/status
) are handled in ChannelBase before reaching the agent.
Sessions
One
qwen-code --acp
process with multiple ACP sessions. Scope per channel:
user
(default),
thread
, or
single
. Routing keys namespaced as
<channelName>:<key>
.
Error Handling
Connection failures
— logged; service continues if at least one channel connects
Bridge crashes
— exponential backoff (max 3 retries),
setBridge()
on all channels, session restore
Session serialization
— per-session promise chains prevent concurrent prompt collisions
Plugin System
The architecture is extensible — new adapters (including third-party) can be added without modifying core. Built-in channels use the same plugin interface (dogfooding).
Plugin Contract
A
ChannelPlugin
declares
channelType
,
displayName
,
requiredConfigFields
, and a
createChannel()
factory. Plugins implement three methods:
Method
Responsibility
connect()
Connect to platform and register message handlers
sendMessage(chatId, text)
Format and deliver agent response
disconnect()
Clean up on shutdown
On inbound messages, plugins build an
Envelope
and call
this.handleInbound(envelope)
— the base class handles the rest: access control, group gating, pairing, session routing, prompt serialization, slash commands, instructions injection, reply context, and crash recovery.
Extension Points
Custom slash commands via
registerCommand()
Working indicators by wrapping
handleInbound()
with typing/reaction display
Tool call hooks via
onToolCall()
Media handling by attaching to Envelope before
handleInbound()
Discovery & Loading
External plugins are
extensions
managed by
ExtensionManager
, declared in
qwen-extension.json
:
{
"name"
:
"my-channel-extension"
,
"version"
:
"1.0.0"
,
"channels"
: {
"my-platform"
: {
"entry"
:
"dist/index.js"
,
"displayName"
:
"My Platform Channel"
}
}
}
Loading sequence at
qwen channel start
: load settings → register built-ins → scan extensions → dynamic import + validate → register (reject collisions) → validate config →
createChannel()
→
connect()
.
Plugins run in-process (no sandbox), same trust model as npm dependencies.
Configuration
{
"channels"
: {
"my-telegram"
: {
"type"
:
"telegram"
,
"token"
:
"$TELEGRAM_BOT_TOKEN"
,
// env var reference
"senderPolicy"
:
"allowlist"
,
// allowlist | pairing | open
"allowedUsers"
: [
"123456"
],
"sessionScope"
:
"user"
,
// user | thread | single
"cwd"
:
"/path/to/project"
,
"model"
:
"qwen3.5-plus"
,
"instructions"
:
"Keep responses short."
,
"groupPolicy"
:
"disabled"
,
// disabled | allowlist | open
"groups"
: {
"*"
: {
"requireMention"
:
true
} },
},
},
}
Auth is plugin-specific: static token (Telegram), app credentials (DingTalk), QR code login (WeChat), proxy token (TMCP).
CLI Commands
# Channels
qwen
channel
start
[name]
# start all or one channel
qwen
channel
stop
# stop running service
qwen
channel
status
# show channels, sessions, uptime
qwen
channel
pairing
list
<
c
h
>
# pending pairing requests
qwen
channel
pairing
approve
<
c
h
>
<
cod
e
>
# approve a request
# Extensions
qwen
extensions
install
<
path-or-packag
e
>
# install
qwen
extensions
link
<
local-pat
h
>
# symlink for dev
qwen
extensions
list
# show installed
qwen
extensions
remove
<
nam
e
>
# uninstall
Package Structure
packages/channels/
├── base/                    # @qwen-code/channel-base
│   └── src/
│       ├── AcpBridge.ts     # ACP process lifecycle, session management
│       ├── SessionRouter.ts # sender ↔ session mapping, persistence
│       ├── SenderGate.ts    # allowlist / pairing / open
│       ├── GroupGate.ts     # group chat policy + mention gating
│       ├── PairingStore.ts  # pairing code generation + approval
│       ├── ChannelBase.ts   # abstract base: routing, slash commands
│       └── types.ts         # Envelope, ChannelConfig, etc.
├── telegram/                # @qwen-code/channel-telegram
├── weixin/                  # @qwen-code/channel-weixin
└── dingtalk/                # @qwen-code/channel-dingtalk
Future Work
Safety & Group Chat
Per-group tool restrictions
—
tools
/
toolsBySender
deny/allow lists per group
Group context history
— ring buffer of recent skipped messages, prepended on @mention
Regex mention patterns
— fallback
mentionPatterns
for unreliable @mention metadata
Per-group instructions
—
instructions
field on
GroupConfig
for per-group personas
/activation
command
— runtime toggle for
requireMention
, persisted to disk
Operational Tooling
qwen channel doctor
— config validation, env vars, bot tokens, network checks
qwen channel status --probe
— real connectivity checks per channel
Platform Expansion
Discord
— Bot API + Gateway, servers/channels/DMs/threads
Slack
— Bolt SDK, Socket Mode, workspaces/channels/DMs/threads
Multi-Agent
Multi-agent routing
— multiple agents with bindings per channel/group/user
Broadcast groups
— multiple agents respond to the same message
Plugin Ecosystem
Community plugin template
—
create-qwen-channel
scaffolding tool
Plugin registry/discovery
—
qwen extensions search
, version compatibility
Last updated on
May 18, 2026
Memory 记忆管理系统
Compact Mode Design: Competitive Analysis & Optimization</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/channels/channels-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Channels
Channels Design
Channels Design
External messaging integrations for Qwen Code — interact with an agent from Telegram, WeChat, and more.
User documentation:
Channels Overview
.
Overview
A
channel
connects an external messaging platform to a Qwen Code agent. Configured in
settings.json
, managed via
qwen channel
subcommands, multi-user (each user gets an isolated ACP session).
Architecture
┌──────────┐                        ┌─────────────────────────────────────┐
│ Telegram │    P...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Channels
Channels Design
Channels Design
External messaging integrations for Qwen Code — interact with an agent from Telegram, WeChat, and more.
User documentation:
Channels Overview
.
Overview
A
channel
connects an external messaging platform to a Qwen Code agent. Configured in
settings.json
, managed via
qwen channel
subcommands, multi-user (each user gets an isolated ACP session).
Architecture
┌──────────┐                        ┌─────────────────────────────────────┐
│ Telegram │    Platform API        │        Channel Service              │
│ User A   │◄──────────────────────►│                                     │
├──────────┤  (WebSocket/polling)   │  ┌───────────┐    ┌──────────────┐  │
│ WeChat   │◄──────────────────────►│  │ Platform   │    │  ACP Bridge  │  │
│ User B   │                        │  │ Adapter    │    │  (shared)    │  │
└──────────┘                        │  │            │    │              │  │
│  │ - connect  │    │  - spawns    │  │
│  │ - receive  │    │    qwen-code │  │
│  │ - send     │    │  - manages   │  │
│  │            │    │    sessions  │  │
│  └─────┬──────┘    └──────┬───────┘  │
│        │                  │          │
│        ▼                  ▼          │
│  ┌─────────────────────────────────┐ │
│  │  SenderGate · GroupGate         │ │
│  │  SessionRouter · ChannelBase    │ │
│  └─────────────────────────────────┘ │
└─────────────────────────────────────┘
│
│ stdio (ACP ndjson)
▼
┌─────────────────────────────────────┐
│        qwen-code --acp              │
│   Session A (user alice, id: "abc") │
│   Session B (user bob,   id: "def") │
└─────────────────────────────────────┘
Platform Adapter
— connects to external API, translates messages to/from Envelopes.
ACP Bridge
— spawns
qwen-code --acp
, manages sessions, emits
textChunk
/
toolCall
/
disconnected
events.
Session Router
— maps senders to ACP sessions via namespaced keys (
<channel>:<sender>
).
Sender Gate
/
Group Gate
— access control (allowlist / pairing / open) and mention gating.
Channel Base
— abstract base with Template Method pattern: plugins override
connect
,
sendMessage
,
disconnect
.
Channel Registry
—
Map<string, ChannelPlugin>
with collision detection.
Envelope
Normalized message format all platforms convert to:
Identity
:
senderId
,
senderName
,
chatId
,
channelName
Content
:
text
, optional
imageBase64
/
imageMimeType
, optional
referencedText
Context
:
isGroup
,
isMentioned
,
isReplyToBot
, optional
threadId
Plugin responsibilities:
senderId
must be stable/unique;
chatId
must distinguish DMs from groups; boolean flags must be accurate for gate logic; @mentions stripped from
text
.
Message Flow
Inbound:  User message → Adapter → GroupGate → SenderGate → Slash commands → SessionRouter → AcpBridge → Agent
Outbound: Agent response → AcpBridge → SessionRouter → Adapter → User
Slash commands (
/clear
,
/help
,
/status
) are handled in ChannelBase before reaching the agent.
Sessions
One
qwen-code --acp
process with multiple ACP sessions. Scope per channel:
user
(default),
thread
, or
single
. Routing keys namespaced as
<channelName>:<key>
.
Error Handling
Connection failures
— logged; service continues if at least one channel connects
Bridge crashes
— exponential backoff (max 3 retries),
setBridge()
on all channels, session restore
Session serialization
— per-session promise chains prevent concurrent prompt collisions
Plugin System
The architecture is extensible — new adapters (including third-party) can be added without modifying core. Built-in channels use the same plugin interface (dogfooding).
Plugin Contract
A
ChannelPlugin
declares
channelType
,
displayName
,
requiredConfigFields
, and a
createChannel()
factory. Plugins implement three methods:
Method
Responsibility
connect()
Connect to platform and register message handlers
sendMessage(chatId, text)
Format and deliver agent response
disconnect()
Clean up on shutdown
On inbound messages, plugins build an
Envelope
and call
this.handleInbound(envelope)
— the base class handles the rest: access control, group gating, pairing, session routing, prompt serialization, slash commands, instructions injection, reply context, and crash recovery.
Extension Points
Custom slash commands via
registerCommand()
Working indicators by wrapping
handleInbound()
with typing/reaction display
Tool call hooks via
onToolCall()
Media handling by attaching to Envelope before
handleInbound()
Discovery & Loading
External plugins are
extensions
managed by
ExtensionManager
, declared in
qwen-extension.json
:
{
"name"
:
"my-channel-extension"
,
"version"
:
"1.0.0"
,
"channels"
: {
"my-platform"
: {
"entry"
:
"dist/index.js"
,
"displayName"
:
"My Platform Channel"
}
}
}
Loading sequence at
qwen channel start
: load settings → register built-ins → scan extensions → dynamic import + validate → register (reject collisions) → validate config →
createChannel()
→
connect()
.
Plugins run in-process (no sandbox), same trust model as npm dependencies.
Configuration
{
"channels"
: {
"my-telegram"
: {
"type"
:
"telegram"
,
"token"
:
"$TELEGRAM_BOT_TOKEN"
,
// env var reference
"senderPolicy"
:
"allowlist"
,
// allowlist | pairing | open
"allowedUsers"
: [
"123456"
],
"sessionScope"
:
"user"
,
// user | thread | single
"cwd"
:
"/path/to/project"
,
"model"
:
"qwen3.5-plus"
,
"instructions"
:
"Keep responses short."
,
"groupPolicy"
:
"disabled"
,
// disabled | allowlist | open
"groups"
: {
"*"
: {
"requireMention"
:
true
} },
},
},
}
Auth is plugin-specific: static token (Telegram), app credentials (DingTalk), QR code login (WeChat), proxy token (TMCP).
CLI Commands
# Channels
qwen
channel
start
[name]
# start all or one channel
qwen
channel
stop
# stop running service
qwen
channel
status
# show channels, sessions, uptime
qwen
channel
pairing
list
<
c
h
>
# pending pairing requests
qwen
channel
pairing
approve
<
c
h
>
<
cod
e
>
# approve a request
# Extensions
qwen
extensions
install
<
path-or-packag
e
>
# install
qwen
extensions
link
<
local-pat
h
>
# symlink for dev
qwen
extensions
list
# show installed
qwen
extensions
remove
<
nam
e
>
# uninstall
Package Structure
packages/channels/
├── base/                    # @qwen-code/channel-base
│   └── src/
│       ├── AcpBridge.ts     # ACP process lifecycle, session management
│       ├── SessionRouter.ts # sender ↔ session mapping, persistence
│       ├── SenderGate.ts    # allowlist / pairing / open
│       ├── GroupGate.ts     # group chat policy + mention gating
│       ├── PairingStore.ts  # pairing code generation + approval
│       ├── ChannelBase.ts   # abstract base: routing, slash commands
│       └── types.ts         # Envelope, ChannelConfig, etc.
├── telegram/                # @qwen-code/channel-telegram
├── weixin/                  # @qwen-code/channel-weixin
└── dingtalk/                # @qwen-code/channel-dingtalk
Future Work
Safety & Group Chat
Per-group tool restrictions
—
tools
/
toolsBySender
deny/allow lists per group
Group context history
— ring buffer of recent skipped messages, prepended on @mention
Regex mention patterns
— fallback
mentionPatterns
for unreliable @mention metadata
Per-group instructions
—
instructions
field on
GroupConfig
for per-group personas
/activation
command
— runtime toggle for
requireMention
, persisted to disk
Operational Tooling
qwen channel doctor
— config validation, env vars, bot tokens, network checks
qwen channel status --probe
— real connectivity checks per channel
Platform Expansion
Discord
— Bot API + Gateway, servers/channels/DMs/threads
Slack
— Bolt SDK, Socket Mode, workspaces/channels/DMs/threads
Multi-Agent
Multi-agent routing
— multiple agents with bindings per channel/group/user
Broadcast groups
— multiple agents respond to the same message
Plugin Ecosystem
Community plugin template
—
create-qwen-channel
scaffolding tool
Plugin registry/discovery
—
qwen extensions search
, version compatibility
Last updated on
May 18, 2026
Memory 记忆管理系统
Compact Mode Design: Competitive Analysis & Optimization</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/channels/channels-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Channels</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/overview/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/overview/</guid>
  <pubDate>Sun, 01 Dec 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
Overview
Channels
Channels let you interact with a Qwen Code agent from messaging platforms like Telegram, WeChat, or DingTalk, instead of the terminal. You send messages from your phone or desktop chat app, and the agent responds just like it would in the CLI.
How It Works
When you run
qwen channel start
, Qwen Code:
Reads channel configurations from your
settings.json
Spawns a single agent process using the
Agent Client Protocol (ACP)
Connects to each messaging pla...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
Overview
Channels
Channels let you interact with a Qwen Code agent from messaging platforms like Telegram, WeChat, or DingTalk, instead of the terminal. You send messages from your phone or desktop chat app, and the agent responds just like it would in the CLI.
How It Works
When you run
qwen channel start
, Qwen Code:
Reads channel configurations from your
settings.json
Spawns a single agent process using the
Agent Client Protocol (ACP)
Connects to each messaging platform and starts listening for messages
Routes incoming messages to the agent and sends responses back to the correct chat
All channels share one agent process with isolated sessions per user. Each channel can have its own working directory, model, and instructions.
Quick Start
Set up a bot on your messaging platform (see channel-specific guides:
Telegram
,
WeChat
,
DingTalk
)
Add the channel configuration to
~/.qwen/settings.json
Run
qwen channel start
to start all channels, or
qwen channel start <name>
for a single channel
Want to connect a platform that isn’t built in? See
Plugins
to add a custom adapter as an extension.
Configuration
Channels are configured under the
channels
key in
settings.json
. Each channel has a name and a set of options:
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"token"
:
"$MY_BOT_TOKEN"
,
"senderPolicy"
:
"allowlist"
,
"allowedUsers"
: [
"123456789"
],
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/working/directory"
,
"instructions"
:
"Optional system instructions for the agent."
,
"groupPolicy"
:
"disabled"
,
"groups"
: {
"*"
: {
"requireMention"
:
true
}
}
}
}
}
Options
Option
Required
Description
type
Yes
Channel type:
telegram
,
weixin
,
dingtalk
, or a custom type from an extension (see
Plugins
)
token
Telegram
Bot token. Supports
$ENV_VAR
syntax to read from environment variables. Not needed for WeChat or DingTalk
clientId
DingTalk
DingTalk AppKey. Supports
$ENV_VAR
syntax
clientSecret
DingTalk
DingTalk AppSecret. Supports
$ENV_VAR
syntax
model
No
Model to use for this channel (e.g.,
qwen3.5-plus
). Overrides the default model. Useful for multimodal models that support image input
senderPolicy
No
Who can talk to the bot:
allowlist
(default),
open
, or
pairing
allowedUsers
No
List of user IDs allowed to use the bot (used by
allowlist
and
pairing
policies)
sessionScope
No
How sessions are scoped:
user
(default),
thread
, or
single
cwd
No
Working directory for the agent. Defaults to the current directory
instructions
No
Custom instructions prepended to the first message of each session
groupPolicy
No
Group chat access:
disabled
(default),
allowlist
, or
open
. See
Group Chats
groups
No
Per-group settings. Keys are group chat IDs or
"*"
for defaults. See
Group Chats
dispatchMode
No
What happens when you send a message while the bot is busy:
steer
(default),
collect
, or
followup
. See
Dispatch Modes
blockStreaming
No
Progressive response delivery:
on
or
off
(default). See
Block Streaming
blockStreamingChunk
No
Chunk size bounds:
{ "minChars": 400, "maxChars": 1000 }
. See
Block Streaming
blockStreamingCoalesce
No
Idle flush:
{ "idleMs": 1500 }
. See
Block Streaming
Sender Policy
Controls who can interact with the bot:
allowlist
(default) — Only users listed in
allowedUsers
can send messages. Others are silently ignored.
pairing
— Unknown senders receive a pairing code. The bot operator approves them via CLI, and they’re added to a persistent allowlist. Users in
allowedUsers
skip pairing entirely. See
DM Pairing
below.
open
— Anyone can send messages. Use with caution.
Session Scope
Controls how conversation sessions are managed:
user
(default) — One session per user. All messages from the same user share a conversation.
thread
— One session per thread/topic. Useful for group chats with threads.
single
— One shared session for all users. Everyone shares the same conversation.
Token Security
Bot tokens should not be stored directly in
settings.json
. Instead, use environment variable references:
{
"token"
:
"$TELEGRAM_BOT_TOKEN"
}
Set the actual token in your shell environment or in a
.env
file that gets loaded before running the channel.
DM Pairing
When
senderPolicy
is set to
"pairing"
, unknown senders go through an approval flow:
An unknown user sends a message to the bot
The bot replies with an 8-character pairing code (e.g.,
VEQDDWXJ
)
The user shares the code with you (the bot operator)
You approve them via CLI:
qwen
channel
pairing
approve
my-channel
VEQDDWXJ
Once approved, the user’s ID is saved to
~/.qwen/channels/<name>-allowlist.json
and all future messages go through normally.
Pairing CLI Commands
# List pending pairing requests
qwen
channel
pairing
list
my-channel
# Approve a request by code
qwen
channel
pairing
approve
my-channel
<
COD
E
>
Pairing Rules
Codes are 8 characters, uppercase, using an unambiguous alphabet (no
0
/
O
/
1
/
I
)
Codes expire after 1 hour
Maximum 3 pending requests per channel at a time — additional requests are ignored until one expires or is approved
Users listed in
allowedUsers
in
settings.json
always skip pairing
Approved users are stored in
~/.qwen/channels/<name>-allowlist.json
— treat this file as sensitive
Group Chats
By default, the bot only works in direct messages. To enable group chat support, set
groupPolicy
to
"allowlist"
or
"open"
.
Group Policy
Controls whether the bot participates in group chats at all:
disabled
(default) — The bot ignores all group messages. Safest option.
allowlist
— The bot only responds in groups explicitly listed in
groups
by chat ID. The
"*"
key provides default settings but does
not
act as a wildcard allow.
open
— The bot responds in all groups it’s added to. Use with caution.
Mention Gating
In groups, the bot requires an
@mention
or a reply to one of its messages by default. This prevents the bot from responding to every message in a group chat.
Configure per-group with the
groups
setting:
{
"groups"
: {
"*"
: {
"requireMention"
:
true
},
"-100123456"
: {
"requireMention"
:
false
}
}
}
"*"
— Default settings for all groups. Only sets config defaults, not an allowlist entry.
Group chat ID
— Override settings for a specific group. Overrides
"*"
defaults.
requireMention
(default:
true
) — When
true
, the bot only responds to messages that @mention it or reply to one of its messages. When
false
, the bot responds to all messages (useful for dedicated task groups).
How group messages are evaluated
1. groupPolicy — is this group allowed?           (no → ignore)
2. requireMention — was the bot mentioned/replied to? (no → ignore)
3. senderPolicy — is this sender approved?         (no → pairing flow)
4. Route to session
Telegram Setup for Groups
Add the bot to a group
Disable privacy mode
in BotFather (
/mybots
→ Bot Settings → Group Privacy → Turn Off) — otherwise the bot won’t see non-command messages
Remove and re-add the bot
to the group after changing privacy mode (Telegram caches this setting)
Finding a Group Chat ID
To find a group’s chat ID for the
groups
allowlist:
Stop the bot if it’s running
Send a message mentioning the bot in the group
Use the Telegram Bot API to check queued updates:
curl
-s
"https://api.telegram.org/bot${
TELEGRAM_BOT_TOKEN
}/getUpdates"
|
python3
-m
json.tool
Look for
message.chat.id
in the response — group IDs are negative numbers (e.g.,
-5170296765
).
Media Support
Channels support sending images and files to the agent, not just text.
Images
Send a photo to the bot and the agent will see it — useful for sharing screenshots, error messages, or diagrams. The image is sent directly to the model as a vision input.
To use image support, configure a multimodal model for the channel:
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"model"
:
"qwen3.5-plus"
,
...
}
}
}
Files
Send a document (PDF, code file, text file, etc.) to the bot. The file is downloaded and saved to a temporary directory, and the agent is told the file path so it can read the contents using its file-reading tools.
Files work with any model — no multimodal support required.
Platform differences
Feature
Telegram
WeChat
DingTalk
Images
Direct download via Bot API
CDN download with AES decryption
downloadCode API (two-step)
Files
Direct download via Bot API (20MB limit)
CDN download with AES decryption
downloadCode API (two-step)
Captions
Photo/file captions included as message text
Not applicable
Rich text: mixed text + images in one message
Dispatch Modes
Controls what happens when you send a new message while the bot is still processing a previous one.
steer
(default) — The bot cancels the current request and starts working on your new message. Best for normal chat, where a follow-up usually means you want to correct or redirect the bot.
collect
— Your new messages are buffered. When the current request finishes, all buffered messages are combined into a single follow-up prompt. Good for async workflows where you want to queue up thoughts.
followup
— Each message is queued and processed as its own separate turn, in order. Useful for batch workflows where each message is independent.
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"dispatchMode"
:
"steer"
,
...
}
}
}
You can also set dispatch mode per group, overriding the channel default:
{
"groups"
: {
"*"
: {
"requireMention"
:
true
,
"dispatchMode"
:
"steer"
},
"-100123456"
: {
"dispatchMode"
:
"collect"
}
}
}
Block Streaming
By default, the agent works for a while and then sends one large response. With block streaming enabled, the response arrives as multiple shorter messages while the agent is still working — similar to how ChatGPT or Claude show progressive output.
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"blockStreaming"
:
"on"
,
"blockStreamingChunk"
: {
"minChars"
:
400
,
"maxChars"
:
1000
},
"blockStreamingCoalesce"
: {
"idleMs"
:
1500
},
...
}
}
}
How it works
The agent’s response is split into blocks at paragraph boundaries and sent as separate messages
minChars
(default 400) — don’t send a block until it’s at least this long, to avoid spamming tiny messages
maxChars
(default 1000) — if a block gets this long without a natural break, send it anyway
idleMs
(default 1500) — if the agent pauses (e.g., running a tool), send what’s buffered so far
When the agent finishes, any remaining text is sent immediately
Only
blockStreaming
is required. The chunk and coalesce settings are optional and have sensible defaults.
Slash Commands
Channels support slash commands. These are handled locally (no agent round-trip):
/help
— List available commands
/clear
— Clear your session and start fresh (aliases:
/reset
,
/new
)
/status
— Show session info and access policy
All other slash commands (e.g.,
/compress
,
/summary
) are forwarded to the agent.
These commands work on all channel types (Telegram, WeChat, DingTalk).
Running
# Start all configured channels (shared agent process)
qwen
channel
start
# Start a single channel
qwen
channel
start
my-channel
# Check if the service is running
qwen
channel
status
# Stop the running service
qwen
channel
stop
The bot runs in the foreground. Press
Ctrl+C
to stop, or use
qwen channel stop
from another terminal.
Multi-Channel Mode
When you run
qwen channel start
without a name, all channels defined in
settings.json
start together sharing a single agent process. Each channel maintains its own sessions — a Telegram user and a WeChat user get separate conversations, even though they share the same agent.
Each channel uses its own
cwd
from its config, so different channels can work on different projects simultaneously.
Service Management
The channel service uses a PID file (
~/.qwen/channels/service.pid
) to track the running instance:
Duplicate prevention
: Running
qwen channel start
while a service is already running will show an error instead of starting a second instance
qwen channel stop
: Gracefully stops the running service from another terminal
qwen channel status
: Shows whether the service is running, its uptime, and session counts per channel
Crash Recovery
If the agent process crashes unexpectedly, the channel service automatically restarts it and attempts to restore all active sessions. Users can continue their conversations without starting over.
Sessions are persisted to
~/.qwen/channels/sessions.json
while the service is running
On crash: the agent restarts within 3 seconds and reloads saved sessions
After 3 consecutive crashes, the service exits with an error
On clean shutdown (Ctrl+C or
qwen channel stop
): session data is cleared — the next start is always fresh
Last updated on
May 18, 2026
i18n
Telegram</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/overview/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
Overview
Channels
Channels let you interact with a Qwen Code agent from messaging platforms like Telegram, WeChat, or DingTalk, instead of the terminal. You send messages from your phone or desktop chat app, and the agent responds just like it would in the CLI.
How It Works
When you run
qwen channel start
, Qwen Code:
Reads channel configurations from your
settings.json
Spawns a single agent process using the
Agent Client Protocol (ACP)
Connects to each messaging pla...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
Overview
Channels
Channels let you interact with a Qwen Code agent from messaging platforms like Telegram, WeChat, or DingTalk, instead of the terminal. You send messages from your phone or desktop chat app, and the agent responds just like it would in the CLI.
How It Works
When you run
qwen channel start
, Qwen Code:
Reads channel configurations from your
settings.json
Spawns a single agent process using the
Agent Client Protocol (ACP)
Connects to each messaging platform and starts listening for messages
Routes incoming messages to the agent and sends responses back to the correct chat
All channels share one agent process with isolated sessions per user. Each channel can have its own working directory, model, and instructions.
Quick Start
Set up a bot on your messaging platform (see channel-specific guides:
Telegram
,
WeChat
,
DingTalk
)
Add the channel configuration to
~/.qwen/settings.json
Run
qwen channel start
to start all channels, or
qwen channel start <name>
for a single channel
Want to connect a platform that isn’t built in? See
Plugins
to add a custom adapter as an extension.
Configuration
Channels are configured under the
channels
key in
settings.json
. Each channel has a name and a set of options:
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"token"
:
"$MY_BOT_TOKEN"
,
"senderPolicy"
:
"allowlist"
,
"allowedUsers"
: [
"123456789"
],
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/working/directory"
,
"instructions"
:
"Optional system instructions for the agent."
,
"groupPolicy"
:
"disabled"
,
"groups"
: {
"*"
: {
"requireMention"
:
true
}
}
}
}
}
Options
Option
Required
Description
type
Yes
Channel type:
telegram
,
weixin
,
dingtalk
, or a custom type from an extension (see
Plugins
)
token
Telegram
Bot token. Supports
$ENV_VAR
syntax to read from environment variables. Not needed for WeChat or DingTalk
clientId
DingTalk
DingTalk AppKey. Supports
$ENV_VAR
syntax
clientSecret
DingTalk
DingTalk AppSecret. Supports
$ENV_VAR
syntax
model
No
Model to use for this channel (e.g.,
qwen3.5-plus
). Overrides the default model. Useful for multimodal models that support image input
senderPolicy
No
Who can talk to the bot:
allowlist
(default),
open
, or
pairing
allowedUsers
No
List of user IDs allowed to use the bot (used by
allowlist
and
pairing
policies)
sessionScope
No
How sessions are scoped:
user
(default),
thread
, or
single
cwd
No
Working directory for the agent. Defaults to the current directory
instructions
No
Custom instructions prepended to the first message of each session
groupPolicy
No
Group chat access:
disabled
(default),
allowlist
, or
open
. See
Group Chats
groups
No
Per-group settings. Keys are group chat IDs or
"*"
for defaults. See
Group Chats
dispatchMode
No
What happens when you send a message while the bot is busy:
steer
(default),
collect
, or
followup
. See
Dispatch Modes
blockStreaming
No
Progressive response delivery:
on
or
off
(default). See
Block Streaming
blockStreamingChunk
No
Chunk size bounds:
{ "minChars": 400, "maxChars": 1000 }
. See
Block Streaming
blockStreamingCoalesce
No
Idle flush:
{ "idleMs": 1500 }
. See
Block Streaming
Sender Policy
Controls who can interact with the bot:
allowlist
(default) — Only users listed in
allowedUsers
can send messages. Others are silently ignored.
pairing
— Unknown senders receive a pairing code. The bot operator approves them via CLI, and they’re added to a persistent allowlist. Users in
allowedUsers
skip pairing entirely. See
DM Pairing
below.
open
— Anyone can send messages. Use with caution.
Session Scope
Controls how conversation sessions are managed:
user
(default) — One session per user. All messages from the same user share a conversation.
thread
— One session per thread/topic. Useful for group chats with threads.
single
— One shared session for all users. Everyone shares the same conversation.
Token Security
Bot tokens should not be stored directly in
settings.json
. Instead, use environment variable references:
{
"token"
:
"$TELEGRAM_BOT_TOKEN"
}
Set the actual token in your shell environment or in a
.env
file that gets loaded before running the channel.
DM Pairing
When
senderPolicy
is set to
"pairing"
, unknown senders go through an approval flow:
An unknown user sends a message to the bot
The bot replies with an 8-character pairing code (e.g.,
VEQDDWXJ
)
The user shares the code with you (the bot operator)
You approve them via CLI:
qwen
channel
pairing
approve
my-channel
VEQDDWXJ
Once approved, the user’s ID is saved to
~/.qwen/channels/<name>-allowlist.json
and all future messages go through normally.
Pairing CLI Commands
# List pending pairing requests
qwen
channel
pairing
list
my-channel
# Approve a request by code
qwen
channel
pairing
approve
my-channel
<
COD
E
>
Pairing Rules
Codes are 8 characters, uppercase, using an unambiguous alphabet (no
0
/
O
/
1
/
I
)
Codes expire after 1 hour
Maximum 3 pending requests per channel at a time — additional requests are ignored until one expires or is approved
Users listed in
allowedUsers
in
settings.json
always skip pairing
Approved users are stored in
~/.qwen/channels/<name>-allowlist.json
— treat this file as sensitive
Group Chats
By default, the bot only works in direct messages. To enable group chat support, set
groupPolicy
to
"allowlist"
or
"open"
.
Group Policy
Controls whether the bot participates in group chats at all:
disabled
(default) — The bot ignores all group messages. Safest option.
allowlist
— The bot only responds in groups explicitly listed in
groups
by chat ID. The
"*"
key provides default settings but does
not
act as a wildcard allow.
open
— The bot responds in all groups it’s added to. Use with caution.
Mention Gating
In groups, the bot requires an
@mention
or a reply to one of its messages by default. This prevents the bot from responding to every message in a group chat.
Configure per-group with the
groups
setting:
{
"groups"
: {
"*"
: {
"requireMention"
:
true
},
"-100123456"
: {
"requireMention"
:
false
}
}
}
"*"
— Default settings for all groups. Only sets config defaults, not an allowlist entry.
Group chat ID
— Override settings for a specific group. Overrides
"*"
defaults.
requireMention
(default:
true
) — When
true
, the bot only responds to messages that @mention it or reply to one of its messages. When
false
, the bot responds to all messages (useful for dedicated task groups).
How group messages are evaluated
1. groupPolicy — is this group allowed?           (no → ignore)
2. requireMention — was the bot mentioned/replied to? (no → ignore)
3. senderPolicy — is this sender approved?         (no → pairing flow)
4. Route to session
Telegram Setup for Groups
Add the bot to a group
Disable privacy mode
in BotFather (
/mybots
→ Bot Settings → Group Privacy → Turn Off) — otherwise the bot won’t see non-command messages
Remove and re-add the bot
to the group after changing privacy mode (Telegram caches this setting)
Finding a Group Chat ID
To find a group’s chat ID for the
groups
allowlist:
Stop the bot if it’s running
Send a message mentioning the bot in the group
Use the Telegram Bot API to check queued updates:
curl
-s
"https://api.telegram.org/bot${
TELEGRAM_BOT_TOKEN
}/getUpdates"
|
python3
-m
json.tool
Look for
message.chat.id
in the response — group IDs are negative numbers (e.g.,
-5170296765
).
Media Support
Channels support sending images and files to the agent, not just text.
Images
Send a photo to the bot and the agent will see it — useful for sharing screenshots, error messages, or diagrams. The image is sent directly to the model as a vision input.
To use image support, configure a multimodal model for the channel:
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"model"
:
"qwen3.5-plus"
,
...
}
}
}
Files
Send a document (PDF, code file, text file, etc.) to the bot. The file is downloaded and saved to a temporary directory, and the agent is told the file path so it can read the contents using its file-reading tools.
Files work with any model — no multimodal support required.
Platform differences
Feature
Telegram
WeChat
DingTalk
Images
Direct download via Bot API
CDN download with AES decryption
downloadCode API (two-step)
Files
Direct download via Bot API (20MB limit)
CDN download with AES decryption
downloadCode API (two-step)
Captions
Photo/file captions included as message text
Not applicable
Rich text: mixed text + images in one message
Dispatch Modes
Controls what happens when you send a new message while the bot is still processing a previous one.
steer
(default) — The bot cancels the current request and starts working on your new message. Best for normal chat, where a follow-up usually means you want to correct or redirect the bot.
collect
— Your new messages are buffered. When the current request finishes, all buffered messages are combined into a single follow-up prompt. Good for async workflows where you want to queue up thoughts.
followup
— Each message is queued and processed as its own separate turn, in order. Useful for batch workflows where each message is independent.
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"dispatchMode"
:
"steer"
,
...
}
}
}
You can also set dispatch mode per group, overriding the channel default:
{
"groups"
: {
"*"
: {
"requireMention"
:
true
,
"dispatchMode"
:
"steer"
},
"-100123456"
: {
"dispatchMode"
:
"collect"
}
}
}
Block Streaming
By default, the agent works for a while and then sends one large response. With block streaming enabled, the response arrives as multiple shorter messages while the agent is still working — similar to how ChatGPT or Claude show progressive output.
{
"channels"
: {
"my-channel"
: {
"type"
:
"telegram"
,
"blockStreaming"
:
"on"
,
"blockStreamingChunk"
: {
"minChars"
:
400
,
"maxChars"
:
1000
},
"blockStreamingCoalesce"
: {
"idleMs"
:
1500
},
...
}
}
}
How it works
The agent’s response is split into blocks at paragraph boundaries and sent as separate messages
minChars
(default 400) — don’t send a block until it’s at least this long, to avoid spamming tiny messages
maxChars
(default 1000) — if a block gets this long without a natural break, send it anyway
idleMs
(default 1500) — if the agent pauses (e.g., running a tool), send what’s buffered so far
When the agent finishes, any remaining text is sent immediately
Only
blockStreaming
is required. The chunk and coalesce settings are optional and have sensible defaults.
Slash Commands
Channels support slash commands. These are handled locally (no agent round-trip):
/help
— List available commands
/clear
— Clear your session and start fresh (aliases:
/reset
,
/new
)
/status
— Show session info and access policy
All other slash commands (e.g.,
/compress
,
/summary
) are forwarded to the agent.
These commands work on all channel types (Telegram, WeChat, DingTalk).
Running
# Start all configured channels (shared agent process)
qwen
channel
start
# Start a single channel
qwen
channel
start
my-channel
# Check if the service is running
qwen
channel
status
# Stop the running service
qwen
channel
stop
The bot runs in the foreground. Press
Ctrl+C
to stop, or use
qwen channel stop
from another terminal.
Multi-Channel Mode
When you run
qwen channel start
without a name, all channels defined in
settings.json
start together sharing a single agent process. Each channel maintains its own sessions — a Telegram user and a WeChat user get separate conversations, even though they share the same agent.
Each channel uses its own
cwd
from its config, so different channels can work on different projects simultaneously.
Service Management
The channel service uses a PID file (
~/.qwen/channels/service.pid
) to track the running instance:
Duplicate prevention
: Running
qwen channel start
while a service is already running will show an error instead of starting a second instance
qwen channel stop
: Gracefully stops the running service from another terminal
qwen channel status
: Shows whether the service is running, its uptime, and session counts per channel
Crash Recovery
If the agent process crashes unexpectedly, the channel service automatically restarts it and attempts to restore all active sessions. Users can continue their conversations without starting over.
Sessions are persisted to
~/.qwen/channels/sessions.json
while the service is running
On crash: the agent restarts within 3 seconds and reloads saved sessions
After 3 consecutive crashes, the service exits with an error
On clean shutdown (Ctrl+C or
qwen channel stop
): session data is cleared — the next start is always fresh
Last updated on
May 18, 2026
i18n
Telegram</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/overview/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Prompt Suggestion Implementation Status</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-implementation/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-implementation/</guid>
  <pubDate>Fri, 29 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Prompt Suggestion
Prompt Suggestion Implementation Status
Prompt Suggestion Implementation Status
Tracks the implementation status of the prompt suggestion (NES) feature across all packages.
Core Module (
packages/core/src/followup/
)
Component
Status
Lines
Description
followupState.ts
✅ Done
~230
Framework-agnostic controller with timer/debounce
suggestionGenerator.ts
✅ Done
~260
LLM generation + 12 filter rules + forked query support
forkedQuery.ts
✅ Done
~240
CacheSafeParams + createFo...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Prompt Suggestion
Prompt Suggestion Implementation Status
Prompt Suggestion Implementation Status
Tracks the implementation status of the prompt suggestion (NES) feature across all packages.
Core Module (
packages/core/src/followup/
)
Component
Status
Lines
Description
followupState.ts
✅ Done
~230
Framework-agnostic controller with timer/debounce
suggestionGenerator.ts
✅ Done
~260
LLM generation + 12 filter rules + forked query support
forkedQuery.ts
✅ Done
~240
CacheSafeParams + createForkedChat + runForkedQuery
overlayFs.ts
✅ Done
~140
Copy-on-write overlay filesystem
speculationToolGate.ts
✅ Done
~150
Tool boundary enforcement with AST shell parser
speculation.ts
✅ Done
~540
Speculation engine with pipelined suggestion + model override
CLI Integration (
packages/cli/
)
Component
Status
Description
AppContainer.tsx
✅ Done
Suggestion generation, speculation lifecycle, UI rendering
InputPrompt.tsx
✅ Done
Tab/Enter/Right Arrow acceptance, dismiss + abort
Composer.tsx
✅ Done
Props threading
UIStateContext.tsx
✅ Done
promptSuggestion + dismissPromptSuggestion
useFollowupSuggestions.tsx
✅ Done
React hook with telemetry + keystroke tracking
settingsSchema.ts
✅ Done
3 feature flags + fastModel setting
settings.schema.json
✅ Done
VSCode settings schema
WebUI Integration (
packages/webui/
)
Component
Status
Description
InputForm.tsx
✅ Done
Tab/Enter/Right Arrow + explicitText submit
useFollowupSuggestions.ts
✅ Done
React hook with onOutcome support
followup.ts
✅ Done
Subpath entry
components.css
✅ Done
Ghost text styling
vite.config.followup.ts
✅ Done
Separate build config
Telemetry (
packages/core/src/telemetry/
)
Component
Status
Description
PromptSuggestionEvent
✅ Done
10 fields
SpeculationEvent
✅ Done
7 fields
logPromptSuggestion()
✅ Done
OpenTelemetry logger
logSpeculation()
✅ Done
OpenTelemetry logger
Test Coverage
Test File
Tests
Description
followupState.test.ts
14
Controller timer, debounce, accept callback, onOutcome, clear
suggestionGenerator.test.ts
16
All 12 filter rules + edge cases + false positives
overlayFs.test.ts
15
COW write, read resolution, apply, cleanup, path traversal
speculationToolGate.test.ts
27
Tool categories, approval mode, shell AST, path rewrite
forkedQuery.test.ts
6
Cache params save/get/clear, deep clone, version detection
speculation.test.ts
7
ensureToolResultPairing edge cases
smoke.test.ts
21
Cross-module E2E: filter + overlay + toolGate + cache + pairing
InputPrompt.test.tsx
4
Tab, Enter+submit, Right Arrow, completion guard
Audit History
Round
Issues Found
Issues Fixed
R1-R4
10
10 (rule engine → LLM, state simplification)
R5-R6
2
2 (Enter keybinding conflict, Right Arrow telemetry)
R7-R8
3
3 (WebUI telemetry, dead type, test coverage)
R9
0
— (convergence)
R10-R11
1
1 (historyManager dep)
R12-R13
1
1 (evaluative regex word boundaries)
Phase 1+2 R1-R4
20+
20+ (permission bypass, overlay safety, race conditions)
Total
37+
37+
Claude Code Alignment
Feature
Alignment
Notes
Prompt text
100%
Identical (brand name only)
12 filter rules
100%+
\b word boundaries improvement
UI interaction (Tab/Enter/Right)
100%
Guard conditions
100%
13 checks
Telemetry
100%
10+7 fields
Cache sharing
✅
DashScope cache_control
Speculation
✅
COW overlay + tool gating
Pipelined suggestion
✅
Generated after speculation completes
State management
100%+
Controller pattern, Object.freeze
Last updated on
May 18, 2026
Prompt Suggestion (NES) Design
Speculation Engine Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-implementation/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Prompt Suggestion
Prompt Suggestion Implementation Status
Prompt Suggestion Implementation Status
Tracks the implementation status of the prompt suggestion (NES) feature across all packages.
Core Module (
packages/core/src/followup/
)
Component
Status
Lines
Description
followupState.ts
✅ Done
~230
Framework-agnostic controller with timer/debounce
suggestionGenerator.ts
✅ Done
~260
LLM generation + 12 filter rules + forked query support
forkedQuery.ts
✅ Done
~240
CacheSafeParams + createFo...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Prompt Suggestion
Prompt Suggestion Implementation Status
Prompt Suggestion Implementation Status
Tracks the implementation status of the prompt suggestion (NES) feature across all packages.
Core Module (
packages/core/src/followup/
)
Component
Status
Lines
Description
followupState.ts
✅ Done
~230
Framework-agnostic controller with timer/debounce
suggestionGenerator.ts
✅ Done
~260
LLM generation + 12 filter rules + forked query support
forkedQuery.ts
✅ Done
~240
CacheSafeParams + createForkedChat + runForkedQuery
overlayFs.ts
✅ Done
~140
Copy-on-write overlay filesystem
speculationToolGate.ts
✅ Done
~150
Tool boundary enforcement with AST shell parser
speculation.ts
✅ Done
~540
Speculation engine with pipelined suggestion + model override
CLI Integration (
packages/cli/
)
Component
Status
Description
AppContainer.tsx
✅ Done
Suggestion generation, speculation lifecycle, UI rendering
InputPrompt.tsx
✅ Done
Tab/Enter/Right Arrow acceptance, dismiss + abort
Composer.tsx
✅ Done
Props threading
UIStateContext.tsx
✅ Done
promptSuggestion + dismissPromptSuggestion
useFollowupSuggestions.tsx
✅ Done
React hook with telemetry + keystroke tracking
settingsSchema.ts
✅ Done
3 feature flags + fastModel setting
settings.schema.json
✅ Done
VSCode settings schema
WebUI Integration (
packages/webui/
)
Component
Status
Description
InputForm.tsx
✅ Done
Tab/Enter/Right Arrow + explicitText submit
useFollowupSuggestions.ts
✅ Done
React hook with onOutcome support
followup.ts
✅ Done
Subpath entry
components.css
✅ Done
Ghost text styling
vite.config.followup.ts
✅ Done
Separate build config
Telemetry (
packages/core/src/telemetry/
)
Component
Status
Description
PromptSuggestionEvent
✅ Done
10 fields
SpeculationEvent
✅ Done
7 fields
logPromptSuggestion()
✅ Done
OpenTelemetry logger
logSpeculation()
✅ Done
OpenTelemetry logger
Test Coverage
Test File
Tests
Description
followupState.test.ts
14
Controller timer, debounce, accept callback, onOutcome, clear
suggestionGenerator.test.ts
16
All 12 filter rules + edge cases + false positives
overlayFs.test.ts
15
COW write, read resolution, apply, cleanup, path traversal
speculationToolGate.test.ts
27
Tool categories, approval mode, shell AST, path rewrite
forkedQuery.test.ts
6
Cache params save/get/clear, deep clone, version detection
speculation.test.ts
7
ensureToolResultPairing edge cases
smoke.test.ts
21
Cross-module E2E: filter + overlay + toolGate + cache + pairing
InputPrompt.test.tsx
4
Tab, Enter+submit, Right Arrow, completion guard
Audit History
Round
Issues Found
Issues Fixed
R1-R4
10
10 (rule engine → LLM, state simplification)
R5-R6
2
2 (Enter keybinding conflict, Right Arrow telemetry)
R7-R8
3
3 (WebUI telemetry, dead type, test coverage)
R9
0
— (convergence)
R10-R11
1
1 (historyManager dep)
R12-R13
1
1 (evaluative regex word boundaries)
Phase 1+2 R1-R4
20+
20+ (permission bypass, overlay safety, race conditions)
Total
37+
37+
Claude Code Alignment
Feature
Alignment
Notes
Prompt text
100%
Identical (brand name only)
12 filter rules
100%+
\b word boundaries improvement
UI interaction (Tab/Enter/Right)
100%
Guard conditions
100%
13 checks
Telemetry
100%
10+7 fields
Cache sharing
✅
DashScope cache_control
Speculation
✅
COW overlay + tool gating
Pipelined suggestion
✅
Generated after speculation completes
State management
100%+
Controller pattern, Object.freeze
Last updated on
May 18, 2026
Prompt Suggestion (NES) Design
Speculation Engine Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-implementation/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Execution and Deployment</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/development/deployment/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/development/deployment/</guid>
  <pubDate>Thu, 28 Nov 2024 00:00:00 +0000</pubDate>
  <category>Deployment</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Qwen Code Execution and Deployment
Qwen Code Execution and Deployment
This document describes how to run Qwen Code and explains the deployment architecture that Qwen Code uses.
Running Qwen Code
There are several ways to run Qwen Code. The option you choose depends on how you intend to use it.
1. Standard installation (Recommended for typical users)
This is the recommended way for end-users to install Qwen Code. It involves downloading the Qwen Code package from the N...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Qwen Code Execution and Deployment
Qwen Code Execution and Deployment
This document describes how to run Qwen Code and explains the deployment architecture that Qwen Code uses.
Running Qwen Code
There are several ways to run Qwen Code. The option you choose depends on how you intend to use it.
1. Standard installation (Recommended for typical users)
This is the recommended way for end-users to install Qwen Code. It involves downloading the Qwen Code package from the NPM registry.
Global install:
npm
install
-g
@qwen-code/qwen-code
Then, run the CLI from anywhere:
qwen
NPX execution:
# Execute the latest version from NPM without a global install
npx
@qwen-code/qwen-code
2. Running in a sandbox (Docker/Podman)
For security and isolation, Qwen Code can be run inside a container. This is the default way that the CLI executes tools that might have side effects.
Directly from the Registry:
You can run the published sandbox image directly. This is useful for environments where you only have Docker and want to run the CLI.
# Run the published sandbox image
docker
run
--rm
-it
ghcr.io/qwenlm/qwen-code:0.0.11
Using the
--sandbox
flag:
If you have Qwen Code installed locally (using the standard installation described above), you can instruct it to run inside the sandbox container.
qwen
--sandbox
-y
-p
"your prompt here"
3. Running from source (Recommended for Qwen Code contributors)
Contributors to the project will want to run the CLI directly from the source code.
Development Mode:
This method provides hot-reloading and is useful for active development.
# From the root of the repository
npm
run
start
Production-like mode (Linked package):
This method simulates a global installation by linking your local package. It’s useful for testing a local build in a production workflow.
# Link the local cli package to your global node_modules
npm
link
packages/cli
# Now you can run your local version using the `qwen` command
qwen
4. Running the latest Qwen Code commit from GitHub
You can run the most recently committed version of Qwen Code directly from the GitHub repository. This is useful for testing features still in development.
# Execute the CLI directly from the main branch on GitHub
npx
https://github.com/QwenLM/qwen-code
Deployment architecture
The execution methods described above are made possible by the following architectural components and processes:
NPM packages
Qwen Code project is a monorepo that publishes core packages to the NPM registry:
@qwen-code/qwen-code-core
: The backend, handling logic and tool execution.
@qwen-code/qwen-code
: The user-facing frontend.
These packages are used when performing the standard installation and when running Qwen Code from the source.
Build and packaging processes
There are two distinct build processes used, depending on the distribution channel:
NPM publication:
For publishing to the NPM registry, the TypeScript source code in
@qwen-code/qwen-code-core
and
@qwen-code/qwen-code
is transpiled into standard JavaScript using the TypeScript Compiler (
tsc
). The resulting
dist/
directory is what gets published in the NPM package. This is a standard approach for TypeScript libraries.
GitHub
npx
execution:
When running the latest version of Qwen Code directly from GitHub, a different process is triggered by the
prepare
script in
package.json
. This script uses
esbuild
to bundle the entire application and its dependencies into a single, self-contained JavaScript file. This bundle is created on-the-fly on the user’s machine and is not checked into the repository.
Docker sandbox image
The Docker-based execution method is supported by the
qwen-code-sandbox
container image. This image is published to a container registry and contains a pre-installed, global version of Qwen Code.
Release process
The release process is automated through GitHub Actions. The release workflow performs the following actions:
Build the NPM packages using
tsc
.
Publish the NPM packages to the artifact registry.
Create GitHub releases with bundled assets.
Last updated on
May 18, 2026
Issue and PR Automation
Extension Releasing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/deployment/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Qwen Code Execution and Deployment
Qwen Code Execution and Deployment
This document describes how to run Qwen Code and explains the deployment architecture that Qwen Code uses.
Running Qwen Code
There are several ways to run Qwen Code. The option you choose depends on how you intend to use it.
1. Standard installation (Recommended for typical users)
This is the recommended way for end-users to install Qwen Code. It involves downloading the Qwen Code package from the N...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Qwen Code Execution and Deployment
Qwen Code Execution and Deployment
This document describes how to run Qwen Code and explains the deployment architecture that Qwen Code uses.
Running Qwen Code
There are several ways to run Qwen Code. The option you choose depends on how you intend to use it.
1. Standard installation (Recommended for typical users)
This is the recommended way for end-users to install Qwen Code. It involves downloading the Qwen Code package from the NPM registry.
Global install:
npm
install
-g
@qwen-code/qwen-code
Then, run the CLI from anywhere:
qwen
NPX execution:
# Execute the latest version from NPM without a global install
npx
@qwen-code/qwen-code
2. Running in a sandbox (Docker/Podman)
For security and isolation, Qwen Code can be run inside a container. This is the default way that the CLI executes tools that might have side effects.
Directly from the Registry:
You can run the published sandbox image directly. This is useful for environments where you only have Docker and want to run the CLI.
# Run the published sandbox image
docker
run
--rm
-it
ghcr.io/qwenlm/qwen-code:0.0.11
Using the
--sandbox
flag:
If you have Qwen Code installed locally (using the standard installation described above), you can instruct it to run inside the sandbox container.
qwen
--sandbox
-y
-p
"your prompt here"
3. Running from source (Recommended for Qwen Code contributors)
Contributors to the project will want to run the CLI directly from the source code.
Development Mode:
This method provides hot-reloading and is useful for active development.
# From the root of the repository
npm
run
start
Production-like mode (Linked package):
This method simulates a global installation by linking your local package. It’s useful for testing a local build in a production workflow.
# Link the local cli package to your global node_modules
npm
link
packages/cli
# Now you can run your local version using the `qwen` command
qwen
4. Running the latest Qwen Code commit from GitHub
You can run the most recently committed version of Qwen Code directly from the GitHub repository. This is useful for testing features still in development.
# Execute the CLI directly from the main branch on GitHub
npx
https://github.com/QwenLM/qwen-code
Deployment architecture
The execution methods described above are made possible by the following architectural components and processes:
NPM packages
Qwen Code project is a monorepo that publishes core packages to the NPM registry:
@qwen-code/qwen-code-core
: The backend, handling logic and tool execution.
@qwen-code/qwen-code
: The user-facing frontend.
These packages are used when performing the standard installation and when running Qwen Code from the source.
Build and packaging processes
There are two distinct build processes used, depending on the distribution channel:
NPM publication:
For publishing to the NPM registry, the TypeScript source code in
@qwen-code/qwen-code-core
and
@qwen-code/qwen-code
is transpiled into standard JavaScript using the TypeScript Compiler (
tsc
). The resulting
dist/
directory is what gets published in the NPM package. This is a standard approach for TypeScript libraries.
GitHub
npx
execution:
When running the latest version of Qwen Code directly from GitHub, a different process is triggered by the
prepare
script in
package.json
. This script uses
esbuild
to bundle the entire application and its dependencies into a single, self-contained JavaScript file. This bundle is created on-the-fly on the user’s machine and is not checked into the repository.
Docker sandbox image
The Docker-based execution method is supported by the
qwen-code-sandbox
container image. This image is published to a container registry and contains a pre-installed, global version of Qwen Code.
Release process
The release process is automated through GitHub Actions. The release workflow performs the following actions:
Build the NPM packages using
tsc
.
Publish the NPM packages to the artifact registry.
Create GitHub releases with bundled assets.
Last updated on
May 18, 2026
Issue and PR Automation
Extension Releasing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/deployment/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Todo Write Tool ( todo_write )</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/todo-write/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/todo-write/</guid>
  <pubDate>Thu, 28 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Todo Write
Todo Write Tool (
todo_write
)
This document describes the
todo_write
tool for Qwen Code.
Description
Use
todo_write
to create and manage a structured task list for your current coding session. This tool helps the AI assistant track progress and organize complex tasks, providing you with visibility into what work is being performed.
Arguments
todo_write
takes one argument:
todos
(array, required): An array of todo items, where each item contains:
content
(string,...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Todo Write
Todo Write Tool (
todo_write
)
This document describes the
todo_write
tool for Qwen Code.
Description
Use
todo_write
to create and manage a structured task list for your current coding session. This tool helps the AI assistant track progress and organize complex tasks, providing you with visibility into what work is being performed.
Arguments
todo_write
takes one argument:
todos
(array, required): An array of todo items, where each item contains:
content
(string, required): The description of the task.
status
(string, required): The current status (
pending
,
in_progress
, or
completed
).
activeForm
(string, required): The present continuous form describing what is being done (e.g., “Running tests”, “Building the project”).
How to use
todo_write
with Qwen Code
The AI assistant will automatically use this tool when working on complex, multi-step tasks. You don’t need to explicitly request it, but you can ask the assistant to create a todo list if you want to see the planned approach for your request.
The tool stores todo lists in your home directory (
~/.qwen/todos/
) with session-specific files, so each coding session maintains its own task list.
When the AI uses this tool
The assistant uses
todo_write
for:
Complex tasks requiring multiple steps
Feature implementations with several components
Refactoring operations across multiple files
Any work involving 3 or more distinct actions
The assistant will not use this tool for simple, single-step tasks or purely informational requests.
todo_write
examples
Creating a feature implementation plan:
todo_write(todos=[
{
"content": "Create user preferences model",
"status": "pending",
"activeForm": "Creating user preferences model"
},
{
"content": "Add API endpoints for preferences",
"status": "pending",
"activeForm": "Adding API endpoints for preferences"
},
{
"content": "Implement frontend components",
"status": "pending",
"activeForm": "Implementing frontend components"
}
])
Important notes
Automatic usage:
The AI assistant manages todo lists automatically during complex tasks.
Progress visibility:
You’ll see todo lists updated in real-time as work progresses.
Session isolation:
Each coding session has its own todo list that doesn’t interfere with others.
Last updated on
May 18, 2026
Shell
Task</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/todo-write/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Todo Write
Todo Write Tool (
todo_write
)
This document describes the
todo_write
tool for Qwen Code.
Description
Use
todo_write
to create and manage a structured task list for your current coding session. This tool helps the AI assistant track progress and organize complex tasks, providing you with visibility into what work is being performed.
Arguments
todo_write
takes one argument:
todos
(array, required): An array of todo items, where each item contains:
content
(string,...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Todo Write
Todo Write Tool (
todo_write
)
This document describes the
todo_write
tool for Qwen Code.
Description
Use
todo_write
to create and manage a structured task list for your current coding session. This tool helps the AI assistant track progress and organize complex tasks, providing you with visibility into what work is being performed.
Arguments
todo_write
takes one argument:
todos
(array, required): An array of todo items, where each item contains:
content
(string, required): The description of the task.
status
(string, required): The current status (
pending
,
in_progress
, or
completed
).
activeForm
(string, required): The present continuous form describing what is being done (e.g., “Running tests”, “Building the project”).
How to use
todo_write
with Qwen Code
The AI assistant will automatically use this tool when working on complex, multi-step tasks. You don’t need to explicitly request it, but you can ask the assistant to create a todo list if you want to see the planned approach for your request.
The tool stores todo lists in your home directory (
~/.qwen/todos/
) with session-specific files, so each coding session maintains its own task list.
When the AI uses this tool
The assistant uses
todo_write
for:
Complex tasks requiring multiple steps
Feature implementations with several components
Refactoring operations across multiple files
Any work involving 3 or more distinct actions
The assistant will not use this tool for simple, single-step tasks or purely informational requests.
todo_write
examples
Creating a feature implementation plan:
todo_write(todos=[
{
"content": "Create user preferences model",
"status": "pending",
"activeForm": "Creating user preferences model"
},
{
"content": "Add API endpoints for preferences",
"status": "pending",
"activeForm": "Adding API endpoints for preferences"
},
{
"content": "Implement frontend components",
"status": "pending",
"activeForm": "Implementing frontend components"
}
])
Important notes
Automatic usage:
The AI assistant manages todo lists automatically during complex tasks.
Progress visibility:
You’ll see todo lists updated in real-time as work progresses.
Session isolation:
Each coding session has its own todo list that doesn’t interfere with others.
Last updated on
May 18, 2026
Shell
Task</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/todo-write/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Compact Mode Design: Competitive Analysis &amp; Optimization</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/compact-mode/compact-mode-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/compact-mode/compact-mode-design/</guid>
  <pubDate>Mon, 25 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Compact Mode
Compact Mode Design: Competitive Analysis &amp; Optimization
Compact Mode Design: Competitive Analysis &amp; Optimization
Ctrl+O compact/verbose mode toggle — competitive analysis with Claude Code, current implementation review, and optimization recommendations.
User documentation:
Settings — ui.compactMode
.
1. Executive Summary
Qwen Code and Claude Code both provide a Ctrl+O shortcut for toggling between compact and detailed tool output views, but the
design philosophy, default sta...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Compact Mode
Compact Mode Design: Competitive Analysis & Optimization
Compact Mode Design: Competitive Analysis & Optimization
Ctrl+O compact/verbose mode toggle — competitive analysis with Claude Code, current implementation review, and optimization recommendations.
User documentation:
Settings — ui.compactMode
.
1. Executive Summary
Qwen Code and Claude Code both provide a Ctrl+O shortcut for toggling between compact and detailed tool output views, but the
design philosophy, default state, and interaction model differ fundamentally
. This document provides a deep source-level comparison, identifies UX gaps, and proposes optimizations for Qwen Code.
Dimension
Claude Code
Qwen Code
Default mode
Compact (verbose=false)
Verbose (compactMode=false)
Toggle semantics
Temporary peek at details
Persistent preference switch
Persistence
Session-only, resets on restart
Persisted to settings.json
Scope
Global screen switch (prompt ↔ transcript)
Per-component rendering toggle
Frozen snapshot
None (no concept)
None (removed)
Per-tool expand hint
Yes (“ctrl+o to expand”)
Yes (“Press Ctrl+O to show full tool output”)
2. Claude Code Implementation Analysis
2.1 Architecture
Claude Code uses a
screen-based
approach rather than a component-level rendering toggle:
┌──────────────────────────────────┐
│         AppState (Zustand)       │
│  verbose: boolean (default: false)│
│  screen: 'prompt' | 'transcript' │
└──────────┬───────────────────────┘
│
┌─────┴──────┐
│  Ctrl+O    │  toggles screen mode
│  Handler    │  NOT a rendering flag
└─────┬──────┘
│
┌─────▼──────────────┐
│    REPL.tsx         │
│  screen='prompt'  → compact view (default)
│  screen='transcript'→ detailed view
└────────────────────┘
2.2 Key Source Files
Component
File
Key Logic
Toggle handler
src/hooks/useGlobalKeybindings.tsx:90-132
Switches
screen
between
'prompt'
and
'transcript'
Keybinding
src/keybindings/defaultBindings.ts:44
app:toggleTranscript
State definition
src/state/AppStateStore.ts:472
verbose: false
(session-only)
Expand hint
src/components/CtrlOToExpand.tsx:29-46
Per-tool “(ctrl+o to expand)” text
Message filter
src/components/Messages.tsx:93-151
filterForBriefTool()
for compact view
Permission
src/components/permissions/PermissionRequest.tsx
Rendered in overlay layer, never hidden
2.3 Design Decisions
Compact is the default.
Users see a clean interface out of the box; detail is opt-in.
Session-scoped.
verbose
resets to
false
on every new session — Claude Code assumes users generally prefer the compact view and only need details temporarily.
Screen-level toggle.
Ctrl+O doesn’t change how components render; it switches the entire display between a “prompt” screen (compact) and a “transcript” screen (detailed).
No frozen snapshot.
There is no snapshot freezing concept. When toggling, the display updates immediately with current state.
Permission dialogs are separate.
Tool approvals are rendered in a dedicated overlay layer that is never affected by the verbose/compact toggle.
Per-tool hint.
CtrlOToExpand
component shows a contextual hint on individual tools when they produce large output, suppressed in sub-agents.
2.4 User Flow
Session start → compact mode (default)
│
├─ Tool outputs are summarized in a single line
├─ Large tool output shows "(ctrl+o to expand)" hint
│
├─ User presses Ctrl+O
│     └─→ Screen switches to transcript (detailed view)
│         └─ User sees all tool output, thinking, etc.
│
├─ User presses Ctrl+O again
│     └─→ Screen switches back to prompt (compact)
│
└─ Session ends → verbose resets to false
3. Qwen Code Implementation Analysis
3.1 Architecture
Qwen Code uses a
component-level rendering flag
that each UI component reads from context:
┌─────────────────────────────────────┐
│      CompactModeContext             │
│  compactMode: boolean (default: false)│
│  setCompactMode: (v) => void        │
└──────────┬──────────────────────────┘
│
┌─────┴──────┐
│  Ctrl+O    │  toggles compactMode
│  Handler    │  persists to settings
└─────┬──────┘
│
┌─────▼──────────────────┐
│  Each component reads  │
│  compactMode and       │
│  decides how to render │
└────────────────────────┘
│
┌─────▼──────────────────────────────┐
│  ToolGroupMessage                   │
│    showCompact = compactMode        │
│      && !hasConfirmingTool          │
│      && !hasErrorTool               │
│      && !isEmbeddedShellFocused     │
│      && !isUserInitiated            │
└────────────────────────────────────┘
3.2 Key Source Files
Component
File
Key Logic
Toggle handler
AppContainer.tsx:1684-1690
Toggles
compactMode
, persists to settings
Context
CompactModeContext.tsx
compactMode
,
setCompactMode
Tool group
ToolGroupMessage.tsx:105-110
showCompact
with 4 force-expand conditions
Tool message
ToolMessage.tsx:346-350
Hides
displayRenderer
in compact mode
Compact display
CompactToolGroupDisplay.tsx:49-108
Single-line summary with status + hint
Confirmation
ToolConfirmationMessage.tsx:113-147
Simplified 3-option compact approval
Tips
Tips.tsx:14-29
Startup tip rotation includes compact mode hint
Settings sync
SettingsDialog.tsx:189-193
Syncs with CompactModeContext + refreshStatic
MainContent
MainContent.tsx:60-76
Renders live pendingHistoryItems
Thinking
HistoryItemDisplay.tsx:123-133
Hides
gemini_thought
in compact mode
3.3 Design Decisions
Verbose is the default.
Users see all tool output and thinking by default.
Persistent preference.
compactMode
is saved to
settings.json
and survives across sessions.
Component-level rendering.
Each component reads
compactMode
from context and adjusts its own rendering.
Force-expand protection.
Four conditions override compact mode to ensure critical UI elements are always visible (confirmations, errors, shell, user-initiated).
No snapshot freezing.
The toggle always shows live output — no frozen snapshots.
Settings dialog sync.
Toggling compact mode from Settings updates React state immediately via
setCompactMode
.
Non-intrusive discoverability.
Compact mode is introduced via the startup Tips rotation rather than a persistent footer indicator, avoiding UI clutter.
3.4 User Flow
Session start → verbose mode (default)
│
├─ All tool outputs, thinking, details visible
│
├─ User presses Ctrl+O (or toggles in Settings)
│     └─→ compactMode = true, persisted
│         ├─ Tool groups show single-line summary
│         ├─ Thinking/thought content hidden
│         └─ Confirmations, errors, shell still expanded
│
├─ User presses Ctrl+O again
│     └─→ compactMode = false, persisted
│         └─ All details visible again
│
└─ Next session → same mode as last session
4. Key Differences Deep Dive
4.1 Default Mode Philosophy
Aspect
Claude Code (compact default)
Qwen Code (verbose default)
First impression
Clean, minimal — professional feel
Information-rich — full transparency
Learning curve
User must learn Ctrl+O to see details
User can immediately see everything
Target audience
Experienced users who trust the tool
Users who want to understand what’s happening
Information overload
Avoided by default
Possible for new users
Discoverability
Per-tool “(ctrl+o to expand)” hints
Startup Tips rotation + ? shortcuts + /help
Analysis:
Claude Code’s compact default works because its user base is generally experienced developers who trust the tool and don’t need to see every tool invocation. Qwen Code’s verbose default is appropriate for its earlier stage where building user trust through transparency is important.
4.2 Persistence Model
Aspect
Claude Code
Qwen Code
Persisted?
No — session-only
Yes — to settings.json
Rationale
Verbose is temporary peek
Mode is user preference
Restart behavior
Always starts compact
Starts with last-used mode
Analysis:
Claude Code treats detail viewing as a momentary need — you look, then go back. Qwen Code treats it as a stable preference — some users always want details, others always want compact. Both are valid; Qwen Code’s approach is more flexible.
4.3 Confirmation Protection
Aspect
Claude Code
Qwen Code
Mechanism
Overlay/modal layer (structurally separate)
Force-expand conditions in
showCompact
Coverage
Complete — approvals can never be hidden
Complete — 4 conditions cover all interactive states
Compact confirmation UI
N/A (overlay is always full)
Simplified 3-option RadioButtonSelect
Analysis:
Claude Code’s architectural separation (overlay layer) is more robust. Qwen Code’s force-expand approach is effective but requires each new interactive state to be explicitly added to the condition list.
4.4 Rendering Approach
Aspect
Claude Code
Qwen Code
Toggle scope
Screen-level (prompt ↔ transcript)
Component-level (each component decides)
Granularity
All-or-nothing
Fine-grained per component
Flexibility
Low — global switch
High — components can override
Consistency
Guaranteed
Depends on each component’s implementation
Analysis:
Qwen Code’s component-level approach is more flexible (e.g., force-expand for specific conditions) but requires more discipline to maintain consistency. Claude Code’s screen-level approach is simpler and guarantees consistent behavior.
5. Optimization Recommendations
5.1 [P0] Keep Verbose as Default — No Change Needed
Qwen Code’s verbose default is the right choice for its current stage. Users who are new to the tool need transparency to build trust. As the product matures, consider making compact the default (like Claude Code).
5.2 [P1] Per-Tool Expansion for Large Outputs
Claude Code shows “(ctrl+o to expand)” on individual tools that produce large output. Qwen Code currently only has a global toggle. Consider:
When a single tool produces output exceeding N lines, show a per-tool “expand” hint in compact mode.
Scope: future enhancement, not current priority.
5.3 [P2] Consider Session-Scoped Override
Some users may want compact mode as their default but occasionally need verbose for a specific session. Consider supporting both:
settings.json
→ persistent default (current behavior)
Ctrl+O during session → temporary override for current session only (Claude Code behavior)
On session restart → revert to settings.json value
This gives users the best of both worlds. Implementation would require separating “settings default” from “session override” state.
5.4 [P2] Structural Separation for Confirmations
Currently, confirmation protection relies on
showCompact
conditions in
ToolGroupMessage
. Consider a more robust approach:
Render confirmations in a separate layer (like Claude Code’s overlay approach).
This would make it architecturally impossible for compact mode to affect confirmations.
Lower priority since the current force-expand approach works correctly.
6. Current Implementation Status
After the
feat/compact-mode-optimization
branch changes:
Feature
Status
Notes
Startup Tips hint
Done
Compact mode tip in Tips rotation (non-intrusive)
Ctrl+O in keyboard shortcuts (?)
Done
Added to KeyboardShortcuts component
Ctrl+O in /help
Done
Added to Help component
Settings dialog sync
Done
Syncs compactMode with CompactModeContext
No snapshot freezing
Done
Toggle always shows live output
Confirmation protection
Done
Force-expand + WaitingForConfirmation guard
Shell protection
Done
!isEmbeddedShellFocused
force-expand
Error protection
Done
!hasErrorTool
force-expand
User docs updated
Done
settings.md, keyboard-shortcuts.md
7. File Reference
Qwen Code
File
Purpose
packages/cli/src/ui/AppContainer.tsx
Toggle handler, state initialization, context provider
packages/cli/src/ui/contexts/CompactModeContext.tsx
Context definition
packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
Force-expand logic
packages/cli/src/ui/components/messages/ToolMessage.tsx
Per-tool output hiding
packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx
Compact view rendering
packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
Compact confirmation UI
packages/cli/src/ui/components/MainContent.tsx
Pending history items rendering
packages/cli/src/ui/components/Tips.tsx
Startup tip with compact mode hint
packages/cli/src/ui/components/Help.tsx
/help shortcut entry
packages/cli/src/ui/components/KeyboardShortcuts.tsx
? shortcut entry
packages/cli/src/ui/components/SettingsDialog.tsx
Settings sync
packages/cli/src/ui/components/HistoryItemDisplay.tsx
Thinking content hiding
packages/cli/src/config/settingsSchema.ts
Setting definition
packages/cli/src/config/keyBindings.ts
Ctrl+O binding
Claude Code (Reference)
File
Purpose
src/hooks/useGlobalKeybindings.tsx
Toggle handler
src/state/AppStateStore.ts
State definition (verbose: false)
src/components/CtrlOToExpand.tsx
Per-tool expand hint
src/components/Messages.tsx
Brief message filter
src/screens/REPL.tsx
Screen-level mode switching
src/components/permissions/PermissionRequest.tsx
Overlay-based confirmation
Last updated on
May 18, 2026
Channels Design
Fork Subagent Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/compact-mode/compact-mode-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Compact Mode
Compact Mode Design: Competitive Analysis &amp; Optimization
Compact Mode Design: Competitive Analysis &amp; Optimization
Ctrl+O compact/verbose mode toggle — competitive analysis with Claude Code, current implementation review, and optimization recommendations.
User documentation:
Settings — ui.compactMode
.
1. Executive Summary
Qwen Code and Claude Code both provide a Ctrl+O shortcut for toggling between compact and detailed tool output views, but the
design philosophy, default sta...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Compact Mode
Compact Mode Design: Competitive Analysis & Optimization
Compact Mode Design: Competitive Analysis & Optimization
Ctrl+O compact/verbose mode toggle — competitive analysis with Claude Code, current implementation review, and optimization recommendations.
User documentation:
Settings — ui.compactMode
.
1. Executive Summary
Qwen Code and Claude Code both provide a Ctrl+O shortcut for toggling between compact and detailed tool output views, but the
design philosophy, default state, and interaction model differ fundamentally
. This document provides a deep source-level comparison, identifies UX gaps, and proposes optimizations for Qwen Code.
Dimension
Claude Code
Qwen Code
Default mode
Compact (verbose=false)
Verbose (compactMode=false)
Toggle semantics
Temporary peek at details
Persistent preference switch
Persistence
Session-only, resets on restart
Persisted to settings.json
Scope
Global screen switch (prompt ↔ transcript)
Per-component rendering toggle
Frozen snapshot
None (no concept)
None (removed)
Per-tool expand hint
Yes (“ctrl+o to expand”)
Yes (“Press Ctrl+O to show full tool output”)
2. Claude Code Implementation Analysis
2.1 Architecture
Claude Code uses a
screen-based
approach rather than a component-level rendering toggle:
┌──────────────────────────────────┐
│         AppState (Zustand)       │
│  verbose: boolean (default: false)│
│  screen: 'prompt' | 'transcript' │
└──────────┬───────────────────────┘
│
┌─────┴──────┐
│  Ctrl+O    │  toggles screen mode
│  Handler    │  NOT a rendering flag
└─────┬──────┘
│
┌─────▼──────────────┐
│    REPL.tsx         │
│  screen='prompt'  → compact view (default)
│  screen='transcript'→ detailed view
└────────────────────┘
2.2 Key Source Files
Component
File
Key Logic
Toggle handler
src/hooks/useGlobalKeybindings.tsx:90-132
Switches
screen
between
'prompt'
and
'transcript'
Keybinding
src/keybindings/defaultBindings.ts:44
app:toggleTranscript
State definition
src/state/AppStateStore.ts:472
verbose: false
(session-only)
Expand hint
src/components/CtrlOToExpand.tsx:29-46
Per-tool “(ctrl+o to expand)” text
Message filter
src/components/Messages.tsx:93-151
filterForBriefTool()
for compact view
Permission
src/components/permissions/PermissionRequest.tsx
Rendered in overlay layer, never hidden
2.3 Design Decisions
Compact is the default.
Users see a clean interface out of the box; detail is opt-in.
Session-scoped.
verbose
resets to
false
on every new session — Claude Code assumes users generally prefer the compact view and only need details temporarily.
Screen-level toggle.
Ctrl+O doesn’t change how components render; it switches the entire display between a “prompt” screen (compact) and a “transcript” screen (detailed).
No frozen snapshot.
There is no snapshot freezing concept. When toggling, the display updates immediately with current state.
Permission dialogs are separate.
Tool approvals are rendered in a dedicated overlay layer that is never affected by the verbose/compact toggle.
Per-tool hint.
CtrlOToExpand
component shows a contextual hint on individual tools when they produce large output, suppressed in sub-agents.
2.4 User Flow
Session start → compact mode (default)
│
├─ Tool outputs are summarized in a single line
├─ Large tool output shows "(ctrl+o to expand)" hint
│
├─ User presses Ctrl+O
│     └─→ Screen switches to transcript (detailed view)
│         └─ User sees all tool output, thinking, etc.
│
├─ User presses Ctrl+O again
│     └─→ Screen switches back to prompt (compact)
│
└─ Session ends → verbose resets to false
3. Qwen Code Implementation Analysis
3.1 Architecture
Qwen Code uses a
component-level rendering flag
that each UI component reads from context:
┌─────────────────────────────────────┐
│      CompactModeContext             │
│  compactMode: boolean (default: false)│
│  setCompactMode: (v) => void        │
└──────────┬──────────────────────────┘
│
┌─────┴──────┐
│  Ctrl+O    │  toggles compactMode
│  Handler    │  persists to settings
└─────┬──────┘
│
┌─────▼──────────────────┐
│  Each component reads  │
│  compactMode and       │
│  decides how to render │
└────────────────────────┘
│
┌─────▼──────────────────────────────┐
│  ToolGroupMessage                   │
│    showCompact = compactMode        │
│      && !hasConfirmingTool          │
│      && !hasErrorTool               │
│      && !isEmbeddedShellFocused     │
│      && !isUserInitiated            │
└────────────────────────────────────┘
3.2 Key Source Files
Component
File
Key Logic
Toggle handler
AppContainer.tsx:1684-1690
Toggles
compactMode
, persists to settings
Context
CompactModeContext.tsx
compactMode
,
setCompactMode
Tool group
ToolGroupMessage.tsx:105-110
showCompact
with 4 force-expand conditions
Tool message
ToolMessage.tsx:346-350
Hides
displayRenderer
in compact mode
Compact display
CompactToolGroupDisplay.tsx:49-108
Single-line summary with status + hint
Confirmation
ToolConfirmationMessage.tsx:113-147
Simplified 3-option compact approval
Tips
Tips.tsx:14-29
Startup tip rotation includes compact mode hint
Settings sync
SettingsDialog.tsx:189-193
Syncs with CompactModeContext + refreshStatic
MainContent
MainContent.tsx:60-76
Renders live pendingHistoryItems
Thinking
HistoryItemDisplay.tsx:123-133
Hides
gemini_thought
in compact mode
3.3 Design Decisions
Verbose is the default.
Users see all tool output and thinking by default.
Persistent preference.
compactMode
is saved to
settings.json
and survives across sessions.
Component-level rendering.
Each component reads
compactMode
from context and adjusts its own rendering.
Force-expand protection.
Four conditions override compact mode to ensure critical UI elements are always visible (confirmations, errors, shell, user-initiated).
No snapshot freezing.
The toggle always shows live output — no frozen snapshots.
Settings dialog sync.
Toggling compact mode from Settings updates React state immediately via
setCompactMode
.
Non-intrusive discoverability.
Compact mode is introduced via the startup Tips rotation rather than a persistent footer indicator, avoiding UI clutter.
3.4 User Flow
Session start → verbose mode (default)
│
├─ All tool outputs, thinking, details visible
│
├─ User presses Ctrl+O (or toggles in Settings)
│     └─→ compactMode = true, persisted
│         ├─ Tool groups show single-line summary
│         ├─ Thinking/thought content hidden
│         └─ Confirmations, errors, shell still expanded
│
├─ User presses Ctrl+O again
│     └─→ compactMode = false, persisted
│         └─ All details visible again
│
└─ Next session → same mode as last session
4. Key Differences Deep Dive
4.1 Default Mode Philosophy
Aspect
Claude Code (compact default)
Qwen Code (verbose default)
First impression
Clean, minimal — professional feel
Information-rich — full transparency
Learning curve
User must learn Ctrl+O to see details
User can immediately see everything
Target audience
Experienced users who trust the tool
Users who want to understand what’s happening
Information overload
Avoided by default
Possible for new users
Discoverability
Per-tool “(ctrl+o to expand)” hints
Startup Tips rotation + ? shortcuts + /help
Analysis:
Claude Code’s compact default works because its user base is generally experienced developers who trust the tool and don’t need to see every tool invocation. Qwen Code’s verbose default is appropriate for its earlier stage where building user trust through transparency is important.
4.2 Persistence Model
Aspect
Claude Code
Qwen Code
Persisted?
No — session-only
Yes — to settings.json
Rationale
Verbose is temporary peek
Mode is user preference
Restart behavior
Always starts compact
Starts with last-used mode
Analysis:
Claude Code treats detail viewing as a momentary need — you look, then go back. Qwen Code treats it as a stable preference — some users always want details, others always want compact. Both are valid; Qwen Code’s approach is more flexible.
4.3 Confirmation Protection
Aspect
Claude Code
Qwen Code
Mechanism
Overlay/modal layer (structurally separate)
Force-expand conditions in
showCompact
Coverage
Complete — approvals can never be hidden
Complete — 4 conditions cover all interactive states
Compact confirmation UI
N/A (overlay is always full)
Simplified 3-option RadioButtonSelect
Analysis:
Claude Code’s architectural separation (overlay layer) is more robust. Qwen Code’s force-expand approach is effective but requires each new interactive state to be explicitly added to the condition list.
4.4 Rendering Approach
Aspect
Claude Code
Qwen Code
Toggle scope
Screen-level (prompt ↔ transcript)
Component-level (each component decides)
Granularity
All-or-nothing
Fine-grained per component
Flexibility
Low — global switch
High — components can override
Consistency
Guaranteed
Depends on each component’s implementation
Analysis:
Qwen Code’s component-level approach is more flexible (e.g., force-expand for specific conditions) but requires more discipline to maintain consistency. Claude Code’s screen-level approach is simpler and guarantees consistent behavior.
5. Optimization Recommendations
5.1 [P0] Keep Verbose as Default — No Change Needed
Qwen Code’s verbose default is the right choice for its current stage. Users who are new to the tool need transparency to build trust. As the product matures, consider making compact the default (like Claude Code).
5.2 [P1] Per-Tool Expansion for Large Outputs
Claude Code shows “(ctrl+o to expand)” on individual tools that produce large output. Qwen Code currently only has a global toggle. Consider:
When a single tool produces output exceeding N lines, show a per-tool “expand” hint in compact mode.
Scope: future enhancement, not current priority.
5.3 [P2] Consider Session-Scoped Override
Some users may want compact mode as their default but occasionally need verbose for a specific session. Consider supporting both:
settings.json
→ persistent default (current behavior)
Ctrl+O during session → temporary override for current session only (Claude Code behavior)
On session restart → revert to settings.json value
This gives users the best of both worlds. Implementation would require separating “settings default” from “session override” state.
5.4 [P2] Structural Separation for Confirmations
Currently, confirmation protection relies on
showCompact
conditions in
ToolGroupMessage
. Consider a more robust approach:
Render confirmations in a separate layer (like Claude Code’s overlay approach).
This would make it architecturally impossible for compact mode to affect confirmations.
Lower priority since the current force-expand approach works correctly.
6. Current Implementation Status
After the
feat/compact-mode-optimization
branch changes:
Feature
Status
Notes
Startup Tips hint
Done
Compact mode tip in Tips rotation (non-intrusive)
Ctrl+O in keyboard shortcuts (?)
Done
Added to KeyboardShortcuts component
Ctrl+O in /help
Done
Added to Help component
Settings dialog sync
Done
Syncs compactMode with CompactModeContext
No snapshot freezing
Done
Toggle always shows live output
Confirmation protection
Done
Force-expand + WaitingForConfirmation guard
Shell protection
Done
!isEmbeddedShellFocused
force-expand
Error protection
Done
!hasErrorTool
force-expand
User docs updated
Done
settings.md, keyboard-shortcuts.md
7. File Reference
Qwen Code
File
Purpose
packages/cli/src/ui/AppContainer.tsx
Toggle handler, state initialization, context provider
packages/cli/src/ui/contexts/CompactModeContext.tsx
Context definition
packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
Force-expand logic
packages/cli/src/ui/components/messages/ToolMessage.tsx
Per-tool output hiding
packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx
Compact view rendering
packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
Compact confirmation UI
packages/cli/src/ui/components/MainContent.tsx
Pending history items rendering
packages/cli/src/ui/components/Tips.tsx
Startup tip with compact mode hint
packages/cli/src/ui/components/Help.tsx
/help shortcut entry
packages/cli/src/ui/components/KeyboardShortcuts.tsx
? shortcut entry
packages/cli/src/ui/components/SettingsDialog.tsx
Settings sync
packages/cli/src/ui/components/HistoryItemDisplay.tsx
Thinking content hiding
packages/cli/src/config/settingsSchema.ts
Setting definition
packages/cli/src/config/keyBindings.ts
Ctrl+O binding
Claude Code (Reference)
File
Purpose
src/hooks/useGlobalKeybindings.tsx
Toggle handler
src/state/AppStateStore.ts
State definition (verbose: false)
src/components/CtrlOToExpand.tsx
Per-tool expand hint
src/components/Messages.tsx
Brief message filter
src/screens/REPL.tsx
Screen-level mode switching
src/components/permissions/PermissionRequest.tsx
Overlay-based confirmation
Last updated on
May 18, 2026
Channels Design
Fork Subagent Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/compact-mode/compact-mode-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Task Tool ( task )</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/task/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/task/</guid>
  <pubDate>Sat, 23 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Task
Task Tool (
task
)
This document describes the
task
tool for Qwen Code.
Description
Use
task
to launch a specialized subagent to handle complex, multi-step tasks autonomously. The Task tool delegates work to specialized agents that can work independently with access to their own set of tools, allowing for parallel task execution and specialized expertise.
Arguments
task
takes the following arguments:
description
(string, required): A short (3-5 word) description of the...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Task
Task Tool (
task
)
This document describes the
task
tool for Qwen Code.
Description
Use
task
to launch a specialized subagent to handle complex, multi-step tasks autonomously. The Task tool delegates work to specialized agents that can work independently with access to their own set of tools, allowing for parallel task execution and specialized expertise.
Arguments
task
takes the following arguments:
description
(string, required): A short (3-5 word) description of the task for user visibility and tracking purposes.
prompt
(string, required): The detailed task prompt for the subagent to execute. Should contain comprehensive instructions for autonomous execution.
subagent_type
(string, required): The type of specialized agent to use for this task. Must match one of the available configured subagents.
How to use
task
with Qwen Code
The Task tool dynamically loads available subagents from your configuration and delegates tasks to them. Each subagent runs independently and can use its own set of tools, allowing for specialized expertise and parallel execution.
When you use the Task tool, the subagent will:
Receive the task prompt with full autonomy
Execute the task using its available tools
Return a final result message
Terminate (subagents are stateless and single-use)
Usage:
task(description="Brief task description", prompt="Detailed task instructions for the subagent", subagent_type="agent_name")
Available Subagents
The available subagents depend on your configuration. Common subagent types might include:
general-purpose
: For complex multi-step tasks requiring various tools
code-reviewer
: For reviewing and analyzing code quality
test-runner
: For running tests and analyzing results
documentation-writer
: For creating and updating documentation
You can view available subagents by using the
/agents
command in Qwen Code.
Task Tool Features
Real-time Progress Updates
The Task tool provides live updates showing:
Subagent execution status
Individual tool calls being made by the subagent
Tool call results and any errors
Overall task progress and completion status
Parallel Execution
You can launch multiple subagents concurrently by calling the Task tool multiple times in a single message, allowing for parallel task execution and improved efficiency.
Specialized Expertise
Each subagent can be configured with:
Specific tool access permissions
Specialized system prompts and instructions
Custom model configurations
Domain-specific knowledge and capabilities
task
examples
Delegating to a general-purpose agent
task(
description="Code refactoring",
prompt="Please refactor the authentication module in src/auth/ to use modern async/await patterns instead of callbacks. Ensure all tests still pass and update any related documentation.",
subagent_type="general-purpose"
)
Running parallel tasks
# Launch code review and test execution in parallel
task(
description="Code review",
prompt="Review the recent changes in the user management module for code quality, security issues, and best practices compliance.",
subagent_type="code-reviewer"
)
task(
description="Run tests",
prompt="Execute the full test suite and analyze any failures. Provide a summary of test coverage and recommendations for improvement.",
subagent_type="test-runner"
)
Documentation generation
task(
description="Update docs",
prompt="Generate comprehensive API documentation for the newly implemented REST endpoints in the orders module. Include request/response examples and error codes.",
subagent_type="documentation-writer"
)
When to Use the Task Tool
Use the Task tool when:
Complex multi-step tasks
- Tasks requiring multiple operations that can be handled autonomously
Specialized expertise
- Tasks that benefit from domain-specific knowledge or tools
Parallel execution
- When you have multiple independent tasks that can run simultaneously
Delegation needs
- When you want to hand off a complete task rather than micromanaging steps
Resource-intensive operations
- Tasks that might take significant time or computational resources
When NOT to Use the Task Tool
Don’t use the Task tool for:
Simple, single-step operations
- Use direct tools like Read, Edit, etc.
Interactive tasks
- Tasks requiring back-and-forth communication
Specific file reads
- Use Read tool directly for better performance
Simple searches
- Use Grep or Glob tools directly
Important Notes
Stateless execution
: Each subagent invocation is independent with no memory of previous executions
Single communication
: Subagents provide one final result message - no ongoing communication
Comprehensive prompts
: Your prompt should contain all necessary context and instructions for autonomous execution
Tool access
: Subagents only have access to tools configured in their specific configuration
Parallel capability
: Multiple subagents can run simultaneously for improved efficiency
Configuration dependent
: Available subagent types depend on your system configuration
Configuration
Subagents are configured through Qwen Code’s agent configuration system. Use the
/agents
command to:
View available subagents
Create new subagent configurations
Modify existing subagent settings
Set tool permissions and capabilities
For more information on configuring subagents, refer to the subagents documentation.
Last updated on
May 18, 2026
Todo Write
Exit Plan Mode</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/task/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Task
Task Tool (
task
)
This document describes the
task
tool for Qwen Code.
Description
Use
task
to launch a specialized subagent to handle complex, multi-step tasks autonomously. The Task tool delegates work to specialized agents that can work independently with access to their own set of tools, allowing for parallel task execution and specialized expertise.
Arguments
task
takes the following arguments:
description
(string, required): A short (3-5 word) description of the...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Task
Task Tool (
task
)
This document describes the
task
tool for Qwen Code.
Description
Use
task
to launch a specialized subagent to handle complex, multi-step tasks autonomously. The Task tool delegates work to specialized agents that can work independently with access to their own set of tools, allowing for parallel task execution and specialized expertise.
Arguments
task
takes the following arguments:
description
(string, required): A short (3-5 word) description of the task for user visibility and tracking purposes.
prompt
(string, required): The detailed task prompt for the subagent to execute. Should contain comprehensive instructions for autonomous execution.
subagent_type
(string, required): The type of specialized agent to use for this task. Must match one of the available configured subagents.
How to use
task
with Qwen Code
The Task tool dynamically loads available subagents from your configuration and delegates tasks to them. Each subagent runs independently and can use its own set of tools, allowing for specialized expertise and parallel execution.
When you use the Task tool, the subagent will:
Receive the task prompt with full autonomy
Execute the task using its available tools
Return a final result message
Terminate (subagents are stateless and single-use)
Usage:
task(description="Brief task description", prompt="Detailed task instructions for the subagent", subagent_type="agent_name")
Available Subagents
The available subagents depend on your configuration. Common subagent types might include:
general-purpose
: For complex multi-step tasks requiring various tools
code-reviewer
: For reviewing and analyzing code quality
test-runner
: For running tests and analyzing results
documentation-writer
: For creating and updating documentation
You can view available subagents by using the
/agents
command in Qwen Code.
Task Tool Features
Real-time Progress Updates
The Task tool provides live updates showing:
Subagent execution status
Individual tool calls being made by the subagent
Tool call results and any errors
Overall task progress and completion status
Parallel Execution
You can launch multiple subagents concurrently by calling the Task tool multiple times in a single message, allowing for parallel task execution and improved efficiency.
Specialized Expertise
Each subagent can be configured with:
Specific tool access permissions
Specialized system prompts and instructions
Custom model configurations
Domain-specific knowledge and capabilities
task
examples
Delegating to a general-purpose agent
task(
description="Code refactoring",
prompt="Please refactor the authentication module in src/auth/ to use modern async/await patterns instead of callbacks. Ensure all tests still pass and update any related documentation.",
subagent_type="general-purpose"
)
Running parallel tasks
# Launch code review and test execution in parallel
task(
description="Code review",
prompt="Review the recent changes in the user management module for code quality, security issues, and best practices compliance.",
subagent_type="code-reviewer"
)
task(
description="Run tests",
prompt="Execute the full test suite and analyze any failures. Provide a summary of test coverage and recommendations for improvement.",
subagent_type="test-runner"
)
Documentation generation
task(
description="Update docs",
prompt="Generate comprehensive API documentation for the newly implemented REST endpoints in the orders module. Include request/response examples and error codes.",
subagent_type="documentation-writer"
)
When to Use the Task Tool
Use the Task tool when:
Complex multi-step tasks
- Tasks requiring multiple operations that can be handled autonomously
Specialized expertise
- Tasks that benefit from domain-specific knowledge or tools
Parallel execution
- When you have multiple independent tasks that can run simultaneously
Delegation needs
- When you want to hand off a complete task rather than micromanaging steps
Resource-intensive operations
- Tasks that might take significant time or computational resources
When NOT to Use the Task Tool
Don’t use the Task tool for:
Simple, single-step operations
- Use direct tools like Read, Edit, etc.
Interactive tasks
- Tasks requiring back-and-forth communication
Specific file reads
- Use Read tool directly for better performance
Simple searches
- Use Grep or Glob tools directly
Important Notes
Stateless execution
: Each subagent invocation is independent with no memory of previous executions
Single communication
: Subagents provide one final result message - no ongoing communication
Comprehensive prompts
: Your prompt should contain all necessary context and instructions for autonomous execution
Tool access
: Subagents only have access to tools configured in their specific configuration
Parallel capability
: Multiple subagents can run simultaneously for improved efficiency
Configuration dependent
: Available subagent types depend on your system configuration
Configuration
Subagents are configured through Qwen Code’s agent configuration system. Use the
/agents
command to:
View available subagents
Create new subagent configurations
Modify existing subagent settings
Set tool permissions and capabilities
For more information on configuring subagents, refer to the subagents documentation.
Last updated on
May 18, 2026
Todo Write
Exit Plan Mode</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/task/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code file system tools</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/file-system/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/file-system/</guid>
  <pubDate>Thu, 21 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
File System
Qwen Code file system tools
Qwen Code provides a comprehensive suite of tools for interacting with the local file system. These tools allow the model to read from, write to, list, search, and modify files and directories, all under your control and typically with confirmation for sensitive operations.
Note:
All file system tools operate within a
rootDirectory
(usually the current working directory where you launched the CLI) for security. Paths that you provide ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
File System
Qwen Code file system tools
Qwen Code provides a comprehensive suite of tools for interacting with the local file system. These tools allow the model to read from, write to, list, search, and modify files and directories, all under your control and typically with confirmation for sensitive operations.
Note:
All file system tools operate within a
rootDirectory
(usually the current working directory where you launched the CLI) for security. Paths that you provide to these tools are generally expected to be absolute or are resolved relative to this root directory.
1.
list_directory
(ListFiles)
list_directory
lists the names of files and subdirectories directly within a specified directory path. It can optionally ignore entries matching provided glob patterns.
Tool name:
list_directory
Display name:
ListFiles
File:
ls.ts
Parameters:
path
(string, required): The absolute path to the directory to list.
ignore
(array of strings, optional): A list of glob patterns to exclude from the listing (e.g.,
["*.log", ".git"]
).
respect_git_ignore
(boolean, optional): Whether to respect
.gitignore
patterns when listing files. Defaults to
true
.
Behavior:
Returns a list of file and directory names.
Indicates whether each entry is a directory.
Sorts entries with directories first, then alphabetically.
Output (
llmContent
):
A string like:
Directory listing for /path/to/your/folder:\n[DIR] subfolder1\nfile1.txt\nfile2.png
Confirmation:
No.
2.
read_file
(ReadFile)
read_file
reads and returns the content of a specified file. This tool handles text files and media files (images, PDFs, audio, video) whose modality is supported by the current model. For text files, it can read specific line ranges. Media files whose modality is not supported by the current model are rejected with a helpful error message. Other binary file types are generally skipped.
Tool name:
read_file
Display name:
ReadFile
File:
read-file.ts
Parameters:
path
(string, required): The absolute path to the file to read.
offset
(number, optional): For text files, the 0-based line number to start reading from. Requires
limit
to be set.
limit
(number, optional): For text files, the maximum number of lines to read. If omitted, reads a default maximum (e.g., 2000 lines) or the entire file if feasible.
Behavior:
For text files: Returns the content. If
offset
and
limit
are used, returns only that slice of lines. Indicates if content was truncated due to line limits or line length limits.
For media files (images, PDFs, audio, video): If the current model supports the file’s modality, returns the file content as a base64-encoded
inlineData
object. If the model does not support the modality, returns an error message with guidance (e.g., suggesting skills or external tools).
For other binary files: Attempts to identify and skip them, returning a message indicating it’s a generic binary file.
Output:
(
llmContent
):
For text files: The file content, potentially prefixed with a truncation message (e.g.,
[File content truncated: showing lines 1-100 of 500 total lines...]\nActual file content...
).
For supported media files: An object containing
inlineData
with
mimeType
and base64
data
(e.g.,
{ inlineData: { mimeType: 'image/png', data: 'base64encodedstring' } }
).
For unsupported media files: An error message string explaining that the current model does not support this modality, with suggestions for alternatives.
For other binary files: A message like
Cannot display content of binary file: /path/to/data.bin
.
Confirmation:
No.
3.
write_file
(WriteFile)
write_file
writes content to a specified file. If the file exists, it will be overwritten. If the file doesn’t exist, it (and any necessary parent directories) will be created.
Tool name:
write_file
Display name:
WriteFile
File:
write-file.ts
Parameters:
file_path
(string, required): The absolute path to the file to write to.
content
(string, required): The content to write into the file.
Behavior:
Writes the provided
content
to the
file_path
.
Creates parent directories if they don’t exist.
Output (
llmContent
):
A success message, e.g.,
Successfully overwrote file: /path/to/your/file.txt
or
Successfully created and wrote to new file: /path/to/new/file.txt
.
Confirmation:
Yes. Shows a diff of changes and asks for user approval before writing.
4.
glob
(Glob)
glob
finds files matching specific glob patterns (e.g.,
src/**/*.ts
,
*.md
), returning absolute paths sorted by modification time (newest first).
Tool name:
glob
Display name:
Glob
File:
glob.ts
Parameters:
pattern
(string, required): The glob pattern to match against (e.g.,
"*.py"
,
"src/**/*.js"
).
path
(string, optional): The directory to search in. If not specified, the current working directory will be used.
Behavior:
Searches for files matching the glob pattern within the specified directory.
Returns a list of absolute paths, sorted with the most recently modified files first.
Respects .gitignore and .qwenignore patterns by default.
Limits results to 100 files to prevent context overflow.
Output (
llmContent
):
A message like:
Found 5 file(s) matching "*.ts" within /path/to/search/dir, sorted by modification time (newest first):\n---\n/path/to/file1.ts\n/path/to/subdir/file2.ts\n---\n[95 files truncated] ...
Confirmation:
No.
5.
grep_search
(Grep)
grep_search
searches for a regular expression pattern within the content of files in a specified directory. Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers.
Tool name:
grep_search
Display name:
Grep
File:
grep.ts
(with
ripGrep.ts
as fallback)
Parameters:
pattern
(string, required): The regular expression pattern to search for in file contents (e.g.,
"function\\s+myFunction"
,
"log.*Error"
).
path
(string, optional): File or directory to search in. Defaults to current working directory.
glob
(string, optional): Glob pattern to filter files (e.g.
"*.js"
,
"src/**/*.{ts,tsx}"
).
limit
(number, optional): Limit output to first N matching lines. Optional - shows all matches if not specified.
Behavior:
Uses ripgrep for fast search when available; otherwise falls back to a JavaScript-based search implementation.
Returns matching lines with file paths and line numbers.
Case-insensitive by default.
Respects .gitignore and .qwenignore patterns.
Limits output to prevent context overflow.
Output (
llmContent
):
A formatted string of matches, e.g.:
Found 3 matches for pattern "myFunction" in path "." (filter: "*.ts"):
---
src/utils.ts:15:export function myFunction() {
src/utils.ts:22:  myFunction.call();
src/index.ts:5:import { myFunction } from './utils';
---
[0 lines truncated] ...
Confirmation:
No.
grep_search
examples
Search for a pattern with default result limiting:
grep_search(pattern="function\\s+myFunction", path="src")
Search for a pattern with custom result limiting:
grep_search(pattern="function", path="src", limit=50)
Search for a pattern with file filtering and custom result limiting:
grep_search(pattern="function", glob="*.js", limit=10)
6.
edit
(Edit)
edit
replaces text within a file. By default it requires
old_string
to match a single unique location; set
replace_all
to
true
when you intentionally want to change every occurrence. This tool is designed for precise, targeted changes and requires significant context around the
old_string
to ensure it modifies the correct location.
Tool name:
edit
Display name:
Edit
File:
edit.ts
Parameters:
file_path
(string, required): The absolute path to the file to modify.
old_string
(string, required): The exact literal text to replace.
CRITICAL:
This string must uniquely identify the single instance to change. It should include sufficient context around the target text, matching whitespace and indentation precisely. If
old_string
is empty, the tool attempts to create a new file at
file_path
with
new_string
as content.
new_string
(string, required): The exact literal text to replace
old_string
with.
replace_all
(boolean, optional): Replace all occurrences of
old_string
. Defaults to
false
.
Behavior:
If
old_string
is empty and
file_path
does not exist, creates a new file with
new_string
as content.
If
old_string
is provided, it reads the
file_path
and attempts to find exactly one occurrence unless
replace_all
is true.
If the match is unique (or
replace_all
is true), it replaces the text with
new_string
.
Enhanced Reliability (Multi-Stage Edit Correction):
To significantly improve the success rate of edits, especially when the model-provided
old_string
might not be perfectly precise, the tool incorporates a multi-stage edit correction mechanism.
If the initial
old_string
isn’t found or matches multiple locations, the tool can leverage the Qwen model to iteratively refine
old_string
(and potentially
new_string
).
This self-correction process attempts to identify the unique segment the model intended to modify, making the
edit
operation more robust even with slightly imperfect initial context.
Failure conditions:
Despite the correction mechanism, the tool will fail if:
file_path
is not absolute or is outside the root directory.
old_string
is not empty, but the
file_path
does not exist.
old_string
is empty, but the
file_path
already exists.
old_string
is not found in the file after attempts to correct it.
old_string
is found multiple times,
replace_all
is false, and the self-correction mechanism cannot resolve it to a single, unambiguous match.
Output (
llmContent
):
On success:
Successfully modified file: /path/to/file.txt (1 replacements).
or
Created new file: /path/to/new_file.txt with provided content.
On failure: An error message explaining the reason (e.g.,
Failed to edit, 0 occurrences found...
,
Failed to edit because the text matches multiple locations...
).
Confirmation:
Yes. Shows a diff of the proposed changes and asks for user approval before writing to the file.
File encoding and platform-specific behavior
Encoding detection and preservation
When reading files, Qwen Code detects the file’s encoding using a multi-step strategy:
UTF-8
— tried first (most modern tooling outputs UTF-8)
chardet
— statistical detection for non-UTF-8 content
System encoding
— falls back to the OS code page (Windows
chcp
/ Unix
LANG
)
Both
write_file
and
edit
preserve the original encoding and BOM (byte order mark) of existing files. If a file was read as GBK with a UTF-8 BOM, it will be written back the same way.
Configuring default encoding for new files
The
defaultFileEncoding
setting controls encoding for
newly created
files (not edits to existing files):
Value
Behavior
(not set)
UTF-8 without BOM, with automatic platform-specific adjustments (see below)
utf-8
UTF-8 without BOM, no automatic adjustments
utf-8-bom
UTF-8 with BOM for all new files
Set it in
.qwen/settings.json
or
~/.qwen/settings.json
:
{
"general"
: {
"defaultFileEncoding"
:
"utf-8-bom"
}
}
Windows: CRLF for batch files
On Windows,
.bat
and
.cmd
files are automatically written with CRLF (
\r\n
) line endings. This is required because
cmd.exe
uses CRLF as its line delimiter — LF-only endings can break multi-line
if
/
else
,
goto
labels, and
for
loops. This applies regardless of encoding settings and only on Windows.
Windows: UTF-8 BOM for PowerShell scripts
On Windows with a
non-UTF-8 system code page
(e.g. GBK/cp936, Big5/cp950, Shift_JIS/cp932), newly created
.ps1
files are automatically written with a UTF-8 BOM. This is necessary because Windows PowerShell 5.1 (the version built into Windows 10/11) reads BOM-less scripts using the system’s ANSI code page. Without a BOM, any non-ASCII characters in the script will be misinterpreted.
This automatic BOM only applies when:
The platform is Windows
The system code page is not UTF-8 (not code page 65001)
The file is a new
.ps1
file (existing files keep their original encoding)
The user has
not
explicitly set
defaultFileEncoding
in settings
PowerShell 7+ (pwsh) defaults to UTF-8 and handles BOM transparently, so the BOM is harmless there.
If you explicitly set
defaultFileEncoding
to
"utf-8"
, the automatic BOM is disabled — this is an intentional escape hatch for repositories or tooling that reject BOMs.
Summary
File type
Platform
Automatic behavior
.bat
,
.cmd
Windows
CRLF line endings
.ps1
Windows (non-UTF-8 code page)
UTF-8 BOM on new files
All others
All
UTF-8 without BOM (default)
These file system tools provide a foundation for Qwen Code to understand and interact with your local project context.
Last updated on
May 18, 2026
Introduction
Multi-File Read</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/file-system/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
File System
Qwen Code file system tools
Qwen Code provides a comprehensive suite of tools for interacting with the local file system. These tools allow the model to read from, write to, list, search, and modify files and directories, all under your control and typically with confirmation for sensitive operations.
Note:
All file system tools operate within a
rootDirectory
(usually the current working directory where you launched the CLI) for security. Paths that you provide ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
File System
Qwen Code file system tools
Qwen Code provides a comprehensive suite of tools for interacting with the local file system. These tools allow the model to read from, write to, list, search, and modify files and directories, all under your control and typically with confirmation for sensitive operations.
Note:
All file system tools operate within a
rootDirectory
(usually the current working directory where you launched the CLI) for security. Paths that you provide to these tools are generally expected to be absolute or are resolved relative to this root directory.
1.
list_directory
(ListFiles)
list_directory
lists the names of files and subdirectories directly within a specified directory path. It can optionally ignore entries matching provided glob patterns.
Tool name:
list_directory
Display name:
ListFiles
File:
ls.ts
Parameters:
path
(string, required): The absolute path to the directory to list.
ignore
(array of strings, optional): A list of glob patterns to exclude from the listing (e.g.,
["*.log", ".git"]
).
respect_git_ignore
(boolean, optional): Whether to respect
.gitignore
patterns when listing files. Defaults to
true
.
Behavior:
Returns a list of file and directory names.
Indicates whether each entry is a directory.
Sorts entries with directories first, then alphabetically.
Output (
llmContent
):
A string like:
Directory listing for /path/to/your/folder:\n[DIR] subfolder1\nfile1.txt\nfile2.png
Confirmation:
No.
2.
read_file
(ReadFile)
read_file
reads and returns the content of a specified file. This tool handles text files and media files (images, PDFs, audio, video) whose modality is supported by the current model. For text files, it can read specific line ranges. Media files whose modality is not supported by the current model are rejected with a helpful error message. Other binary file types are generally skipped.
Tool name:
read_file
Display name:
ReadFile
File:
read-file.ts
Parameters:
path
(string, required): The absolute path to the file to read.
offset
(number, optional): For text files, the 0-based line number to start reading from. Requires
limit
to be set.
limit
(number, optional): For text files, the maximum number of lines to read. If omitted, reads a default maximum (e.g., 2000 lines) or the entire file if feasible.
Behavior:
For text files: Returns the content. If
offset
and
limit
are used, returns only that slice of lines. Indicates if content was truncated due to line limits or line length limits.
For media files (images, PDFs, audio, video): If the current model supports the file’s modality, returns the file content as a base64-encoded
inlineData
object. If the model does not support the modality, returns an error message with guidance (e.g., suggesting skills or external tools).
For other binary files: Attempts to identify and skip them, returning a message indicating it’s a generic binary file.
Output:
(
llmContent
):
For text files: The file content, potentially prefixed with a truncation message (e.g.,
[File content truncated: showing lines 1-100 of 500 total lines...]\nActual file content...
).
For supported media files: An object containing
inlineData
with
mimeType
and base64
data
(e.g.,
{ inlineData: { mimeType: 'image/png', data: 'base64encodedstring' } }
).
For unsupported media files: An error message string explaining that the current model does not support this modality, with suggestions for alternatives.
For other binary files: A message like
Cannot display content of binary file: /path/to/data.bin
.
Confirmation:
No.
3.
write_file
(WriteFile)
write_file
writes content to a specified file. If the file exists, it will be overwritten. If the file doesn’t exist, it (and any necessary parent directories) will be created.
Tool name:
write_file
Display name:
WriteFile
File:
write-file.ts
Parameters:
file_path
(string, required): The absolute path to the file to write to.
content
(string, required): The content to write into the file.
Behavior:
Writes the provided
content
to the
file_path
.
Creates parent directories if they don’t exist.
Output (
llmContent
):
A success message, e.g.,
Successfully overwrote file: /path/to/your/file.txt
or
Successfully created and wrote to new file: /path/to/new/file.txt
.
Confirmation:
Yes. Shows a diff of changes and asks for user approval before writing.
4.
glob
(Glob)
glob
finds files matching specific glob patterns (e.g.,
src/**/*.ts
,
*.md
), returning absolute paths sorted by modification time (newest first).
Tool name:
glob
Display name:
Glob
File:
glob.ts
Parameters:
pattern
(string, required): The glob pattern to match against (e.g.,
"*.py"
,
"src/**/*.js"
).
path
(string, optional): The directory to search in. If not specified, the current working directory will be used.
Behavior:
Searches for files matching the glob pattern within the specified directory.
Returns a list of absolute paths, sorted with the most recently modified files first.
Respects .gitignore and .qwenignore patterns by default.
Limits results to 100 files to prevent context overflow.
Output (
llmContent
):
A message like:
Found 5 file(s) matching "*.ts" within /path/to/search/dir, sorted by modification time (newest first):\n---\n/path/to/file1.ts\n/path/to/subdir/file2.ts\n---\n[95 files truncated] ...
Confirmation:
No.
5.
grep_search
(Grep)
grep_search
searches for a regular expression pattern within the content of files in a specified directory. Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers.
Tool name:
grep_search
Display name:
Grep
File:
grep.ts
(with
ripGrep.ts
as fallback)
Parameters:
pattern
(string, required): The regular expression pattern to search for in file contents (e.g.,
"function\\s+myFunction"
,
"log.*Error"
).
path
(string, optional): File or directory to search in. Defaults to current working directory.
glob
(string, optional): Glob pattern to filter files (e.g.
"*.js"
,
"src/**/*.{ts,tsx}"
).
limit
(number, optional): Limit output to first N matching lines. Optional - shows all matches if not specified.
Behavior:
Uses ripgrep for fast search when available; otherwise falls back to a JavaScript-based search implementation.
Returns matching lines with file paths and line numbers.
Case-insensitive by default.
Respects .gitignore and .qwenignore patterns.
Limits output to prevent context overflow.
Output (
llmContent
):
A formatted string of matches, e.g.:
Found 3 matches for pattern "myFunction" in path "." (filter: "*.ts"):
---
src/utils.ts:15:export function myFunction() {
src/utils.ts:22:  myFunction.call();
src/index.ts:5:import { myFunction } from './utils';
---
[0 lines truncated] ...
Confirmation:
No.
grep_search
examples
Search for a pattern with default result limiting:
grep_search(pattern="function\\s+myFunction", path="src")
Search for a pattern with custom result limiting:
grep_search(pattern="function", path="src", limit=50)
Search for a pattern with file filtering and custom result limiting:
grep_search(pattern="function", glob="*.js", limit=10)
6.
edit
(Edit)
edit
replaces text within a file. By default it requires
old_string
to match a single unique location; set
replace_all
to
true
when you intentionally want to change every occurrence. This tool is designed for precise, targeted changes and requires significant context around the
old_string
to ensure it modifies the correct location.
Tool name:
edit
Display name:
Edit
File:
edit.ts
Parameters:
file_path
(string, required): The absolute path to the file to modify.
old_string
(string, required): The exact literal text to replace.
CRITICAL:
This string must uniquely identify the single instance to change. It should include sufficient context around the target text, matching whitespace and indentation precisely. If
old_string
is empty, the tool attempts to create a new file at
file_path
with
new_string
as content.
new_string
(string, required): The exact literal text to replace
old_string
with.
replace_all
(boolean, optional): Replace all occurrences of
old_string
. Defaults to
false
.
Behavior:
If
old_string
is empty and
file_path
does not exist, creates a new file with
new_string
as content.
If
old_string
is provided, it reads the
file_path
and attempts to find exactly one occurrence unless
replace_all
is true.
If the match is unique (or
replace_all
is true), it replaces the text with
new_string
.
Enhanced Reliability (Multi-Stage Edit Correction):
To significantly improve the success rate of edits, especially when the model-provided
old_string
might not be perfectly precise, the tool incorporates a multi-stage edit correction mechanism.
If the initial
old_string
isn’t found or matches multiple locations, the tool can leverage the Qwen model to iteratively refine
old_string
(and potentially
new_string
).
This self-correction process attempts to identify the unique segment the model intended to modify, making the
edit
operation more robust even with slightly imperfect initial context.
Failure conditions:
Despite the correction mechanism, the tool will fail if:
file_path
is not absolute or is outside the root directory.
old_string
is not empty, but the
file_path
does not exist.
old_string
is empty, but the
file_path
already exists.
old_string
is not found in the file after attempts to correct it.
old_string
is found multiple times,
replace_all
is false, and the self-correction mechanism cannot resolve it to a single, unambiguous match.
Output (
llmContent
):
On success:
Successfully modified file: /path/to/file.txt (1 replacements).
or
Created new file: /path/to/new_file.txt with provided content.
On failure: An error message explaining the reason (e.g.,
Failed to edit, 0 occurrences found...
,
Failed to edit because the text matches multiple locations...
).
Confirmation:
Yes. Shows a diff of the proposed changes and asks for user approval before writing to the file.
File encoding and platform-specific behavior
Encoding detection and preservation
When reading files, Qwen Code detects the file’s encoding using a multi-step strategy:
UTF-8
— tried first (most modern tooling outputs UTF-8)
chardet
— statistical detection for non-UTF-8 content
System encoding
— falls back to the OS code page (Windows
chcp
/ Unix
LANG
)
Both
write_file
and
edit
preserve the original encoding and BOM (byte order mark) of existing files. If a file was read as GBK with a UTF-8 BOM, it will be written back the same way.
Configuring default encoding for new files
The
defaultFileEncoding
setting controls encoding for
newly created
files (not edits to existing files):
Value
Behavior
(not set)
UTF-8 without BOM, with automatic platform-specific adjustments (see below)
utf-8
UTF-8 without BOM, no automatic adjustments
utf-8-bom
UTF-8 with BOM for all new files
Set it in
.qwen/settings.json
or
~/.qwen/settings.json
:
{
"general"
: {
"defaultFileEncoding"
:
"utf-8-bom"
}
}
Windows: CRLF for batch files
On Windows,
.bat
and
.cmd
files are automatically written with CRLF (
\r\n
) line endings. This is required because
cmd.exe
uses CRLF as its line delimiter — LF-only endings can break multi-line
if
/
else
,
goto
labels, and
for
loops. This applies regardless of encoding settings and only on Windows.
Windows: UTF-8 BOM for PowerShell scripts
On Windows with a
non-UTF-8 system code page
(e.g. GBK/cp936, Big5/cp950, Shift_JIS/cp932), newly created
.ps1
files are automatically written with a UTF-8 BOM. This is necessary because Windows PowerShell 5.1 (the version built into Windows 10/11) reads BOM-less scripts using the system’s ANSI code page. Without a BOM, any non-ASCII characters in the script will be misinterpreted.
This automatic BOM only applies when:
The platform is Windows
The system code page is not UTF-8 (not code page 65001)
The file is a new
.ps1
file (existing files keep their original encoding)
The user has
not
explicitly set
defaultFileEncoding
in settings
PowerShell 7+ (pwsh) defaults to UTF-8 and handles BOM transparently, so the BOM is harmless there.
If you explicitly set
defaultFileEncoding
to
"utf-8"
, the automatic BOM is disabled — this is an intentional escape hatch for repositories or tooling that reject BOMs.
Summary
File type
Platform
Automatic behavior
.bat
,
.cmd
Windows
CRLF line endings
.ps1
Windows (non-UTF-8 code page)
UTF-8 BOM on new files
All others
All
UTF-8 without BOM (default)
These file system tools provide a foundation for Qwen Code to understand and interact with your local project context.
Last updated on
May 18, 2026
Introduction
Multi-File Read</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/file-system/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>OpenRouter Auth and Model Management Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/openrouter-auth-and-models/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/openrouter-auth-and-models/</guid>
  <pubDate>Tue, 19 Nov 2024 00:00:00 +0000</pubDate>
  <category>Models</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
OpenRouter Auth and Model Management Design
OpenRouter Auth and Model Management Design
This document captures the design intent behind the OpenRouter auth flow and the
model management changes introduced with it. It intentionally focuses on the
product and architectural choices, not implementation history.
Goals
Let users authenticate with OpenRouter from both CLI and
/auth
.
Reuse the existing OpenAI-compatible provider path instead of adding a new auth
type for OpenRouter.
Make the fir...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
OpenRouter Auth and Model Management Design
OpenRouter Auth and Model Management Design
This document captures the design intent behind the OpenRouter auth flow and the
model management changes introduced with it. It intentionally focuses on the
product and architectural choices, not implementation history.
Goals
Let users authenticate with OpenRouter from both CLI and
/auth
.
Reuse the existing OpenAI-compatible provider path instead of adding a new auth
type for OpenRouter.
Make the first-run experience usable without asking users to manage hundreds of
models immediately.
Keep a clear path toward richer model management via
/manage-models
.
OpenRouter Auth
OpenRouter is integrated as an OpenAI-compatible provider:
auth type:
AuthType.USE_OPENAI
provider settings:
modelProviders.openai
API key env var:
OPENROUTER_API_KEY
base URL:
https://openrouter.ai/api/v1
This avoids introducing an OpenRouter-specific
AuthType
when the runtime model
provider path is already OpenAI-compatible. It keeps auth status, model
resolution, provider selection, and settings schema aligned with the existing
provider abstraction.
The user-facing flows are:
qwen auth openrouter --key <key>
for automation or direct API-key setup.
qwen auth openrouter
for browser-based OAuth.
/auth
→ API Key → OpenRouter for the TUI flow.
Browser OAuth uses OpenRouter’s PKCE flow and writes the exchanged API key into
settings before refreshing auth as
AuthType.USE_OPENAI
.
Model Management
OpenRouter exposes a large dynamic model catalog. Writing every discovered model
into
modelProviders.openai
would make
/model
noisy and would turn a long-term
settings field into a cache of a remote catalog.
The key design split is:
Catalog
: the full set of models discovered from a source such as
OpenRouter.
Enabled set
: the smaller set of models that should appear in
/model
and
be persisted in user settings.
For the initial OpenRouter flow, auth should finish with a useful default enabled
set instead of interrupting the user with a large picker. The recommended set
should be small, stable, and biased toward models that let users try the product
successfully, including free models when available.
/model
remains a fast model switcher. It should not become the place where
users browse and curate a full provider catalog.
/manage-models
Richer model management belongs in a separate
/manage-models
entry point. That
flow should let users:
browse discovered models;
search by id, display name, provider prefix, and derived tags such as
free
or
vision
;
see which models are currently enabled;
enable or disable models in batches.
The source dimension must remain part of this design. OpenRouter is only the
first dynamic catalog source; future sources such as ModelScope and ModelStudio
should fit the same shape. UI complexity can be reduced, but the underlying
source abstraction should stay available as the extension point.
Current Boundary
This change should do the minimum needed to make OpenRouter auth and model setup
pleasant:
OAuth or key-based auth configures OpenRouter through the existing
OpenAI-compatible provider path.
The initial enabled model set is curated instead of dumping the full catalog
into settings.
Full catalog storage, browsing, filtering, and batch management are deferred to
/manage-models
.
The design principle is simple: authentication should get users to a working
state quickly, while model curation should live in a dedicated management flow.
Last updated on
May 18, 2026
Fork Subagent Design
Prompt Suggestion (NES) Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/openrouter-auth-and-models/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
OpenRouter Auth and Model Management Design
OpenRouter Auth and Model Management Design
This document captures the design intent behind the OpenRouter auth flow and the
model management changes introduced with it. It intentionally focuses on the
product and architectural choices, not implementation history.
Goals
Let users authenticate with OpenRouter from both CLI and
/auth
.
Reuse the existing OpenAI-compatible provider path instead of adding a new auth
type for OpenRouter.
Make the fir...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
OpenRouter Auth and Model Management Design
OpenRouter Auth and Model Management Design
This document captures the design intent behind the OpenRouter auth flow and the
model management changes introduced with it. It intentionally focuses on the
product and architectural choices, not implementation history.
Goals
Let users authenticate with OpenRouter from both CLI and
/auth
.
Reuse the existing OpenAI-compatible provider path instead of adding a new auth
type for OpenRouter.
Make the first-run experience usable without asking users to manage hundreds of
models immediately.
Keep a clear path toward richer model management via
/manage-models
.
OpenRouter Auth
OpenRouter is integrated as an OpenAI-compatible provider:
auth type:
AuthType.USE_OPENAI
provider settings:
modelProviders.openai
API key env var:
OPENROUTER_API_KEY
base URL:
https://openrouter.ai/api/v1
This avoids introducing an OpenRouter-specific
AuthType
when the runtime model
provider path is already OpenAI-compatible. It keeps auth status, model
resolution, provider selection, and settings schema aligned with the existing
provider abstraction.
The user-facing flows are:
qwen auth openrouter --key <key>
for automation or direct API-key setup.
qwen auth openrouter
for browser-based OAuth.
/auth
→ API Key → OpenRouter for the TUI flow.
Browser OAuth uses OpenRouter’s PKCE flow and writes the exchanged API key into
settings before refreshing auth as
AuthType.USE_OPENAI
.
Model Management
OpenRouter exposes a large dynamic model catalog. Writing every discovered model
into
modelProviders.openai
would make
/model
noisy and would turn a long-term
settings field into a cache of a remote catalog.
The key design split is:
Catalog
: the full set of models discovered from a source such as
OpenRouter.
Enabled set
: the smaller set of models that should appear in
/model
and
be persisted in user settings.
For the initial OpenRouter flow, auth should finish with a useful default enabled
set instead of interrupting the user with a large picker. The recommended set
should be small, stable, and biased toward models that let users try the product
successfully, including free models when available.
/model
remains a fast model switcher. It should not become the place where
users browse and curate a full provider catalog.
/manage-models
Richer model management belongs in a separate
/manage-models
entry point. That
flow should let users:
browse discovered models;
search by id, display name, provider prefix, and derived tags such as
free
or
vision
;
see which models are currently enabled;
enable or disable models in batches.
The source dimension must remain part of this design. OpenRouter is only the
first dynamic catalog source; future sources such as ModelScope and ModelStudio
should fit the same shape. UI complexity can be reduced, but the underlying
source abstraction should stay available as the extension point.
Current Boundary
This change should do the minimum needed to make OpenRouter auth and model setup
pleasant:
OAuth or key-based auth configures OpenRouter through the existing
OpenAI-compatible provider path.
The initial enabled model set is curated instead of dumping the full catalog
into settings.
Full catalog storage, browsing, filtering, and batch management are deferred to
/manage-models
.
The design principle is simple: authentication should get users to a working
state quickly, while model curation should live in a dedicated management flow.
Last updated on
May 18, 2026
Fork Subagent Design
Prompt Suggestion (NES) Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/openrouter-auth-and-models/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Channel Plugin Developer Guide</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/channel-plugins/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/channel-plugins/</guid>
  <pubDate>Sun, 17 Nov 2024 00:00:00 +0000</pubDate>
  <category>Plugins</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Channel Plugin Guide
Channel Plugin Developer Guide
A channel plugin connects Qwen Code to a messaging platform. It’s packaged as an
extension
and loaded at startup. For user-facing docs on installing and configuring plugins, see
Plugins
.
How It Fits Together
Your plugin sits in the Platform Adapter layer. You handle platform-specific concerns (connecting, receiving messages, sending responses).
ChannelBase
handles everything else (access control, session routing, prompt queuing...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Channel Plugin Guide
Channel Plugin Developer Guide
A channel plugin connects Qwen Code to a messaging platform. It’s packaged as an
extension
and loaded at startup. For user-facing docs on installing and configuring plugins, see
Plugins
.
How It Fits Together
Your plugin sits in the Platform Adapter layer. You handle platform-specific concerns (connecting, receiving messages, sending responses).
ChannelBase
handles everything else (access control, session routing, prompt queuing, slash commands, crash recovery).
Your Plugin  →  builds Envelope  →  handleInbound()
ChannelBase  →  gates → commands → routing → AcpBridge.prompt()
ChannelBase  →  calls your sendMessage() with the agent's response
The Plugin Object
Your extension entry point exports a
plugin
conforming to
ChannelPlugin
:
import
type
{ ChannelPlugin }
from
'@qwen-code/channel-base'
;
import
{ MyChannel }
from
'./MyChannel.js'
;
export
const
plugin
:
ChannelPlugin
=
{
channelType:
'my-platform'
,
// Unique ID, used in settings.json "type" field
displayName:
'My Platform'
,
// Shown in CLI output
requiredConfigFields: [
'apiKey'
],
// Validated at startup (beyond standard ChannelConfig)
createChannel
: (
name
,
config
,
bridge
,
options
)
=>
new
MyChannel
(name, config, bridge, options),
};
The Channel Adapter
Extend
ChannelBase
and implement three methods:
import
{ ChannelBase }
from
'@qwen-code/channel-base'
;
import
type
{ Envelope }
from
'@qwen-code/channel-base'
;
export
class
MyChannel
extends
ChannelBase
{
async
connect
()
:
Promise
<
void
> {
// Connect to your platform, register message handlers
// When a message arrives:
const
envelope
:
Envelope
=
{
channelName:
this
.name,
senderId:
'...'
,
// Stable, unique platform user ID
senderName:
'...'
,
// Display name
chatId:
'...'
,
// Chat/conversation ID (distinct for DMs vs groups)
text:
'...'
,
// Message text (strip @mentions)
isGroup:
false
,
// Accurate — used by GroupGate
isMentioned:
false
,
// Accurate — used by GroupGate
isReplyToBot:
false
,
// Accurate — used by GroupGate
};
this
.
handleInbound
(envelope);
}
async
sendMessage
(
chatId
:
string
,
text
:
string
)
:
Promise
<
void
> {
// Format markdown → platform format, chunk if needed, deliver
}
disconnect
()
:
void
{
// Clean up connections
}
}
The Envelope
The normalized message object you build from platform data. The boolean flags drive gate logic, so they must be accurate.
Field
Type
Required
Notes
channelName
string
Yes
Use
this.name
senderId
string
Yes
Must be stable across messages (used for session routing + access control)
senderName
string
Yes
Display name
chatId
string
Yes
Must distinguish DMs from groups
text
string
Yes
Strip bot @mentions
threadId
string
No
For
sessionScope: "thread"
messageId
string
No
Platform message ID — useful for response correlation
isGroup
boolean
Yes
GroupGate relies on this
isMentioned
boolean
Yes
GroupGate relies on this
isReplyToBot
boolean
Yes
GroupGate relies on this
referencedText
string
No
Quoted message — prepended as context
imageBase64
string
No
Base64-encoded image (legacy — prefer
attachments
)
imageMimeType
string
No
e.g.,
image/jpeg
(legacy — prefer
attachments
)
attachments
Attachment[]
No
Structured media attachments (see below)
Attachments
Use the
attachments
array for images, files, audio, and video.
handleInbound()
resolves them automatically: images with base64
data
are sent to the model as vision input, files with a
filePath
get their path appended to the prompt so the agent can read them.
interface
Attachment
{
type
:
'image'
|
'file'
|
'audio'
|
'video'
;
data
?:
string
;
// base64-encoded data (images, small files)
filePath
?:
string
;
// absolute path to local file (large files saved to disk)
mimeType
:
string
;
// e.g. 'application/pdf', 'image/jpeg'
fileName
?:
string
;
// original file name from the platform
}
Example — handling a file upload in your adapter:
import
{ writeFileSync, mkdirSync, existsSync }
from
'node:fs'
;
import
{ join }
from
'node:path'
;
import
{ tmpdir }
from
'node:os'
;
const
buf
=
await
downloadFromPlatform
(fileId);
const
dir
=
join
(
tmpdir
(),
'channel-files'
);
if
(
!
existsSync
(dir))
mkdirSync
(dir, { recursive:
true
});
const
filePath
=
join
(dir, fileName);
writeFileSync
(filePath, buf);
envelope.attachments
=
[
{
type:
'file'
,
filePath,
mimeType:
'application/pdf'
,
fileName,
},
];
The legacy
imageBase64
/
imageMimeType
fields still work for backwards compatibility but
attachments
is preferred for new code.
Extension Manifest
Your
qwen-extension.json
declares the channel type. The key must match
channelType
in your plugin object:
{
"name"
:
"my-channel-extension"
,
"version"
:
"1.0.0"
,
"channels"
: {
"my-platform"
: {
"entry"
:
"dist/index.js"
,
"displayName"
:
"My Platform Channel"
}
}
}
Optional Extension Points
Custom slash commands
— register in your constructor:
this
.
registerCommand
(
'mycommand'
,
async
(
envelope
,
args
)
=>
{
await
this
.
sendMessage
(envelope.chatId,
'Response'
);
return
true
;
// handled, don't forward to agent
});
Working indicators
— override
onPromptStart()
and
onPromptEnd()
to show platform-specific typing indicators. These hooks fire only when a prompt actually begins processing — not for buffered messages (collect mode) or gated/blocked messages:
protected override
onPromptStart
(chatId: string, sessionId: string, messageId
?:
string):
void
{
this
.platformClient.
sendTyping
(chatId);
// your platform API
}
protected override
onPromptEnd
(chatId: string, sessionId: string, messageId
?:
string):
void
{
this
.platformClient.
stopTyping
(chatId);
}
Tool call hooks
— override
onToolCall()
to display agent activity (e.g., “Running shell command…”).
Streaming hooks
— override
onResponseChunk(chatId, chunk, sessionId)
for per-chunk progressive display (e.g., editing a message in-place). Override
onResponseComplete(chatId, fullText, sessionId)
to customize final delivery.
Block streaming
— set
blockStreaming: "on"
in the channel config. The base class automatically splits responses into multiple messages at paragraph boundaries. No plugin code needed — it works alongside
onResponseChunk
.
Media
— populate
envelope.attachments
with images/files. See
Attachments
above.
Reference Implementations
Plugin example
(
packages/channels/plugin-example/
) — minimal WebSocket-based adapter, good starting point
Telegram
(
packages/channels/telegram/
) — full-featured: images, files, formatting, typing indicators
DingTalk
(
packages/channels/dingtalk/
) — stream-based with rich text handling
Last updated on
May 18, 2026
Java SDK (alpha)
Introduction</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/channel-plugins/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Channel Plugin Guide
Channel Plugin Developer Guide
A channel plugin connects Qwen Code to a messaging platform. It’s packaged as an
extension
and loaded at startup. For user-facing docs on installing and configuring plugins, see
Plugins
.
How It Fits Together
Your plugin sits in the Platform Adapter layer. You handle platform-specific concerns (connecting, receiving messages, sending responses).
ChannelBase
handles everything else (access control, session routing, prompt queuing...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Channel Plugin Guide
Channel Plugin Developer Guide
A channel plugin connects Qwen Code to a messaging platform. It’s packaged as an
extension
and loaded at startup. For user-facing docs on installing and configuring plugins, see
Plugins
.
How It Fits Together
Your plugin sits in the Platform Adapter layer. You handle platform-specific concerns (connecting, receiving messages, sending responses).
ChannelBase
handles everything else (access control, session routing, prompt queuing, slash commands, crash recovery).
Your Plugin  →  builds Envelope  →  handleInbound()
ChannelBase  →  gates → commands → routing → AcpBridge.prompt()
ChannelBase  →  calls your sendMessage() with the agent's response
The Plugin Object
Your extension entry point exports a
plugin
conforming to
ChannelPlugin
:
import
type
{ ChannelPlugin }
from
'@qwen-code/channel-base'
;
import
{ MyChannel }
from
'./MyChannel.js'
;
export
const
plugin
:
ChannelPlugin
=
{
channelType:
'my-platform'
,
// Unique ID, used in settings.json "type" field
displayName:
'My Platform'
,
// Shown in CLI output
requiredConfigFields: [
'apiKey'
],
// Validated at startup (beyond standard ChannelConfig)
createChannel
: (
name
,
config
,
bridge
,
options
)
=>
new
MyChannel
(name, config, bridge, options),
};
The Channel Adapter
Extend
ChannelBase
and implement three methods:
import
{ ChannelBase }
from
'@qwen-code/channel-base'
;
import
type
{ Envelope }
from
'@qwen-code/channel-base'
;
export
class
MyChannel
extends
ChannelBase
{
async
connect
()
:
Promise
<
void
> {
// Connect to your platform, register message handlers
// When a message arrives:
const
envelope
:
Envelope
=
{
channelName:
this
.name,
senderId:
'...'
,
// Stable, unique platform user ID
senderName:
'...'
,
// Display name
chatId:
'...'
,
// Chat/conversation ID (distinct for DMs vs groups)
text:
'...'
,
// Message text (strip @mentions)
isGroup:
false
,
// Accurate — used by GroupGate
isMentioned:
false
,
// Accurate — used by GroupGate
isReplyToBot:
false
,
// Accurate — used by GroupGate
};
this
.
handleInbound
(envelope);
}
async
sendMessage
(
chatId
:
string
,
text
:
string
)
:
Promise
<
void
> {
// Format markdown → platform format, chunk if needed, deliver
}
disconnect
()
:
void
{
// Clean up connections
}
}
The Envelope
The normalized message object you build from platform data. The boolean flags drive gate logic, so they must be accurate.
Field
Type
Required
Notes
channelName
string
Yes
Use
this.name
senderId
string
Yes
Must be stable across messages (used for session routing + access control)
senderName
string
Yes
Display name
chatId
string
Yes
Must distinguish DMs from groups
text
string
Yes
Strip bot @mentions
threadId
string
No
For
sessionScope: "thread"
messageId
string
No
Platform message ID — useful for response correlation
isGroup
boolean
Yes
GroupGate relies on this
isMentioned
boolean
Yes
GroupGate relies on this
isReplyToBot
boolean
Yes
GroupGate relies on this
referencedText
string
No
Quoted message — prepended as context
imageBase64
string
No
Base64-encoded image (legacy — prefer
attachments
)
imageMimeType
string
No
e.g.,
image/jpeg
(legacy — prefer
attachments
)
attachments
Attachment[]
No
Structured media attachments (see below)
Attachments
Use the
attachments
array for images, files, audio, and video.
handleInbound()
resolves them automatically: images with base64
data
are sent to the model as vision input, files with a
filePath
get their path appended to the prompt so the agent can read them.
interface
Attachment
{
type
:
'image'
|
'file'
|
'audio'
|
'video'
;
data
?:
string
;
// base64-encoded data (images, small files)
filePath
?:
string
;
// absolute path to local file (large files saved to disk)
mimeType
:
string
;
// e.g. 'application/pdf', 'image/jpeg'
fileName
?:
string
;
// original file name from the platform
}
Example — handling a file upload in your adapter:
import
{ writeFileSync, mkdirSync, existsSync }
from
'node:fs'
;
import
{ join }
from
'node:path'
;
import
{ tmpdir }
from
'node:os'
;
const
buf
=
await
downloadFromPlatform
(fileId);
const
dir
=
join
(
tmpdir
(),
'channel-files'
);
if
(
!
existsSync
(dir))
mkdirSync
(dir, { recursive:
true
});
const
filePath
=
join
(dir, fileName);
writeFileSync
(filePath, buf);
envelope.attachments
=
[
{
type:
'file'
,
filePath,
mimeType:
'application/pdf'
,
fileName,
},
];
The legacy
imageBase64
/
imageMimeType
fields still work for backwards compatibility but
attachments
is preferred for new code.
Extension Manifest
Your
qwen-extension.json
declares the channel type. The key must match
channelType
in your plugin object:
{
"name"
:
"my-channel-extension"
,
"version"
:
"1.0.0"
,
"channels"
: {
"my-platform"
: {
"entry"
:
"dist/index.js"
,
"displayName"
:
"My Platform Channel"
}
}
}
Optional Extension Points
Custom slash commands
— register in your constructor:
this
.
registerCommand
(
'mycommand'
,
async
(
envelope
,
args
)
=>
{
await
this
.
sendMessage
(envelope.chatId,
'Response'
);
return
true
;
// handled, don't forward to agent
});
Working indicators
— override
onPromptStart()
and
onPromptEnd()
to show platform-specific typing indicators. These hooks fire only when a prompt actually begins processing — not for buffered messages (collect mode) or gated/blocked messages:
protected override
onPromptStart
(chatId: string, sessionId: string, messageId
?:
string):
void
{
this
.platformClient.
sendTyping
(chatId);
// your platform API
}
protected override
onPromptEnd
(chatId: string, sessionId: string, messageId
?:
string):
void
{
this
.platformClient.
stopTyping
(chatId);
}
Tool call hooks
— override
onToolCall()
to display agent activity (e.g., “Running shell command…”).
Streaming hooks
— override
onResponseChunk(chatId, chunk, sessionId)
for per-chunk progressive display (e.g., editing a message in-place). Override
onResponseComplete(chatId, fullText, sessionId)
to customize final delivery.
Block streaming
— set
blockStreaming: "on"
in the channel config. The base class automatically splits responses into multiple messages at paragraph boundaries. No plugin code needed — it works alongside
onResponseChunk
.
Media
— populate
envelope.attachments
with images/files. See
Attachments
above.
Reference Implementations
Plugin example
(
packages/channels/plugin-example/
) — minimal WebSocket-based adapter, good starting point
Telegram
(
packages/channels/telegram/
) — full-featured: images, files, formatting, typing indicators
DingTalk
(
packages/channels/dingtalk/
) — stream-based with rich text handling
Last updated on
May 18, 2026
Java SDK (alpha)
Introduction</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/channel-plugins/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Github Actions：qwen-code-action</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/integration-github-action/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/integration-github-action/</guid>
  <pubDate>Fri, 15 Nov 2024 00:00:00 +0000</pubDate>
  <category>Integrations</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Github Actions
Github Actions：qwen-code-action
Overview
qwen-code-action
is a GitHub Action that integrates
Qwen Code
into your development workflow via the
Qwen Code CLI
. It acts both as an autonomous agent for critical routine coding tasks, and an on-demand collaborator you can quickly delegate work to.
Use it to perform GitHub pull request reviews, triage issues, perform code analysis and modification, and more using
Qwen Code
conversationally (e.g.,
@qwencoder fix this issue
) di...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Github Actions
Github Actions：qwen-code-action
Overview
qwen-code-action
is a GitHub Action that integrates
Qwen Code
into your development workflow via the
Qwen Code CLI
. It acts both as an autonomous agent for critical routine coding tasks, and an on-demand collaborator you can quickly delegate work to.
Use it to perform GitHub pull request reviews, triage issues, perform code analysis and modification, and more using
Qwen Code
conversationally (e.g.,
@qwencoder fix this issue
) directly inside your GitHub repositories.
Features
Automation
: Trigger workflows based on events (e.g. issue opening) or schedules (e.g. nightly).
On-demand Collaboration
: Trigger workflows in issue and pull request
comments by mentioning the
Qwen Code CLI
(e.g.,
@qwencoder /review
).
Extensible with Tools
: Leverage
Qwen Code
models’ tool-calling capabilities to interact with other CLIs like the
GitHub CLI
(
gh
).
Customizable
: Use a
QWEN.md
file in your repository to provide
project-specific instructions and context to
Qwen Code CLI
.
Quick Start
Get started with Qwen Code CLI in your repository in just a few minutes:
1. Get a Qwen API Key
Obtain your API key from
DashScope
(Alibaba Cloud’s AI platform)
2. Add it as a GitHub Secret
Store your API key as a secret named
QWEN_API_KEY
in your repository:
Go to your repository’s
Settings > Secrets and variables > Actions
Click
New repository secret
Name:
QWEN_API_KEY
, Value: your API key
3. Update your .gitignore
Add the following entries to your
.gitignore
file:
# qwen-code-cli settings
.qwen/
# GitHub App credentials
gha-creds-*.json
4. Choose a Workflow
You have two options to set up a workflow:
Option A: Use setup command (Recommended)
Start the Qwen Code CLI in your terminal:
qwen
In Qwen Code CLI in your terminal, type:
/setup-github
Option B: Manually copy workflows
Copy the pre-built workflows from the
examples/workflows
directory to your repository’s
.github/workflows
directory. Note: the
qwen-dispatch.yml
workflow must also be copied, which triggers the workflows to run.
5. Try it out
Pull Request Review:
Open a pull request in your repository and wait for automatic review
Comment
@qwencoder /review
on an existing pull request to manually trigger a review
Issue Triage:
Open an issue and wait for automatic triage
Comment
@qwencoder /triage
on existing issues to manually trigger triaging
General AI Assistance:
In any issue or pull request, mention
@qwencoder
followed by your request
Examples:
@qwencoder explain this code change
@qwencoder suggest improvements for this function
@qwencoder help me debug this error
@qwencoder write unit tests for this component
Workflows
This action provides several pre-built workflows for different use cases. Each workflow is designed to be copied into your repository’s
.github/workflows
directory and customized as needed.
Qwen Code Dispatch
This workflow acts as a central dispatcher for Qwen Code CLI, routing requests to the appropriate workflow based on the triggering event and the command provided in the comment. For a detailed guide on how to set up the dispatch workflow, go to the
Qwen Code Dispatch workflow documentation
.
Issue Triage
This action can be used to triage GitHub Issues automatically or on a schedule. For a detailed guide on how to set up the issue triage system, go to the
GitHub Issue Triage workflow documentation
.
Pull Request Review
This action can be used to automatically review pull requests when they are opened. For a detailed guide on how to set up the pull request review system, go to the
GitHub PR Review workflow documentation
.
Qwen Code CLI Assistant
This type of action can be used to invoke a general-purpose, conversational Qwen Code AI assistant within the pull requests and issues to perform a wide range of tasks. For a detailed guide on how to set up the general-purpose Qwen Code CLI workflow, go to the
Qwen Code Assistant workflow documentation
.
Configuration
Inputs
qwen*api_key
: *(Optional)_ The API key for the Qwen API.
qwen*cli_version
: *(Optional, default:
latest
)_ The version of the Qwen Code CLI to install. Can be “latest”, “preview”, “nightly”, a specific version number, or a git branch, tag, or commit. For more information, see
Qwen Code CLI releases
.
qwen*debug
: *(Optional)_ Enable debug logging and output streaming.
qwen*model
: *(Optional)_ The model to use with Qwen Code.
prompt
:
(Optional, default:
You are a helpful assistant.
)
A string passed to the Qwen Code CLI’s
--prompt
argument
.
settings
:
(Optional)
A JSON string written to
.qwen/settings.json
to configure the CLI’s
project
settings.
For more details, see the documentation on
settings files
.
use*qwen_code_assist
: *(Optional, default:
false
)_ Whether to use Code Assist for Qwen Code model access instead of the default Qwen Code API key.
For more information, see the
Qwen Code CLI documentation
.
use*vertex_ai
: *(Optional, default:
false
)_ Whether to use Vertex AI for Qwen Code model access instead of the default Qwen Code API key.
For more information, see the
Qwen Code CLI documentation
.
extensions
:
(Optional)
A list of Qwen Code CLI extensions to install.
upload*artifacts
: *(Optional, default:
false
)_ Whether to upload artifacts to the github action.
use*pnpm
: *(Optional, default:
false
)_ Whether or not to use pnpm instead of npm to install qwen-code-cli
workflow*name
: *(Optional, default:
${{ github.workflow }}
)_ The GitHub workflow name, used for telemetry purposes.
Outputs
summary
: The summarized output from the Qwen Code CLI execution.
error
: The error output from the Qwen Code CLI execution, if any.
Repository Variables
We recommend setting the following values as repository variables so they can be reused across all workflows. Alternatively, you can set them inline as action inputs in individual workflows or to override repository-level values.
Name
Description
Type
Required
When Required
DEBUG
Enables debug logging for the Qwen Code CLI.
Variable
No
Never
QWEN_CLI_VERSION
Controls which version of the Qwen Code CLI is installed.
Variable
No
Pinning the CLI version
APP_ID
GitHub App ID for custom authentication.
Variable
No
Using a custom GitHub App
To add a repository variable:
Go to your repository’s
Settings > Secrets and variables > Actions > New variable
.
Enter the variable name and value.
Save.
For details about repository variables, refer to the
GitHub documentation on variables
.
Secrets
You can set the following secrets in your repository:
Name
Description
Required
When Required
QWEN_API_KEY
Your Qwen API key from DashScope.
Yes
Required for all workflows that call Qwen.
APP_PRIVATE_KEY
Private key for your GitHub App (PEM format).
No
Using a custom GitHub App.
To add a secret:
Go to your repository’s
Settings > Secrets and variables >Actions > New repository secret
.
Enter the secret name and value.
Save.
For more information, refer to the
official GitHub documentation on creating and using encrypted secrets
.
Authentication
This action requires authentication to the GitHub API and optionally to Qwen Code services.
GitHub Authentication
You can authenticate with GitHub in two ways:
Default
GITHUB_TOKEN
:
For simpler use cases, the action can use the
default
GITHUB_TOKEN
provided by the workflow.
Custom GitHub App (Recommended):
For the most secure and flexible
authentication, we recommend creating a custom GitHub App.
For detailed setup instructions for both Qwen and GitHub authentication, go to the
Authentication documentation
.
Extensions
The Qwen Code CLI can be extended with additional functionality through extensions.
These extensions are installed from source from their GitHub repositories.
For detailed instructions on how to set up and configure extensions, go to the
Extensions documentation
.
Best Practices
To ensure the security, reliability, and efficiency of your automated workflows, we strongly recommend following our best practices. These guidelines cover key areas such as repository security, workflow configuration, and monitoring.
Key recommendations include:
Securing Your Repository:
Implementing branch and tag protection, and restricting pull request approvers.
Monitoring and Auditing:
Regularly reviewing action logs and enabling OpenTelemetry for deeper insights into performance and behavior.
For a comprehensive guide on securing your repository and workflows, please refer to our
Best Practices documentation
.
Customization
Create a QWEN.md file in the root of your repository to provide
project-specific context and instructions to
Qwen Code CLI
. This is useful for defining
coding conventions, architectural patterns, or other guidelines the model should
follow for a given repository.
Contributing
Contributions are welcome! Check out the Qwen Code CLI
Contributing Guide
for more details on how to get started.
Last updated on
May 18, 2026
JetBrains IDEs
Commands</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-github-action/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Github Actions
Github Actions：qwen-code-action
Overview
qwen-code-action
is a GitHub Action that integrates
Qwen Code
into your development workflow via the
Qwen Code CLI
. It acts both as an autonomous agent for critical routine coding tasks, and an on-demand collaborator you can quickly delegate work to.
Use it to perform GitHub pull request reviews, triage issues, perform code analysis and modification, and more using
Qwen Code
conversationally (e.g.,
@qwencoder fix this issue
) di...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Github Actions
Github Actions：qwen-code-action
Overview
qwen-code-action
is a GitHub Action that integrates
Qwen Code
into your development workflow via the
Qwen Code CLI
. It acts both as an autonomous agent for critical routine coding tasks, and an on-demand collaborator you can quickly delegate work to.
Use it to perform GitHub pull request reviews, triage issues, perform code analysis and modification, and more using
Qwen Code
conversationally (e.g.,
@qwencoder fix this issue
) directly inside your GitHub repositories.
Features
Automation
: Trigger workflows based on events (e.g. issue opening) or schedules (e.g. nightly).
On-demand Collaboration
: Trigger workflows in issue and pull request
comments by mentioning the
Qwen Code CLI
(e.g.,
@qwencoder /review
).
Extensible with Tools
: Leverage
Qwen Code
models’ tool-calling capabilities to interact with other CLIs like the
GitHub CLI
(
gh
).
Customizable
: Use a
QWEN.md
file in your repository to provide
project-specific instructions and context to
Qwen Code CLI
.
Quick Start
Get started with Qwen Code CLI in your repository in just a few minutes:
1. Get a Qwen API Key
Obtain your API key from
DashScope
(Alibaba Cloud’s AI platform)
2. Add it as a GitHub Secret
Store your API key as a secret named
QWEN_API_KEY
in your repository:
Go to your repository’s
Settings > Secrets and variables > Actions
Click
New repository secret
Name:
QWEN_API_KEY
, Value: your API key
3. Update your .gitignore
Add the following entries to your
.gitignore
file:
# qwen-code-cli settings
.qwen/
# GitHub App credentials
gha-creds-*.json
4. Choose a Workflow
You have two options to set up a workflow:
Option A: Use setup command (Recommended)
Start the Qwen Code CLI in your terminal:
qwen
In Qwen Code CLI in your terminal, type:
/setup-github
Option B: Manually copy workflows
Copy the pre-built workflows from the
examples/workflows
directory to your repository’s
.github/workflows
directory. Note: the
qwen-dispatch.yml
workflow must also be copied, which triggers the workflows to run.
5. Try it out
Pull Request Review:
Open a pull request in your repository and wait for automatic review
Comment
@qwencoder /review
on an existing pull request to manually trigger a review
Issue Triage:
Open an issue and wait for automatic triage
Comment
@qwencoder /triage
on existing issues to manually trigger triaging
General AI Assistance:
In any issue or pull request, mention
@qwencoder
followed by your request
Examples:
@qwencoder explain this code change
@qwencoder suggest improvements for this function
@qwencoder help me debug this error
@qwencoder write unit tests for this component
Workflows
This action provides several pre-built workflows for different use cases. Each workflow is designed to be copied into your repository’s
.github/workflows
directory and customized as needed.
Qwen Code Dispatch
This workflow acts as a central dispatcher for Qwen Code CLI, routing requests to the appropriate workflow based on the triggering event and the command provided in the comment. For a detailed guide on how to set up the dispatch workflow, go to the
Qwen Code Dispatch workflow documentation
.
Issue Triage
This action can be used to triage GitHub Issues automatically or on a schedule. For a detailed guide on how to set up the issue triage system, go to the
GitHub Issue Triage workflow documentation
.
Pull Request Review
This action can be used to automatically review pull requests when they are opened. For a detailed guide on how to set up the pull request review system, go to the
GitHub PR Review workflow documentation
.
Qwen Code CLI Assistant
This type of action can be used to invoke a general-purpose, conversational Qwen Code AI assistant within the pull requests and issues to perform a wide range of tasks. For a detailed guide on how to set up the general-purpose Qwen Code CLI workflow, go to the
Qwen Code Assistant workflow documentation
.
Configuration
Inputs
qwen*api_key
: *(Optional)_ The API key for the Qwen API.
qwen*cli_version
: *(Optional, default:
latest
)_ The version of the Qwen Code CLI to install. Can be “latest”, “preview”, “nightly”, a specific version number, or a git branch, tag, or commit. For more information, see
Qwen Code CLI releases
.
qwen*debug
: *(Optional)_ Enable debug logging and output streaming.
qwen*model
: *(Optional)_ The model to use with Qwen Code.
prompt
:
(Optional, default:
You are a helpful assistant.
)
A string passed to the Qwen Code CLI’s
--prompt
argument
.
settings
:
(Optional)
A JSON string written to
.qwen/settings.json
to configure the CLI’s
project
settings.
For more details, see the documentation on
settings files
.
use*qwen_code_assist
: *(Optional, default:
false
)_ Whether to use Code Assist for Qwen Code model access instead of the default Qwen Code API key.
For more information, see the
Qwen Code CLI documentation
.
use*vertex_ai
: *(Optional, default:
false
)_ Whether to use Vertex AI for Qwen Code model access instead of the default Qwen Code API key.
For more information, see the
Qwen Code CLI documentation
.
extensions
:
(Optional)
A list of Qwen Code CLI extensions to install.
upload*artifacts
: *(Optional, default:
false
)_ Whether to upload artifacts to the github action.
use*pnpm
: *(Optional, default:
false
)_ Whether or not to use pnpm instead of npm to install qwen-code-cli
workflow*name
: *(Optional, default:
${{ github.workflow }}
)_ The GitHub workflow name, used for telemetry purposes.
Outputs
summary
: The summarized output from the Qwen Code CLI execution.
error
: The error output from the Qwen Code CLI execution, if any.
Repository Variables
We recommend setting the following values as repository variables so they can be reused across all workflows. Alternatively, you can set them inline as action inputs in individual workflows or to override repository-level values.
Name
Description
Type
Required
When Required
DEBUG
Enables debug logging for the Qwen Code CLI.
Variable
No
Never
QWEN_CLI_VERSION
Controls which version of the Qwen Code CLI is installed.
Variable
No
Pinning the CLI version
APP_ID
GitHub App ID for custom authentication.
Variable
No
Using a custom GitHub App
To add a repository variable:
Go to your repository’s
Settings > Secrets and variables > Actions > New variable
.
Enter the variable name and value.
Save.
For details about repository variables, refer to the
GitHub documentation on variables
.
Secrets
You can set the following secrets in your repository:
Name
Description
Required
When Required
QWEN_API_KEY
Your Qwen API key from DashScope.
Yes
Required for all workflows that call Qwen.
APP_PRIVATE_KEY
Private key for your GitHub App (PEM format).
No
Using a custom GitHub App.
To add a secret:
Go to your repository’s
Settings > Secrets and variables >Actions > New repository secret
.
Enter the secret name and value.
Save.
For more information, refer to the
official GitHub documentation on creating and using encrypted secrets
.
Authentication
This action requires authentication to the GitHub API and optionally to Qwen Code services.
GitHub Authentication
You can authenticate with GitHub in two ways:
Default
GITHUB_TOKEN
:
For simpler use cases, the action can use the
default
GITHUB_TOKEN
provided by the workflow.
Custom GitHub App (Recommended):
For the most secure and flexible
authentication, we recommend creating a custom GitHub App.
For detailed setup instructions for both Qwen and GitHub authentication, go to the
Authentication documentation
.
Extensions
The Qwen Code CLI can be extended with additional functionality through extensions.
These extensions are installed from source from their GitHub repositories.
For detailed instructions on how to set up and configure extensions, go to the
Extensions documentation
.
Best Practices
To ensure the security, reliability, and efficiency of your automated workflows, we strongly recommend following our best practices. These guidelines cover key areas such as repository security, workflow configuration, and monitoring.
Key recommendations include:
Securing Your Repository:
Implementing branch and tag protection, and restricting pull request approvers.
Monitoring and Auditing:
Regularly reviewing action logs and enabling OpenTelemetry for deeper insights into performance and behavior.
For a comprehensive guide on securing your repository and workflows, please refer to our
Best Practices documentation
.
Customization
Create a QWEN.md file in the root of your repository to provide
project-specific context and instructions to
Qwen Code CLI
. This is useful for defining
coding conventions, architectural patterns, or other guidelines the model should
follow for a given repository.
Contributing
Contributions are welcome! Check out the Qwen Code CLI
Contributing Guide
for more details on how to get started.
Last updated on
May 18, 2026
JetBrains IDEs
Commands</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-github-action/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Hooks</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/hooks/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/hooks/</guid>
  <pubDate>Mon, 11 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Hooks
Qwen Code Hooks
Overview
Qwen Code hooks provide a powerful mechanism for extending and customizing the behavior of the Qwen Code application. Hooks allow users to execute custom scripts or programs at specific points in the application lifecycle, such as before tool execution, after tool execution, at session start/end, and during other key events.
Hooks are enabled by default. You can temporarily disable all hooks by setting
disableAllHooks
to
true
in your settings fi...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Hooks
Qwen Code Hooks
Overview
Qwen Code hooks provide a powerful mechanism for extending and customizing the behavior of the Qwen Code application. Hooks allow users to execute custom scripts or programs at specific points in the application lifecycle, such as before tool execution, after tool execution, at session start/end, and during other key events.
Hooks are enabled by default. You can temporarily disable all hooks by setting
disableAllHooks
to
true
in your settings file (at the top level, alongside
hooks
):
{
"disableAllHooks"
:
true
,
"hooks"
: {
"PreToolUse"
: [
...
]
}
}
This disables all hooks without deleting their configurations.
What are Hooks?
Hooks are user-defined scripts or programs that are automatically executed by Qwen Code at predefined points in the application flow. They allow users to:
Monitor and audit tool usage
Enforce security policies
Inject additional context into conversations
Customize application behavior based on events
Integrate with external systems and services
Modify tool inputs or responses programmatically
Hook Types
Qwen Code supports three hook executor types:
Type
Description
command
Execute a shell command. Receives JSON via
stdin
, returns results via
stdout
.
http
Send JSON as a
POST
request body to a specified URL. Returns results via HTTP response body.
function
Directly call a registered JavaScript function (session-level hooks only).
Command Hooks
Command hooks execute commands via child processes. Input JSON is passed through stdin, and output is returned via stdout.
Configuration:
Field
Type
Required
Description
type
"command"
Yes
Hook type
command
string
Yes
Command to execute
name
string
No
Hook name (for logging)
description
string
No
Hook description
timeout
number
No
Timeout in milliseconds, default 60000
async
boolean
No
Whether to run asynchronously in background
env
Record<string, string>
No
Environment variables
shell
"bash" | "powershell"
No
Shell to use
statusMessage
string
No
Status message displayed during execution
Example:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"WriteFile"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"$QWEN_PROJECT_DIR/.qwen/hooks/security-check.sh"
,
"name"
:
"security-check"
,
"timeout"
:
10000
}
]
}
]
}
}
HTTP Hooks
HTTP hooks send hook input as POST requests to specified URLs. They support URL whitelists, DNS-level SSRF protection, environment variable interpolation, and other security features.
Configuration:
Field
Type
Required
Description
type
"http"
Yes
Hook type
url
string
Yes
Target URL
headers
Record<string, string>
No
Request headers (supports env var interpolation)
allowedEnvVars
string[]
No
Whitelist of environment variables allowed in URL/headers
timeout
number
No
Timeout in seconds, default 600
name
string
No
Hook name (for logging)
statusMessage
string
No
Status message displayed during execution
once
boolean
No
Execute only once per event per session (HTTP hooks only)
Security Features:
URL Whitelist
: Configure allowed URL patterns via
allowedUrls
SSRF Protection
: Blocks private IPs (10.x.x.x, 172.16-31.x.x, 192.168.x.x, etc.) but allows loopback addresses (127.0.0.1, ::1)
DNS Validation
: Validates domain resolution before requests to prevent DNS rebinding attacks
Environment Variable Interpolation
:
${VAR}
syntax, only allows variables in
allowedEnvVars
whitelist
Example:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"*"
,
"hooks"
: [
{
"type"
:
"http"
,
"url"
:
"http://127.0.0.1:8080/hooks/pre-tool-use"
,
"headers"
: {
"Authorization"
:
"Bearer ${HOOK_API_KEY}"
},
"allowedEnvVars"
: [
"HOOK_API_KEY"
],
"timeout"
:
10
,
"name"
:
"remote-security-check"
}
]
}
]
}
}
Function Hooks
Function hooks directly call registered JavaScript/TypeScript functions. They are used internally by the Skill system and are not currently exposed as a public API for end users.
Note
: For most use cases, use
command hooks
or
HTTP hooks
instead, which can be configured in settings files.
Hook Events
Hooks fire at specific points during a Qwen Code session. Different events support different matchers to filter trigger conditions.
Event
Triggered When
Matcher Target
PreToolUse
Before tool execution
Tool name (
WriteFile
,
ReadFile
,
Bash
, etc.)
PostToolUse
After successful tool execution
Tool name
PostToolUseFailure
After tool execution fails
Tool name
UserPromptSubmit
After user submits prompt
None (always fires)
SessionStart
When session starts or resumes
Source (
startup
,
resume
,
clear
,
compact
)
SessionEnd
When session ends
Reason (
clear
,
logout
,
prompt_input_exit
, etc.)
Stop
When Claude prepares to conclude response
None (always fires)
SubagentStart
When subagent starts
Agent type (
Bash
,
Explorer
,
Plan
, etc.)
SubagentStop
When subagent stops
Agent type
PreCompact
Before conversation compaction
Trigger (
manual
,
auto
)
Notification
When notifications are sent
Type (
permission_prompt
,
idle_prompt
,
auth_success
)
PermissionRequest
When permission dialog is shown
Tool name
Matcher Patterns
matcher
is a regular expression used to filter trigger conditions.
Event Type
Events
Matcher Support
Matcher Target
Tool Events
PreToolUse
,
PostToolUse
,
PostToolUseFailure
,
PermissionRequest
✅ Regex
Tool name:
WriteFile
,
ReadFile
,
Bash
, etc.
Subagent Events
SubagentStart
,
SubagentStop
✅ Regex
Agent type:
Bash
,
Explorer
, etc.
Session Events
SessionStart
✅ Regex
Source:
startup
,
resume
,
clear
,
compact
Session Events
SessionEnd
✅ Regex
Reason:
clear
,
logout
,
prompt_input_exit
, etc.
Notification Events
Notification
✅ Exact match
Type:
permission_prompt
,
idle_prompt
,
auth_success
Compact Events
PreCompact
✅ Exact match
Trigger:
manual
,
auto
Prompt Events
UserPromptSubmit
❌ No
N/A
Stop Events
Stop
❌ No
N/A
Matcher Syntax:
Empty string
""
or
"*"
matches all events of that type
Standard regex syntax supported (e.g.,
^Bash$
,
Read.*
,
(WriteFile|Edit)
)
Examples:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"^Bash$"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'bash check' >> /tmp/hooks.log"
}
]
},
{
"matcher"
:
"Write.*"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'write check' >> /tmp/hooks.log"
}
]
},
{
"matcher"
:
"*"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'all tools' >> /tmp/hooks.log"
}
]
}
],
"SubagentStart"
: [
{
"matcher"
:
"^(Bash|Explorer)$"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'subagent check' >> /tmp/hooks.log"
}
]
}
]
}
}
Input/Output Rules
Hook Input Structure
All hooks receive standardized input in JSON format through stdin (command) or POST body (http).
Common Fields:
{
"session_id"
:
"string"
,
"transcript_path"
:
"string"
,
"cwd"
:
"string"
,
"hook_event_name"
:
"string"
,
"timestamp"
:
"string"
}
Event-specific fields are added based on the hook type. When running in a subagent,
agent_id
and
agent_type
are additionally included.
Hook Output Structure
Hook output is returned via
stdout
(command) or HTTP response body (http) as JSON.
Exit Code Behavior (Command Hooks):
Exit Code
Behavior
0
Success. Parse JSON in
stdout
to control behavior.
2
Blocking error
. Ignores
stdout
, passes
stderr
as error feedback to the model.
Other
Non-blocking error.
stderr
only shown in debug mode, execution continues.
Output Structure:
Hook output supports three categories of fields:
Common Fields
:
continue
,
stopReason
,
suppressOutput
,
systemMessage
Top-level Decision
:
decision
,
reason
(used by some events)
Event-specific Control
:
hookSpecificOutput
(must include
hookEventName
)
{
"continue"
:
true
,
"decision"
:
"allow"
,
"reason"
:
"Operation approved"
,
"hookSpecificOutput"
: {
"hookEventName"
:
"PreToolUse"
,
"additionalContext"
:
"Additional context information"
}
}
Individual Hook Event Details
PreToolUse
Purpose
: Executed before a tool is used to allow for permission checks, input validation, or context injection.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_name"
:
"name of the tool being executed"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"tool_use_id"
:
"unique identifier for this tool use instance"
}
Output Options
:
hookSpecificOutput.permissionDecision
: “allow”, “deny”, or “ask” (REQUIRED)
hookSpecificOutput.permissionDecisionReason
: explanation for the decision (REQUIRED)
hookSpecificOutput.updatedInput
: modified tool input parameters to use instead of original
hookSpecificOutput.additionalContext
: additional context information
Note
: While standard hook output fields like
decision
and
reason
are technically supported by the underlying class, the official interface expects the
hookSpecificOutput
with
permissionDecision
and
permissionDecisionReason
.
Example Output
:
{
"hookSpecificOutput"
: {
"hookEventName"
:
"PreToolUse"
,
"permissionDecision"
:
"deny"
,
"permissionDecisionReason"
:
"Security policy blocks database writes"
,
"additionalContext"
:
"Current environment: production. Proceed with caution."
}
}
PostToolUse
Purpose
: Executed after a tool completes successfully to process results, log outcomes, or inject additional context.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_name"
:
"name of the tool that was executed"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"tool_response"
:
"object containing the tool's response"
,
"tool_use_id"
:
"unique identifier for this tool use instance"
}
Output Options
:
decision
: “allow”, “deny”, “block” (defaults to “allow” if not specified)
reason
: reason for the decision
hookSpecificOutput.additionalContext
: additional information to be included
Example Output
:
{
"decision"
:
"allow"
,
"reason"
:
"Tool executed successfully"
,
"hookSpecificOutput"
: {
"additionalContext"
:
"File modification recorded in audit log"
}
}
PostToolUseFailure
Purpose
: Executed when a tool execution fails to handle errors, send alerts, or record failures.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_use_id"
:
"unique identifier for the tool use"
,
"tool_name"
:
"name of the tool that failed"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"error"
:
"error message describing the failure"
,
"is_interrupt"
:
"boolean indicating if failure was due to user interruption (optional)"
}
Output Options
:
hookSpecificOutput.additionalContext
: error handling information
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Error: File not found. Failure logged in monitoring system."
}
}
UserPromptSubmit
Purpose
: Executed when the user submits a prompt to modify, validate, or enrich the input.
Event-specific fields
:
{
"prompt"
:
"the user's submitted prompt text"
}
Output Options
:
decision
: “allow”, “deny”, “block”, or “ask”
reason
: human-readable explanation for the decision
hookSpecificOutput.additionalContext
: additional context to append to the prompt (optional)
Note
: Since UserPromptSubmitOutput extends HookOutput, all standard fields are available but only additionalContext in hookSpecificOutput is specifically defined for this event.
Example Output
:
{
"decision"
:
"allow"
,
"reason"
:
"Prompt reviewed and approved"
,
"hookSpecificOutput"
: {
"additionalContext"
:
"Remember to follow company coding standards."
}
}
SessionStart
Purpose
: Executed when a new session starts to perform initialization tasks.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"source"
:
"startup | resume | clear | compact"
,
"model"
:
"the model being used"
,
"agent_type"
:
"the type of agent if applicable (optional)"
}
Output Options
:
hookSpecificOutput.additionalContext
: context to be available in the session
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Session started with security policies enabled."
}
}
SessionEnd
Purpose
: Executed when a session ends to perform cleanup tasks.
Event-specific fields
:
{
"reason"
:
"clear | logout | prompt_input_exit | bypass_permissions_disabled | other"
}
Output Options
:
Standard hook output fields (typically not used for blocking)
Stop
Purpose
: Executed before Qwen concludes its response to provide final feedback or summaries.
Event-specific fields
:
{
"stop_hook_active"
:
"boolean indicating if stop hook is active"
,
"last_assistant_message"
:
"the last message from the assistant"
}
Output Options
:
decision
: “allow”, “deny”, “block”, or “ask”
reason
: human-readable explanation for the decision
stopReason
: feedback to include in the stop response
continue
: set to false to stop execution
hookSpecificOutput.additionalContext
: additional context information
Note
: Since StopOutput extends HookOutput, all standard fields are available but the stopReason field is particularly relevant for this event.
Example Output
:
{
"decision"
:
"block"
,
"reason"
:
"Must be provided when Qwen Code is blocked from stopping"
}
StopFailure
Purpose
: Executed when the turn ends due to an API error (instead of Stop). This is a
fire-and-forget
event - hook output and exit codes are ignored.
Event-specific fields
:
{
"error"
:
"rate_limit | authentication_failed | billing_error | invalid_request | server_error | max_output_tokens | unknown"
,
"error_details"
:
"detailed error message (optional)"
,
"last_assistant_message"
:
"the last message from the assistant before the error (optional)"
}
Matcher
: Matches against the
error
field. For example,
"matcher": "rate_limit"
will only trigger for rate limit errors.
Output Options
:
None
- StopFailure is fire-and-forget. All hook output and exit codes are ignored.
Exit Code Handling
:
Exit Code
Behavior
Any
Ignored (fire-and-forget)
Example Configuration
:
{
"hooks"
: {
"StopFailure"
: [
{
"matcher"
:
"rate_limit"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"/path/to/rate-limit-alert.sh"
,
"name"
:
"rate-limit-alerter"
}
]
}
]
}
}
Use Cases
:
Rate limit monitoring and alerting
Authentication failure logging
Billing error notifications
Error statistics collection
SubagentStart
Purpose
: Executed when a subagent (like the Task tool) is started to set up context or permissions.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"agent_id"
:
"identifier for the subagent"
,
"agent_type"
:
"type of agent (Bash, Explorer, Plan, Custom, etc.)"
}
Output Options
:
hookSpecificOutput.additionalContext
: initial context for the subagent
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Subagent initialized with restricted permissions."
}
}
SubagentStop
Purpose
: Executed when a subagent finishes to perform finalization tasks.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"stop_hook_active"
:
"boolean indicating if stop hook is active"
,
"agent_id"
:
"identifier for the subagent"
,
"agent_type"
:
"type of agent"
,
"agent_transcript_path"
:
"path to the subagent's transcript"
,
"last_assistant_message"
:
"the last message from the subagent"
}
Output Options
:
decision
: “allow”, “deny”, “block”, or “ask”
reason
: human-readable explanation for the decision
Example Output
:
{
"decision"
:
"block"
,
"reason"
:
"Must be provided when Qwen Code is blocked from stopping"
}
PreCompact
Purpose
: Executed before conversation compaction to prepare or log the compaction.
Event-specific fields
:
{
"trigger"
:
"manual | auto"
,
"custom_instructions"
:
"custom instructions currently set"
}
Output Options
:
hookSpecificOutput.additionalContext
: context to include before compaction
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Compacting conversation to maintain optimal context window."
}
}
PostCompact
Purpose
: Executed after conversation compaction completes to archive summaries or track usage.
Event-specific fields
:
{
"trigger"
:
"manual | auto"
,
"compact_summary"
:
"the summary generated by the compaction process"
}
Matcher
: Matches against the
trigger
field. For example,
"matcher": "manual"
will only trigger for manual compaction via
/compact
command.
Output Options
:
hookSpecificOutput.additionalContext
: additional context (for logging only)
Standard hook output fields (for logging only)
Note
: PostCompact is
not
in the official decision mode supported events list. The
decision
field and other control fields do not produce any control effects - they are only used for logging purposes.
Exit Code Handling
:
Exit Code
Behavior
0
Success - stdout shown to user in verbose mode
Other
Non-blocking error - stderr shown to user in verbose mode
Example Configuration
:
{
"hooks"
: {
"PostCompact"
: [
{
"matcher"
:
"manual"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"/path/to/save-compact-summary.sh"
,
"name"
:
"save-summary"
}
]
}
]
}
}
Use Cases
:
Summary archiving to files or databases
Usage statistics tracking
Context change monitoring
Audit logging for compaction operations
Notification
Purpose
: Executed when notifications are sent to customize or intercept them.
Event-specific fields
:
{
"message"
:
"notification message content"
,
"title"
:
"notification title (optional)"
,
"notification_type"
:
"permission_prompt | idle_prompt | auth_success"
}
Note
:
elicitation_dialog
type is defined but not currently implemented.
Output Options
:
hookSpecificOutput.additionalContext
: additional information to include
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Notification processed by monitoring system."
}
}
PermissionRequest
Purpose
: Executed when permission dialogs are displayed to automate decisions or update permissions.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_name"
:
"name of the tool requesting permission"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"permission_suggestions"
:
"array of suggested permissions (optional)"
}
Output Options
:
hookSpecificOutput.decision
: structured object with permission decision details:
behavior
: “allow” or “deny”
updatedInput
: modified tool input (optional)
updatedPermissions
: modified permissions (optional)
message
: message to show to user (optional)
interrupt
: whether to interrupt the workflow (optional)
Example Output
:
{
"hookSpecificOutput"
: {
"decision"
: {
"behavior"
:
"allow"
,
"message"
:
"Permission granted based on security policy"
,
"interrupt"
:
false
}
}
}
Hook Configuration
Hooks are configured in Qwen Code settings, typically in
.qwen/settings.json
or user configuration files:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"^Bash$"
,
"sequential"
:
false
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"/path/to/security-check.sh"
,
"name"
:
"security-check"
,
"description"
:
"Run security checks before tool execution"
,
"timeout"
:
30000
}
]
}
],
"SessionStart"
: [
{
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'Session started'"
,
"name"
:
"session-init"
}
]
}
]
}
}
Hook Execution
Parallel vs Sequential Execution
By default, hooks execute in parallel for better performance
Use
sequential: true
in hook definition to enforce order-dependent execution
Sequential hooks can modify input for subsequent hooks in the chain
Async Hooks
Only
command
type supports asynchronous execution. Setting
"async": true
runs the hook in the background without blocking the main flow.
Features:
Cannot return decision control (operation has already occurred)
Results are injected in the next conversation turn via
systemMessage
or
additionalContext
Suitable for auditing, logging, background testing, etc.
Example:
{
"hooks"
: {
"PostToolUse"
: [
{
"matcher"
:
"WriteFile|Edit"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"$QWEN_PROJECT_DIR/.qwen/hooks/run-tests-async.sh"
,
"async"
:
true
,
"timeout"
:
300000
}
]
}
]
}
}
#!/bin/bash
INPUT
=
$(
cat
)
FILE_PATH
=
$(
echo
"
$INPUT
"
|
jq
-r
'.tool_input.file_path // empty'
)
if
[[
"
$FILE_PATH
"
!=
*
.ts
&&
"
$FILE_PATH
"
!=
*
.js ]];
then
exit
0
;
fi
RESULT
=
$(
npm
test
2>&1
)
if
[
$?
-eq
0
];
then
echo
"{
\"
systemMessage
\"
:
\"
Tests passed after editing
$FILE_PATH
\"
}"
else
echo
"{
\"
systemMessage
\"
:
\"
Tests failed:
$RESULT
\"
}"
fi
Security Model
Hooks run in the user’s environment with user privileges
Project-level hooks require trusted folder status
Timeouts prevent hanging hooks (default: 60 seconds)
Best Practices
Example 1: Security Validation Hook
A PreToolUse hook that logs and potentially blocks dangerous commands:
security_check.sh
#!/bin/bash
# Read input from stdin
INPUT
=
$(
cat
)
# Parse the input to extract tool info
TOOL_NAME
=
$(
echo
"
$INPUT
"
|
jq
-r
'.tool_name'
)
TOOL_INPUT
=
$(
echo
"
$INPUT
"
|
jq
-r
'.tool_input'
)
# Check for potentially dangerous operations
if
echo
"
$TOOL_INPUT
"
|
grep
-qiE
"(rm.*-rf|mv.*\/|chmod.*777)"
;
then
echo
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Security policy blocks dangerous command"
}
}'
exit
2
# Blocking error
fi
# Log the operation
echo
"INFO: Tool
$TOOL_NAME
executed safely at $(
date
)"
>>
/var/log/qwen-security.log
# Allow with additional context
echo
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Security check passed",
"additionalContext": "Command approved by security policy"
}
}'
exit
0
Configure in
.qwen/settings.json
:
{
"hooks"
: {
"PreToolUse"
: [
{
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"${SECURITY_CHECK_SCRIPT}"
,
"name"
:
"security-checker"
,
"description"
:
"Security validation for bash commands"
,
"timeout"
:
10000
}
]
}
]
}
}
Example 2: HTTP Audit Hook
A PostToolUse HTTP hook that sends all tool execution records to a remote audit service:
{
"hooks"
: {
"PostToolUse"
: [
{
"matcher"
:
"*"
,
"hooks"
: [
{
"type"
:
"http"
,
"url"
:
"https://audit.example.com/api/tool-execution"
,
"headers"
: {
"Authorization"
:
"Bearer ${AUDIT_API_TOKEN}"
,
"Content-Type"
:
"application/json"
},
"allowedEnvVars"
: [
"AUDIT_API_TOKEN"
],
"timeout"
:
10
,
"name"
:
"audit-logger"
}
]
}
]
}
}
Example 3: User Prompt Validation Hook
A UserPromptSubmit hook that validates user prompts for sensitive information and provides context for long prompts:
prompt_validator.py
import
json
import
sys
import
re
# Load input from stdin
try
:
input_data
=
json.load(sys.stdin)
except
json.JSONDecodeError
as
e:
print
(
f
"Error: Invalid JSON input:
{
e
}
"
,
file
=
sys.stderr)
exit
(
1
)
user_prompt
=
input_data.get(
"prompt"
,
""
)
# Sensitive words list
sensitive_words
=
[
"password"
,
"secret"
,
"token"
,
"api_key"
]
# Check for sensitive information
for
word
in
sensitive_words:
if
re.search(
rf
"\b
{
word
}
\b"
, user_prompt.lower()):
# Block prompts containing sensitive information
output
=
{
"decision"
:
"block"
,
"reason"
:
f
"Prompt contains sensitive information '
{
word
}
'. Please remove sensitive content and resubmit."
,
"hookSpecificOutput"
: {
"hookEventName"
:
"UserPromptSubmit"
}
}
print
(json.dumps(output))
exit
(
0
)
# Check prompt length and add warning context if too long
if
len
(user_prompt)
>
1000
:
output
=
{
"hookSpecificOutput"
: {
"hookEventName"
:
"UserPromptSubmit"
,
"additionalContext"
:
"Note: User submitted a long prompt. Please read carefully and ensure all requirements are understood."
}
}
print
(json.dumps(output))
exit
(
0
)
# No processing needed for normal cases
exit
(
0
)
Troubleshooting
Check application logs for hook execution details
Verify hook script permissions and executability
Ensure proper JSON formatting in hook outputs
Use specific matcher patterns to avoid unintended hook execution
Use
--debug
mode to see detailed hook matching and execution information
Temporarily disable all hooks: add
"disableAllHooks": true
in settings
Last updated on
May 18, 2026
Plugins
Status Line</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/hooks/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Hooks
Qwen Code Hooks
Overview
Qwen Code hooks provide a powerful mechanism for extending and customizing the behavior of the Qwen Code application. Hooks allow users to execute custom scripts or programs at specific points in the application lifecycle, such as before tool execution, after tool execution, at session start/end, and during other key events.
Hooks are enabled by default. You can temporarily disable all hooks by setting
disableAllHooks
to
true
in your settings fi...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Hooks
Qwen Code Hooks
Overview
Qwen Code hooks provide a powerful mechanism for extending and customizing the behavior of the Qwen Code application. Hooks allow users to execute custom scripts or programs at specific points in the application lifecycle, such as before tool execution, after tool execution, at session start/end, and during other key events.
Hooks are enabled by default. You can temporarily disable all hooks by setting
disableAllHooks
to
true
in your settings file (at the top level, alongside
hooks
):
{
"disableAllHooks"
:
true
,
"hooks"
: {
"PreToolUse"
: [
...
]
}
}
This disables all hooks without deleting their configurations.
What are Hooks?
Hooks are user-defined scripts or programs that are automatically executed by Qwen Code at predefined points in the application flow. They allow users to:
Monitor and audit tool usage
Enforce security policies
Inject additional context into conversations
Customize application behavior based on events
Integrate with external systems and services
Modify tool inputs or responses programmatically
Hook Types
Qwen Code supports three hook executor types:
Type
Description
command
Execute a shell command. Receives JSON via
stdin
, returns results via
stdout
.
http
Send JSON as a
POST
request body to a specified URL. Returns results via HTTP response body.
function
Directly call a registered JavaScript function (session-level hooks only).
Command Hooks
Command hooks execute commands via child processes. Input JSON is passed through stdin, and output is returned via stdout.
Configuration:
Field
Type
Required
Description
type
"command"
Yes
Hook type
command
string
Yes
Command to execute
name
string
No
Hook name (for logging)
description
string
No
Hook description
timeout
number
No
Timeout in milliseconds, default 60000
async
boolean
No
Whether to run asynchronously in background
env
Record<string, string>
No
Environment variables
shell
"bash" | "powershell"
No
Shell to use
statusMessage
string
No
Status message displayed during execution
Example:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"WriteFile"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"$QWEN_PROJECT_DIR/.qwen/hooks/security-check.sh"
,
"name"
:
"security-check"
,
"timeout"
:
10000
}
]
}
]
}
}
HTTP Hooks
HTTP hooks send hook input as POST requests to specified URLs. They support URL whitelists, DNS-level SSRF protection, environment variable interpolation, and other security features.
Configuration:
Field
Type
Required
Description
type
"http"
Yes
Hook type
url
string
Yes
Target URL
headers
Record<string, string>
No
Request headers (supports env var interpolation)
allowedEnvVars
string[]
No
Whitelist of environment variables allowed in URL/headers
timeout
number
No
Timeout in seconds, default 600
name
string
No
Hook name (for logging)
statusMessage
string
No
Status message displayed during execution
once
boolean
No
Execute only once per event per session (HTTP hooks only)
Security Features:
URL Whitelist
: Configure allowed URL patterns via
allowedUrls
SSRF Protection
: Blocks private IPs (10.x.x.x, 172.16-31.x.x, 192.168.x.x, etc.) but allows loopback addresses (127.0.0.1, ::1)
DNS Validation
: Validates domain resolution before requests to prevent DNS rebinding attacks
Environment Variable Interpolation
:
${VAR}
syntax, only allows variables in
allowedEnvVars
whitelist
Example:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"*"
,
"hooks"
: [
{
"type"
:
"http"
,
"url"
:
"http://127.0.0.1:8080/hooks/pre-tool-use"
,
"headers"
: {
"Authorization"
:
"Bearer ${HOOK_API_KEY}"
},
"allowedEnvVars"
: [
"HOOK_API_KEY"
],
"timeout"
:
10
,
"name"
:
"remote-security-check"
}
]
}
]
}
}
Function Hooks
Function hooks directly call registered JavaScript/TypeScript functions. They are used internally by the Skill system and are not currently exposed as a public API for end users.
Note
: For most use cases, use
command hooks
or
HTTP hooks
instead, which can be configured in settings files.
Hook Events
Hooks fire at specific points during a Qwen Code session. Different events support different matchers to filter trigger conditions.
Event
Triggered When
Matcher Target
PreToolUse
Before tool execution
Tool name (
WriteFile
,
ReadFile
,
Bash
, etc.)
PostToolUse
After successful tool execution
Tool name
PostToolUseFailure
After tool execution fails
Tool name
UserPromptSubmit
After user submits prompt
None (always fires)
SessionStart
When session starts or resumes
Source (
startup
,
resume
,
clear
,
compact
)
SessionEnd
When session ends
Reason (
clear
,
logout
,
prompt_input_exit
, etc.)
Stop
When Claude prepares to conclude response
None (always fires)
SubagentStart
When subagent starts
Agent type (
Bash
,
Explorer
,
Plan
, etc.)
SubagentStop
When subagent stops
Agent type
PreCompact
Before conversation compaction
Trigger (
manual
,
auto
)
Notification
When notifications are sent
Type (
permission_prompt
,
idle_prompt
,
auth_success
)
PermissionRequest
When permission dialog is shown
Tool name
Matcher Patterns
matcher
is a regular expression used to filter trigger conditions.
Event Type
Events
Matcher Support
Matcher Target
Tool Events
PreToolUse
,
PostToolUse
,
PostToolUseFailure
,
PermissionRequest
✅ Regex
Tool name:
WriteFile
,
ReadFile
,
Bash
, etc.
Subagent Events
SubagentStart
,
SubagentStop
✅ Regex
Agent type:
Bash
,
Explorer
, etc.
Session Events
SessionStart
✅ Regex
Source:
startup
,
resume
,
clear
,
compact
Session Events
SessionEnd
✅ Regex
Reason:
clear
,
logout
,
prompt_input_exit
, etc.
Notification Events
Notification
✅ Exact match
Type:
permission_prompt
,
idle_prompt
,
auth_success
Compact Events
PreCompact
✅ Exact match
Trigger:
manual
,
auto
Prompt Events
UserPromptSubmit
❌ No
N/A
Stop Events
Stop
❌ No
N/A
Matcher Syntax:
Empty string
""
or
"*"
matches all events of that type
Standard regex syntax supported (e.g.,
^Bash$
,
Read.*
,
(WriteFile|Edit)
)
Examples:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"^Bash$"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'bash check' >> /tmp/hooks.log"
}
]
},
{
"matcher"
:
"Write.*"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'write check' >> /tmp/hooks.log"
}
]
},
{
"matcher"
:
"*"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'all tools' >> /tmp/hooks.log"
}
]
}
],
"SubagentStart"
: [
{
"matcher"
:
"^(Bash|Explorer)$"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'subagent check' >> /tmp/hooks.log"
}
]
}
]
}
}
Input/Output Rules
Hook Input Structure
All hooks receive standardized input in JSON format through stdin (command) or POST body (http).
Common Fields:
{
"session_id"
:
"string"
,
"transcript_path"
:
"string"
,
"cwd"
:
"string"
,
"hook_event_name"
:
"string"
,
"timestamp"
:
"string"
}
Event-specific fields are added based on the hook type. When running in a subagent,
agent_id
and
agent_type
are additionally included.
Hook Output Structure
Hook output is returned via
stdout
(command) or HTTP response body (http) as JSON.
Exit Code Behavior (Command Hooks):
Exit Code
Behavior
0
Success. Parse JSON in
stdout
to control behavior.
2
Blocking error
. Ignores
stdout
, passes
stderr
as error feedback to the model.
Other
Non-blocking error.
stderr
only shown in debug mode, execution continues.
Output Structure:
Hook output supports three categories of fields:
Common Fields
:
continue
,
stopReason
,
suppressOutput
,
systemMessage
Top-level Decision
:
decision
,
reason
(used by some events)
Event-specific Control
:
hookSpecificOutput
(must include
hookEventName
)
{
"continue"
:
true
,
"decision"
:
"allow"
,
"reason"
:
"Operation approved"
,
"hookSpecificOutput"
: {
"hookEventName"
:
"PreToolUse"
,
"additionalContext"
:
"Additional context information"
}
}
Individual Hook Event Details
PreToolUse
Purpose
: Executed before a tool is used to allow for permission checks, input validation, or context injection.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_name"
:
"name of the tool being executed"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"tool_use_id"
:
"unique identifier for this tool use instance"
}
Output Options
:
hookSpecificOutput.permissionDecision
: “allow”, “deny”, or “ask” (REQUIRED)
hookSpecificOutput.permissionDecisionReason
: explanation for the decision (REQUIRED)
hookSpecificOutput.updatedInput
: modified tool input parameters to use instead of original
hookSpecificOutput.additionalContext
: additional context information
Note
: While standard hook output fields like
decision
and
reason
are technically supported by the underlying class, the official interface expects the
hookSpecificOutput
with
permissionDecision
and
permissionDecisionReason
.
Example Output
:
{
"hookSpecificOutput"
: {
"hookEventName"
:
"PreToolUse"
,
"permissionDecision"
:
"deny"
,
"permissionDecisionReason"
:
"Security policy blocks database writes"
,
"additionalContext"
:
"Current environment: production. Proceed with caution."
}
}
PostToolUse
Purpose
: Executed after a tool completes successfully to process results, log outcomes, or inject additional context.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_name"
:
"name of the tool that was executed"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"tool_response"
:
"object containing the tool's response"
,
"tool_use_id"
:
"unique identifier for this tool use instance"
}
Output Options
:
decision
: “allow”, “deny”, “block” (defaults to “allow” if not specified)
reason
: reason for the decision
hookSpecificOutput.additionalContext
: additional information to be included
Example Output
:
{
"decision"
:
"allow"
,
"reason"
:
"Tool executed successfully"
,
"hookSpecificOutput"
: {
"additionalContext"
:
"File modification recorded in audit log"
}
}
PostToolUseFailure
Purpose
: Executed when a tool execution fails to handle errors, send alerts, or record failures.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_use_id"
:
"unique identifier for the tool use"
,
"tool_name"
:
"name of the tool that failed"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"error"
:
"error message describing the failure"
,
"is_interrupt"
:
"boolean indicating if failure was due to user interruption (optional)"
}
Output Options
:
hookSpecificOutput.additionalContext
: error handling information
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Error: File not found. Failure logged in monitoring system."
}
}
UserPromptSubmit
Purpose
: Executed when the user submits a prompt to modify, validate, or enrich the input.
Event-specific fields
:
{
"prompt"
:
"the user's submitted prompt text"
}
Output Options
:
decision
: “allow”, “deny”, “block”, or “ask”
reason
: human-readable explanation for the decision
hookSpecificOutput.additionalContext
: additional context to append to the prompt (optional)
Note
: Since UserPromptSubmitOutput extends HookOutput, all standard fields are available but only additionalContext in hookSpecificOutput is specifically defined for this event.
Example Output
:
{
"decision"
:
"allow"
,
"reason"
:
"Prompt reviewed and approved"
,
"hookSpecificOutput"
: {
"additionalContext"
:
"Remember to follow company coding standards."
}
}
SessionStart
Purpose
: Executed when a new session starts to perform initialization tasks.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"source"
:
"startup | resume | clear | compact"
,
"model"
:
"the model being used"
,
"agent_type"
:
"the type of agent if applicable (optional)"
}
Output Options
:
hookSpecificOutput.additionalContext
: context to be available in the session
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Session started with security policies enabled."
}
}
SessionEnd
Purpose
: Executed when a session ends to perform cleanup tasks.
Event-specific fields
:
{
"reason"
:
"clear | logout | prompt_input_exit | bypass_permissions_disabled | other"
}
Output Options
:
Standard hook output fields (typically not used for blocking)
Stop
Purpose
: Executed before Qwen concludes its response to provide final feedback or summaries.
Event-specific fields
:
{
"stop_hook_active"
:
"boolean indicating if stop hook is active"
,
"last_assistant_message"
:
"the last message from the assistant"
}
Output Options
:
decision
: “allow”, “deny”, “block”, or “ask”
reason
: human-readable explanation for the decision
stopReason
: feedback to include in the stop response
continue
: set to false to stop execution
hookSpecificOutput.additionalContext
: additional context information
Note
: Since StopOutput extends HookOutput, all standard fields are available but the stopReason field is particularly relevant for this event.
Example Output
:
{
"decision"
:
"block"
,
"reason"
:
"Must be provided when Qwen Code is blocked from stopping"
}
StopFailure
Purpose
: Executed when the turn ends due to an API error (instead of Stop). This is a
fire-and-forget
event - hook output and exit codes are ignored.
Event-specific fields
:
{
"error"
:
"rate_limit | authentication_failed | billing_error | invalid_request | server_error | max_output_tokens | unknown"
,
"error_details"
:
"detailed error message (optional)"
,
"last_assistant_message"
:
"the last message from the assistant before the error (optional)"
}
Matcher
: Matches against the
error
field. For example,
"matcher": "rate_limit"
will only trigger for rate limit errors.
Output Options
:
None
- StopFailure is fire-and-forget. All hook output and exit codes are ignored.
Exit Code Handling
:
Exit Code
Behavior
Any
Ignored (fire-and-forget)
Example Configuration
:
{
"hooks"
: {
"StopFailure"
: [
{
"matcher"
:
"rate_limit"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"/path/to/rate-limit-alert.sh"
,
"name"
:
"rate-limit-alerter"
}
]
}
]
}
}
Use Cases
:
Rate limit monitoring and alerting
Authentication failure logging
Billing error notifications
Error statistics collection
SubagentStart
Purpose
: Executed when a subagent (like the Task tool) is started to set up context or permissions.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"agent_id"
:
"identifier for the subagent"
,
"agent_type"
:
"type of agent (Bash, Explorer, Plan, Custom, etc.)"
}
Output Options
:
hookSpecificOutput.additionalContext
: initial context for the subagent
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Subagent initialized with restricted permissions."
}
}
SubagentStop
Purpose
: Executed when a subagent finishes to perform finalization tasks.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"stop_hook_active"
:
"boolean indicating if stop hook is active"
,
"agent_id"
:
"identifier for the subagent"
,
"agent_type"
:
"type of agent"
,
"agent_transcript_path"
:
"path to the subagent's transcript"
,
"last_assistant_message"
:
"the last message from the subagent"
}
Output Options
:
decision
: “allow”, “deny”, “block”, or “ask”
reason
: human-readable explanation for the decision
Example Output
:
{
"decision"
:
"block"
,
"reason"
:
"Must be provided when Qwen Code is blocked from stopping"
}
PreCompact
Purpose
: Executed before conversation compaction to prepare or log the compaction.
Event-specific fields
:
{
"trigger"
:
"manual | auto"
,
"custom_instructions"
:
"custom instructions currently set"
}
Output Options
:
hookSpecificOutput.additionalContext
: context to include before compaction
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Compacting conversation to maintain optimal context window."
}
}
PostCompact
Purpose
: Executed after conversation compaction completes to archive summaries or track usage.
Event-specific fields
:
{
"trigger"
:
"manual | auto"
,
"compact_summary"
:
"the summary generated by the compaction process"
}
Matcher
: Matches against the
trigger
field. For example,
"matcher": "manual"
will only trigger for manual compaction via
/compact
command.
Output Options
:
hookSpecificOutput.additionalContext
: additional context (for logging only)
Standard hook output fields (for logging only)
Note
: PostCompact is
not
in the official decision mode supported events list. The
decision
field and other control fields do not produce any control effects - they are only used for logging purposes.
Exit Code Handling
:
Exit Code
Behavior
0
Success - stdout shown to user in verbose mode
Other
Non-blocking error - stderr shown to user in verbose mode
Example Configuration
:
{
"hooks"
: {
"PostCompact"
: [
{
"matcher"
:
"manual"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"/path/to/save-compact-summary.sh"
,
"name"
:
"save-summary"
}
]
}
]
}
}
Use Cases
:
Summary archiving to files or databases
Usage statistics tracking
Context change monitoring
Audit logging for compaction operations
Notification
Purpose
: Executed when notifications are sent to customize or intercept them.
Event-specific fields
:
{
"message"
:
"notification message content"
,
"title"
:
"notification title (optional)"
,
"notification_type"
:
"permission_prompt | idle_prompt | auth_success"
}
Note
:
elicitation_dialog
type is defined but not currently implemented.
Output Options
:
hookSpecificOutput.additionalContext
: additional information to include
Standard hook output fields
Example Output
:
{
"hookSpecificOutput"
: {
"additionalContext"
:
"Notification processed by monitoring system."
}
}
PermissionRequest
Purpose
: Executed when permission dialogs are displayed to automate decisions or update permissions.
Event-specific fields
:
{
"permission_mode"
:
"default | plan | auto_edit | yolo"
,
"tool_name"
:
"name of the tool requesting permission"
,
"tool_input"
:
"object containing the tool's input parameters"
,
"permission_suggestions"
:
"array of suggested permissions (optional)"
}
Output Options
:
hookSpecificOutput.decision
: structured object with permission decision details:
behavior
: “allow” or “deny”
updatedInput
: modified tool input (optional)
updatedPermissions
: modified permissions (optional)
message
: message to show to user (optional)
interrupt
: whether to interrupt the workflow (optional)
Example Output
:
{
"hookSpecificOutput"
: {
"decision"
: {
"behavior"
:
"allow"
,
"message"
:
"Permission granted based on security policy"
,
"interrupt"
:
false
}
}
}
Hook Configuration
Hooks are configured in Qwen Code settings, typically in
.qwen/settings.json
or user configuration files:
{
"hooks"
: {
"PreToolUse"
: [
{
"matcher"
:
"^Bash$"
,
"sequential"
:
false
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"/path/to/security-check.sh"
,
"name"
:
"security-check"
,
"description"
:
"Run security checks before tool execution"
,
"timeout"
:
30000
}
]
}
],
"SessionStart"
: [
{
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"echo 'Session started'"
,
"name"
:
"session-init"
}
]
}
]
}
}
Hook Execution
Parallel vs Sequential Execution
By default, hooks execute in parallel for better performance
Use
sequential: true
in hook definition to enforce order-dependent execution
Sequential hooks can modify input for subsequent hooks in the chain
Async Hooks
Only
command
type supports asynchronous execution. Setting
"async": true
runs the hook in the background without blocking the main flow.
Features:
Cannot return decision control (operation has already occurred)
Results are injected in the next conversation turn via
systemMessage
or
additionalContext
Suitable for auditing, logging, background testing, etc.
Example:
{
"hooks"
: {
"PostToolUse"
: [
{
"matcher"
:
"WriteFile|Edit"
,
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"$QWEN_PROJECT_DIR/.qwen/hooks/run-tests-async.sh"
,
"async"
:
true
,
"timeout"
:
300000
}
]
}
]
}
}
#!/bin/bash
INPUT
=
$(
cat
)
FILE_PATH
=
$(
echo
"
$INPUT
"
|
jq
-r
'.tool_input.file_path // empty'
)
if
[[
"
$FILE_PATH
"
!=
*
.ts
&&
"
$FILE_PATH
"
!=
*
.js ]];
then
exit
0
;
fi
RESULT
=
$(
npm
test
2>&1
)
if
[
$?
-eq
0
];
then
echo
"{
\"
systemMessage
\"
:
\"
Tests passed after editing
$FILE_PATH
\"
}"
else
echo
"{
\"
systemMessage
\"
:
\"
Tests failed:
$RESULT
\"
}"
fi
Security Model
Hooks run in the user’s environment with user privileges
Project-level hooks require trusted folder status
Timeouts prevent hanging hooks (default: 60 seconds)
Best Practices
Example 1: Security Validation Hook
A PreToolUse hook that logs and potentially blocks dangerous commands:
security_check.sh
#!/bin/bash
# Read input from stdin
INPUT
=
$(
cat
)
# Parse the input to extract tool info
TOOL_NAME
=
$(
echo
"
$INPUT
"
|
jq
-r
'.tool_name'
)
TOOL_INPUT
=
$(
echo
"
$INPUT
"
|
jq
-r
'.tool_input'
)
# Check for potentially dangerous operations
if
echo
"
$TOOL_INPUT
"
|
grep
-qiE
"(rm.*-rf|mv.*\/|chmod.*777)"
;
then
echo
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Security policy blocks dangerous command"
}
}'
exit
2
# Blocking error
fi
# Log the operation
echo
"INFO: Tool
$TOOL_NAME
executed safely at $(
date
)"
>>
/var/log/qwen-security.log
# Allow with additional context
echo
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Security check passed",
"additionalContext": "Command approved by security policy"
}
}'
exit
0
Configure in
.qwen/settings.json
:
{
"hooks"
: {
"PreToolUse"
: [
{
"hooks"
: [
{
"type"
:
"command"
,
"command"
:
"${SECURITY_CHECK_SCRIPT}"
,
"name"
:
"security-checker"
,
"description"
:
"Security validation for bash commands"
,
"timeout"
:
10000
}
]
}
]
}
}
Example 2: HTTP Audit Hook
A PostToolUse HTTP hook that sends all tool execution records to a remote audit service:
{
"hooks"
: {
"PostToolUse"
: [
{
"matcher"
:
"*"
,
"hooks"
: [
{
"type"
:
"http"
,
"url"
:
"https://audit.example.com/api/tool-execution"
,
"headers"
: {
"Authorization"
:
"Bearer ${AUDIT_API_TOKEN}"
,
"Content-Type"
:
"application/json"
},
"allowedEnvVars"
: [
"AUDIT_API_TOKEN"
],
"timeout"
:
10
,
"name"
:
"audit-logger"
}
]
}
]
}
}
Example 3: User Prompt Validation Hook
A UserPromptSubmit hook that validates user prompts for sensitive information and provides context for long prompts:
prompt_validator.py
import
json
import
sys
import
re
# Load input from stdin
try
:
input_data
=
json.load(sys.stdin)
except
json.JSONDecodeError
as
e:
print
(
f
"Error: Invalid JSON input:
{
e
}
"
,
file
=
sys.stderr)
exit
(
1
)
user_prompt
=
input_data.get(
"prompt"
,
""
)
# Sensitive words list
sensitive_words
=
[
"password"
,
"secret"
,
"token"
,
"api_key"
]
# Check for sensitive information
for
word
in
sensitive_words:
if
re.search(
rf
"\b
{
word
}
\b"
, user_prompt.lower()):
# Block prompts containing sensitive information
output
=
{
"decision"
:
"block"
,
"reason"
:
f
"Prompt contains sensitive information '
{
word
}
'. Please remove sensitive content and resubmit."
,
"hookSpecificOutput"
: {
"hookEventName"
:
"UserPromptSubmit"
}
}
print
(json.dumps(output))
exit
(
0
)
# Check prompt length and add warning context if too long
if
len
(user_prompt)
>
1000
:
output
=
{
"hookSpecificOutput"
: {
"hookEventName"
:
"UserPromptSubmit"
,
"additionalContext"
:
"Note: User submitted a long prompt. Please read carefully and ensure all requirements are understood."
}
}
print
(json.dumps(output))
exit
(
0
)
# No processing needed for normal cases
exit
(
0
)
Troubleshooting
Check application logs for hook execution details
Verify hook script permissions and executability
Ensure proper JSON formatting in hook outputs
Use specific matcher patterns to avoid unintended hook execution
Use
--debug
mode to see detailed hook matching and execution information
Temporarily disable all hooks: add
"disableAllHooks": true
in settings
Last updated on
May 18, 2026
Plugins
Status Line</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/hooks/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Speculation Engine Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/speculation-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/speculation-design/</guid>
  <pubDate>Sun, 10 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Prompt Suggestion
Speculation Engine Design
Speculation Engine Design
Speculatively executes the accepted suggestion before the user confirms, using copy-on-write file isolation. Results appear instantly when the user presses Tab.
Overview
When a prompt suggestion is shown, the
speculation engine
immediately starts executing it in the background using a forked GeminiChat. File writes go to a temporary overlay directory. If the user accepts the suggestion, overlay files are copied to the r...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Prompt Suggestion
Speculation Engine Design
Speculation Engine Design
Speculatively executes the accepted suggestion before the user confirms, using copy-on-write file isolation. Results appear instantly when the user presses Tab.
Overview
When a prompt suggestion is shown, the
speculation engine
immediately starts executing it in the background using a forked GeminiChat. File writes go to a temporary overlay directory. If the user accepts the suggestion, overlay files are copied to the real filesystem and the speculated conversation is injected into the main chat history. If the user types something else, the speculation is aborted and the overlay is cleaned up.
Architecture
User sees suggestion "commit this"
│
▼
┌──────────────────────────────────────────────────────────────┐
│  startSpeculation()                                          │
│                                                              │
│  ┌─────────────────┐    ┌────────────────────┐               │
│  │ Forked GeminiChat│    │  OverlayFs          │              │
│  │ (cache-shared)   │    │  /tmp/qwen-         │              │
│  │                  │    │   speculation/       │              │
│  │  systemInstruction│   │   {pid}/{id}/        │              │
│  │  + tools          │   │                      │              │
│  │  + history prefix │   │  COW: first write    │              │
│  │                  │    │  copies original     │              │
│  └────────┬─────────┘    └──────────┬───────────┘             │
│           │                         │                         │
│           ▼                         │                         │
│  ┌──────────────────────────────────┴──────────────────────┐  │
│  │  Speculative Loop (max 20 turns, 100 messages)          │  │
│  │                                                         │  │
│  │  Model response                                         │  │
│  │       │                                                 │  │
│  │       ▼                                                 │  │
│  │  ┌──────────────────────────────────────────────────┐   │  │
│  │  │  speculationToolGate                             │   │  │
│  │  │                                                  │   │  │
│  │  │  Read/Grep/Glob/LS/LSP → allow (+ overlay read) │   │  │
│  │  │  Edit/WriteFile → redirect to overlay            │   │  │
│  │  │    (only in auto-edit/yolo mode)                 │   │  │
│  │  │  Shell → AST check read-only? allow : boundary   │   │  │
│  │  │  WebFetch/WebSearch → boundary                   │   │  │
│  │  │  Agent/Skill/Memory/Ask → boundary               │   │  │
│  │  │  Unknown/MCP → boundary                          │   │  │
│  │  └──────────────────────────────────────────────────┘   │  │
│  │       │                                                 │  │
│  │       ▼                                                 │  │
│  │  Tool execution: toolRegistry.getTool → build → execute │  │
│  │  (bypasses CoreToolScheduler — gated by toolGate)       │  │
│  │                                                         │  │
│  └─────────────────────────────────────────────────────────┘  │
│                                                              │
│  On completion → generatePipelinedSuggestion()               │
└──────────────────────────────────────────────────────────────┘
│
│  User presses Tab / Enter
▼
┌─── status === 'completed'? ───┐
│ YES                      NO (boundary) │
▼                                ▼
┌─────────────────────────┐  ┌────────────────────────┐
│  acceptSpeculation()    │  │  Discard speculation    │
│                         │  │  abort + cleanup        │
│  1. applyToReal()       │  │  Submit query normally  │
│  2. ensureToolPairing() │  │  (addMessage)           │
│  3. addHistory()        │  └────────────────────────┘
│  4. render tool_group   │
│  5. cleanup overlay     │
│  6. pipelined suggest   │
└─────────────────────────┘
│
│  User types instead
▼
┌──────────────────────────────────────────────────────────────┐
│  abortSpeculation()                                          │
│                                                              │
│  1. abortController.abort() — cancel LLM call               │
│  2. overlayFs.cleanup() — delete temp directory              │
│  3. Update speculation state (no telemetry on abort)         │
└──────────────────────────────────────────────────────────────┘
Copy-on-Write Overlay
Real CWD: /home/user/project/
Overlay:  /tmp/qwen-speculation/12345/a1b2c3d4/
Write to src/app.ts:
1. Copy /home/user/project/src/app.ts → overlay/src/app.ts (first time only)
2. Tool writes to overlay/src/app.ts
Read from src/app.ts:
- If in writtenFiles → read from overlay/src/app.ts
- Otherwise → read from /home/user/project/src/app.ts
New file (src/new.ts):
- Create overlay/src/new.ts directly (no original to copy)
Accept:
- copyFile(overlay/src/app.ts → /home/user/project/src/app.ts)
- copyFile(overlay/src/new.ts → /home/user/project/src/new.ts)
- rm -rf overlay/
Abort:
- rm -rf overlay/
Tool Gate Security
Tool
Action
Condition
read_file, grep, glob, ls, lsp
allow
Read paths resolved through overlay
edit, write_file
redirect
Only in auto-edit / yolo approval mode
edit, write_file
boundary
In default / plan approval mode
shell
allow
isShellCommandReadOnlyAST()
returns true
shell
boundary
Non-read-only commands
web_fetch, web_search
boundary
Network requests require user consent
agent, skill, memory, ask_user, todo_write, exit_plan_mode
boundary
Cannot interact with user during speculation
Unknown / MCP tools
boundary
Safe default
Path Rewrite
Write tools
:
rewritePathArgs()
redirects
file_path
to overlay via
overlayFs.redirectWrite()
Read tools
:
resolveReadPaths()
redirects
file_path
to overlay via
overlayFs.resolveReadPath()
if previously written
Rewrite failure
: Treated as boundary (e.g., absolute path outside cwd throws in
redirectWrite
)
Boundary Handling
When a boundary is hit mid-turn:
Already-executed tool calls are preserved (index-based tracking, not name-based)
Unexecuted function calls are stripped from the model message
Partial tool responses are added to history
ensureToolResultPairing()
validates completeness before injection
Pipelined Suggestion
After speculation completes (no boundary), a second LLM call generates the
next
suggestion:
Context: original conversation + "commit this" + speculated messages
→ LLM predicts: "push it"
→ Stored in state.pipelinedSuggestion
→ On accept: setPromptSuggestion("push it") — appears instantly
This enables Tab-Tab-Tab workflows where each acceptance immediately shows the next step.
The pipelined suggestion reuses the exported
SUGGESTION_PROMPT
constant from
suggestionGenerator.ts
(not a local copy) to ensure consistent quality with initial suggestions.
Fast Model
startSpeculation
accepts an optional
options.model
parameter, threaded through
runSpeculativeLoop
and
generatePipelinedSuggestion
to
runForkedQuery
. Configured via the top-level
fastModel
setting (empty = use main model). The same
fastModel
is used for all background tasks: suggestion generation, speculation, and pipelined suggestions. Set via
/model --fast <name>
or
settings.json
.
UI Rendering
When speculation completes,
acceptSpeculation
renders results via
historyManager.addItem()
:
User messages
: rendered as
type: 'user'
items
Model text
: rendered as
type: 'gemini'
items
Tool calls
: rendered as
type: 'tool_group'
items with structured
IndividualToolCallDisplay
entries (tool name, argument description, result text, status)
This shows the user the full speculation output including tool call details, not just plain text.
Forked Query (Cache Sharing)
CacheSafeParams
interface
CacheSafeParams
{
generationConfig
:
GenerateContentConfig
;
// systemInstruction + tools
history
:
Content
[];
// curated, max 40 entries
model
:
string
;
version
:
number
;
// increments on config changes
}
Saved after each successful main turn in
GeminiClient.sendMessageStream()
Cleared on
startChat()
/
resetChat()
to prevent cross-session leakage
History truncated to 40 entries;
createForkedChat
uses shallow copies (params are already deep-cloned snapshots)
Thinking mode explicitly disabled (
thinkingConfig: { includeThoughts: false }
) — reasoning tokens are not needed for speculation and would waste cost/latency. This does not affect cache prefix matching (determined by systemInstruction + tools + history only)
Version detection via
JSON.stringify
comparison of systemInstruction + tools
Cache Mechanism
DashScope already enables prefix caching via:
X-DashScope-CacheControl: enable
header
cache_control: { type: 'ephemeral' }
annotations on messages and tools
The forked
GeminiChat
uses identical
generationConfig
(including tools) and history prefix, so DashScope’s existing cache mechanism produces cache hits automatically.
Constants
Constant
Value
Description
MAX_SPECULATION_TURNS
20
Maximum API round-trips
MAX_SPECULATION_MESSAGES
100
Maximum messages in speculated history
SUGGESTION_DELAY_MS
300
Delay before showing suggestion
ACCEPT_DEBOUNCE_MS
100
Debounce lock for rapid accepts
MAX_HISTORY_FOR_CACHE
40
History entries saved in CacheSafeParams
File Structure
packages/core/src/followup/
├── followupState.ts          # Framework-agnostic state controller
├── suggestionGenerator.ts    # LLM-based suggestion generation + 12 filter rules
├── forkedQuery.ts            # Cache-aware forked query infrastructure
├── overlayFs.ts              # Copy-on-write overlay filesystem
├── speculationToolGate.ts    # Tool boundary enforcement
├── speculation.ts            # Speculation engine (start/accept/abort)
└── index.ts                  # Module exports
Last updated on
May 18, 2026
Prompt Suggestion Implementation Status
Session Recap Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/speculation-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Prompt Suggestion
Speculation Engine Design
Speculation Engine Design
Speculatively executes the accepted suggestion before the user confirms, using copy-on-write file isolation. Results appear instantly when the user presses Tab.
Overview
When a prompt suggestion is shown, the
speculation engine
immediately starts executing it in the background using a forked GeminiChat. File writes go to a temporary overlay directory. If the user accepts the suggestion, overlay files are copied to the r...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Prompt Suggestion
Speculation Engine Design
Speculation Engine Design
Speculatively executes the accepted suggestion before the user confirms, using copy-on-write file isolation. Results appear instantly when the user presses Tab.
Overview
When a prompt suggestion is shown, the
speculation engine
immediately starts executing it in the background using a forked GeminiChat. File writes go to a temporary overlay directory. If the user accepts the suggestion, overlay files are copied to the real filesystem and the speculated conversation is injected into the main chat history. If the user types something else, the speculation is aborted and the overlay is cleaned up.
Architecture
User sees suggestion "commit this"
│
▼
┌──────────────────────────────────────────────────────────────┐
│  startSpeculation()                                          │
│                                                              │
│  ┌─────────────────┐    ┌────────────────────┐               │
│  │ Forked GeminiChat│    │  OverlayFs          │              │
│  │ (cache-shared)   │    │  /tmp/qwen-         │              │
│  │                  │    │   speculation/       │              │
│  │  systemInstruction│   │   {pid}/{id}/        │              │
│  │  + tools          │   │                      │              │
│  │  + history prefix │   │  COW: first write    │              │
│  │                  │    │  copies original     │              │
│  └────────┬─────────┘    └──────────┬───────────┘             │
│           │                         │                         │
│           ▼                         │                         │
│  ┌──────────────────────────────────┴──────────────────────┐  │
│  │  Speculative Loop (max 20 turns, 100 messages)          │  │
│  │                                                         │  │
│  │  Model response                                         │  │
│  │       │                                                 │  │
│  │       ▼                                                 │  │
│  │  ┌──────────────────────────────────────────────────┐   │  │
│  │  │  speculationToolGate                             │   │  │
│  │  │                                                  │   │  │
│  │  │  Read/Grep/Glob/LS/LSP → allow (+ overlay read) │   │  │
│  │  │  Edit/WriteFile → redirect to overlay            │   │  │
│  │  │    (only in auto-edit/yolo mode)                 │   │  │
│  │  │  Shell → AST check read-only? allow : boundary   │   │  │
│  │  │  WebFetch/WebSearch → boundary                   │   │  │
│  │  │  Agent/Skill/Memory/Ask → boundary               │   │  │
│  │  │  Unknown/MCP → boundary                          │   │  │
│  │  └──────────────────────────────────────────────────┘   │  │
│  │       │                                                 │  │
│  │       ▼                                                 │  │
│  │  Tool execution: toolRegistry.getTool → build → execute │  │
│  │  (bypasses CoreToolScheduler — gated by toolGate)       │  │
│  │                                                         │  │
│  └─────────────────────────────────────────────────────────┘  │
│                                                              │
│  On completion → generatePipelinedSuggestion()               │
└──────────────────────────────────────────────────────────────┘
│
│  User presses Tab / Enter
▼
┌─── status === 'completed'? ───┐
│ YES                      NO (boundary) │
▼                                ▼
┌─────────────────────────┐  ┌────────────────────────┐
│  acceptSpeculation()    │  │  Discard speculation    │
│                         │  │  abort + cleanup        │
│  1. applyToReal()       │  │  Submit query normally  │
│  2. ensureToolPairing() │  │  (addMessage)           │
│  3. addHistory()        │  └────────────────────────┘
│  4. render tool_group   │
│  5. cleanup overlay     │
│  6. pipelined suggest   │
└─────────────────────────┘
│
│  User types instead
▼
┌──────────────────────────────────────────────────────────────┐
│  abortSpeculation()                                          │
│                                                              │
│  1. abortController.abort() — cancel LLM call               │
│  2. overlayFs.cleanup() — delete temp directory              │
│  3. Update speculation state (no telemetry on abort)         │
└──────────────────────────────────────────────────────────────┘
Copy-on-Write Overlay
Real CWD: /home/user/project/
Overlay:  /tmp/qwen-speculation/12345/a1b2c3d4/
Write to src/app.ts:
1. Copy /home/user/project/src/app.ts → overlay/src/app.ts (first time only)
2. Tool writes to overlay/src/app.ts
Read from src/app.ts:
- If in writtenFiles → read from overlay/src/app.ts
- Otherwise → read from /home/user/project/src/app.ts
New file (src/new.ts):
- Create overlay/src/new.ts directly (no original to copy)
Accept:
- copyFile(overlay/src/app.ts → /home/user/project/src/app.ts)
- copyFile(overlay/src/new.ts → /home/user/project/src/new.ts)
- rm -rf overlay/
Abort:
- rm -rf overlay/
Tool Gate Security
Tool
Action
Condition
read_file, grep, glob, ls, lsp
allow
Read paths resolved through overlay
edit, write_file
redirect
Only in auto-edit / yolo approval mode
edit, write_file
boundary
In default / plan approval mode
shell
allow
isShellCommandReadOnlyAST()
returns true
shell
boundary
Non-read-only commands
web_fetch, web_search
boundary
Network requests require user consent
agent, skill, memory, ask_user, todo_write, exit_plan_mode
boundary
Cannot interact with user during speculation
Unknown / MCP tools
boundary
Safe default
Path Rewrite
Write tools
:
rewritePathArgs()
redirects
file_path
to overlay via
overlayFs.redirectWrite()
Read tools
:
resolveReadPaths()
redirects
file_path
to overlay via
overlayFs.resolveReadPath()
if previously written
Rewrite failure
: Treated as boundary (e.g., absolute path outside cwd throws in
redirectWrite
)
Boundary Handling
When a boundary is hit mid-turn:
Already-executed tool calls are preserved (index-based tracking, not name-based)
Unexecuted function calls are stripped from the model message
Partial tool responses are added to history
ensureToolResultPairing()
validates completeness before injection
Pipelined Suggestion
After speculation completes (no boundary), a second LLM call generates the
next
suggestion:
Context: original conversation + "commit this" + speculated messages
→ LLM predicts: "push it"
→ Stored in state.pipelinedSuggestion
→ On accept: setPromptSuggestion("push it") — appears instantly
This enables Tab-Tab-Tab workflows where each acceptance immediately shows the next step.
The pipelined suggestion reuses the exported
SUGGESTION_PROMPT
constant from
suggestionGenerator.ts
(not a local copy) to ensure consistent quality with initial suggestions.
Fast Model
startSpeculation
accepts an optional
options.model
parameter, threaded through
runSpeculativeLoop
and
generatePipelinedSuggestion
to
runForkedQuery
. Configured via the top-level
fastModel
setting (empty = use main model). The same
fastModel
is used for all background tasks: suggestion generation, speculation, and pipelined suggestions. Set via
/model --fast <name>
or
settings.json
.
UI Rendering
When speculation completes,
acceptSpeculation
renders results via
historyManager.addItem()
:
User messages
: rendered as
type: 'user'
items
Model text
: rendered as
type: 'gemini'
items
Tool calls
: rendered as
type: 'tool_group'
items with structured
IndividualToolCallDisplay
entries (tool name, argument description, result text, status)
This shows the user the full speculation output including tool call details, not just plain text.
Forked Query (Cache Sharing)
CacheSafeParams
interface
CacheSafeParams
{
generationConfig
:
GenerateContentConfig
;
// systemInstruction + tools
history
:
Content
[];
// curated, max 40 entries
model
:
string
;
version
:
number
;
// increments on config changes
}
Saved after each successful main turn in
GeminiClient.sendMessageStream()
Cleared on
startChat()
/
resetChat()
to prevent cross-session leakage
History truncated to 40 entries;
createForkedChat
uses shallow copies (params are already deep-cloned snapshots)
Thinking mode explicitly disabled (
thinkingConfig: { includeThoughts: false }
) — reasoning tokens are not needed for speculation and would waste cost/latency. This does not affect cache prefix matching (determined by systemInstruction + tools + history only)
Version detection via
JSON.stringify
comparison of systemInstruction + tools
Cache Mechanism
DashScope already enables prefix caching via:
X-DashScope-CacheControl: enable
header
cache_control: { type: 'ephemeral' }
annotations on messages and tools
The forked
GeminiChat
uses identical
generationConfig
(including tools) and history prefix, so DashScope’s existing cache mechanism produces cache hits automatically.
Constants
Constant
Value
Description
MAX_SPECULATION_TURNS
20
Maximum API round-trips
MAX_SPECULATION_MESSAGES
100
Maximum messages in speculated history
SUGGESTION_DELAY_MS
300
Delay before showing suggestion
ACCEPT_DEBOUNCE_MS
100
Debounce lock for rapid accepts
MAX_HISTORY_FOR_CACHE
40
History entries saved in CacheSafeParams
File Structure
packages/core/src/followup/
├── followupState.ts          # Framework-agnostic state controller
├── suggestionGenerator.ts    # LLM-based suggestion generation + 12 filter rules
├── forkedQuery.ts            # Cache-aware forked query infrastructure
├── overlayFs.ts              # Copy-on-write overlay filesystem
├── speculationToolGate.ts    # Tool boundary enforcement
├── speculation.ts            # Speculation engine (start/accept/abort)
└── index.ts                  # Module exports
Last updated on
May 18, 2026
Prompt Suggestion Implementation Status
Session Recap Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/speculation-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Example Proxy Script</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/examples/proxy-script/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/examples/proxy-script/</guid>
  <pubDate>Thu, 07 Nov 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Example Proxy Script
The following is an example of a proxy script that can be used with the
QWEN_SANDBOX_PROXY_COMMAND
environment variable. This script only allows
HTTPS
connections to
example.com:443
and declines all other requests.
#!/usr/bin/env node
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
// Example proxy server that listens on :::8877 and only allows HTTPS connections to example.com.
// Set `QWEN_SANDBOX_PROXY_COMMAND=scripts/exa...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Example Proxy Script
The following is an example of a proxy script that can be used with the
QWEN_SANDBOX_PROXY_COMMAND
environment variable. This script only allows
HTTPS
connections to
example.com:443
and declines all other requests.
#!/usr/bin/env node
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
// Example proxy server that listens on :::8877 and only allows HTTPS connections to example.com.
// Set `QWEN_SANDBOX_PROXY_COMMAND=scripts/example-proxy.js` to run proxy alongside sandbox
// Test via `curl https://example.com` inside sandbox (in shell mode or via shell tool)
import
http
from
'node:http'
;
import
net
from
'node:net'
;
import
{ URL }
from
'node:url'
;
import
console
from
'node:console'
;
const
PROXY_PORT
=
8877
;
const
ALLOWED_DOMAINS
=
[
'example.com'
,
'googleapis.com'
];
const
ALLOWED_PORT
=
'443'
;
const
server
=
http.
createServer
((
req
,
res
)
=>
{
// Deny all requests other than CONNECT for HTTPS
console.
log
(
`[PROXY] Denying non-CONNECT request for: ${
req
.
method
} ${
req
.
url
}`
,
);
res.
writeHead
(
405
, {
'Content-Type'
:
'text/plain'
});
res.
end
(
'Method Not Allowed'
);
});
server.
on
(
'connect'
, (
req
,
clientSocket
,
head
)
=>
{
// req.url will be in the format "hostname:port" for a CONNECT request.
const
{
port
,
hostname
}
=
new
URL
(
`http://${
req
.
url
}`
);
console.
log
(
`[PROXY] Intercepted CONNECT request for: ${
hostname
}:${
port
}`
);
if
(
ALLOWED_DOMAINS
.
some
(
(
domain
)
=>
hostname
==
domain
||
hostname.
endsWith
(
`.${
domain
}`
),
)
&&
port
===
ALLOWED_PORT
) {
console.
log
(
`[PROXY] Allowing connection to ${
hostname
}:${
port
}`
);
// Establish a TCP connection to the original destination.
const
serverSocket
=
net.
connect
(port, hostname, ()
=>
{
clientSocket.
write
(
'HTTP/1.1 200 Connection Established
\r\n\r\n
'
);
// Create a tunnel by piping data between the client and the destination server.
serverSocket.
write
(head);
serverSocket.
pipe
(clientSocket);
clientSocket.
pipe
(serverSocket);
});
serverSocket.
on
(
'error'
, (
err
)
=>
{
console.
error
(
`[PROXY] Error connecting to destination: ${
err
.
message
}`
);
clientSocket.
end
(
`HTTP/1.1 502 Bad Gateway
\r\n\r\n
`
);
});
}
else
{
console.
log
(
`[PROXY] Denying connection to ${
hostname
}:${
port
}`
);
clientSocket.
end
(
'HTTP/1.1 403 Forbidden
\r\n\r\n
'
);
}
clientSocket.
on
(
'error'
, (
err
)
=>
{
// This can happen if the client hangs up.
console.
error
(
`[PROXY] Client socket error: ${
err
.
message
}`
);
});
});
server.
listen
(
PROXY_PORT
, ()
=>
{
const
address
=
server.
address
();
console.
log
(
`[PROXY] Proxy listening on ${
address
.
address
}:${
address
.
port
}`
);
console.
log
(
`[PROXY] Allowing HTTPS connections to domains: ${
ALLOWED_DOMAINS
.
join
(
', '
)
}`
,
);
});
Last updated on
May 18, 2026
Roadmap</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/examples/proxy-script/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Example Proxy Script
The following is an example of a proxy script that can be used with the
QWEN_SANDBOX_PROXY_COMMAND
environment variable. This script only allows
HTTPS
connections to
example.com:443
and declines all other requests.
#!/usr/bin/env node
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
// Example proxy server that listens on :::8877 and only allows HTTPS connections to example.com.
// Set `QWEN_SANDBOX_PROXY_COMMAND=scripts/exa...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Example Proxy Script
The following is an example of a proxy script that can be used with the
QWEN_SANDBOX_PROXY_COMMAND
environment variable. This script only allows
HTTPS
connections to
example.com:443
and declines all other requests.
#!/usr/bin/env node
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
// Example proxy server that listens on :::8877 and only allows HTTPS connections to example.com.
// Set `QWEN_SANDBOX_PROXY_COMMAND=scripts/example-proxy.js` to run proxy alongside sandbox
// Test via `curl https://example.com` inside sandbox (in shell mode or via shell tool)
import
http
from
'node:http'
;
import
net
from
'node:net'
;
import
{ URL }
from
'node:url'
;
import
console
from
'node:console'
;
const
PROXY_PORT
=
8877
;
const
ALLOWED_DOMAINS
=
[
'example.com'
,
'googleapis.com'
];
const
ALLOWED_PORT
=
'443'
;
const
server
=
http.
createServer
((
req
,
res
)
=>
{
// Deny all requests other than CONNECT for HTTPS
console.
log
(
`[PROXY] Denying non-CONNECT request for: ${
req
.
method
} ${
req
.
url
}`
,
);
res.
writeHead
(
405
, {
'Content-Type'
:
'text/plain'
});
res.
end
(
'Method Not Allowed'
);
});
server.
on
(
'connect'
, (
req
,
clientSocket
,
head
)
=>
{
// req.url will be in the format "hostname:port" for a CONNECT request.
const
{
port
,
hostname
}
=
new
URL
(
`http://${
req
.
url
}`
);
console.
log
(
`[PROXY] Intercepted CONNECT request for: ${
hostname
}:${
port
}`
);
if
(
ALLOWED_DOMAINS
.
some
(
(
domain
)
=>
hostname
==
domain
||
hostname.
endsWith
(
`.${
domain
}`
),
)
&&
port
===
ALLOWED_PORT
) {
console.
log
(
`[PROXY] Allowing connection to ${
hostname
}:${
port
}`
);
// Establish a TCP connection to the original destination.
const
serverSocket
=
net.
connect
(port, hostname, ()
=>
{
clientSocket.
write
(
'HTTP/1.1 200 Connection Established
\r\n\r\n
'
);
// Create a tunnel by piping data between the client and the destination server.
serverSocket.
write
(head);
serverSocket.
pipe
(clientSocket);
clientSocket.
pipe
(serverSocket);
});
serverSocket.
on
(
'error'
, (
err
)
=>
{
console.
error
(
`[PROXY] Error connecting to destination: ${
err
.
message
}`
);
clientSocket.
end
(
`HTTP/1.1 502 Bad Gateway
\r\n\r\n
`
);
});
}
else
{
console.
log
(
`[PROXY] Denying connection to ${
hostname
}:${
port
}`
);
clientSocket.
end
(
'HTTP/1.1 403 Forbidden
\r\n\r\n
'
);
}
clientSocket.
on
(
'error'
, (
err
)
=>
{
// This can happen if the client hangs up.
console.
error
(
`[PROXY] Client socket error: ${
err
.
message
}`
);
});
});
server.
listen
(
PROXY_PORT
, ()
=>
{
const
address
=
server.
address
();
console.
log
(
`[PROXY] Proxy listening on ${
address
.
address
}:${
address
.
port
}`
);
console.
log
(
`[PROXY] Allowing HTTPS connections to domains: ${
ALLOWED_DOMAINS
.
join
(
', '
)
}`
,
);
});
Last updated on
May 18, 2026
Roadmap</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/examples/proxy-script/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Memory | Qwen Code Docs</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/configuration/memory/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/configuration/memory/</guid>
  <pubDate>Wed, 06 Nov 2024 00:00:00 +0000</pubDate>
  <category>Configuration</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Memory
Last updated on
May 18, 2026
Authentication
Trusted Folders</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Memory
Last updated on
May 18, 2026
Authentication
Trusted Folders</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/memory/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Memory
Last updated on
May 18, 2026
Authentication
Trusted Folders</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Memory
Last updated on
May 18, 2026
Authentication
Trusted Folders</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/memory/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Tool-Use Summary Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/tool-use-summary/tool-use-summary-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/tool-use-summary/tool-use-summary-design/</guid>
  <pubDate>Sun, 27 Oct 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Tool Use Summary
Tool-Use Summary Design
Tool-Use Summary Design
Fast-model labels for parallel tool batches — motivation, competitive analysis with Claude Code, architecture, and the append-only-Static rationale that drove the current full-mode render.
User documentation:
Tool-Use Summaries
.
1. Executive Summary
After each tool batch completes, Qwen Code fires a short fast-model call that returns a git-commit-subject-style label summarizing the batch. The label shows as an inline dim
● ...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Tool Use Summary
Tool-Use Summary Design
Tool-Use Summary Design
Fast-model labels for parallel tool batches — motivation, competitive analysis with Claude Code, architecture, and the append-only-Static rationale that drove the current full-mode render.
User documentation:
Tool-Use Summaries
.
1. Executive Summary
After each tool batch completes, Qwen Code fires a short fast-model call that returns a git-commit-subject-style label summarizing the batch. The label shows as an inline dim
● <label>
line in full mode and replaces the generic
Tool × N
header in compact mode. Generation runs fire-and-forget in parallel with the next turn’s API stream, so its ~1s latency is hidden behind main-model streaming.
Dimension
Claude Code
Qwen Code
Trigger point
query.ts
— after a tool batch finalizes
useGeminiStream.ts
→
handleCompletedTools
— same lifecycle point
Generation model
Haiku via
queryHaiku
Configured
fastModel
via
GeminiClient.generateContent
Subagent behavior
!toolUseContext.agentId
— main session only
Implicit — subagents run through
agents/runtime/
, not
useGeminiStream
Scheduling
Fire-and-forget, awaited right before the next turn’s stream emits
Fire-and-forget, appended to history when resolved
Output shape
ToolUseSummaryMessage
yielded into the SDK stream
HistoryItemToolUseSummary
added to UI history + factory exported for future SDK use
Gate
CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES
env, default
off
experimental.emitToolUseSummaries
setting (default
on
) + env override
Primary consumer
Mobile / SDK clients
CLI compact mode + full mode, future SDK
Prompt
Git-commit-subject, past tense, most distinctive noun (verbatim port)
Identical system prompt
Input truncation
300 chars per tool field via
truncateJson
Identical
Intent prefix
First 200 chars of the assistant’s last message
Identical
Prompt caching
enablePromptCaching: true
on the Haiku call
Not yet wired (forked-agent route available; flagged as future optimization)
Label post-processing
Raw model text
cleanSummary
(strips markdown, quotes, error-prefixes; caps at 100 chars, ReDoS-bounded)
Session persistence
Stream-only; each session regenerates
UI history only;
ChatRecordingService
does not persist
tool_use_summary
entries
2. Claude Code Implementation Analysis
2.1 Flow
Claude Code runs the tool loop in
query.ts
. After a tool batch executes and its results are normalized, the generator function forks a Haiku call, keeps the pending promise on
nextPendingToolUseSummary
, and continues with the next turn’s API call. The Haiku latency (~1s) overlaps the main model’s streaming (5–30s), so the user sees zero added latency. Right before emitting the next turn’s content, the generator awaits the pending summary and yields a
tool_use_summary
message into the stream.
tool_batch_complete → fork queryHaiku (fire-and-forget)
↓
next_turn_stream_starts
↓
← summary Promise resolves during streaming →
↓
await pendingToolUseSummary → yield ToolUseSummaryMessage
↓
continue with next turn
2.2 Key source files
Component
File
Key logic
Generator
services/toolUseSummary/toolUseSummaryGenerator.ts:45-97
generateToolUseSummary({ tools, signal, isNonInteractiveSession, lastAssistantText })
Trigger
query.ts:1411-1482
Guard by
emitToolUseSummaries
gate + no-subagent; fork Haiku; carry promise
Await + emit
query.ts:1055-1060
Await
pendingToolUseSummary
at next-turn boundary, yield message
Message factory
utils/messages.ts:5105-5116
createToolUseSummaryMessage(summary, precedingToolUseIds)
Feature gate
query/config.ts:23,36-38
emitToolUseSummaries: isEnvTruthy(CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES)
2.3 Design decisions
Always generate when the gate is on, regardless of compact/detail state.
The summary is a stream-level artifact; the UI decides whether to render it.
Emit as a first-class message type.
tool_use_summary
sits alongside
user
,
assistant
,
tool_result
in the SDK stream with a
precedingToolUseIds
field for consumers to correlate against the batch.
Subagents are excluded.
!toolUseContext.agentId
— subagent output is aggregated upstream; individual subagent batches would produce noisy labels that never surface in the primary UI.
Default off.
The env-only gate keeps cost at zero unless a downstream SDK consumer opts in. The CC terminal itself does not render the message.
Input truncation at 300 chars per field.
Covers the dominant cost risk — a single large tool result blowing up the prompt — while keeping enough signal for the label.
3. Qwen Code Implementation
3.1 Flow
Qwen Code hooks the same lifecycle point (
useGeminiStream.handleCompletedTools
) but renders on both sides of
ui.compactMode
so the feature is useful to CLI users without any SDK plumbing.
tool_batch_complete (handleCompletedTools)
↓
config.getEmitToolUseSummaries()?
↓
fork generateToolUseSummary (fire-and-forget)
↓
submitQuery() for next turn (streaming starts)
↓
← summary Promise resolves during streaming →
↓
addItem({type:'tool_use_summary', summary, precedingToolUseIds})
↓
HistoryItemDisplay renders:
compactMode=false → ● <label> standalone line
compactMode=true  → hidden; MainContent lookup injects into CompactToolGroupDisplay header
3.2 Key source files
Component
File
Key logic
Service
packages/core/src/services/toolUseSummary.ts
generateToolUseSummary
,
truncateJson
,
cleanSummary
, message factory
Config gate
packages/core/src/config/config.ts:getEmitToolUseSummaries
Env override → settings → default (true)
Trigger
packages/cli/src/ui/hooks/useGeminiStream.ts:handleCompletedTools
Fires fast-model call, addItem on resolve
Full-mode render
packages/cli/src/ui/components/HistoryItemDisplay.tsx
Renders
● <label>
line when
!compactMode
Compact-mode lookup
packages/cli/src/ui/components/MainContent.tsx
summaryByCallId
map →
compactLabel
prop to each tool_group
Compact header
packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx
Replaces default
Tool × N
with
<Summary> · N tools
when label present
Merge handling
packages/cli/src/ui/utils/mergeCompactToolGroups.ts
Treats
tool_use_summary
as hidden-in-compact for adjacency
UI type
packages/cli/src/ui/types.ts:HistoryItemToolUseSummary
{ type: 'tool_use_summary', summary, precedingToolUseIds }
3.3 The
<Static>
append-only constraint
The central architectural decision in this PR is
why the full-mode label is a standalone history item and not a decoration on the tool_group itself
.
Qwen Code renders the transcript via Ink’s
<Static>
. Static is append-only: once an item is committed to the terminal buffer, Ink will not repaint that region unless
refreshStatic()
is called to clear and re-render the entire transcript. This is the performance model the CLI depends on — static items don’t re-render on every keystroke.
Now consider the fast-model call’s timing:
T0   tool batch completes, tool_group is pushed to history
T0+ε tool_group renders through <Static> and is committed to the buffer
T0+1s fast-model call resolves with a label
At T0+1s, we cannot retroactively add the label to the already-committed tool_group. Two options exist:
Update the tool_group’s props + call
refreshStatic()
.
Works, but causes a full transcript repaint on every batch — one of the most expensive UI operations in the app. Visible flash. Unacceptable for a cosmetic label.
Render the summary as its own new history item appended
after
the tool_group.
Static handles this natively — new items append cleanly, no repaint.
This PR takes option 2 in full mode. The
tool_use_summary
entry is a real history item, rendered as a single dim
● <label>
line by
HistoryItemDisplay
. No
refreshStatic
needed.
Compact mode is different because of
mergeCompactToolGroups
. When consecutive tool
groups merge,
MainContent
already calls
refreshStatic()
— that’s an existing codepath, and it re-renders the merged group with the label looked up from history. So compact mode _does
get the label as a header replacement. To avoid rendering the same label twice (once as the compact header, once as a trailing
● <label>
line),
HistoryItemDisplay
hides the standalone line when
compactMode
is true.
Full mode              Compact mode (with merge)
───────────            ─────────────────────────
[tool_group]           [merged tool_group — header replaced via lookup]
● <label>              (● <label> line is hidden)
3.4 Gate semantics
Three layers, resolved in order of precedence:
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0|1|true|false
— env override, highest priority.
experimental.emitToolUseSummaries
in
settings.json
— default
true
.
Implicit skip — if
config.getFastModel()
returns
undefined
, generation is skipped regardless of the gate. No error, no user-visible change.
3.5 Output cleaning
cleanSummary
runs on every model response before it is added to history:
Take the first line only (drops model reasoning preambles).
Strip bullet prefixes (
-
,
*
,
•
) — models sometimes return the label as a list item.
Strip surrounding quotes/backticks via a bounded
{1,10}
regex (CodeQL-safe; no real label has more than a handful of wrapping quotes).
Strip prefix labels (
Label:
,
Summary:
,
Result:
,
Output:
) that some models prepend.
Reject error-message shapes (
API error: ...
,
Error: ...
,
I cannot ...
,
I can't ...
,
Unable to ...
) — returns empty string so no history item is added.
Hard-cap length at 100 characters (mobile UI truncates around 30; the slack covers CJK phrases).
3.6 Telemetry
The summary generation call sets
promptId: 'tool_use_summary_generation'
so its token usage is accounted separately in
/stats
. This lets users see the exact incremental cost of the feature without conflating it with prompt suggestions or the main session’s usage.
4. Deviations from Claude Code (and why)
Deviation
Why
Settings layer in addition to env gate
Qwen Code renders the label in the CLI; users need a persistent switch, not a per-shell env export.
Default
on
instead of off
Label is immediately user-visible in both display modes; users configuring
fastModel
are opting into fast-model features already.
Dedicated
cleanSummary
post-processing
Qwen Code supports more heterogeneous providers than CC; some models prepend
Label:
or wrap in quotes. Normalizing at the boundary keeps the UI consistent.
Stores
HistoryItemToolUseSummary
rather than emitting a stream message
CLI-first implementation; the SDK-stream route is a future PR. The
ToolUseSummaryMessage
factory is already exported for that work.
Prompt caching not yet wired
The fast model is often the same as the main model for users who haven’t configured a separate one. Adding cache sharing requires routing via
forkedAgent.ts
; tracked as a follow-up.
Dual render paths (full-mode inline + compact-mode header)
Qwen Code’s default is
ui.compactMode: false
; without the inline full-mode render, the feature would be invisible to most users.
5. Known limitations
No session persistence.
tool_use_summary
is not written to the chat recording JSONL. Resuming a session loses labels; tool groups render with the generic header as a fallback. Low-priority: labels regenerate naturally as the user continues the session.
No SDK stream emission yet.
The message factory is exported, but the CLI does not yet feed
tool_use_summary
into the SDK bridge. Follow-up PR.
No prompt caching.
Each batch incurs a fresh input-token cost. Negligible in absolute terms (~300 tokens) but measurable if you run dozens of batches per turn.
Summary for merged compact groups picks the first contributing batch’s label.
If a user fires ten dissimilar batches back-to-back (tight loop, not typical), the merged compact header will show only the leading batch’s intent. Trade-off accepted: fanning out per-batch labels in a merged view is visually noisier than taking the first.
Fast model required.
Without a configured
fastModel
, generation is skipped. Falling back to the main model is deliberately disallowed to keep the cost profile bounded.
6. Future work
Wire
ToolUseSummaryMessage
into the SDK bridge so the existing factory gets used downstream.
Route generation via
forkedAgent.ts
with
enablePromptCaching
so repeated tool-name prefixes hit provider caches.
Optional: persist
tool_use_summary
entries to
ChatRecordingService
and replay on session resume.
Optional: per-tool-name label shortcuts (e.g., always
Read <filename>
for a single
read_file
call) as a pre-LLM fast path.
Last updated on
May 18, 2026
Slash Command 重构路线图</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/tool-use-summary/tool-use-summary-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Tool Use Summary
Tool-Use Summary Design
Tool-Use Summary Design
Fast-model labels for parallel tool batches — motivation, competitive analysis with Claude Code, architecture, and the append-only-Static rationale that drove the current full-mode render.
User documentation:
Tool-Use Summaries
.
1. Executive Summary
After each tool batch completes, Qwen Code fires a short fast-model call that returns a git-commit-subject-style label summarizing the batch. The label shows as an inline dim
● ...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Tool Use Summary
Tool-Use Summary Design
Tool-Use Summary Design
Fast-model labels for parallel tool batches — motivation, competitive analysis with Claude Code, architecture, and the append-only-Static rationale that drove the current full-mode render.
User documentation:
Tool-Use Summaries
.
1. Executive Summary
After each tool batch completes, Qwen Code fires a short fast-model call that returns a git-commit-subject-style label summarizing the batch. The label shows as an inline dim
● <label>
line in full mode and replaces the generic
Tool × N
header in compact mode. Generation runs fire-and-forget in parallel with the next turn’s API stream, so its ~1s latency is hidden behind main-model streaming.
Dimension
Claude Code
Qwen Code
Trigger point
query.ts
— after a tool batch finalizes
useGeminiStream.ts
→
handleCompletedTools
— same lifecycle point
Generation model
Haiku via
queryHaiku
Configured
fastModel
via
GeminiClient.generateContent
Subagent behavior
!toolUseContext.agentId
— main session only
Implicit — subagents run through
agents/runtime/
, not
useGeminiStream
Scheduling
Fire-and-forget, awaited right before the next turn’s stream emits
Fire-and-forget, appended to history when resolved
Output shape
ToolUseSummaryMessage
yielded into the SDK stream
HistoryItemToolUseSummary
added to UI history + factory exported for future SDK use
Gate
CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES
env, default
off
experimental.emitToolUseSummaries
setting (default
on
) + env override
Primary consumer
Mobile / SDK clients
CLI compact mode + full mode, future SDK
Prompt
Git-commit-subject, past tense, most distinctive noun (verbatim port)
Identical system prompt
Input truncation
300 chars per tool field via
truncateJson
Identical
Intent prefix
First 200 chars of the assistant’s last message
Identical
Prompt caching
enablePromptCaching: true
on the Haiku call
Not yet wired (forked-agent route available; flagged as future optimization)
Label post-processing
Raw model text
cleanSummary
(strips markdown, quotes, error-prefixes; caps at 100 chars, ReDoS-bounded)
Session persistence
Stream-only; each session regenerates
UI history only;
ChatRecordingService
does not persist
tool_use_summary
entries
2. Claude Code Implementation Analysis
2.1 Flow
Claude Code runs the tool loop in
query.ts
. After a tool batch executes and its results are normalized, the generator function forks a Haiku call, keeps the pending promise on
nextPendingToolUseSummary
, and continues with the next turn’s API call. The Haiku latency (~1s) overlaps the main model’s streaming (5–30s), so the user sees zero added latency. Right before emitting the next turn’s content, the generator awaits the pending summary and yields a
tool_use_summary
message into the stream.
tool_batch_complete → fork queryHaiku (fire-and-forget)
↓
next_turn_stream_starts
↓
← summary Promise resolves during streaming →
↓
await pendingToolUseSummary → yield ToolUseSummaryMessage
↓
continue with next turn
2.2 Key source files
Component
File
Key logic
Generator
services/toolUseSummary/toolUseSummaryGenerator.ts:45-97
generateToolUseSummary({ tools, signal, isNonInteractiveSession, lastAssistantText })
Trigger
query.ts:1411-1482
Guard by
emitToolUseSummaries
gate + no-subagent; fork Haiku; carry promise
Await + emit
query.ts:1055-1060
Await
pendingToolUseSummary
at next-turn boundary, yield message
Message factory
utils/messages.ts:5105-5116
createToolUseSummaryMessage(summary, precedingToolUseIds)
Feature gate
query/config.ts:23,36-38
emitToolUseSummaries: isEnvTruthy(CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES)
2.3 Design decisions
Always generate when the gate is on, regardless of compact/detail state.
The summary is a stream-level artifact; the UI decides whether to render it.
Emit as a first-class message type.
tool_use_summary
sits alongside
user
,
assistant
,
tool_result
in the SDK stream with a
precedingToolUseIds
field for consumers to correlate against the batch.
Subagents are excluded.
!toolUseContext.agentId
— subagent output is aggregated upstream; individual subagent batches would produce noisy labels that never surface in the primary UI.
Default off.
The env-only gate keeps cost at zero unless a downstream SDK consumer opts in. The CC terminal itself does not render the message.
Input truncation at 300 chars per field.
Covers the dominant cost risk — a single large tool result blowing up the prompt — while keeping enough signal for the label.
3. Qwen Code Implementation
3.1 Flow
Qwen Code hooks the same lifecycle point (
useGeminiStream.handleCompletedTools
) but renders on both sides of
ui.compactMode
so the feature is useful to CLI users without any SDK plumbing.
tool_batch_complete (handleCompletedTools)
↓
config.getEmitToolUseSummaries()?
↓
fork generateToolUseSummary (fire-and-forget)
↓
submitQuery() for next turn (streaming starts)
↓
← summary Promise resolves during streaming →
↓
addItem({type:'tool_use_summary', summary, precedingToolUseIds})
↓
HistoryItemDisplay renders:
compactMode=false → ● <label> standalone line
compactMode=true  → hidden; MainContent lookup injects into CompactToolGroupDisplay header
3.2 Key source files
Component
File
Key logic
Service
packages/core/src/services/toolUseSummary.ts
generateToolUseSummary
,
truncateJson
,
cleanSummary
, message factory
Config gate
packages/core/src/config/config.ts:getEmitToolUseSummaries
Env override → settings → default (true)
Trigger
packages/cli/src/ui/hooks/useGeminiStream.ts:handleCompletedTools
Fires fast-model call, addItem on resolve
Full-mode render
packages/cli/src/ui/components/HistoryItemDisplay.tsx
Renders
● <label>
line when
!compactMode
Compact-mode lookup
packages/cli/src/ui/components/MainContent.tsx
summaryByCallId
map →
compactLabel
prop to each tool_group
Compact header
packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx
Replaces default
Tool × N
with
<Summary> · N tools
when label present
Merge handling
packages/cli/src/ui/utils/mergeCompactToolGroups.ts
Treats
tool_use_summary
as hidden-in-compact for adjacency
UI type
packages/cli/src/ui/types.ts:HistoryItemToolUseSummary
{ type: 'tool_use_summary', summary, precedingToolUseIds }
3.3 The
<Static>
append-only constraint
The central architectural decision in this PR is
why the full-mode label is a standalone history item and not a decoration on the tool_group itself
.
Qwen Code renders the transcript via Ink’s
<Static>
. Static is append-only: once an item is committed to the terminal buffer, Ink will not repaint that region unless
refreshStatic()
is called to clear and re-render the entire transcript. This is the performance model the CLI depends on — static items don’t re-render on every keystroke.
Now consider the fast-model call’s timing:
T0   tool batch completes, tool_group is pushed to history
T0+ε tool_group renders through <Static> and is committed to the buffer
T0+1s fast-model call resolves with a label
At T0+1s, we cannot retroactively add the label to the already-committed tool_group. Two options exist:
Update the tool_group’s props + call
refreshStatic()
.
Works, but causes a full transcript repaint on every batch — one of the most expensive UI operations in the app. Visible flash. Unacceptable for a cosmetic label.
Render the summary as its own new history item appended
after
the tool_group.
Static handles this natively — new items append cleanly, no repaint.
This PR takes option 2 in full mode. The
tool_use_summary
entry is a real history item, rendered as a single dim
● <label>
line by
HistoryItemDisplay
. No
refreshStatic
needed.
Compact mode is different because of
mergeCompactToolGroups
. When consecutive tool
groups merge,
MainContent
already calls
refreshStatic()
— that’s an existing codepath, and it re-renders the merged group with the label looked up from history. So compact mode _does
get the label as a header replacement. To avoid rendering the same label twice (once as the compact header, once as a trailing
● <label>
line),
HistoryItemDisplay
hides the standalone line when
compactMode
is true.
Full mode              Compact mode (with merge)
───────────            ─────────────────────────
[tool_group]           [merged tool_group — header replaced via lookup]
● <label>              (● <label> line is hidden)
3.4 Gate semantics
Three layers, resolved in order of precedence:
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0|1|true|false
— env override, highest priority.
experimental.emitToolUseSummaries
in
settings.json
— default
true
.
Implicit skip — if
config.getFastModel()
returns
undefined
, generation is skipped regardless of the gate. No error, no user-visible change.
3.5 Output cleaning
cleanSummary
runs on every model response before it is added to history:
Take the first line only (drops model reasoning preambles).
Strip bullet prefixes (
-
,
*
,
•
) — models sometimes return the label as a list item.
Strip surrounding quotes/backticks via a bounded
{1,10}
regex (CodeQL-safe; no real label has more than a handful of wrapping quotes).
Strip prefix labels (
Label:
,
Summary:
,
Result:
,
Output:
) that some models prepend.
Reject error-message shapes (
API error: ...
,
Error: ...
,
I cannot ...
,
I can't ...
,
Unable to ...
) — returns empty string so no history item is added.
Hard-cap length at 100 characters (mobile UI truncates around 30; the slack covers CJK phrases).
3.6 Telemetry
The summary generation call sets
promptId: 'tool_use_summary_generation'
so its token usage is accounted separately in
/stats
. This lets users see the exact incremental cost of the feature without conflating it with prompt suggestions or the main session’s usage.
4. Deviations from Claude Code (and why)
Deviation
Why
Settings layer in addition to env gate
Qwen Code renders the label in the CLI; users need a persistent switch, not a per-shell env export.
Default
on
instead of off
Label is immediately user-visible in both display modes; users configuring
fastModel
are opting into fast-model features already.
Dedicated
cleanSummary
post-processing
Qwen Code supports more heterogeneous providers than CC; some models prepend
Label:
or wrap in quotes. Normalizing at the boundary keeps the UI consistent.
Stores
HistoryItemToolUseSummary
rather than emitting a stream message
CLI-first implementation; the SDK-stream route is a future PR. The
ToolUseSummaryMessage
factory is already exported for that work.
Prompt caching not yet wired
The fast model is often the same as the main model for users who haven’t configured a separate one. Adding cache sharing requires routing via
forkedAgent.ts
; tracked as a follow-up.
Dual render paths (full-mode inline + compact-mode header)
Qwen Code’s default is
ui.compactMode: false
; without the inline full-mode render, the feature would be invisible to most users.
5. Known limitations
No session persistence.
tool_use_summary
is not written to the chat recording JSONL. Resuming a session loses labels; tool groups render with the generic header as a fallback. Low-priority: labels regenerate naturally as the user continues the session.
No SDK stream emission yet.
The message factory is exported, but the CLI does not yet feed
tool_use_summary
into the SDK bridge. Follow-up PR.
No prompt caching.
Each batch incurs a fresh input-token cost. Negligible in absolute terms (~300 tokens) but measurable if you run dozens of batches per turn.
Summary for merged compact groups picks the first contributing batch’s label.
If a user fires ten dissimilar batches back-to-back (tight loop, not typical), the merged compact header will show only the leading batch’s intent. Trade-off accepted: fanning out per-batch labels in a merged view is visually noisier than taking the first.
Fast model required.
Without a configured
fastModel
, generation is skipped. Falling back to the main model is deliberately disallowed to keep the cost profile bounded.
6. Future work
Wire
ToolUseSummaryMessage
into the SDK bridge so the existing factory gets used downstream.
Route generation via
forkedAgent.ts
with
enablePromptCaching
so repeated tool-name prefixes hit provider caches.
Optional: persist
tool_use_summary
entries to
ChatRecordingService
and replay on session resume.
Optional: per-tool-name label shortcuts (e.g., always
Read <filename>
for a single
read_file
call) as a pre-LLM fast path.
Last updated on
May 18, 2026
Slash Command 重构路线图</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/tool-use-summary/tool-use-summary-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Observability with OpenTelemetry</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/development/telemetry/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/development/telemetry/</guid>
  <pubDate>Sun, 20 Oct 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Telemetry
Observability with OpenTelemetry
Learn how to enable and setup OpenTelemetry for Qwen Code.
Observability with OpenTelemetry
Key Benefits
OpenTelemetry Integration
Configuration
Aliyun Telemetry
Manual OTLP Export
Local Telemetry
File-based Output (Recommended)
Collector-Based Export (Advanced)
Logs and Metrics
Logs
Metrics
Key Benefits
🔍 Usage Analytics
: Understand interaction patterns and feature adoption
across your team
⚡ Performance Monitoring
: Track ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Telemetry
Observability with OpenTelemetry
Learn how to enable and setup OpenTelemetry for Qwen Code.
Observability with OpenTelemetry
Key Benefits
OpenTelemetry Integration
Configuration
Aliyun Telemetry
Manual OTLP Export
Local Telemetry
File-based Output (Recommended)
Collector-Based Export (Advanced)
Logs and Metrics
Logs
Metrics
Key Benefits
🔍 Usage Analytics
: Understand interaction patterns and feature adoption
across your team
⚡ Performance Monitoring
: Track response times, token consumption, and
resource utilization
🐛 Real-time Debugging
: Identify bottlenecks, failures, and error patterns
as they occur
📊 Workflow Optimization
: Make informed decisions to improve
configurations and processes
🏢 Enterprise Governance
: Monitor usage across teams, track costs, ensure
compliance, and integrate with existing monitoring infrastructure
OpenTelemetry Integration
Built on
OpenTelemetry
— the vendor-neutral, industry-standard
observability framework — Qwen Code’s observability system provides:
Universal Compatibility
: Export to any OpenTelemetry backend (Aliyun,
Jaeger, Prometheus, Datadog, etc.)
Standardized Data
: Use consistent formats and collection methods across
your toolchain
Future-Proof Integration
: Connect with existing and future observability
infrastructure
No Vendor Lock-in
: Switch between backends without changing your
instrumentation
Configuration
Note
⚠️ Special Note: This feature requires corresponding code changes. This documentation is provided in advance; please refer to future code updates for actual functionality.
All telemetry behavior is controlled through your
.qwen/settings.json
file.
These settings can be overridden by environment variables or CLI flags.
Setting
Environment Variable
CLI Flag
Description
Values
Default
enabled
QWEN_TELEMETRY_ENABLED
--telemetry
/
--no-telemetry
Enable or disable telemetry
true
/
false
false
target
QWEN_TELEMETRY_TARGET
--telemetry-target <local|gcp>
Where to send telemetry data
"gcp"
/
"local"
"local"
otlpEndpoint
QWEN_TELEMETRY_OTLP_ENDPOINT
--telemetry-otlp-endpoint <URL>
OTLP collector endpoint
URL string
http://localhost:4317
otlpProtocol
QWEN_TELEMETRY_OTLP_PROTOCOL
--telemetry-otlp-protocol <grpc|http>
OTLP transport protocol
"grpc"
/
"http"
"grpc"
otlpTracesEndpoint
QWEN_TELEMETRY_OTLP_TRACES_ENDPOINT
-
Per-signal endpoint override for traces (HTTP only)
URL string
-
otlpLogsEndpoint
QWEN_TELEMETRY_OTLP_LOGS_ENDPOINT
-
Per-signal endpoint override for logs (HTTP only)
URL string
-
otlpMetricsEndpoint
QWEN_TELEMETRY_OTLP_METRICS_ENDPOINT
-
Per-signal endpoint override for metrics (HTTP only)
URL string
-
outfile
QWEN_TELEMETRY_OUTFILE
--telemetry-outfile <path>
Save telemetry to file (overrides
otlpEndpoint
)
file path
-
logPrompts
QWEN_TELEMETRY_LOG_PROMPTS
--telemetry-log-prompts
/
--no-telemetry-log-prompts
Include prompts in telemetry logs
true
/
false
true
useCollector
QWEN_TELEMETRY_USE_COLLECTOR
-
Use external OTLP collector (advanced)
true
/
false
false
Note on boolean environment variables:
For the boolean settings (
enabled
,
logPrompts
,
useCollector
), setting the corresponding environment variable to
true
or
1
will enable the feature. Any other value will disable it.
HTTP OTLP signal routing:
When using HTTP protocol (
otlpProtocol: "http"
),
Qwen Code automatically appends signal-specific paths (
/v1/traces
,
/v1/logs
,
/v1/metrics
) to the base
otlpEndpoint
. For example,
http://collector:4318
becomes
http://collector:4318/v1/traces
for traces. If the URL already ends
with a signal path, it is used as-is. Per-signal endpoint overrides
(
otlpTracesEndpoint
, etc.) take precedence over the base endpoint and are used
verbatim. gRPC protocol uses service-based routing and does not append paths.
The per-signal endpoint environment variables also accept the standard
OpenTelemetry names:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
,
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
.
The
QWEN_TELEMETRY_OTLP_*
variants take precedence over the
OTEL_*
variants.
For detailed information about all configuration options, see the
Configuration Guide
.
Aliyun Telemetry
Manual OTLP Export
To view Qwen Code telemetry in Alibaba Cloud Managed Service for
OpenTelemetry, configure Qwen Code to export to the OTLP endpoint
provided by ARMS.
Setting
"target": "gcp"
alone does not configure the export
destination. If
otlpEndpoint
is not set, Qwen Code still defaults to
http://localhost:4317
. If
outfile
is set, it overrides
otlpEndpoint
and telemetry is written to the file instead of being
sent to Alibaba Cloud.
Enable telemetry in your
.qwen/settings.json
and set the OTLP
endpoint:
Option A: gRPC protocol
(standard OTLP endpoint):
{
"telemetry"
: {
"enabled"
:
true
,
"target"
:
"gcp"
,
"otlpEndpoint"
:
"https://<your-otlp-endpoint>"
,
"otlpProtocol"
:
"grpc"
}
}
Option B: HTTP protocol with per-signal endpoints
(for backends
that use non-standard paths, e.g.,
/api/otlp/traces
instead of
/v1/traces
):
{
"telemetry"
: {
"enabled"
:
true
,
"otlpProtocol"
:
"http"
,
"otlpTracesEndpoint"
:
"http://<host>/<token>/api/otlp/traces"
,
"otlpLogsEndpoint"
:
"http://<host>/<token>/api/otlp/logs"
,
"otlpMetricsEndpoint"
:
"http://<host>/<token>/api/otlp/metrics"
}
}
Note:
When using HTTP protocol with only
otlpEndpoint
(no
per-signal overrides), Qwen Code appends standard OTLP paths
(
/v1/traces
,
/v1/logs
,
/v1/metrics
) to the base URL. If your
backend uses different paths, use per-signal endpoint overrides as
shown in Option B.
If your Alibaba Cloud endpoint requires authentication, provide OTLP
headers through standard OpenTelemetry environment variables such as
OTEL_EXPORTER_OTLP_HEADERS
(or the signal-specific variants). Qwen
Code does not currently expose OTLP auth headers directly in
.qwen/settings.json
.
Run Qwen Code and send prompts.
View telemetry in Managed Service for OpenTelemetry:
Product overview:
What is Managed Service for OpenTelemetry?
Getting started:
Get started with Managed Service for OpenTelemetry
Console entry points:
China mainland:
trace.console.aliyun.com
(legacy console:
tracing.console.aliyun.com
)
International:
arms.console.alibabacloud.com
In the console, use
Applications
to inspect traces and service
topology.
To locate the OTLP endpoint and access information:
New console
(
trace.console.aliyun.com
or international):
navigate to
Integration Center
.
Legacy console
(
tracing.console.aliyun.com
): navigate to
Cluster Configurations
→
Access point information
.
Local Telemetry
For local development and debugging, you can capture telemetry data locally:
File-based Output (Recommended)
Enable telemetry in your
.qwen/settings.json
:
{
"telemetry"
: {
"enabled"
:
true
,
"target"
:
"local"
,
"otlpEndpoint"
:
""
,
"outfile"
:
".qwen/telemetry.log"
}
}
Run Qwen Code and send prompts.
View logs and metrics in the specified file (e.g.,
.qwen/telemetry.log
).
Collector-Based Export (Advanced)
Run the automation script:
npm
run
telemetry
--
--target=local
This will:
Download and start Jaeger and OTEL collector
Configure your workspace for local telemetry
Provide a Jaeger UI at
http://localhost:16686
Save logs/metrics to
~/.qwen/tmp/<projectHash>/otel/collector.log
Stop collector on exit (e.g.
Ctrl+C
)
Run Qwen Code and send prompts.
View traces at
http://localhost:16686
and logs/metrics in the collector log
file.
Logs and Metrics
The following section describes the structure of logs and metrics generated for
Qwen Code.
A
sessionId
is included as a common attribute on all logs and metrics.
Logs
Logs are timestamped records of specific events. The following events are logged for Qwen Code:
qwen-code.config
: This event occurs once at startup with the CLI’s configuration.
Attributes
:
model
(string)
sandbox_enabled
(boolean)
core_tools_enabled
(string)
approval_mode
(string)
file_filtering_respect_git_ignore
(boolean)
debug_mode
(boolean)
truncate_tool_output_threshold
(number)
truncate_tool_output_lines
(number)
hooks
(string, comma-separated hook event types, omitted if hooks disabled)
ide_enabled
(boolean)
interactive_shell_enabled
(boolean)
mcp_servers
(string)
output_format
(string: “text” or “json”)
qwen-code.user_prompt
: This event occurs when a user submits a prompt.
Attributes
:
prompt_length
(int)
prompt_id
(string)
prompt
(string, this attribute is excluded if
log_prompts_enabled
is
configured to be
false
)
auth_type
(string)
qwen-code.tool_call
: This event occurs for each function call.
Attributes
:
function_name
function_args
duration_ms
success
(boolean)
decision
(string: “accept”, “reject”, “auto_accept”, or “modify”, if
applicable)
error
(if applicable)
error_type
(if applicable)
content_length
(int, if applicable)
metadata
(if applicable, dictionary of string -> any)
qwen-code.file_operation
: This event occurs for each file operation.
Attributes
:
tool_name
(string)
operation
(string: “create”, “read”, “update”)
lines
(int, if applicable)
mimetype
(string, if applicable)
extension
(string, if applicable)
programming_language
(string, if applicable)
diff_stat
(json string, if applicable): A JSON string with the following members:
ai_added_lines
(int)
ai_removed_lines
(int)
user_added_lines
(int)
user_removed_lines
(int)
qwen-code.api_request
: This event occurs when making a request to Qwen API.
Attributes
:
model
request_text
(if applicable)
qwen-code.api_error
: This event occurs if the API request fails.
Attributes
:
model
error
error_type
status_code
duration_ms
auth_type
qwen-code.api_response
: This event occurs upon receiving a response from Qwen API.
Attributes
:
model
status_code
duration_ms
error
(optional)
input_token_count
output_token_count
cached_content_token_count
thoughts_token_count
response_text
(if applicable)
auth_type
qwen-code.tool_output_truncated
: This event occurs when the output of a tool call is too large and gets truncated.
Attributes
:
tool_name
(string)
original_content_length
(int)
truncated_content_length
(int)
threshold
(int)
lines
(int)
prompt_id
(string)
qwen-code.malformed_json_response
: This event occurs when a
generateJson
response from Qwen API cannot be parsed as a json.
Attributes
:
model
qwen-code.flash_fallback
: This event occurs when Qwen Code switches to flash as fallback.
Attributes
:
auth_type
qwen-code.slash_command
: This event occurs when a user executes a slash command.
Attributes
:
command
(string)
subcommand
(string, if applicable)
qwen-code.extension_enable
: This event occurs when an extension is enabled
qwen-code.extension_install
: This event occurs when an extension is installed
Attributes
:
extension_name
(string)
extension_version
(string)
extension_source
(string)
status
(string)
qwen-code.extension_uninstall
: This event occurs when an extension is uninstalled
Metrics
Metrics are numerical measurements of behavior over time. The following metrics are collected for Qwen Code (metric names remain
qwen-code.*
for compatibility):
qwen-code.session.count
(Counter, Int): Incremented once per CLI startup.
qwen-code.tool.call.count
(Counter, Int): Counts tool calls.
Attributes
:
function_name
success
(boolean)
decision
(string: “accept”, “reject”, or “modify”, if applicable)
tool_type
(string: “mcp”, or “native”, if applicable)
qwen-code.tool.call.latency
(Histogram, ms): Measures tool call latency.
Attributes
:
function_name
decision
(string: “accept”, “reject”, or “modify”, if applicable)
qwen-code.api.request.count
(Counter, Int): Counts all API requests.
Attributes
:
model
status_code
error_type
(if applicable)
qwen-code.api.request.latency
(Histogram, ms): Measures API request latency.
Attributes
:
model
qwen-code.token.usage
(Counter, Int): Counts the number of tokens used.
Attributes
:
model
type
(string: “input”, “output”, “thought”, or “cache”)
qwen-code.file.operation.count
(Counter, Int): Counts file operations.
Attributes
:
operation
(string: “create”, “read”, “update”): The type of file operation.
lines
(Int, if applicable): Number of lines in the file.
mimetype
(string, if applicable): Mimetype of the file.
extension
(string, if applicable): File extension of the file.
model_added_lines
(Int, if applicable): Number of lines added/changed by the model.
model_removed_lines
(Int, if applicable): Number of lines removed/changed by the model.
user_added_lines
(Int, if applicable): Number of lines added/changed by user in AI proposed changes.
user_removed_lines
(Int, if applicable): Number of lines removed/changed by user in AI proposed changes.
programming_language
(string, if applicable): The programming language of the file.
qwen-code.chat_compression
(Counter, Int): Counts chat compression operations
Attributes
:
tokens_before
: (Int): Number of tokens in context prior to compression
tokens_after
: (Int): Number of tokens in context after compression
Last updated on
May 18, 2026
NPM
Integration Tests</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/telemetry/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Telemetry
Observability with OpenTelemetry
Learn how to enable and setup OpenTelemetry for Qwen Code.
Observability with OpenTelemetry
Key Benefits
OpenTelemetry Integration
Configuration
Aliyun Telemetry
Manual OTLP Export
Local Telemetry
File-based Output (Recommended)
Collector-Based Export (Advanced)
Logs and Metrics
Logs
Metrics
Key Benefits
🔍 Usage Analytics
: Understand interaction patterns and feature adoption
across your team
⚡ Performance Monitoring
: Track ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Telemetry
Observability with OpenTelemetry
Learn how to enable and setup OpenTelemetry for Qwen Code.
Observability with OpenTelemetry
Key Benefits
OpenTelemetry Integration
Configuration
Aliyun Telemetry
Manual OTLP Export
Local Telemetry
File-based Output (Recommended)
Collector-Based Export (Advanced)
Logs and Metrics
Logs
Metrics
Key Benefits
🔍 Usage Analytics
: Understand interaction patterns and feature adoption
across your team
⚡ Performance Monitoring
: Track response times, token consumption, and
resource utilization
🐛 Real-time Debugging
: Identify bottlenecks, failures, and error patterns
as they occur
📊 Workflow Optimization
: Make informed decisions to improve
configurations and processes
🏢 Enterprise Governance
: Monitor usage across teams, track costs, ensure
compliance, and integrate with existing monitoring infrastructure
OpenTelemetry Integration
Built on
OpenTelemetry
— the vendor-neutral, industry-standard
observability framework — Qwen Code’s observability system provides:
Universal Compatibility
: Export to any OpenTelemetry backend (Aliyun,
Jaeger, Prometheus, Datadog, etc.)
Standardized Data
: Use consistent formats and collection methods across
your toolchain
Future-Proof Integration
: Connect with existing and future observability
infrastructure
No Vendor Lock-in
: Switch between backends without changing your
instrumentation
Configuration
Note
⚠️ Special Note: This feature requires corresponding code changes. This documentation is provided in advance; please refer to future code updates for actual functionality.
All telemetry behavior is controlled through your
.qwen/settings.json
file.
These settings can be overridden by environment variables or CLI flags.
Setting
Environment Variable
CLI Flag
Description
Values
Default
enabled
QWEN_TELEMETRY_ENABLED
--telemetry
/
--no-telemetry
Enable or disable telemetry
true
/
false
false
target
QWEN_TELEMETRY_TARGET
--telemetry-target <local|gcp>
Where to send telemetry data
"gcp"
/
"local"
"local"
otlpEndpoint
QWEN_TELEMETRY_OTLP_ENDPOINT
--telemetry-otlp-endpoint <URL>
OTLP collector endpoint
URL string
http://localhost:4317
otlpProtocol
QWEN_TELEMETRY_OTLP_PROTOCOL
--telemetry-otlp-protocol <grpc|http>
OTLP transport protocol
"grpc"
/
"http"
"grpc"
otlpTracesEndpoint
QWEN_TELEMETRY_OTLP_TRACES_ENDPOINT
-
Per-signal endpoint override for traces (HTTP only)
URL string
-
otlpLogsEndpoint
QWEN_TELEMETRY_OTLP_LOGS_ENDPOINT
-
Per-signal endpoint override for logs (HTTP only)
URL string
-
otlpMetricsEndpoint
QWEN_TELEMETRY_OTLP_METRICS_ENDPOINT
-
Per-signal endpoint override for metrics (HTTP only)
URL string
-
outfile
QWEN_TELEMETRY_OUTFILE
--telemetry-outfile <path>
Save telemetry to file (overrides
otlpEndpoint
)
file path
-
logPrompts
QWEN_TELEMETRY_LOG_PROMPTS
--telemetry-log-prompts
/
--no-telemetry-log-prompts
Include prompts in telemetry logs
true
/
false
true
useCollector
QWEN_TELEMETRY_USE_COLLECTOR
-
Use external OTLP collector (advanced)
true
/
false
false
Note on boolean environment variables:
For the boolean settings (
enabled
,
logPrompts
,
useCollector
), setting the corresponding environment variable to
true
or
1
will enable the feature. Any other value will disable it.
HTTP OTLP signal routing:
When using HTTP protocol (
otlpProtocol: "http"
),
Qwen Code automatically appends signal-specific paths (
/v1/traces
,
/v1/logs
,
/v1/metrics
) to the base
otlpEndpoint
. For example,
http://collector:4318
becomes
http://collector:4318/v1/traces
for traces. If the URL already ends
with a signal path, it is used as-is. Per-signal endpoint overrides
(
otlpTracesEndpoint
, etc.) take precedence over the base endpoint and are used
verbatim. gRPC protocol uses service-based routing and does not append paths.
The per-signal endpoint environment variables also accept the standard
OpenTelemetry names:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
,
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
.
The
QWEN_TELEMETRY_OTLP_*
variants take precedence over the
OTEL_*
variants.
For detailed information about all configuration options, see the
Configuration Guide
.
Aliyun Telemetry
Manual OTLP Export
To view Qwen Code telemetry in Alibaba Cloud Managed Service for
OpenTelemetry, configure Qwen Code to export to the OTLP endpoint
provided by ARMS.
Setting
"target": "gcp"
alone does not configure the export
destination. If
otlpEndpoint
is not set, Qwen Code still defaults to
http://localhost:4317
. If
outfile
is set, it overrides
otlpEndpoint
and telemetry is written to the file instead of being
sent to Alibaba Cloud.
Enable telemetry in your
.qwen/settings.json
and set the OTLP
endpoint:
Option A: gRPC protocol
(standard OTLP endpoint):
{
"telemetry"
: {
"enabled"
:
true
,
"target"
:
"gcp"
,
"otlpEndpoint"
:
"https://<your-otlp-endpoint>"
,
"otlpProtocol"
:
"grpc"
}
}
Option B: HTTP protocol with per-signal endpoints
(for backends
that use non-standard paths, e.g.,
/api/otlp/traces
instead of
/v1/traces
):
{
"telemetry"
: {
"enabled"
:
true
,
"otlpProtocol"
:
"http"
,
"otlpTracesEndpoint"
:
"http://<host>/<token>/api/otlp/traces"
,
"otlpLogsEndpoint"
:
"http://<host>/<token>/api/otlp/logs"
,
"otlpMetricsEndpoint"
:
"http://<host>/<token>/api/otlp/metrics"
}
}
Note:
When using HTTP protocol with only
otlpEndpoint
(no
per-signal overrides), Qwen Code appends standard OTLP paths
(
/v1/traces
,
/v1/logs
,
/v1/metrics
) to the base URL. If your
backend uses different paths, use per-signal endpoint overrides as
shown in Option B.
If your Alibaba Cloud endpoint requires authentication, provide OTLP
headers through standard OpenTelemetry environment variables such as
OTEL_EXPORTER_OTLP_HEADERS
(or the signal-specific variants). Qwen
Code does not currently expose OTLP auth headers directly in
.qwen/settings.json
.
Run Qwen Code and send prompts.
View telemetry in Managed Service for OpenTelemetry:
Product overview:
What is Managed Service for OpenTelemetry?
Getting started:
Get started with Managed Service for OpenTelemetry
Console entry points:
China mainland:
trace.console.aliyun.com
(legacy console:
tracing.console.aliyun.com
)
International:
arms.console.alibabacloud.com
In the console, use
Applications
to inspect traces and service
topology.
To locate the OTLP endpoint and access information:
New console
(
trace.console.aliyun.com
or international):
navigate to
Integration Center
.
Legacy console
(
tracing.console.aliyun.com
): navigate to
Cluster Configurations
→
Access point information
.
Local Telemetry
For local development and debugging, you can capture telemetry data locally:
File-based Output (Recommended)
Enable telemetry in your
.qwen/settings.json
:
{
"telemetry"
: {
"enabled"
:
true
,
"target"
:
"local"
,
"otlpEndpoint"
:
""
,
"outfile"
:
".qwen/telemetry.log"
}
}
Run Qwen Code and send prompts.
View logs and metrics in the specified file (e.g.,
.qwen/telemetry.log
).
Collector-Based Export (Advanced)
Run the automation script:
npm
run
telemetry
--
--target=local
This will:
Download and start Jaeger and OTEL collector
Configure your workspace for local telemetry
Provide a Jaeger UI at
http://localhost:16686
Save logs/metrics to
~/.qwen/tmp/<projectHash>/otel/collector.log
Stop collector on exit (e.g.
Ctrl+C
)
Run Qwen Code and send prompts.
View traces at
http://localhost:16686
and logs/metrics in the collector log
file.
Logs and Metrics
The following section describes the structure of logs and metrics generated for
Qwen Code.
A
sessionId
is included as a common attribute on all logs and metrics.
Logs
Logs are timestamped records of specific events. The following events are logged for Qwen Code:
qwen-code.config
: This event occurs once at startup with the CLI’s configuration.
Attributes
:
model
(string)
sandbox_enabled
(boolean)
core_tools_enabled
(string)
approval_mode
(string)
file_filtering_respect_git_ignore
(boolean)
debug_mode
(boolean)
truncate_tool_output_threshold
(number)
truncate_tool_output_lines
(number)
hooks
(string, comma-separated hook event types, omitted if hooks disabled)
ide_enabled
(boolean)
interactive_shell_enabled
(boolean)
mcp_servers
(string)
output_format
(string: “text” or “json”)
qwen-code.user_prompt
: This event occurs when a user submits a prompt.
Attributes
:
prompt_length
(int)
prompt_id
(string)
prompt
(string, this attribute is excluded if
log_prompts_enabled
is
configured to be
false
)
auth_type
(string)
qwen-code.tool_call
: This event occurs for each function call.
Attributes
:
function_name
function_args
duration_ms
success
(boolean)
decision
(string: “accept”, “reject”, “auto_accept”, or “modify”, if
applicable)
error
(if applicable)
error_type
(if applicable)
content_length
(int, if applicable)
metadata
(if applicable, dictionary of string -> any)
qwen-code.file_operation
: This event occurs for each file operation.
Attributes
:
tool_name
(string)
operation
(string: “create”, “read”, “update”)
lines
(int, if applicable)
mimetype
(string, if applicable)
extension
(string, if applicable)
programming_language
(string, if applicable)
diff_stat
(json string, if applicable): A JSON string with the following members:
ai_added_lines
(int)
ai_removed_lines
(int)
user_added_lines
(int)
user_removed_lines
(int)
qwen-code.api_request
: This event occurs when making a request to Qwen API.
Attributes
:
model
request_text
(if applicable)
qwen-code.api_error
: This event occurs if the API request fails.
Attributes
:
model
error
error_type
status_code
duration_ms
auth_type
qwen-code.api_response
: This event occurs upon receiving a response from Qwen API.
Attributes
:
model
status_code
duration_ms
error
(optional)
input_token_count
output_token_count
cached_content_token_count
thoughts_token_count
response_text
(if applicable)
auth_type
qwen-code.tool_output_truncated
: This event occurs when the output of a tool call is too large and gets truncated.
Attributes
:
tool_name
(string)
original_content_length
(int)
truncated_content_length
(int)
threshold
(int)
lines
(int)
prompt_id
(string)
qwen-code.malformed_json_response
: This event occurs when a
generateJson
response from Qwen API cannot be parsed as a json.
Attributes
:
model
qwen-code.flash_fallback
: This event occurs when Qwen Code switches to flash as fallback.
Attributes
:
auth_type
qwen-code.slash_command
: This event occurs when a user executes a slash command.
Attributes
:
command
(string)
subcommand
(string, if applicable)
qwen-code.extension_enable
: This event occurs when an extension is enabled
qwen-code.extension_install
: This event occurs when an extension is installed
Attributes
:
extension_name
(string)
extension_version
(string)
extension_source
(string)
status
(string)
qwen-code.extension_uninstall
: This event occurs when an extension is uninstalled
Metrics
Metrics are numerical measurements of behavior over time. The following metrics are collected for Qwen Code (metric names remain
qwen-code.*
for compatibility):
qwen-code.session.count
(Counter, Int): Incremented once per CLI startup.
qwen-code.tool.call.count
(Counter, Int): Counts tool calls.
Attributes
:
function_name
success
(boolean)
decision
(string: “accept”, “reject”, or “modify”, if applicable)
tool_type
(string: “mcp”, or “native”, if applicable)
qwen-code.tool.call.latency
(Histogram, ms): Measures tool call latency.
Attributes
:
function_name
decision
(string: “accept”, “reject”, or “modify”, if applicable)
qwen-code.api.request.count
(Counter, Int): Counts all API requests.
Attributes
:
model
status_code
error_type
(if applicable)
qwen-code.api.request.latency
(Histogram, ms): Measures API request latency.
Attributes
:
model
qwen-code.token.usage
(Counter, Int): Counts the number of tokens used.
Attributes
:
model
type
(string: “input”, “output”, “thought”, or “cache”)
qwen-code.file.operation.count
(Counter, Int): Counts file operations.
Attributes
:
operation
(string: “create”, “read”, “update”): The type of file operation.
lines
(Int, if applicable): Number of lines in the file.
mimetype
(string, if applicable): Mimetype of the file.
extension
(string, if applicable): File extension of the file.
model_added_lines
(Int, if applicable): Number of lines added/changed by the model.
model_removed_lines
(Int, if applicable): Number of lines removed/changed by the model.
user_added_lines
(Int, if applicable): Number of lines added/changed by user in AI proposed changes.
user_removed_lines
(Int, if applicable): Number of lines removed/changed by user in AI proposed changes.
programming_language
(string, if applicable): The programming language of the file.
qwen-code.chat_compression
(Counter, Int): Counts chat compression operations
Attributes
:
tokens_before
: (Int): Number of tokens in context prior to compression
tokens_after
: (Int): Number of tokens in context after compression
Last updated on
May 18, 2026
NPM
Integration Tests</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/telemetry/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Ignoring Files</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/configuration/qwen-ignore/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/configuration/qwen-ignore/</guid>
  <pubDate>Thu, 03 Oct 2024 00:00:00 +0000</pubDate>
  <category>Configuration</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Ignoring Files
Ignoring Files
This document provides an overview of the Qwen Ignore (
.qwenignore
) feature of Qwen Code.
Qwen Code includes the ability to automatically ignore files, similar to
.gitignore
(used by Git). Adding paths to your
.qwenignore
file will exclude them from tools that support this feature, although they will still be visible to other services (such as Git).
How it works
When you add a path to your
.qwenignore
file, tools that respect this file wil...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Ignoring Files
Ignoring Files
This document provides an overview of the Qwen Ignore (
.qwenignore
) feature of Qwen Code.
Qwen Code includes the ability to automatically ignore files, similar to
.gitignore
(used by Git). Adding paths to your
.qwenignore
file will exclude them from tools that support this feature, although they will still be visible to other services (such as Git).
How it works
When you add a path to your
.qwenignore
file, tools that respect this file will exclude matching files and directories from their operations. For example, when you use the
read_many_files
command, any paths in your
.qwenignore
file will be automatically excluded.
For the most part,
.qwenignore
follows the conventions of
.gitignore
files:
Blank lines and lines starting with
#
are ignored.
Standard glob patterns are supported (such as
*
,
?
, and
[]
).
Putting a
/
at the end will only match directories.
Putting a
/
at the beginning anchors the path relative to the
.qwenignore
file.
!
negates a pattern.
You can update your
.qwenignore
file at any time. To apply the changes, you must restart your Qwen Code session.
How to use
.qwenignore
Step
Description
Enable .qwenignore
Create a file named
.qwenignore
in your project root directory
Add ignore rules
Open
.qwenignore
file and add paths to ignore, example:
/archive/
or
apikeys.txt
.qwenignore
examples
You can use
.qwenignore
to ignore directories and files:
# Exclude your /packages/ directory and all subdirectories
/packages/
# Exclude your apikeys.txt file
apikeys.txt
You can use wildcards in your
.qwenignore
file with
*
:
# Exclude all .md files
*.md
Finally, you can exclude files and directories from exclusion with
!
:
# Exclude all .md files except README.md
*.md
!README.md
To remove paths from your
.qwenignore
file, delete the relevant lines.
Last updated on
May 18, 2026
Authentication
Trusted Folders</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/qwen-ignore/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Ignoring Files
Ignoring Files
This document provides an overview of the Qwen Ignore (
.qwenignore
) feature of Qwen Code.
Qwen Code includes the ability to automatically ignore files, similar to
.gitignore
(used by Git). Adding paths to your
.qwenignore
file will exclude them from tools that support this feature, although they will still be visible to other services (such as Git).
How it works
When you add a path to your
.qwenignore
file, tools that respect this file wil...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Ignoring Files
Ignoring Files
This document provides an overview of the Qwen Ignore (
.qwenignore
) feature of Qwen Code.
Qwen Code includes the ability to automatically ignore files, similar to
.gitignore
(used by Git). Adding paths to your
.qwenignore
file will exclude them from tools that support this feature, although they will still be visible to other services (such as Git).
How it works
When you add a path to your
.qwenignore
file, tools that respect this file will exclude matching files and directories from their operations. For example, when you use the
read_many_files
command, any paths in your
.qwenignore
file will be automatically excluded.
For the most part,
.qwenignore
follows the conventions of
.gitignore
files:
Blank lines and lines starting with
#
are ignored.
Standard glob patterns are supported (such as
*
,
?
, and
[]
).
Putting a
/
at the end will only match directories.
Putting a
/
at the beginning anchors the path relative to the
.qwenignore
file.
!
negates a pattern.
You can update your
.qwenignore
file at any time. To apply the changes, you must restart your Qwen Code session.
How to use
.qwenignore
Step
Description
Enable .qwenignore
Create a file named
.qwenignore
in your project root directory
Add ignore rules
Open
.qwenignore
file and add paths to ignore, example:
/archive/
or
apikeys.txt
.qwenignore
examples
You can use
.qwenignore
to ignore directories and files:
# Exclude your /packages/ directory and all subdirectories
/packages/
# Exclude your apikeys.txt file
apikeys.txt
You can use wildcards in your
.qwenignore
file with
*
:
# Exclude all .md files
*.md
Finally, you can exclude files and directories from exclusion with
!
:
# Exclude all .md files except README.md
*.md
!README.md
To remove paths from your
.qwenignore
file, delete the relevant lines.
Last updated on
May 18, 2026
Authentication
Trusted Folders</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/qwen-ignore/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Approval Mode</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/approval-mode/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/approval-mode/</guid>
  <pubDate>Thu, 03 Oct 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Approval Mode
Approval Mode
Qwen Code offers four distinct permission modes that allow you to flexibly control how AI interacts with your code and system based on task complexity and risk level.
Permission Modes Comparison
Mode
File Editing
Shell Commands
Best For
Risk Level
Plan
​
❌ Read-only analysis only
❌ Not executed
• Code exploration
• Planning complex changes
• Safe code review
Lowest
Default
​
✅ Manual approval required
✅ Manual approval required
• New/unfamiliar cod...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Approval Mode
Approval Mode
Qwen Code offers four distinct permission modes that allow you to flexibly control how AI interacts with your code and system based on task complexity and risk level.
Permission Modes Comparison
Mode
File Editing
Shell Commands
Best For
Risk Level
Plan
​
❌ Read-only analysis only
❌ Not executed
• Code exploration
• Planning complex changes
• Safe code review
Lowest
Default
​
✅ Manual approval required
✅ Manual approval required
• New/unfamiliar codebases
• Critical systems
• Team collaboration
• Learning and teaching
Low
Auto-Edit
​
✅ Auto-approved
❌ Manual approval required
• Daily development tasks
• Refactoring and code improvements
• Safe automation
Medium
YOLO
​
✅ Auto-approved
✅ Auto-approved
• Trusted personal projects
• Automated scripts/CI/CD
• Batch processing tasks
Highest
Quick Reference Guide
Start in Plan Mode
: Great for understanding before making changes
Work in Default Mode
: The balanced choice for most development work
Switch to Auto-Edit
: When you’re making lots of safe code changes
Use YOLO sparingly
: Only for trusted automation in controlled environments
Tip
You can quickly cycle through modes during a session using
Shift+Tab
(or
Tab
on Windows). The terminal status bar shows your current mode, so you always know what permissions Qwen Code has.
1. Use Plan Mode for safe code analysis
Plan Mode instructs Qwen Code to create a plan by analyzing the codebase with
read-only
operations, perfect for exploring codebases, planning complex changes, or reviewing code safely.
When to use Plan Mode
Multi-step implementation
: When your feature requires making edits to many files
Code exploration
: When you want to research the codebase thoroughly before changing anything
Interactive development
: When you want to iterate on the direction with Qwen Code
How to use Plan Mode
Turn on Plan Mode during a session
You can switch into Plan Mode during a session using
Shift+Tab
(or
Tab
on Windows) to cycle through permission modes.
If you are in Normal Mode,
Shift+Tab
(or
Tab
on Windows) first switches into
auto-edits
Mode, indicated by
⏵⏵ accept edits on
at the bottom of the terminal. A subsequent
Shift+Tab
(or
Tab
on Windows) will switch into Plan Mode, indicated by
⏸ plan mode
.
Use the
/plan
command
The
/plan
command provides a quick shortcut for entering and exiting Plan Mode:
/plan
# Enter plan mode
/plan
refactor
the
auth
module
# Enter plan mode and start planning
/plan
exit
# Exit plan mode, restore previous mode
When you exit Plan Mode with
/plan exit
, your previous approval mode is automatically restored (e.g., if you were in Auto-Edit before entering Plan Mode, you’ll return to Auto-Edit).
Start a new session in Plan Mode
To start a new session in Plan Mode, use the
/approval-mode
then select
plan
/approval-mode
Run “headless” queries in Plan Mode
You can also run a query in Plan Mode directly with
-p
or
prompt
:
qwen
--prompt
"What is machine learning?"
Example: Planning a complex refactor
/plan
I
need
to
refactor
our
authentication
system
to
use
OAuth2.
Create
a
detailed
migration
plan.
Qwen Code enters Plan Mode and analyzes the current implementation to create a comprehensive plan. Refine with follow-ups:
What about backward compatibility?
How should we handle database migration?
Configure Plan Mode as default
// .qwen/settings.json
{
"permissions"
: {
"defaultMode"
:
"plan"
}
}
2. Use Default Mode for Controlled Interaction
Default Mode is the standard way to work with Qwen Code. In this mode, you maintain full control over all potentially risky operations - Qwen Code will ask for your approval before making any file changes or executing shell commands.
When to use Default Mode
New to a codebase
: When you’re exploring an unfamiliar project and want to be extra cautious
Critical systems
: When working on production code, infrastructure, or sensitive data
Learning and teaching
: When you want to understand each step Qwen Code is taking
Team collaboration
: When multiple people are working on the same codebase
Complex operations
: When the changes involve multiple files or complex logic
How to use Default Mode
Turn on Default Mode during a session
You can switch into Default Mode during a session using
Shift+Tab
​ (or
Tab
on Windows) to cycle through permission modes. If you’re in any other mode, pressing
Shift+Tab
(or
Tab
on Windows) will eventually cycle back to Default Mode, indicated by the absence of any mode indicator at the bottom of the terminal.
Start a new session in Default Mode
Default Mode is the initial mode when you start Qwen Code. If you’ve changed modes and want to return to Default Mode, use:
/approval-mode default
Run “headless” queries in Default Mode
When running headless commands, Default Mode is the default behavior. You can explicitly specify it with:
qwen --prompt "Analyze this code for potential bugs"
Example: Safely implementing a feature
/approval-mode default
I need to add user profile pictures to our application. The pictures should be stored in an S3 bucket and the URLs saved in the database.
Qwen Code will analyze your codebase and propose a plan. It will then ask for approval before:
Creating new files (controllers, models, migrations)
Modifying existing files (adding new columns, updating APIs)
Running any shell commands (database migrations, dependency installation)
You can review each proposed change and approve or reject it individually.
Configure Default Mode as default
//
.qwen/settings.json
{
"permissions"
:
{
"defaultMode"
:
"default"
}
}
3. Auto Edits Mode
Auto-Edit Mode instructs Qwen Code to automatically approve file edits while requiring manual approval for shell commands, ideal for accelerating development workflows while maintaining system safety.
When to use Auto-Accept Edits Mode
Daily development
: Ideal for most coding tasks
Safe automation
: Allows AI to modify code while preventing accidental execution of dangerous commands
Team collaboration
: Use in shared projects to avoid unintended impacts on others
How to switch to this mode
# Switch via command
/approval-mode auto-edit
# Or use keyboard shortcut
Shift+Tab (or Tab on Windows) # Switch from other modes
Workflow Example
You ask Qwen Code to refactor a function
AI analyzes the code and proposes changes
Automatically
​ applies all file changes without confirmation
If tests need to be run, it will
request approval
​ to execute
npm test
4. YOLO Mode - Full Automation
YOLO Mode grants Qwen Code the highest permissions, automatically approving all tool calls including file editing and shell commands.
When to use YOLO Mode
Automated scripts
: Running predefined automated tasks
CI/CD pipelines
: Automated execution in controlled environments
Personal projects
: Rapid iteration in fully trusted environments
Batch processing
: Tasks requiring multi-step command chains
Warning
Use YOLO Mode with caution
: AI can execute any command with your terminal permissions. Ensure:
You trust the current codebase
You understand all actions AI will perform
Important files are backed up or committed to version control
How to enable YOLO Mode
# Temporarily enable (current session only)
/approval-mode yolo
# Set as project default
/approval-mode yolo --project
# Set as user global default
/approval-mode yolo --user
Configuration Example
//
.qwen/settings.json
{
"permissions"
:
{
"defaultMode"
:
"yolo",
"confirmShellCommands"
:
false
,
"confirmFileEdits"
:
false
}
}
Automated Workflow Example
# Fully automated refactoring task
qwen
--prompt
"Run the test suite, fix all failing tests, then commit changes"
# Without human intervention, AI will:
# 1. Run test commands (auto-approved)
# 2. Fix failed test cases (auto-edit files)
# 3. Execute git commit (auto-approved)
Mode Switching & Configuration
Keyboard Shortcut Switching
During a Qwen Code session, use
Shift+Tab
​ (or
Tab
on Windows) to quickly cycle through the four modes:
Default Mode → Auto-Edit Mode → YOLO Mode → Plan Mode → Default Mode
Persistent Configuration
// Project-level: ./.qwen/settings.json
// User-level: ~/.qwen/settings.json
{
"permissions": {
"defaultMode": "auto-edit",  // or "plan" or "yolo"
"confirmShellCommands": true,
"confirmFileEdits": true
}
}
Mode Usage Recommendations
New to codebase
: Start with
Plan Mode
​ for safe exploration
Daily development tasks
: Use
Auto-Accept Edits
​ (default mode), efficient and safe
Automated scripts
: Use
YOLO Mode
​ in controlled environments for full automation
Complex refactoring
: Use
Plan Mode
​ first for detailed planning, then switch to appropriate mode for execution
Last updated on
May 18, 2026
Dual Output
MCP</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/approval-mode/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Approval Mode
Approval Mode
Qwen Code offers four distinct permission modes that allow you to flexibly control how AI interacts with your code and system based on task complexity and risk level.
Permission Modes Comparison
Mode
File Editing
Shell Commands
Best For
Risk Level
Plan
​
❌ Read-only analysis only
❌ Not executed
• Code exploration
• Planning complex changes
• Safe code review
Lowest
Default
​
✅ Manual approval required
✅ Manual approval required
• New/unfamiliar cod...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Approval Mode
Approval Mode
Qwen Code offers four distinct permission modes that allow you to flexibly control how AI interacts with your code and system based on task complexity and risk level.
Permission Modes Comparison
Mode
File Editing
Shell Commands
Best For
Risk Level
Plan
​
❌ Read-only analysis only
❌ Not executed
• Code exploration
• Planning complex changes
• Safe code review
Lowest
Default
​
✅ Manual approval required
✅ Manual approval required
• New/unfamiliar codebases
• Critical systems
• Team collaboration
• Learning and teaching
Low
Auto-Edit
​
✅ Auto-approved
❌ Manual approval required
• Daily development tasks
• Refactoring and code improvements
• Safe automation
Medium
YOLO
​
✅ Auto-approved
✅ Auto-approved
• Trusted personal projects
• Automated scripts/CI/CD
• Batch processing tasks
Highest
Quick Reference Guide
Start in Plan Mode
: Great for understanding before making changes
Work in Default Mode
: The balanced choice for most development work
Switch to Auto-Edit
: When you’re making lots of safe code changes
Use YOLO sparingly
: Only for trusted automation in controlled environments
Tip
You can quickly cycle through modes during a session using
Shift+Tab
(or
Tab
on Windows). The terminal status bar shows your current mode, so you always know what permissions Qwen Code has.
1. Use Plan Mode for safe code analysis
Plan Mode instructs Qwen Code to create a plan by analyzing the codebase with
read-only
operations, perfect for exploring codebases, planning complex changes, or reviewing code safely.
When to use Plan Mode
Multi-step implementation
: When your feature requires making edits to many files
Code exploration
: When you want to research the codebase thoroughly before changing anything
Interactive development
: When you want to iterate on the direction with Qwen Code
How to use Plan Mode
Turn on Plan Mode during a session
You can switch into Plan Mode during a session using
Shift+Tab
(or
Tab
on Windows) to cycle through permission modes.
If you are in Normal Mode,
Shift+Tab
(or
Tab
on Windows) first switches into
auto-edits
Mode, indicated by
⏵⏵ accept edits on
at the bottom of the terminal. A subsequent
Shift+Tab
(or
Tab
on Windows) will switch into Plan Mode, indicated by
⏸ plan mode
.
Use the
/plan
command
The
/plan
command provides a quick shortcut for entering and exiting Plan Mode:
/plan
# Enter plan mode
/plan
refactor
the
auth
module
# Enter plan mode and start planning
/plan
exit
# Exit plan mode, restore previous mode
When you exit Plan Mode with
/plan exit
, your previous approval mode is automatically restored (e.g., if you were in Auto-Edit before entering Plan Mode, you’ll return to Auto-Edit).
Start a new session in Plan Mode
To start a new session in Plan Mode, use the
/approval-mode
then select
plan
/approval-mode
Run “headless” queries in Plan Mode
You can also run a query in Plan Mode directly with
-p
or
prompt
:
qwen
--prompt
"What is machine learning?"
Example: Planning a complex refactor
/plan
I
need
to
refactor
our
authentication
system
to
use
OAuth2.
Create
a
detailed
migration
plan.
Qwen Code enters Plan Mode and analyzes the current implementation to create a comprehensive plan. Refine with follow-ups:
What about backward compatibility?
How should we handle database migration?
Configure Plan Mode as default
// .qwen/settings.json
{
"permissions"
: {
"defaultMode"
:
"plan"
}
}
2. Use Default Mode for Controlled Interaction
Default Mode is the standard way to work with Qwen Code. In this mode, you maintain full control over all potentially risky operations - Qwen Code will ask for your approval before making any file changes or executing shell commands.
When to use Default Mode
New to a codebase
: When you’re exploring an unfamiliar project and want to be extra cautious
Critical systems
: When working on production code, infrastructure, or sensitive data
Learning and teaching
: When you want to understand each step Qwen Code is taking
Team collaboration
: When multiple people are working on the same codebase
Complex operations
: When the changes involve multiple files or complex logic
How to use Default Mode
Turn on Default Mode during a session
You can switch into Default Mode during a session using
Shift+Tab
​ (or
Tab
on Windows) to cycle through permission modes. If you’re in any other mode, pressing
Shift+Tab
(or
Tab
on Windows) will eventually cycle back to Default Mode, indicated by the absence of any mode indicator at the bottom of the terminal.
Start a new session in Default Mode
Default Mode is the initial mode when you start Qwen Code. If you’ve changed modes and want to return to Default Mode, use:
/approval-mode default
Run “headless” queries in Default Mode
When running headless commands, Default Mode is the default behavior. You can explicitly specify it with:
qwen --prompt "Analyze this code for potential bugs"
Example: Safely implementing a feature
/approval-mode default
I need to add user profile pictures to our application. The pictures should be stored in an S3 bucket and the URLs saved in the database.
Qwen Code will analyze your codebase and propose a plan. It will then ask for approval before:
Creating new files (controllers, models, migrations)
Modifying existing files (adding new columns, updating APIs)
Running any shell commands (database migrations, dependency installation)
You can review each proposed change and approve or reject it individually.
Configure Default Mode as default
//
.qwen/settings.json
{
"permissions"
:
{
"defaultMode"
:
"default"
}
}
3. Auto Edits Mode
Auto-Edit Mode instructs Qwen Code to automatically approve file edits while requiring manual approval for shell commands, ideal for accelerating development workflows while maintaining system safety.
When to use Auto-Accept Edits Mode
Daily development
: Ideal for most coding tasks
Safe automation
: Allows AI to modify code while preventing accidental execution of dangerous commands
Team collaboration
: Use in shared projects to avoid unintended impacts on others
How to switch to this mode
# Switch via command
/approval-mode auto-edit
# Or use keyboard shortcut
Shift+Tab (or Tab on Windows) # Switch from other modes
Workflow Example
You ask Qwen Code to refactor a function
AI analyzes the code and proposes changes
Automatically
​ applies all file changes without confirmation
If tests need to be run, it will
request approval
​ to execute
npm test
4. YOLO Mode - Full Automation
YOLO Mode grants Qwen Code the highest permissions, automatically approving all tool calls including file editing and shell commands.
When to use YOLO Mode
Automated scripts
: Running predefined automated tasks
CI/CD pipelines
: Automated execution in controlled environments
Personal projects
: Rapid iteration in fully trusted environments
Batch processing
: Tasks requiring multi-step command chains
Warning
Use YOLO Mode with caution
: AI can execute any command with your terminal permissions. Ensure:
You trust the current codebase
You understand all actions AI will perform
Important files are backed up or committed to version control
How to enable YOLO Mode
# Temporarily enable (current session only)
/approval-mode yolo
# Set as project default
/approval-mode yolo --project
# Set as user global default
/approval-mode yolo --user
Configuration Example
//
.qwen/settings.json
{
"permissions"
:
{
"defaultMode"
:
"yolo",
"confirmShellCommands"
:
false
,
"confirmFileEdits"
:
false
}
}
Automated Workflow Example
# Fully automated refactoring task
qwen
--prompt
"Run the test suite, fix all failing tests, then commit changes"
# Without human intervention, AI will:
# 1. Run test commands (auto-approved)
# 2. Fix failed test cases (auto-edit files)
# 3. Execute git commit (auto-approved)
Mode Switching & Configuration
Keyboard Shortcut Switching
During a Qwen Code session, use
Shift+Tab
​ (or
Tab
on Windows) to quickly cycle through the four modes:
Default Mode → Auto-Edit Mode → YOLO Mode → Plan Mode → Default Mode
Persistent Configuration
// Project-level: ./.qwen/settings.json
// User-level: ~/.qwen/settings.json
{
"permissions": {
"defaultMode": "auto-edit",  // or "plan" or "yolo"
"confirmShellCommands": true,
"confirmFileEdits": true
}
}
Mode Usage Recommendations
New to codebase
: Start with
Plan Mode
​ for safe exploration
Daily development tasks
: Use
Auto-Accept Edits
​ (default mode), efficient and safe
Automated scripts
: Use
YOLO Mode
​ in controlled environments for full automation
Complex refactoring
: Use
Plan Mode
​ first for detailed planning, then switch to appropriate mode for execution
Last updated on
May 18, 2026
Dual Output
MCP</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/approval-mode/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Followup Suggestions</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/followup-suggestions/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/followup-suggestions/</guid>
  <pubDate>Mon, 30 Sep 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Followup Suggestions
Followup Suggestions
Qwen Code can predict what you want to type next and show it as ghost text in the input area. This feature uses an LLM call to analyze the conversation context and generate a natural next step suggestion.
This feature works end-to-end in the CLI. In the WebUI, the hook and UI plumbing are available, but host applications must trigger suggestion generation and wire the followup state for suggestions to appear.
How It Works
After Qwen C...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Followup Suggestions
Followup Suggestions
Qwen Code can predict what you want to type next and show it as ghost text in the input area. This feature uses an LLM call to analyze the conversation context and generate a natural next step suggestion.
This feature works end-to-end in the CLI. In the WebUI, the hook and UI plumbing are available, but host applications must trigger suggestion generation and wire the followup state for suggestions to appear.
How It Works
After Qwen Code finishes responding, a suggestion appears as dimmed text in the input area after a short delay (~300ms). For example, after fixing a bug, you might see:
> run the tests
The suggestion is generated by sending the conversation history to the model, which predicts what you would naturally type next. If the response contains an explicit tip (e.g.,
Tip: type post comments to publish findings
), the suggested action is extracted automatically.
Accepting Suggestions
Key
Action
Tab
Accept the suggestion and fill it into the input
Enter
Accept the suggestion and submit it immediately
Right Arrow
Accept the suggestion and fill it into the input
Any typing
Dismiss the suggestion and type normally
When Suggestions Appear
Suggestions are generated when all of the following conditions are met:
The model has completed its response (not during streaming)
At least 2 model turns have occurred in the conversation
There are no errors in the most recent response
No confirmation dialogs are pending (e.g., shell confirmation, permissions)
The approval mode is not set to
plan
The feature is enabled in settings (enabled by default)
Suggestions will not appear in non-interactive mode (e.g., headless/SDK mode).
Suggestions are automatically dismissed when:
You start typing
A new model turn begins
The suggestion is accepted
Fast Model
By default, suggestions use the same model as your main conversation. For faster and cheaper suggestions, configure a dedicated fast model:
Via command
/model --fast qwen3-coder-flash
Or use
/model --fast
(without a model name) to open a selection dialog.
Via settings.json
{
"fastModel"
:
"qwen3-coder-flash"
}
The fast model is used for prompt suggestions and speculative execution. When not configured, the main conversation model is used as fallback.
Thinking/reasoning mode is automatically disabled for all background tasks (suggestion generation and speculation), regardless of your main model’s thinking configuration. This avoids wasting tokens on internal reasoning that isn’t needed for these tasks.
Configuration
These settings can be configured in
settings.json
:
Setting
Type
Default
Description
ui.enableFollowupSuggestions
boolean
true
Enable or disable followup suggestions
ui.enableCacheSharing
boolean
true
Use cache-aware forked queries to reduce cost (experimental)
ui.enableSpeculation
boolean
false
Speculatively execute suggestions before submission (experimental)
fastModel
string
""
Model for prompt suggestions and speculative execution
Example
{
"fastModel"
:
"qwen3-coder-flash"
,
"ui"
: {
"enableFollowupSuggestions"
:
true
,
"enableCacheSharing"
:
true
}
}
Monitoring
Suggestion model usage appears in
/stats
output, showing tokens consumed by the fast model for suggestion generation.
The fast model is also shown in
/about
output under “Fast Model”.
Suggestion Quality
Suggestions go through quality filters to ensure they are useful:
Must be 2-12 words (CJK: 2-30 characters), under 100 characters total
Cannot be evaluative (“looks good”, “thanks”)
Cannot use AI voice (“Let me…”, “I’ll…”)
Cannot be multiple sentences or contain formatting (markdown, newlines)
Cannot be meta-commentary (“nothing to suggest”, “silence”)
Cannot be error messages or prefixed labels (“Suggestion: …”)
Single-word suggestions are only allowed for common commands (yes, commit, push, etc.)
Slash commands (e.g.,
/commit
) are always allowed as single-word suggestions
Last updated on
May 18, 2026
Code Review
Tool-Use Summaries</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/followup-suggestions/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Followup Suggestions
Followup Suggestions
Qwen Code can predict what you want to type next and show it as ghost text in the input area. This feature uses an LLM call to analyze the conversation context and generate a natural next step suggestion.
This feature works end-to-end in the CLI. In the WebUI, the hook and UI plumbing are available, but host applications must trigger suggestion generation and wire the followup state for suggestions to appear.
How It Works
After Qwen C...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Followup Suggestions
Followup Suggestions
Qwen Code can predict what you want to type next and show it as ghost text in the input area. This feature uses an LLM call to analyze the conversation context and generate a natural next step suggestion.
This feature works end-to-end in the CLI. In the WebUI, the hook and UI plumbing are available, but host applications must trigger suggestion generation and wire the followup state for suggestions to appear.
How It Works
After Qwen Code finishes responding, a suggestion appears as dimmed text in the input area after a short delay (~300ms). For example, after fixing a bug, you might see:
> run the tests
The suggestion is generated by sending the conversation history to the model, which predicts what you would naturally type next. If the response contains an explicit tip (e.g.,
Tip: type post comments to publish findings
), the suggested action is extracted automatically.
Accepting Suggestions
Key
Action
Tab
Accept the suggestion and fill it into the input
Enter
Accept the suggestion and submit it immediately
Right Arrow
Accept the suggestion and fill it into the input
Any typing
Dismiss the suggestion and type normally
When Suggestions Appear
Suggestions are generated when all of the following conditions are met:
The model has completed its response (not during streaming)
At least 2 model turns have occurred in the conversation
There are no errors in the most recent response
No confirmation dialogs are pending (e.g., shell confirmation, permissions)
The approval mode is not set to
plan
The feature is enabled in settings (enabled by default)
Suggestions will not appear in non-interactive mode (e.g., headless/SDK mode).
Suggestions are automatically dismissed when:
You start typing
A new model turn begins
The suggestion is accepted
Fast Model
By default, suggestions use the same model as your main conversation. For faster and cheaper suggestions, configure a dedicated fast model:
Via command
/model --fast qwen3-coder-flash
Or use
/model --fast
(without a model name) to open a selection dialog.
Via settings.json
{
"fastModel"
:
"qwen3-coder-flash"
}
The fast model is used for prompt suggestions and speculative execution. When not configured, the main conversation model is used as fallback.
Thinking/reasoning mode is automatically disabled for all background tasks (suggestion generation and speculation), regardless of your main model’s thinking configuration. This avoids wasting tokens on internal reasoning that isn’t needed for these tasks.
Configuration
These settings can be configured in
settings.json
:
Setting
Type
Default
Description
ui.enableFollowupSuggestions
boolean
true
Enable or disable followup suggestions
ui.enableCacheSharing
boolean
true
Use cache-aware forked queries to reduce cost (experimental)
ui.enableSpeculation
boolean
false
Speculatively execute suggestions before submission (experimental)
fastModel
string
""
Model for prompt suggestions and speculative execution
Example
{
"fastModel"
:
"qwen3-coder-flash"
,
"ui"
: {
"enableFollowupSuggestions"
:
true
,
"enableCacheSharing"
:
true
}
}
Monitoring
Suggestion model usage appears in
/stats
output, showing tokens consumed by the fast model for suggestion generation.
The fast model is also shown in
/about
output under “Fast Model”.
Suggestion Quality
Suggestions go through quality filters to ensure they are useful:
Must be 2-12 words (CJK: 2-30 characters), under 100 characters total
Cannot be evaluative (“looks good”, “thanks”)
Cannot use AI voice (“Let me…”, “I’ll…”)
Cannot be multiple sentences or contain formatting (markdown, newlines)
Cannot be meta-commentary (“nothing to suggest”, “silence”)
Cannot be error messages or prefixed labels (“Suggestion: …”)
Single-word suggestions are only allowed for common commands (yes, commit, push, etc.)
Slash commands (e.g.,
/commit
) are always allowed as single-word suggestions
Last updated on
May 18, 2026
Code Review
Tool-Use Summaries</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/followup-suggestions/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Extension Releasing</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/extension/extension-releasing/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/extension/extension-releasing/</guid>
  <pubDate>Sun, 29 Sep 2024 00:00:00 +0000</pubDate>
  <category>Extensions</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Extension
Extension Releasing
Extension Releasing
There are three primary ways of releasing extensions to users:
Git repository
Github Releases
npm Registry
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship p...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Extension
Extension Releasing
Extension Releasing
There are three primary ways of releasing extensions to users:
Git repository
Github Releases
npm Registry
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship platform specific binary files. npm registry releases are ideal for teams that already use npm for package distribution, especially with private registries.
Releasing through a git repository
This is the most flexible and simple option. All you need to do us create a publicly accessible git repo (such as a public github repository) and then users can install your extension using
qwen extensions install <your-repo-uri>
, or for a GitHub repository they can use the simplified
qwen extensions install <org>/<repo>
format. They can optionally depend on a specific ref (branch/tag/commit) using the
--ref=<some-ref>
argument, this defaults to the default branch.
Whenever commits are pushed to the ref that a user depends on, they will be prompted to update the extension. Note that this also allows for easy rollbacks, the HEAD commit is always treated as the latest version regardless of the actual version in the
qwen-extension.json
file.
Managing release channels using a git repository
Users can depend on any ref from your git repo, such as a branch or tag, which allows you to manage multiple release channels.
For instance, you can maintain a
stable
branch, which users can install this way
qwen extensions install <your-repo-uri> --ref=stable
. Or, you could make this the default by treating your default branch as your stable release branch, and doing development in a different branch (for instance called
dev
). You can maintain as many branches or tags as you like, providing maximum flexibility for you and your users.
Note that these
ref
arguments can be tags, branches, or even specific commits, which allows users to depend on a specific version of your extension. It is up to you how you want to manage your tags and branches.
Example releasing flow using a git repo
While there are many options for how you want to manage releases using a git flow, we recommend treating your default branch as your “stable” release branch. This means that the default behavior for
qwen extensions install <your-repo-uri>
is to be on the stable release branch.
Lets say you want to maintain three standard release channels,
stable
,
preview
, and
dev
. You would do all your standard development in the
dev
branch. When you are ready to do a preview release, you merge that branch into your
preview
branch. When you are ready to promote your preview branch to stable, you merge
preview
into your stable branch (which might be your default branch or a different branch).
You can also cherry pick changes from one branch into another using
git cherry-pick
, but do note that this will result in your branches having a slightly divergent history from each other, unless you force push changes to your branches on each release to restore the history to a clean slate (which may not be possible for the default branch depending on your repository settings). If you plan on doing cherry picks, you may want to avoid having your default branch be the stable branch to avoid force-pushing to the default branch which should generally be avoided.
Releasing through Github releases
Qwen Code extensions can be distributed through
GitHub Releases
. This provides a faster and more reliable initial installation experience for users, as it avoids the need to clone the repository.
Each release includes at least one archive file, which contains the full contents of the repo at the tag that it was linked to. Releases may also include
pre-built archives
if your extension requires some build step or has platform specific binaries attached to it.
When checking for updates, qwen code will just look for the latest release on github (you must mark it as such when creating the release), unless the user installed a specific release by passing
--ref=<some-release-tag>
. We do not at this time support opting in to pre-release releases or semver.
Custom pre-built archives
Custom archives must be attached directly to the github release as assets and must be fully self-contained. This means they should include the entire extension, see
archive structure
.
If your extension is platform-independent, you can provide a single generic asset. In this case, there should be only one asset attached to the release.
Custom archives may also be used if you want to develop your extension within a larger repository, you can build an archive which has a different layout from the repo itself (for instance it might just be an archive of a subdirectory containing the extension).
Platform specific archives
To ensure Qwen Code can automatically find the correct release asset for each platform, you must follow this naming convention. The CLI will search for assets in the following order:
Platform and Architecture-Specific:
{platform}.{arch}.{name}.{extension}
Platform-Specific:
{platform}.{name}.{extension}
Generic:
If only one asset is provided, it will be used as a generic fallback.
{name}
: The name of your extension.
{platform}
: The operating system. Supported values are:
darwin
(macOS)
linux
win32
(Windows)
{arch}
: The architecture. Supported values are:
x64
arm64
{extension}
: The file extension of the archive (e.g.,
.tar.gz
or
.zip
).
Examples:
darwin.arm64.my-tool.tar.gz
(specific to Apple Silicon Macs)
darwin.my-tool.tar.gz
(for all Macs)
linux.x64.my-tool.tar.gz
win32.my-tool.zip
Archive structure
Archives must be fully contained extensions and have all the standard requirements - specifically the
qwen-extension.json
file must be at the root of the archive.
The rest of the layout should look exactly the same as a typical extension, see
extensions.md
.
Example GitHub Actions workflow
Here is an example of a GitHub Actions workflow that builds and releases a Qwen Code extension for multiple platforms:
name
:
Release Extension
on
:
push
:
tags
:
-
'v*'
jobs
:
release
:
runs-on
:
ubuntu-latest
steps
:
-
uses
:
actions/checkout@v3
-
name
:
Set up Node.js
uses
:
actions/setup-node@v3
with
:
node-version
:
'20'
-
name
:
Install dependencies
run
:
npm ci
-
name
:
Build extension
run
:
npm run build
-
name
:
Create release assets
run
:
|
npm run package -- --platform=darwin --arch=arm64
npm run package -- --platform=linux --arch=x64
npm run package -- --platform=win32 --arch=x64
-
name
:
Create GitHub Release
uses
:
softprops/action-gh-release@v1
with
:
files
:
|
release/darwin.arm64.my-tool.tar.gz
release/linux.arm64.my-tool.tar.gz
release/win32.arm64.my-tool.zip
Releasing through npm registry
You can publish Qwen Code extensions as scoped npm packages (e.g.
@your-org/my-extension
). This is a good fit when:
Your team already uses npm for package distribution
You need private registry support with existing auth infrastructure
You want version resolution and access control handled by npm
Package requirements
Your npm package must include a
qwen-extension.json
file at the package root. This is the same config file used by all Qwen Code extensions — the npm tarball is simply another delivery mechanism.
A minimal package structure looks like:
my-extension/
├── package.json
├── qwen-extension.json
├── QWEN.md              # optional context file
├── commands/             # optional custom commands
├── skills/               # optional custom skills
└── agents/               # optional custom subagents
Make sure
qwen-extension.json
is included in your published package (i.e. not excluded by
.npmignore
or the
files
field in
package.json
).
Publishing
Use standard npm publishing tools:
# Publish to the default registry
npm
publish
# Publish to a private/custom registry
npm
publish
--registry
https://your-registry.com
Installation
Users install your extension using the scoped package name:
# Install latest version
qwen
extensions
install
@your-org/my-extension
# Install a specific version
qwen
extensions
install
@your-org/my-extension@1.2.0
# Install from a custom registry
qwen
extensions
install
@your-org/my-extension
--registry
https://your-registry.com
Update behavior
Extensions installed without a version pin (e.g.
@scope/pkg
) track the
latest
dist-tag.
Extensions installed with a dist-tag (e.g.
@scope/pkg@beta
) track that specific tag.
Extensions pinned to an exact version (e.g.
@scope/pkg@1.2.0
) are always considered up-to-date and will not prompt for updates.
Authentication for private registries
Qwen Code reads npm auth credentials automatically:
NPM_TOKEN
environment variable
— highest priority
.npmrc
file
— supports both host-level and path-scoped
_authToken
entries (e.g.
//your-registry.com/:_authToken=TOKEN
or
//pkgs.dev.azure.com/org/_packaging/feed/npm/registry/:_authToken=TOKEN
)
.npmrc
files are read from the current directory and the user’s home directory.
Managing release channels
You can use npm dist-tags to manage release channels:
# Publish a beta release
npm
publish
--tag
beta
# Users install beta channel
qwen
extensions
install
@your-org/my-extension@beta
This works similarly to git branch-based release channels but uses npm’s native dist-tag mechanism.
Last updated on
May 18, 2026
Introduction
Troubleshooting</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/extension/extension-releasing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Extension
Extension Releasing
Extension Releasing
There are three primary ways of releasing extensions to users:
Git repository
Github Releases
npm Registry
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship p...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Extension
Extension Releasing
Extension Releasing
There are three primary ways of releasing extensions to users:
Git repository
Github Releases
npm Registry
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship platform specific binary files. npm registry releases are ideal for teams that already use npm for package distribution, especially with private registries.
Releasing through a git repository
This is the most flexible and simple option. All you need to do us create a publicly accessible git repo (such as a public github repository) and then users can install your extension using
qwen extensions install <your-repo-uri>
, or for a GitHub repository they can use the simplified
qwen extensions install <org>/<repo>
format. They can optionally depend on a specific ref (branch/tag/commit) using the
--ref=<some-ref>
argument, this defaults to the default branch.
Whenever commits are pushed to the ref that a user depends on, they will be prompted to update the extension. Note that this also allows for easy rollbacks, the HEAD commit is always treated as the latest version regardless of the actual version in the
qwen-extension.json
file.
Managing release channels using a git repository
Users can depend on any ref from your git repo, such as a branch or tag, which allows you to manage multiple release channels.
For instance, you can maintain a
stable
branch, which users can install this way
qwen extensions install <your-repo-uri> --ref=stable
. Or, you could make this the default by treating your default branch as your stable release branch, and doing development in a different branch (for instance called
dev
). You can maintain as many branches or tags as you like, providing maximum flexibility for you and your users.
Note that these
ref
arguments can be tags, branches, or even specific commits, which allows users to depend on a specific version of your extension. It is up to you how you want to manage your tags and branches.
Example releasing flow using a git repo
While there are many options for how you want to manage releases using a git flow, we recommend treating your default branch as your “stable” release branch. This means that the default behavior for
qwen extensions install <your-repo-uri>
is to be on the stable release branch.
Lets say you want to maintain three standard release channels,
stable
,
preview
, and
dev
. You would do all your standard development in the
dev
branch. When you are ready to do a preview release, you merge that branch into your
preview
branch. When you are ready to promote your preview branch to stable, you merge
preview
into your stable branch (which might be your default branch or a different branch).
You can also cherry pick changes from one branch into another using
git cherry-pick
, but do note that this will result in your branches having a slightly divergent history from each other, unless you force push changes to your branches on each release to restore the history to a clean slate (which may not be possible for the default branch depending on your repository settings). If you plan on doing cherry picks, you may want to avoid having your default branch be the stable branch to avoid force-pushing to the default branch which should generally be avoided.
Releasing through Github releases
Qwen Code extensions can be distributed through
GitHub Releases
. This provides a faster and more reliable initial installation experience for users, as it avoids the need to clone the repository.
Each release includes at least one archive file, which contains the full contents of the repo at the tag that it was linked to. Releases may also include
pre-built archives
if your extension requires some build step or has platform specific binaries attached to it.
When checking for updates, qwen code will just look for the latest release on github (you must mark it as such when creating the release), unless the user installed a specific release by passing
--ref=<some-release-tag>
. We do not at this time support opting in to pre-release releases or semver.
Custom pre-built archives
Custom archives must be attached directly to the github release as assets and must be fully self-contained. This means they should include the entire extension, see
archive structure
.
If your extension is platform-independent, you can provide a single generic asset. In this case, there should be only one asset attached to the release.
Custom archives may also be used if you want to develop your extension within a larger repository, you can build an archive which has a different layout from the repo itself (for instance it might just be an archive of a subdirectory containing the extension).
Platform specific archives
To ensure Qwen Code can automatically find the correct release asset for each platform, you must follow this naming convention. The CLI will search for assets in the following order:
Platform and Architecture-Specific:
{platform}.{arch}.{name}.{extension}
Platform-Specific:
{platform}.{name}.{extension}
Generic:
If only one asset is provided, it will be used as a generic fallback.
{name}
: The name of your extension.
{platform}
: The operating system. Supported values are:
darwin
(macOS)
linux
win32
(Windows)
{arch}
: The architecture. Supported values are:
x64
arm64
{extension}
: The file extension of the archive (e.g.,
.tar.gz
or
.zip
).
Examples:
darwin.arm64.my-tool.tar.gz
(specific to Apple Silicon Macs)
darwin.my-tool.tar.gz
(for all Macs)
linux.x64.my-tool.tar.gz
win32.my-tool.zip
Archive structure
Archives must be fully contained extensions and have all the standard requirements - specifically the
qwen-extension.json
file must be at the root of the archive.
The rest of the layout should look exactly the same as a typical extension, see
extensions.md
.
Example GitHub Actions workflow
Here is an example of a GitHub Actions workflow that builds and releases a Qwen Code extension for multiple platforms:
name
:
Release Extension
on
:
push
:
tags
:
-
'v*'
jobs
:
release
:
runs-on
:
ubuntu-latest
steps
:
-
uses
:
actions/checkout@v3
-
name
:
Set up Node.js
uses
:
actions/setup-node@v3
with
:
node-version
:
'20'
-
name
:
Install dependencies
run
:
npm ci
-
name
:
Build extension
run
:
npm run build
-
name
:
Create release assets
run
:
|
npm run package -- --platform=darwin --arch=arm64
npm run package -- --platform=linux --arch=x64
npm run package -- --platform=win32 --arch=x64
-
name
:
Create GitHub Release
uses
:
softprops/action-gh-release@v1
with
:
files
:
|
release/darwin.arm64.my-tool.tar.gz
release/linux.arm64.my-tool.tar.gz
release/win32.arm64.my-tool.zip
Releasing through npm registry
You can publish Qwen Code extensions as scoped npm packages (e.g.
@your-org/my-extension
). This is a good fit when:
Your team already uses npm for package distribution
You need private registry support with existing auth infrastructure
You want version resolution and access control handled by npm
Package requirements
Your npm package must include a
qwen-extension.json
file at the package root. This is the same config file used by all Qwen Code extensions — the npm tarball is simply another delivery mechanism.
A minimal package structure looks like:
my-extension/
├── package.json
├── qwen-extension.json
├── QWEN.md              # optional context file
├── commands/             # optional custom commands
├── skills/               # optional custom skills
└── agents/               # optional custom subagents
Make sure
qwen-extension.json
is included in your published package (i.e. not excluded by
.npmignore
or the
files
field in
package.json
).
Publishing
Use standard npm publishing tools:
# Publish to the default registry
npm
publish
# Publish to a private/custom registry
npm
publish
--registry
https://your-registry.com
Installation
Users install your extension using the scoped package name:
# Install latest version
qwen
extensions
install
@your-org/my-extension
# Install a specific version
qwen
extensions
install
@your-org/my-extension@1.2.0
# Install from a custom registry
qwen
extensions
install
@your-org/my-extension
--registry
https://your-registry.com
Update behavior
Extensions installed without a version pin (e.g.
@scope/pkg
) track the
latest
dist-tag.
Extensions installed with a dist-tag (e.g.
@scope/pkg@beta
) track that specific tag.
Extensions pinned to an exact version (e.g.
@scope/pkg@1.2.0
) are always considered up-to-date and will not prompt for updates.
Authentication for private registries
Qwen Code reads npm auth credentials automatically:
NPM_TOKEN
environment variable
— highest priority
.npmrc
file
— supports both host-level and path-scoped
_authToken
entries (e.g.
//your-registry.com/:_authToken=TOKEN
or
//pkgs.dev.azure.com/org/_packaging/feed/npm/registry/:_authToken=TOKEN
)
.npmrc
files are read from the current directory and the user’s home directory.
Managing release channels
You can use npm dist-tags to manage release channels:
# Publish a beta release
npm
publish
--tag
beta
# Users install beta channel
qwen
extensions
install
@your-org/my-extension@beta
This works similarly to git branch-based release channels but uses npm’s native dist-tag mechanism.
Last updated on
May 18, 2026
Introduction
Troubleshooting</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/extension/extension-releasing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Extension Releasing</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension-releasing/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension-releasing/</guid>
  <pubDate>Tue, 17 Sep 2024 00:00:00 +0000</pubDate>
  <category>Extensions</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Extensions
Extension Releasing
Extension Releasing
There are two primary ways of releasing extensions to users:
Git repository
Github Releases
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship platform s...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Extensions
Extension Releasing
Extension Releasing
There are two primary ways of releasing extensions to users:
Git repository
Github Releases
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship platform specific binary files.
Releasing through a git repository
This is the most flexible and simple option. All you need to do us create a publicly accessible git repo (such as a public github repository) and then users can install your extension using
qwen extensions install <your-repo-uri>
, or for a GitHub repository they can use the simplified
qwen extensions install <org>/<repo>
format. They can optionally depend on a specific ref (branch/tag/commit) using the
--ref=<some-ref>
argument, this defaults to the default branch.
Whenever commits are pushed to the ref that a user depends on, they will be prompted to update the extension. Note that this also allows for easy rollbacks, the HEAD commit is always treated as the latest version regardless of the actual version in the
qwen-extension.json
file.
Managing release channels using a git repository
Users can depend on any ref from your git repo, such as a branch or tag, which allows you to manage multiple release channels.
For instance, you can maintain a
stable
branch, which users can install this way
qwen extensions install <your-repo-uri> --ref=stable
. Or, you could make this the default by treating your default branch as your stable release branch, and doing development in a different branch (for instance called
dev
). You can maintain as many branches or tags as you like, providing maximum flexibility for you and your users.
Note that these
ref
arguments can be tags, branches, or even specific commits, which allows users to depend on a specific version of your extension. It is up to you how you want to manage your tags and branches.
Example releasing flow using a git repo
While there are many options for how you want to manage releases using a git flow, we recommend treating your default branch as your “stable” release branch. This means that the default behavior for
qwen extensions install <your-repo-uri>
is to be on the stable release branch.
Lets say you want to maintain three standard release channels,
stable
,
preview
, and
dev
. You would do all your standard development in the
dev
branch. When you are ready to do a preview release, you merge that branch into your
preview
branch. When you are ready to promote your preview branch to stable, you merge
preview
into your stable branch (which might be your default branch or a different branch).
You can also cherry pick changes from one branch into another using
git cherry-pick
, but do note that this will result in your branches having a slightly divergent history from each other, unless you force push changes to your branches on each release to restore the history to a clean slate (which may not be possible for the default branch depending on your repository settings). If you plan on doing cherry picks, you may want to avoid having your default branch be the stable branch to avoid force-pushing to the default branch which should generally be avoided.
Releasing through Github releases
Qwen Code extensions can be distributed through
GitHub Releases
. This provides a faster and more reliable initial installation experience for users, as it avoids the need to clone the repository.
Each release includes at least one archive file, which contains the full contents of the repo at the tag that it was linked to. Releases may also include
pre-built archives
if your extension requires some build step or has platform specific binaries attached to it.
When checking for updates, qwen code will just look for the latest release on github (you must mark it as such when creating the release), unless the user installed a specific release by passing
--ref=<some-release-tag>
. We do not at this time support opting in to pre-release releases or semver.
Custom pre-built archives
Custom archives must be attached directly to the github release as assets and must be fully self-contained. This means they should include the entire extension, see
archive structure
.
If your extension is platform-independent, you can provide a single generic asset. In this case, there should be only one asset attached to the release.
Custom archives may also be used if you want to develop your extension within a larger repository, you can build an archive which has a different layout from the repo itself (for instance it might just be an archive of a subdirectory containing the extension).
Platform specific archives
To ensure Qwen Code can automatically find the correct release asset for each platform, you must follow this naming convention. The CLI will search for assets in the following order:
Platform and Architecture-Specific:
{platform}.{arch}.{name}.{extension}
Platform-Specific:
{platform}.{name}.{extension}
Generic:
If only one asset is provided, it will be used as a generic fallback.
{name}
: The name of your extension.
{platform}
: The operating system. Supported values are:
darwin
(macOS)
linux
win32
(Windows)
{arch}
: The architecture. Supported values are:
x64
arm64
{extension}
: The file extension of the archive (e.g.,
.tar.gz
or
.zip
).
Examples:
darwin.arm64.my-tool.tar.gz
(specific to Apple Silicon Macs)
darwin.my-tool.tar.gz
(for all Macs)
linux.x64.my-tool.tar.gz
win32.my-tool.zip
Archive structure
Archives must be fully contained extensions and have all the standard requirements - specifically the
qwen-extension.json
file must be at the root of the archive.
The rest of the layout should look exactly the same as a typical extension, see
extensions.md
.
Example GitHub Actions workflow
Here is an example of a GitHub Actions workflow that builds and releases a Qwen Code extension for multiple platforms:
name
:
Release Extension
on
:
push
:
tags
:
-
'v*'
jobs
:
release
:
runs-on
:
ubuntu-latest
steps
:
-
uses
:
actions/checkout@v3
-
name
:
Set up Node.js
uses
:
actions/setup-node@v3
with
:
node-version
:
'20'
-
name
:
Install dependencies
run
:
npm ci
-
name
:
Build extension
run
:
npm run build
-
name
:
Create release assets
run
:
|
npm run package -- --platform=darwin --arch=arm64
npm run package -- --platform=linux --arch=x64
npm run package -- --platform=win32 --arch=x64
-
name
:
Create GitHub Release
uses
:
softprops/action-gh-release@v1
with
:
files
:
|
release/darwin.arm64.my-tool.tar.gz
release/linux.arm64.my-tool.tar.gz
release/win32.arm64.my-tool.zip
Last updated on
May 18, 2026
Qwen Code Extensions
Getting Started with Qwen Code Extensions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension-releasing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Extensions
Extension Releasing
Extension Releasing
There are two primary ways of releasing extensions to users:
Git repository
Github Releases
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship platform s...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Extensions
Extension Releasing
Extension Releasing
There are two primary ways of releasing extensions to users:
Git repository
Github Releases
Git repository releases tend to be the simplest and most flexible approach, while GitHub releases can be more efficient on initial install as they are shipped as single archives instead of requiring a git clone which downloads each file individually. Github releases may also contain platform specific archives if you need to ship platform specific binary files.
Releasing through a git repository
This is the most flexible and simple option. All you need to do us create a publicly accessible git repo (such as a public github repository) and then users can install your extension using
qwen extensions install <your-repo-uri>
, or for a GitHub repository they can use the simplified
qwen extensions install <org>/<repo>
format. They can optionally depend on a specific ref (branch/tag/commit) using the
--ref=<some-ref>
argument, this defaults to the default branch.
Whenever commits are pushed to the ref that a user depends on, they will be prompted to update the extension. Note that this also allows for easy rollbacks, the HEAD commit is always treated as the latest version regardless of the actual version in the
qwen-extension.json
file.
Managing release channels using a git repository
Users can depend on any ref from your git repo, such as a branch or tag, which allows you to manage multiple release channels.
For instance, you can maintain a
stable
branch, which users can install this way
qwen extensions install <your-repo-uri> --ref=stable
. Or, you could make this the default by treating your default branch as your stable release branch, and doing development in a different branch (for instance called
dev
). You can maintain as many branches or tags as you like, providing maximum flexibility for you and your users.
Note that these
ref
arguments can be tags, branches, or even specific commits, which allows users to depend on a specific version of your extension. It is up to you how you want to manage your tags and branches.
Example releasing flow using a git repo
While there are many options for how you want to manage releases using a git flow, we recommend treating your default branch as your “stable” release branch. This means that the default behavior for
qwen extensions install <your-repo-uri>
is to be on the stable release branch.
Lets say you want to maintain three standard release channels,
stable
,
preview
, and
dev
. You would do all your standard development in the
dev
branch. When you are ready to do a preview release, you merge that branch into your
preview
branch. When you are ready to promote your preview branch to stable, you merge
preview
into your stable branch (which might be your default branch or a different branch).
You can also cherry pick changes from one branch into another using
git cherry-pick
, but do note that this will result in your branches having a slightly divergent history from each other, unless you force push changes to your branches on each release to restore the history to a clean slate (which may not be possible for the default branch depending on your repository settings). If you plan on doing cherry picks, you may want to avoid having your default branch be the stable branch to avoid force-pushing to the default branch which should generally be avoided.
Releasing through Github releases
Qwen Code extensions can be distributed through
GitHub Releases
. This provides a faster and more reliable initial installation experience for users, as it avoids the need to clone the repository.
Each release includes at least one archive file, which contains the full contents of the repo at the tag that it was linked to. Releases may also include
pre-built archives
if your extension requires some build step or has platform specific binaries attached to it.
When checking for updates, qwen code will just look for the latest release on github (you must mark it as such when creating the release), unless the user installed a specific release by passing
--ref=<some-release-tag>
. We do not at this time support opting in to pre-release releases or semver.
Custom pre-built archives
Custom archives must be attached directly to the github release as assets and must be fully self-contained. This means they should include the entire extension, see
archive structure
.
If your extension is platform-independent, you can provide a single generic asset. In this case, there should be only one asset attached to the release.
Custom archives may also be used if you want to develop your extension within a larger repository, you can build an archive which has a different layout from the repo itself (for instance it might just be an archive of a subdirectory containing the extension).
Platform specific archives
To ensure Qwen Code can automatically find the correct release asset for each platform, you must follow this naming convention. The CLI will search for assets in the following order:
Platform and Architecture-Specific:
{platform}.{arch}.{name}.{extension}
Platform-Specific:
{platform}.{name}.{extension}
Generic:
If only one asset is provided, it will be used as a generic fallback.
{name}
: The name of your extension.
{platform}
: The operating system. Supported values are:
darwin
(macOS)
linux
win32
(Windows)
{arch}
: The architecture. Supported values are:
x64
arm64
{extension}
: The file extension of the archive (e.g.,
.tar.gz
or
.zip
).
Examples:
darwin.arm64.my-tool.tar.gz
(specific to Apple Silicon Macs)
darwin.my-tool.tar.gz
(for all Macs)
linux.x64.my-tool.tar.gz
win32.my-tool.zip
Archive structure
Archives must be fully contained extensions and have all the standard requirements - specifically the
qwen-extension.json
file must be at the root of the archive.
The rest of the layout should look exactly the same as a typical extension, see
extensions.md
.
Example GitHub Actions workflow
Here is an example of a GitHub Actions workflow that builds and releases a Qwen Code extension for multiple platforms:
name
:
Release Extension
on
:
push
:
tags
:
-
'v*'
jobs
:
release
:
runs-on
:
ubuntu-latest
steps
:
-
uses
:
actions/checkout@v3
-
name
:
Set up Node.js
uses
:
actions/setup-node@v3
with
:
node-version
:
'20'
-
name
:
Install dependencies
run
:
npm ci
-
name
:
Build extension
run
:
npm run build
-
name
:
Create release assets
run
:
|
npm run package -- --platform=darwin --arch=arm64
npm run package -- --platform=linux --arch=x64
npm run package -- --platform=win32 --arch=x64
-
name
:
Create GitHub Release
uses
:
softprops/action-gh-release@v1
with
:
files
:
|
release/darwin.arm64.my-tool.tar.gz
release/linux.arm64.my-tool.tar.gz
release/win32.arm64.my-tool.zip
Last updated on
May 18, 2026
Qwen Code Extensions
Getting Started with Qwen Code Extensions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension-releasing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Session Title Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/session-title/session-title-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/session-title/session-title-design/</guid>
  <pubDate>Tue, 17 Sep 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Session Title
Session Title Design
Session Title Design
A 3-7 word sentence-case session title generated by the fast model after
the first assistant turn. Persisted in the session JSONL with a
titleSource: &apos;auto&apos; | &apos;manual&apos;
tag, surfaced in the session picker,
and regeneratable on demand via
/rename --auto
.
Overview
/rename
(#3093) lets a user label a session so they can find it again in
the picker later, but until they run it the picker shows the first user
prompt — often truncated mid-...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Session Title
Session Title Design
Session Title Design
A 3-7 word sentence-case session title generated by the fast model after
the first assistant turn. Persisted in the session JSONL with a
titleSource: 'auto' | 'manual'
tag, surfaced in the session picker,
and regeneratable on demand via
/rename --auto
.
Overview
/rename
(#3093) lets a user label a session so they can find it again in
the picker later, but until they run it the picker shows the first user
prompt — often truncated mid-sentence, or describing a framing question
rather than what the session actually became about. Manual renaming is
optional friction most users never do.
The goal is to make session names
useful by default
:
Descriptive
of what the session actually accomplished, not just the
opening line. 3-7 words, sentence case, git-commit-subject style.
Best-effort
: fires in the background after the first reply; if it
fails the user never sees an error.
Deferential to the user
: never clobber a
/rename
title the user
chose deliberately, even across CLI tabs on the same session.
Explicitly regeneratable
via
/rename --auto
for the “auto title
became stale / I want a fresh one” case.
Triggers
Trigger
Conditions
Implementation
Auto
After
recordAssistantTurn
fires. Skipped if an existing title is set, another attempt is in-flight, cap reached, non-interactive, env disabled, or no fast model.
ChatRecordingService.maybeTriggerAutoTitle
— fire-and-forget
Manual
User runs
/rename --auto
renameCommand.ts
via
tryGenerateSessionTitle
Both paths funnel into a single function —
tryGenerateSessionTitle(config, signal)
— to guarantee identical prompt, schema, model selection, and
sanitization. The auto trigger is a best-effort background call; the
manual
/rename --auto
is a blocking user action that surfaces a
reason-specific error on failure.
Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│                        packages/core/src/services/                      │
│                                                                         │
│  ┌──────────────────────────┐                                           │
│  │ chatRecordingService.ts  │                                           │
│  │                          │                                           │
│  │  recordAssistantTurn()   │                                           │
│  │     │                    │                                           │
│  │     ↓                    │                                           │
│  │  maybeTriggerAutoTitle() │── 6 guards ──→ IIFE(autoTitleController)  │
│  │     │                    │                       │                   │
│  │     └── resume hydrate   │                       ↓                   │
│  │         via              │          tryGenerateSessionTitle          │
│  │         getSessionTitle- │          (sessionTitle.ts)                │
│  │         Info             │                       │                   │
│  │                          │                       ↓                   │
│  └──────────────────────────┘          BaseLlmClient.generateJson       │
│                                        (fastModel + JSON schema)        │
│                                                       │                 │
│  ┌──────────────────────────┐                         ↓                 │
│  │ sessionService.ts        │         sanitizeTitle + sanity checks     │
│  │                          │                         │                 │
│  │  getSessionTitleInfo()   │◀── cross-process        ↓                 │
│  │      uses                │    re-read             recordCustomTitle  │
│  │  readLastJsonString-     │    before write        (…, 'auto')        │
│  │  FieldsSync              │                                           │
│  │  (sessionStorageUtils)   │                                           │
│  └──────────────────────────┘                                           │
│                                                                         │
│                          ┌─────────────────────┐                        │
│                          │ utils/terminalSafe  │                        │
│                          │ stripTerminalCtrl-  │                        │
│                          │ Sequences           │                        │
│                          └─────────────────────┘                        │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                     packages/cli/src/ui/                                │
│                                                                         │
│  commands/renameCommand.ts     ─── /rename <name>          → manual      │
│                                ─── /rename                 → kebab       │
│                                ─── /rename --auto          → auto       │
│                                ─── /rename -- --literal    → manual     │
│                                ─── /rename --unknown-flag  → error      │
│                                                                         │
│  components/SessionPicker.tsx  ── dims rows where                       │
│                                   session.titleSource === 'auto'        │
└─────────────────────────────────────────────────────────────────────────┘
Files
File
Responsibility
packages/core/src/services/sessionTitle.ts
One-shot LLM call + history filter + sanitize. Exports
tryGenerateSessionTitle
.
packages/core/src/services/chatRecordingService.ts
maybeTriggerAutoTitle
trigger, guards, cross-process re-read, abort-on-finalize.
packages/core/src/services/sessionService.ts
getSessionTitleInfo
public accessor;
renameSession
accepts
titleSource
.
packages/core/src/utils/sessionStorageUtils.ts
extractLastJsonStringFields
+
readLastJsonStringFieldsSync
atomic pair reader.
packages/core/src/utils/terminalSafe.ts
stripTerminalControlSequences
shared by sentence-case and kebab paths.
packages/cli/src/ui/commands/renameCommand.ts
/rename --auto
, sentinel parser, failure-reason message map.
packages/cli/src/ui/components/SessionPicker.tsx
Dim styling for
titleSource === 'auto'
.
Prompt Design
System Prompt
Replaces the main agent’s system prompt for this single call so the model
only tries to label the session, not behave as a coding assistant.
Bullets below correspond 1:1 with
TITLE_SYSTEM_PROMPT
:
3-7 words, sentence case (only first word and proper nouns capitalized).
No trailing punctuation, no markdown, no quotes.
Match the dominant language of the conversation; for Chinese, budget
roughly 12-20 characters.
Be specific about the user’s actual goal — name the feature, bug, or
subject area. Avoid vague catch-alls like “Code changes” or “Help
request”.
Four good examples (three English + one Chinese) and four bad examples
(too vague / too long / wrong case / trailing punctuation).
Return only a JSON object with a single
title
key.
Structured Output (JSON schema)
Instead of wrapping output in tags (as session-recap does), we use
BaseLlmClient.generateJson
with a function-calling schema:
const
TITLE_SCHEMA
=
{
type:
'object'
,
properties: {
title: {
type:
'string'
,
description:
'A concise sentence-case session title, 3-7 words, no trailing punctuation.'
,
},
},
required: [
'title'
],
};
Why function calling rather than free text + tag extraction:
Cross-provider reliability — OpenAI-compatible endpoints, Gemini, and
Qwen’s native tool-calling all implement function calling; tag parsing
would rely on every model respecting a text convention.
No reasoning-preamble leakage — the function call arguments come back
structured, so a “thinking” paragraph before the answer can’t bleed
into the title.
Simpler post-processing — a single
typeof result.title === 'string'
check plus
sanitizeTitle
covers every realistic model drift.
The model may still return something the schema allows but the UX
rejects (empty string, whitespace-only, 500 chars, markdown fencing,
control chars).
sanitizeTitle
handles all of these and returns
''
→
service returns
{ok: false, reason: 'empty_result'}
.
Call Parameters
Parameter
Value
Reason
model
getFastModel()
— no fallback
Auto-titling on main-model tokens is too expensive to be silent.
schema
TITLE_SCHEMA
Forces
{title: string}
; filters shape drift at the transport layer.
maxOutputTokens
100
More than enough for 7 words plus schema overhead.
temperature
0.2
Mostly deterministic — session titles benefit from stability across regeneration.
maxAttempts
1
Titles are best-effort cosmetic metadata; retries would queue behind user-visible main traffic.
Contrast with session-recap, which falls back to the main model. Title
generation is triggered automatically and often; silently spending
main-model tokens without a user opt-in is a real bill surprise. Manual
/rename --auto
explicitly fails with
no_fast_model
rather than
fallback — forcing the user to make the fast-model choice consciously.
History Filtering
geminiClient.getChat().getHistory()
returns
Content[]
that includes
tool calls, tool responses (often 10K+ tokens of file content), and model
thought parts. Feeding that raw into the title LLM would bias the label
toward implementation noise like “Called grep on auth module”.
filterToDialog
keeps only
user
/
model
entries with non-empty text
and no
thought
/
thoughtSignature
parts.
takeRecentDialog
slices to
the last 20 messages and refuses to start on a dangling model/tool
response.
flattenToTail
converts to “Role: text” lines and slices the
last 1000 characters.
The 1000-character tail slice
A session that starts with
help me debug X
but pivots to refactoring Y
should be titled about Y. Titling by the head locks in the opening
framing; titling by the tail captures what the session became.
UTF-16 surrogate handling
.slice(-1000)
on a UTF-16 code-unit boundary can orphan a high or low
surrogate if a CJK supplementary char or emoji gets cut. Some providers
respond to the resulting invalid UTF-16 with a 400 — which, without
handling, would burn an attempt for no reason.
flattenToTail
drops a
leading orphaned low surrogate;
sanitizeTitle
scrubs any orphaned
surrogate after the max-length trim on the output path too.
Persistence
Record shape
CustomTitleRecordPayload
grows an optional
titleSource: 'auto' | 'manual'
field:
{
"type"
:
"system"
,
"subtype"
:
"custom_title"
,
"systemPayload"
: {
"customTitle"
:
"Debug login button on mobile"
,
"titleSource"
:
"auto"
,
},
}
The field is optional, and absent-in-legacy records are treated as
undefined
.
SessionPicker
dims rows only on a strict
=== 'auto'
match — a pre-change user
/rename
title is never silently reclassified
as a model guess.
Resume hydration
On resume,
ChatRecordingService
constructor calls
sessionService.getSessionTitleInfo(sessionId)
to read
both
the
title and its source. Without hydrating the source,
finalize()
’s
re-append (which runs on every session lifecycle event) would rewrite
auto as manual on every resume cycle — silently stripping the dim
affordance.
Atomic pair read
extractLastJsonStringFields
returns
customTitle
and
titleSource
from the
same matching line
in a single scan. Two separate
readLastJsonStringFieldSync
calls could land on different records if
an older line has only the primary field, yielding a mismatched pair.
The extractor also requires a proper closing quote on the primary value,
so a crash-truncated trailing record can’t win the latest-match race.
Full-file scan cap
Phase-2 (when the tail-window fast path misses) streams the whole file
in 64KB chunks. Capped at
MAX_FULL_SCAN_BYTES = 64 MB
so a corrupt
multi-GB JSONL can’t freeze the session picker on the main event loop.
The picker’s latency envelope survives corruption.
Symlink defense
Session reads open with
O_NOFOLLOW
(falls back to plain read-only on
Windows, where the constant is not exposed). Defense in depth so a
symlink planted in
~/.qwen/projects/<proj>/chats/
can’t redirect a
metadata read to an unrelated file.
Concurrency and Edge Cases
Trigger guard order
maybeTriggerAutoTitle
checks six conditions in this exact order — each
short-circuits the rest so the cheap ones run first:
currentCustomTitle
set → skip. Never overwrite manual / prior auto.
autoTitleController !== undefined
→ skip. One attempt at a time.
autoTitleAttempts >= 3
→ skip. Cap bounds total waste.
!config.isInteractive()
→ skip. Headless
qwen -p
/ CI never spends
fast-model tokens on a one-shot session.
autoTitleDisabledByEnv()
→ skip.
QWEN_DISABLE_AUTO_TITLE=1
explicit opt-out.
!config.getFastModel()
→ skip. No fast-model → no-op.
Why the cap is 3, not 1
The first assistant turn can be a pure tool-call with no user-visible
text (e.g. the model opens with a
grep
).
tryGenerateSessionTitle
returns
{ok: false, reason: 'empty_history'}
in that case. Without a
retry window, an entire session’s chance at a title would be burned on
turn 1 before the user said anything interesting. Cap of 3 covers the
common “first turn is noise” case while still bounding runaway retry on
a persistently failing fast model.
Cross-process manual-rename race
Two CLI tabs on the same session file can diverge in memory. Tab A runs
/rename foo
and writes
titleSource: manual
. Tab B’s
ChatRecordingService
has its own
currentCustomTitle = undefined
and
would naively overwrite with an auto title.
After the LLM call resolves, the IIFE re-reads the JSONL via
sessionService.getSessionTitleInfo
. If the file shows
source: 'manual'
, the IIFE bails AND syncs its in-memory state so
subsequent turns respect the rename too. Cost: one 64KB tail read per
successful generation; negligible.
Abort propagation on
finalize()
autoTitleController
doubles as the in-flight flag.
finalize()
(run
on session switch and process shutdown) calls
autoTitleController.abort()
before re-appending the title record. The
LLM socket is cancelled promptly; session switch doesn’t wait on a slow
fast-model call. The IIFE’s
finally
block clears
autoTitleController
only if it’s still the active one, so a finalize
mid-flight doesn’t race a concurrent
recordAssistantTurn
.
Manual
/rename
lands mid-flight
Between the IIFE’s
await
completing and the
recordCustomTitle('auto')
call, the user could
/rename foo
. The IIFE re-checks
this.currentTitleSource === 'manual'
and bails. The in-process check
AND the cross-process re-read both run; manual wins at both layers.
Configuration
User-facing knobs
Setting / env var
Default
Effect
fastModel
unset
Required for auto-titling. Unset → no-op (no main-model fallback).
QWEN_DISABLE_AUTO_TITLE=1
unset
Opt out of the auto trigger without unsetting
fastModel
.
/rename --auto
still works on request.
No
settings.json
toggle — the env var is the only user-visible
off-switch. Rationale: the feature is cosmetic and cheap; a settings
toggle would add a UI surface for something that can live as a one-time
env export for the few users who want to disable it.
Why auto doesn’t fall back to the main model
Auto-titling is triggered unconditionally after every assistant turn.
If a user without a fast model were silently charged main-model tokens
for every new session’s title, the cost delta is invisible until the
monthly bill arrives. Failing quietly (no-op, no title, no cost) is the
safer default.
/rename --auto
surfaces
no_fast_model
as an
actionable error so the user can set one if they want to.
Observability
createDebugLogger('SESSION_TITLE')
emits
debugLogger.warn
from the
generator’s catch block. Failures are fully transparent to the user —
auto-title is an auxiliary feature and never throws into the UI.
Developers can grep for the
[SESSION_TITLE]
tag in the debug log
(
~/.qwen/debug/<sessionId>.txt
;
latest.txt
symlinks to the current
session). A working end-to-end call produces no log output; a failing
one gets one WARN line with the underlying error message.
Security Hardening
The title value is rendered verbatim in the terminal (session picker)
AND persisted in a user-readable JSONL file. Both surfaces are attack
reachable if a compromised or prompt-injected fast model returns
hostile text.
Concern
Guard
ANSI / OSC-8 / CSI injection
stripTerminalControlSequences
before both JSONL write and picker render.
Clickable-link smuggle via OSC-8
Same — OSC sequences stripped as whole units, not just the ESC byte.
Invalid UTF-16 surrogates
Scrubbed in
flattenToTail
(LLM input) and
sanitizeTitle
(LLM output after max-length trim).
Subtype-line spoof via user message content
lineContains: '"subtype":"custom_title"'
— user text that happens to contain the literal phrase can’t shadow a real record.
Symlink redirect on session reads
O_NOFOLLOW
(no-op on Windows where the constant is missing).
Truncated trailing JSONL record
extractLastJsonStringFields
requires a closing quote before a record wins the latest-match race.
Pathological file size freezing the picker
MAX_FULL_SCAN_BYTES = 64 MB
cap on Phase-2 full-file scan.
Paired CJK bracket decorators (
【Draft】
)
Stripped as a unit so a lone closing bracket doesn’t dangle.
Out of Scope
Item
Why not
Auto-regenerate when the title goes stale
/rename --auto
is the explicit user-triggered path. Silent mid-session title swaps would confuse users scrolling back through the picker.
WebUI / VSCode dim-styling parity
Those surfaces read
customTitle
already and will show auto titles as if manual. A follow-up can wire the
titleSource
through.
Settings-dialog toggle for auto generation
Env var is the single knob. Full settings UI is easy to add later if user demand surfaces.
i18n locale catalog entries for new strings
Consistent with existing
/rename
strings, which fall through to English. A repo-wide i18n pass is out of scope.
Migration to re-classify legacy records
Back-compat by design: absent
titleSource
is treated as manual. Rewriting old records would risk losing user intent.
Non-interactive auto-titling
qwen -p
/ CI scripts throw the session away; fast-model tokens for a title no one will ever resume is pure waste.
Last updated on
May 18, 2026
Session Recap Design
Qwen Code Command 模块重构方案</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/session-title/session-title-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Session Title
Session Title Design
Session Title Design
A 3-7 word sentence-case session title generated by the fast model after
the first assistant turn. Persisted in the session JSONL with a
titleSource: &apos;auto&apos; | &apos;manual&apos;
tag, surfaced in the session picker,
and regeneratable on demand via
/rename --auto
.
Overview
/rename
(#3093) lets a user label a session so they can find it again in
the picker later, but until they run it the picker shows the first user
prompt — often truncated mid-...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Session Title
Session Title Design
Session Title Design
A 3-7 word sentence-case session title generated by the fast model after
the first assistant turn. Persisted in the session JSONL with a
titleSource: 'auto' | 'manual'
tag, surfaced in the session picker,
and regeneratable on demand via
/rename --auto
.
Overview
/rename
(#3093) lets a user label a session so they can find it again in
the picker later, but until they run it the picker shows the first user
prompt — often truncated mid-sentence, or describing a framing question
rather than what the session actually became about. Manual renaming is
optional friction most users never do.
The goal is to make session names
useful by default
:
Descriptive
of what the session actually accomplished, not just the
opening line. 3-7 words, sentence case, git-commit-subject style.
Best-effort
: fires in the background after the first reply; if it
fails the user never sees an error.
Deferential to the user
: never clobber a
/rename
title the user
chose deliberately, even across CLI tabs on the same session.
Explicitly regeneratable
via
/rename --auto
for the “auto title
became stale / I want a fresh one” case.
Triggers
Trigger
Conditions
Implementation
Auto
After
recordAssistantTurn
fires. Skipped if an existing title is set, another attempt is in-flight, cap reached, non-interactive, env disabled, or no fast model.
ChatRecordingService.maybeTriggerAutoTitle
— fire-and-forget
Manual
User runs
/rename --auto
renameCommand.ts
via
tryGenerateSessionTitle
Both paths funnel into a single function —
tryGenerateSessionTitle(config, signal)
— to guarantee identical prompt, schema, model selection, and
sanitization. The auto trigger is a best-effort background call; the
manual
/rename --auto
is a blocking user action that surfaces a
reason-specific error on failure.
Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│                        packages/core/src/services/                      │
│                                                                         │
│  ┌──────────────────────────┐                                           │
│  │ chatRecordingService.ts  │                                           │
│  │                          │                                           │
│  │  recordAssistantTurn()   │                                           │
│  │     │                    │                                           │
│  │     ↓                    │                                           │
│  │  maybeTriggerAutoTitle() │── 6 guards ──→ IIFE(autoTitleController)  │
│  │     │                    │                       │                   │
│  │     └── resume hydrate   │                       ↓                   │
│  │         via              │          tryGenerateSessionTitle          │
│  │         getSessionTitle- │          (sessionTitle.ts)                │
│  │         Info             │                       │                   │
│  │                          │                       ↓                   │
│  └──────────────────────────┘          BaseLlmClient.generateJson       │
│                                        (fastModel + JSON schema)        │
│                                                       │                 │
│  ┌──────────────────────────┐                         ↓                 │
│  │ sessionService.ts        │         sanitizeTitle + sanity checks     │
│  │                          │                         │                 │
│  │  getSessionTitleInfo()   │◀── cross-process        ↓                 │
│  │      uses                │    re-read             recordCustomTitle  │
│  │  readLastJsonString-     │    before write        (…, 'auto')        │
│  │  FieldsSync              │                                           │
│  │  (sessionStorageUtils)   │                                           │
│  └──────────────────────────┘                                           │
│                                                                         │
│                          ┌─────────────────────┐                        │
│                          │ utils/terminalSafe  │                        │
│                          │ stripTerminalCtrl-  │                        │
│                          │ Sequences           │                        │
│                          └─────────────────────┘                        │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                     packages/cli/src/ui/                                │
│                                                                         │
│  commands/renameCommand.ts     ─── /rename <name>          → manual      │
│                                ─── /rename                 → kebab       │
│                                ─── /rename --auto          → auto       │
│                                ─── /rename -- --literal    → manual     │
│                                ─── /rename --unknown-flag  → error      │
│                                                                         │
│  components/SessionPicker.tsx  ── dims rows where                       │
│                                   session.titleSource === 'auto'        │
└─────────────────────────────────────────────────────────────────────────┘
Files
File
Responsibility
packages/core/src/services/sessionTitle.ts
One-shot LLM call + history filter + sanitize. Exports
tryGenerateSessionTitle
.
packages/core/src/services/chatRecordingService.ts
maybeTriggerAutoTitle
trigger, guards, cross-process re-read, abort-on-finalize.
packages/core/src/services/sessionService.ts
getSessionTitleInfo
public accessor;
renameSession
accepts
titleSource
.
packages/core/src/utils/sessionStorageUtils.ts
extractLastJsonStringFields
+
readLastJsonStringFieldsSync
atomic pair reader.
packages/core/src/utils/terminalSafe.ts
stripTerminalControlSequences
shared by sentence-case and kebab paths.
packages/cli/src/ui/commands/renameCommand.ts
/rename --auto
, sentinel parser, failure-reason message map.
packages/cli/src/ui/components/SessionPicker.tsx
Dim styling for
titleSource === 'auto'
.
Prompt Design
System Prompt
Replaces the main agent’s system prompt for this single call so the model
only tries to label the session, not behave as a coding assistant.
Bullets below correspond 1:1 with
TITLE_SYSTEM_PROMPT
:
3-7 words, sentence case (only first word and proper nouns capitalized).
No trailing punctuation, no markdown, no quotes.
Match the dominant language of the conversation; for Chinese, budget
roughly 12-20 characters.
Be specific about the user’s actual goal — name the feature, bug, or
subject area. Avoid vague catch-alls like “Code changes” or “Help
request”.
Four good examples (three English + one Chinese) and four bad examples
(too vague / too long / wrong case / trailing punctuation).
Return only a JSON object with a single
title
key.
Structured Output (JSON schema)
Instead of wrapping output in tags (as session-recap does), we use
BaseLlmClient.generateJson
with a function-calling schema:
const
TITLE_SCHEMA
=
{
type:
'object'
,
properties: {
title: {
type:
'string'
,
description:
'A concise sentence-case session title, 3-7 words, no trailing punctuation.'
,
},
},
required: [
'title'
],
};
Why function calling rather than free text + tag extraction:
Cross-provider reliability — OpenAI-compatible endpoints, Gemini, and
Qwen’s native tool-calling all implement function calling; tag parsing
would rely on every model respecting a text convention.
No reasoning-preamble leakage — the function call arguments come back
structured, so a “thinking” paragraph before the answer can’t bleed
into the title.
Simpler post-processing — a single
typeof result.title === 'string'
check plus
sanitizeTitle
covers every realistic model drift.
The model may still return something the schema allows but the UX
rejects (empty string, whitespace-only, 500 chars, markdown fencing,
control chars).
sanitizeTitle
handles all of these and returns
''
→
service returns
{ok: false, reason: 'empty_result'}
.
Call Parameters
Parameter
Value
Reason
model
getFastModel()
— no fallback
Auto-titling on main-model tokens is too expensive to be silent.
schema
TITLE_SCHEMA
Forces
{title: string}
; filters shape drift at the transport layer.
maxOutputTokens
100
More than enough for 7 words plus schema overhead.
temperature
0.2
Mostly deterministic — session titles benefit from stability across regeneration.
maxAttempts
1
Titles are best-effort cosmetic metadata; retries would queue behind user-visible main traffic.
Contrast with session-recap, which falls back to the main model. Title
generation is triggered automatically and often; silently spending
main-model tokens without a user opt-in is a real bill surprise. Manual
/rename --auto
explicitly fails with
no_fast_model
rather than
fallback — forcing the user to make the fast-model choice consciously.
History Filtering
geminiClient.getChat().getHistory()
returns
Content[]
that includes
tool calls, tool responses (often 10K+ tokens of file content), and model
thought parts. Feeding that raw into the title LLM would bias the label
toward implementation noise like “Called grep on auth module”.
filterToDialog
keeps only
user
/
model
entries with non-empty text
and no
thought
/
thoughtSignature
parts.
takeRecentDialog
slices to
the last 20 messages and refuses to start on a dangling model/tool
response.
flattenToTail
converts to “Role: text” lines and slices the
last 1000 characters.
The 1000-character tail slice
A session that starts with
help me debug X
but pivots to refactoring Y
should be titled about Y. Titling by the head locks in the opening
framing; titling by the tail captures what the session became.
UTF-16 surrogate handling
.slice(-1000)
on a UTF-16 code-unit boundary can orphan a high or low
surrogate if a CJK supplementary char or emoji gets cut. Some providers
respond to the resulting invalid UTF-16 with a 400 — which, without
handling, would burn an attempt for no reason.
flattenToTail
drops a
leading orphaned low surrogate;
sanitizeTitle
scrubs any orphaned
surrogate after the max-length trim on the output path too.
Persistence
Record shape
CustomTitleRecordPayload
grows an optional
titleSource: 'auto' | 'manual'
field:
{
"type"
:
"system"
,
"subtype"
:
"custom_title"
,
"systemPayload"
: {
"customTitle"
:
"Debug login button on mobile"
,
"titleSource"
:
"auto"
,
},
}
The field is optional, and absent-in-legacy records are treated as
undefined
.
SessionPicker
dims rows only on a strict
=== 'auto'
match — a pre-change user
/rename
title is never silently reclassified
as a model guess.
Resume hydration
On resume,
ChatRecordingService
constructor calls
sessionService.getSessionTitleInfo(sessionId)
to read
both
the
title and its source. Without hydrating the source,
finalize()
’s
re-append (which runs on every session lifecycle event) would rewrite
auto as manual on every resume cycle — silently stripping the dim
affordance.
Atomic pair read
extractLastJsonStringFields
returns
customTitle
and
titleSource
from the
same matching line
in a single scan. Two separate
readLastJsonStringFieldSync
calls could land on different records if
an older line has only the primary field, yielding a mismatched pair.
The extractor also requires a proper closing quote on the primary value,
so a crash-truncated trailing record can’t win the latest-match race.
Full-file scan cap
Phase-2 (when the tail-window fast path misses) streams the whole file
in 64KB chunks. Capped at
MAX_FULL_SCAN_BYTES = 64 MB
so a corrupt
multi-GB JSONL can’t freeze the session picker on the main event loop.
The picker’s latency envelope survives corruption.
Symlink defense
Session reads open with
O_NOFOLLOW
(falls back to plain read-only on
Windows, where the constant is not exposed). Defense in depth so a
symlink planted in
~/.qwen/projects/<proj>/chats/
can’t redirect a
metadata read to an unrelated file.
Concurrency and Edge Cases
Trigger guard order
maybeTriggerAutoTitle
checks six conditions in this exact order — each
short-circuits the rest so the cheap ones run first:
currentCustomTitle
set → skip. Never overwrite manual / prior auto.
autoTitleController !== undefined
→ skip. One attempt at a time.
autoTitleAttempts >= 3
→ skip. Cap bounds total waste.
!config.isInteractive()
→ skip. Headless
qwen -p
/ CI never spends
fast-model tokens on a one-shot session.
autoTitleDisabledByEnv()
→ skip.
QWEN_DISABLE_AUTO_TITLE=1
explicit opt-out.
!config.getFastModel()
→ skip. No fast-model → no-op.
Why the cap is 3, not 1
The first assistant turn can be a pure tool-call with no user-visible
text (e.g. the model opens with a
grep
).
tryGenerateSessionTitle
returns
{ok: false, reason: 'empty_history'}
in that case. Without a
retry window, an entire session’s chance at a title would be burned on
turn 1 before the user said anything interesting. Cap of 3 covers the
common “first turn is noise” case while still bounding runaway retry on
a persistently failing fast model.
Cross-process manual-rename race
Two CLI tabs on the same session file can diverge in memory. Tab A runs
/rename foo
and writes
titleSource: manual
. Tab B’s
ChatRecordingService
has its own
currentCustomTitle = undefined
and
would naively overwrite with an auto title.
After the LLM call resolves, the IIFE re-reads the JSONL via
sessionService.getSessionTitleInfo
. If the file shows
source: 'manual'
, the IIFE bails AND syncs its in-memory state so
subsequent turns respect the rename too. Cost: one 64KB tail read per
successful generation; negligible.
Abort propagation on
finalize()
autoTitleController
doubles as the in-flight flag.
finalize()
(run
on session switch and process shutdown) calls
autoTitleController.abort()
before re-appending the title record. The
LLM socket is cancelled promptly; session switch doesn’t wait on a slow
fast-model call. The IIFE’s
finally
block clears
autoTitleController
only if it’s still the active one, so a finalize
mid-flight doesn’t race a concurrent
recordAssistantTurn
.
Manual
/rename
lands mid-flight
Between the IIFE’s
await
completing and the
recordCustomTitle('auto')
call, the user could
/rename foo
. The IIFE re-checks
this.currentTitleSource === 'manual'
and bails. The in-process check
AND the cross-process re-read both run; manual wins at both layers.
Configuration
User-facing knobs
Setting / env var
Default
Effect
fastModel
unset
Required for auto-titling. Unset → no-op (no main-model fallback).
QWEN_DISABLE_AUTO_TITLE=1
unset
Opt out of the auto trigger without unsetting
fastModel
.
/rename --auto
still works on request.
No
settings.json
toggle — the env var is the only user-visible
off-switch. Rationale: the feature is cosmetic and cheap; a settings
toggle would add a UI surface for something that can live as a one-time
env export for the few users who want to disable it.
Why auto doesn’t fall back to the main model
Auto-titling is triggered unconditionally after every assistant turn.
If a user without a fast model were silently charged main-model tokens
for every new session’s title, the cost delta is invisible until the
monthly bill arrives. Failing quietly (no-op, no title, no cost) is the
safer default.
/rename --auto
surfaces
no_fast_model
as an
actionable error so the user can set one if they want to.
Observability
createDebugLogger('SESSION_TITLE')
emits
debugLogger.warn
from the
generator’s catch block. Failures are fully transparent to the user —
auto-title is an auxiliary feature and never throws into the UI.
Developers can grep for the
[SESSION_TITLE]
tag in the debug log
(
~/.qwen/debug/<sessionId>.txt
;
latest.txt
symlinks to the current
session). A working end-to-end call produces no log output; a failing
one gets one WARN line with the underlying error message.
Security Hardening
The title value is rendered verbatim in the terminal (session picker)
AND persisted in a user-readable JSONL file. Both surfaces are attack
reachable if a compromised or prompt-injected fast model returns
hostile text.
Concern
Guard
ANSI / OSC-8 / CSI injection
stripTerminalControlSequences
before both JSONL write and picker render.
Clickable-link smuggle via OSC-8
Same — OSC sequences stripped as whole units, not just the ESC byte.
Invalid UTF-16 surrogates
Scrubbed in
flattenToTail
(LLM input) and
sanitizeTitle
(LLM output after max-length trim).
Subtype-line spoof via user message content
lineContains: '"subtype":"custom_title"'
— user text that happens to contain the literal phrase can’t shadow a real record.
Symlink redirect on session reads
O_NOFOLLOW
(no-op on Windows where the constant is missing).
Truncated trailing JSONL record
extractLastJsonStringFields
requires a closing quote before a record wins the latest-match race.
Pathological file size freezing the picker
MAX_FULL_SCAN_BYTES = 64 MB
cap on Phase-2 full-file scan.
Paired CJK bracket decorators (
【Draft】
)
Stripped as a unit so a lone closing bracket doesn’t dangle.
Out of Scope
Item
Why not
Auto-regenerate when the title goes stale
/rename --auto
is the explicit user-triggered path. Silent mid-session title swaps would confuse users scrolling back through the picker.
WebUI / VSCode dim-styling parity
Those surfaces read
customTitle
already and will show auto titles as if manual. A follow-up can wire the
titleSource
through.
Settings-dialog toggle for auto generation
Env var is the single knob. Full settings UI is easy to add later if user demand surfaces.
i18n locale catalog entries for new strings
Consistent with existing
/rename
strings, which fall through to English. A repo-wide i18n pass is out of scope.
Migration to re-classify legacy records
Back-compat by design: absent
titleSource
is treated as manual. Rewriting old records would risk losing user intent.
Non-interactive auto-titling
qwen -p
/ CI scripts throw the session away; fast-model tokens for a title no one will ever resume is pure waste.
Last updated on
May 18, 2026
Session Recap Design
Qwen Code Command 模块重构方案</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/session-title/session-title-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Common workflows</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/common-workflow/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/common-workflow/</guid>
  <pubDate>Wed, 11 Sep 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Command Workflows
Common workflows
Learn about common workflows with Qwen Code.
Each task in this document includes clear instructions, example commands, and best practices to help you get the most from Qwen Code.
Understand new codebases
Get a quick codebase overview
Suppose you’ve just joined a new project and need to understand its structure quickly.
1. Navigate to the project root directory
cd
/path/to/project
2. Start Qwen Code
qwen
3. Ask for a high-level overview
give me an ove...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Command Workflows
Common workflows
Learn about common workflows with Qwen Code.
Each task in this document includes clear instructions, example commands, and best practices to help you get the most from Qwen Code.
Understand new codebases
Get a quick codebase overview
Suppose you’ve just joined a new project and need to understand its structure quickly.
1. Navigate to the project root directory
cd
/path/to/project
2. Start Qwen Code
qwen
3. Ask for a high-level overview
give me an overview of this codebase
4. Dive deeper into specific components
explain the main architecture patterns used here
what are the key data models?
how is authentication handled?
Tip
Start with broad questions, then narrow down to specific areas
Ask about coding conventions and patterns used in the project
Request a glossary of project-specific terms
Find relevant code
Suppose you need to locate code related to a specific feature or functionality.
1. Ask Qwen Code to find relevant files
find the files that handle user authentication
2. Get context on how components interact
how do these authentication files work together?
3. Understand the execution flow
trace the login process from front-end to database
Tip
Be specific about what you’re looking for
Use domain language from the project
Fix bugs efficiently
Suppose you’ve encountered an error message and need to find and fix its source.
1. Share the error with Qwen Code
I'm seeing an error when I run npm test
2. Ask for fix recommendations
suggest a few ways to fix the @ts-ignore in user.ts
3. Apply the fix
update user.tsto add the null check you suggested
Tip
Tell Qwen Code the command to reproduce the issue and get a stack trace
Mention any steps to reproduce the error
Let Qwen Code know if the error is intermittent or consistent
Refactor code
Suppose you need to update old code to use modern patterns and practices.
1. Identify legacy code for refactoring
find deprecated API usage in our codebase
2. Get refactoring recommendations
suggest how to refactor utils.js to use modern JavaScript features
3. Apply the changes safely
refactor utils.js to use ES 2024 features while maintaining the same behavior
4. Verify the refactoring
run tests for the refactored code
Tip
Ask Qwen Code to explain the benefits of the modern approach
Request that changes maintain backward compatibility when needed
Do refactoring in small, testable increments
Use specialized subagents
Suppose you want to use specialized AI subagents to handle specific tasks more effectively.
1. View available subagents
/agents
This shows all available subagents and lets you create new ones.
2. Use subagents automatically
Qwen Code automatically delegates appropriate tasks to specialized subagents:
review my recent code changes for security issues
run all tests and fix any failures
3. Explicitly request specific subagents
use the code-reviewer subagent to check the auth module
have the debugger subagent investigate why users can't log in
4. Create custom subagents for your workflow
/agents
Then select “create” and follow the prompts to define:
A unique identifier that describes the subagent’s purpose (for example,
code-reviewer
,
api-designer
).
When Qwen Code should use this agent
Which tools it can access
A system prompt describing the agent’s role and behavior
Tip
Create project-specific subagents in
.qwen/agents/
for team sharing
Use descriptive
description
fields to enable automatic delegation
Limit tool access to what each subagent actually needs
Know more about
Sub Agents
Know more about
Approval Mode
Work with tests
Suppose you need to add tests for uncovered code.
1. Identify untested code
find functions in NotificationsService.swift that are not covered by tests
2. Generate test scaffolding
add tests for the notification service
3. Add meaningful test cases
add test cases for edge conditions in the notification service
4. Run and verify tests
run the new tests and fix any failures
Qwen Code can generate tests that follow your project’s existing patterns and conventions. When asking for tests, be specific about what behavior you want to verify. Qwen Code examines your existing test files to match the style, frameworks, and assertion patterns already in use.
For comprehensive coverage, ask Qwen Code to identify edge cases you might have missed. Qwen Code can analyze your code paths and suggest tests for error conditions, boundary values, and unexpected inputs that are easy to overlook.
Create pull requests
Suppose you need to create a well-documented pull request for your changes.
1. Summarize your changes
summarize the changes I've made to the authentication module
2. Generate a pull request with Qwen Code
create a pr
3. Review and refine
enhance the PR description with more context about the security improvements
4. Add testing details
add information about how these changes were tested
Tip
Ask Qwen Code directly to make a PR for you
Review Qwen Code’s generated PR before submitting
Ask Qwen Code to highlight potential risks or considerations
Handle documentation
Suppose you need to add or update documentation for your code.
1. Identify undocumented code
find functions without proper JSDoc comments in the auth module
2. Generate documentation
add JSDoc comments to the undocumented functions in auth.js
3. Review and enhance
improve the generated documentation with more context and examples
4. Verify documentation
check if the documentation follows our project standards
Tip
Specify the documentation style you want (JSDoc, docstrings, etc.)
Ask for examples in the documentation
Request documentation for public APIs, interfaces, and complex logic
Reference files and directories
Use
@
to quickly include files or directories without waiting for Qwen Code to read them.
1. Reference a single file
Explain the logic in @src/utils/auth.js
This includes the full content of the file in the conversation.
2. Reference a directory
What's the structure of @src/components?
This provides a directory listing with file information.
3. Reference MCP resources
Show me the data from @github: repos/owner/repo/issues
This fetches data from connected MCP servers using the format @server: resource. See
MCP
for details.
Tip
File paths can be relative or absolute
@ file references add
QWEN.md
in the file’s directory and parent directories to context
Directory references show file listings, not contents
You can reference multiple files in a single message (for example, “
@file 1.js
and
@file 2.js
”)
Resume previous conversations
Suppose you’ve been working on a task with Qwen Code and need to continue where you left off in a later session.
Qwen Code provides two options for resuming previous conversations:
--continue
to automatically continue the most recent conversation
--resume
to display a conversation picker
1. Continue the most recent conversation
qwen
--continue
This immediately resumes your most recent conversation without any prompts.
2. Continue in non-interactive mode
qwen
--continue
--p
"Continue with my task"
Use
--print
with
--continue
to resume the most recent conversation in non-interactive mode, perfect for scripts or automation.
3. Show conversation picker
qwen
--resume
This displays an interactive conversation selector with a clean list view showing:
Session summary (or initial prompt)
Metadata: time elapsed, message count, and git branch
Use arrow keys to navigate and press Enter to select a conversation. Press Esc to exit.
Tip
Conversation history is stored locally on your machine
Use
--continue
for quick access to your most recent conversation
Use
--resume
when you need to select a specific past conversation
When resuming, you’ll see the entire conversation history before continuing
The resumed conversation starts with the same model and configuration as the original
How it works
:
Conversation Storage
: All conversations are automatically saved locally with their full message history
Message Deserialization
: When resuming, the entire message history is restored to maintain context
Tool State
: Tool usage and results from the previous conversation are preserved
Context Restoration
: The conversation resumes with all previous context intact
Examples
:
# Continue most recent conversation
qwen
--continue
# Continue most recent conversation with a specific prompt
qwen
--continue
--p
"Show me our progress"
# Show conversation picker
qwen
--resume
# Continue most recent conversation in non-interactive mode
qwen
--continue
--p
"Run the tests again"
Run parallel Qwen Code sessions with Git worktrees
Suppose you need to work on multiple tasks simultaneously with complete code isolation between Qwen Code instances.
1. Understand Git worktrees
Git worktrees allow you to check out multiple branches from the same repository into separate directories. Each worktree has its own working directory with isolated files, while sharing the same Git history. Learn more in the
official Git worktree documentation
.
2. Create a new worktree
# Create a new worktree with a new branch
git
worktree
add
../project-feature-a
-b
feature-a
# Or create a worktree with an existing branch
git
worktree
add
../project-bugfix
bugfix-123
This creates a new directory with a separate working copy of your repository.
3. Run Qwen Code in each worktree
# Navigate to your worktree
cd
../project-feature-a
# Run Qwen Code in this isolated environment
qwen
4. Run Qwen Code in another worktree
cd
../project-bugfix
qwen
5. Manage your worktrees
# List all worktrees
git
worktree
list
# Remove a worktree when done
git
worktree
remove
../project-feature-a
Tip
Each worktree has its own independent file state, making it perfect for parallel Qwen Code sessions
Changes made in one worktree won’t affect others, preventing Qwen Code instances from interfering with each other
All worktrees share the same Git history and remote connections
For long-running tasks, you can have Qwen Code working in one worktree while you continue development in another
Use descriptive directory names to easily identify which task each worktree is for
Remember to initialize your development environment in each new worktree according to your project’s setup. Depending on your stack, this might include:
JavaScript projects: Running dependency installation (
npm install
,
yarn
)
Python projects: Setting up virtual environments or installing with package managers
Other languages: Following your project’s standard setup process
Use Qwen Code as a unix-style utility
Add Qwen Code to your verification process
Suppose you want to use Qwen Code as a linter or code reviewer.
Add Qwen Code to your build script:
// package.json
{
...
"scripts"
: {
...
"lint:Qwen Code"
:
"qwen -p 'you are a linter. please look at the changes vs. main and report any issues related to typos. report the filename and line number on one line, and a description of the issue on the second line. do not return any other text.'"
}
}
Tip
Use Qwen Code for automated code review in your CI/CD pipeline
Customize the prompt to check for specific issues relevant to your project
Consider creating multiple scripts for different types of verification
Pipe in, pipe out
Suppose you want to pipe data into Qwen Code, and get back data in a structured format.
Pipe data through Qwen Code:
cat
build-error.txt
|
qwen
-p
'concisely explain the root cause of this build error'
>
output.txt
Tip
Use pipes to integrate Qwen-Code into existing shell scripts
Combine with other Unix tools for powerful workflows
Consider using —output-format for structured output
Control output format
Suppose you need Qwen Code’s output in a specific format, especially when integrating Qwen Code into scripts or other tools.
1. Use text format (default)
cat
data.txt
|
qwen
-p
'summarize this data'
--output-format
text
>
summary.txt
This outputs just Qwen Code’s plain text response (default behavior).
2. Use JSON format
cat
code.py
|
qwen
-p
'analyze this code for bugs'
--output-format
json
>
analysis.json
This outputs a JSON array of messages with metadata including cost and duration.
3. Use streaming JSON format
cat
log.txt
|
qwen
-p
'parse this log file for errors'
--output-format
stream-json
This outputs a series of JSON objects in real-time as Qwen Code processes the request. Each message is a valid JSON object, but the entire output is not valid JSON if concatenated.
Tip
Use
--output-format text
for simple integrations where you just need Qwen Code’s response
Use
--output-format json
when you need the full conversation log
Use
--output-format stream-json
for real-time output of each conversation turn
Ask Qwen Code about its capabilities
Qwen Code has built-in access to its documentation and can answer questions about its own features and limitations.
Example questions
can Qwen Code create pull requests?
how does Qwen Code handle permissions?
what slash commands are available?
how do I use MCP with Qwen Code?
how do I configure Qwen Code for Amazon Bedrock?
what are the limitations of Qwen Code?
Note
Qwen Code provides documentation-based answers to these questions. For executable examples and hands-on demonstrations, refer to the specific workflow sections above.
Tip
Qwen Code always has access to the latest Qwen Code documentation, regardless of the version you’re using
Ask specific questions to get detailed answers
Qwen Code can explain complex features like MCP integration, enterprise configurations, and advanced workflows
Last updated on
May 18, 2026
QuickStart
Visual Studio Code</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/common-workflow/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Command Workflows
Common workflows
Learn about common workflows with Qwen Code.
Each task in this document includes clear instructions, example commands, and best practices to help you get the most from Qwen Code.
Understand new codebases
Get a quick codebase overview
Suppose you’ve just joined a new project and need to understand its structure quickly.
1. Navigate to the project root directory
cd
/path/to/project
2. Start Qwen Code
qwen
3. Ask for a high-level overview
give me an ove...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Command Workflows
Common workflows
Learn about common workflows with Qwen Code.
Each task in this document includes clear instructions, example commands, and best practices to help you get the most from Qwen Code.
Understand new codebases
Get a quick codebase overview
Suppose you’ve just joined a new project and need to understand its structure quickly.
1. Navigate to the project root directory
cd
/path/to/project
2. Start Qwen Code
qwen
3. Ask for a high-level overview
give me an overview of this codebase
4. Dive deeper into specific components
explain the main architecture patterns used here
what are the key data models?
how is authentication handled?
Tip
Start with broad questions, then narrow down to specific areas
Ask about coding conventions and patterns used in the project
Request a glossary of project-specific terms
Find relevant code
Suppose you need to locate code related to a specific feature or functionality.
1. Ask Qwen Code to find relevant files
find the files that handle user authentication
2. Get context on how components interact
how do these authentication files work together?
3. Understand the execution flow
trace the login process from front-end to database
Tip
Be specific about what you’re looking for
Use domain language from the project
Fix bugs efficiently
Suppose you’ve encountered an error message and need to find and fix its source.
1. Share the error with Qwen Code
I'm seeing an error when I run npm test
2. Ask for fix recommendations
suggest a few ways to fix the @ts-ignore in user.ts
3. Apply the fix
update user.tsto add the null check you suggested
Tip
Tell Qwen Code the command to reproduce the issue and get a stack trace
Mention any steps to reproduce the error
Let Qwen Code know if the error is intermittent or consistent
Refactor code
Suppose you need to update old code to use modern patterns and practices.
1. Identify legacy code for refactoring
find deprecated API usage in our codebase
2. Get refactoring recommendations
suggest how to refactor utils.js to use modern JavaScript features
3. Apply the changes safely
refactor utils.js to use ES 2024 features while maintaining the same behavior
4. Verify the refactoring
run tests for the refactored code
Tip
Ask Qwen Code to explain the benefits of the modern approach
Request that changes maintain backward compatibility when needed
Do refactoring in small, testable increments
Use specialized subagents
Suppose you want to use specialized AI subagents to handle specific tasks more effectively.
1. View available subagents
/agents
This shows all available subagents and lets you create new ones.
2. Use subagents automatically
Qwen Code automatically delegates appropriate tasks to specialized subagents:
review my recent code changes for security issues
run all tests and fix any failures
3. Explicitly request specific subagents
use the code-reviewer subagent to check the auth module
have the debugger subagent investigate why users can't log in
4. Create custom subagents for your workflow
/agents
Then select “create” and follow the prompts to define:
A unique identifier that describes the subagent’s purpose (for example,
code-reviewer
,
api-designer
).
When Qwen Code should use this agent
Which tools it can access
A system prompt describing the agent’s role and behavior
Tip
Create project-specific subagents in
.qwen/agents/
for team sharing
Use descriptive
description
fields to enable automatic delegation
Limit tool access to what each subagent actually needs
Know more about
Sub Agents
Know more about
Approval Mode
Work with tests
Suppose you need to add tests for uncovered code.
1. Identify untested code
find functions in NotificationsService.swift that are not covered by tests
2. Generate test scaffolding
add tests for the notification service
3. Add meaningful test cases
add test cases for edge conditions in the notification service
4. Run and verify tests
run the new tests and fix any failures
Qwen Code can generate tests that follow your project’s existing patterns and conventions. When asking for tests, be specific about what behavior you want to verify. Qwen Code examines your existing test files to match the style, frameworks, and assertion patterns already in use.
For comprehensive coverage, ask Qwen Code to identify edge cases you might have missed. Qwen Code can analyze your code paths and suggest tests for error conditions, boundary values, and unexpected inputs that are easy to overlook.
Create pull requests
Suppose you need to create a well-documented pull request for your changes.
1. Summarize your changes
summarize the changes I've made to the authentication module
2. Generate a pull request with Qwen Code
create a pr
3. Review and refine
enhance the PR description with more context about the security improvements
4. Add testing details
add information about how these changes were tested
Tip
Ask Qwen Code directly to make a PR for you
Review Qwen Code’s generated PR before submitting
Ask Qwen Code to highlight potential risks or considerations
Handle documentation
Suppose you need to add or update documentation for your code.
1. Identify undocumented code
find functions without proper JSDoc comments in the auth module
2. Generate documentation
add JSDoc comments to the undocumented functions in auth.js
3. Review and enhance
improve the generated documentation with more context and examples
4. Verify documentation
check if the documentation follows our project standards
Tip
Specify the documentation style you want (JSDoc, docstrings, etc.)
Ask for examples in the documentation
Request documentation for public APIs, interfaces, and complex logic
Reference files and directories
Use
@
to quickly include files or directories without waiting for Qwen Code to read them.
1. Reference a single file
Explain the logic in @src/utils/auth.js
This includes the full content of the file in the conversation.
2. Reference a directory
What's the structure of @src/components?
This provides a directory listing with file information.
3. Reference MCP resources
Show me the data from @github: repos/owner/repo/issues
This fetches data from connected MCP servers using the format @server: resource. See
MCP
for details.
Tip
File paths can be relative or absolute
@ file references add
QWEN.md
in the file’s directory and parent directories to context
Directory references show file listings, not contents
You can reference multiple files in a single message (for example, “
@file 1.js
and
@file 2.js
”)
Resume previous conversations
Suppose you’ve been working on a task with Qwen Code and need to continue where you left off in a later session.
Qwen Code provides two options for resuming previous conversations:
--continue
to automatically continue the most recent conversation
--resume
to display a conversation picker
1. Continue the most recent conversation
qwen
--continue
This immediately resumes your most recent conversation without any prompts.
2. Continue in non-interactive mode
qwen
--continue
--p
"Continue with my task"
Use
--print
with
--continue
to resume the most recent conversation in non-interactive mode, perfect for scripts or automation.
3. Show conversation picker
qwen
--resume
This displays an interactive conversation selector with a clean list view showing:
Session summary (or initial prompt)
Metadata: time elapsed, message count, and git branch
Use arrow keys to navigate and press Enter to select a conversation. Press Esc to exit.
Tip
Conversation history is stored locally on your machine
Use
--continue
for quick access to your most recent conversation
Use
--resume
when you need to select a specific past conversation
When resuming, you’ll see the entire conversation history before continuing
The resumed conversation starts with the same model and configuration as the original
How it works
:
Conversation Storage
: All conversations are automatically saved locally with their full message history
Message Deserialization
: When resuming, the entire message history is restored to maintain context
Tool State
: Tool usage and results from the previous conversation are preserved
Context Restoration
: The conversation resumes with all previous context intact
Examples
:
# Continue most recent conversation
qwen
--continue
# Continue most recent conversation with a specific prompt
qwen
--continue
--p
"Show me our progress"
# Show conversation picker
qwen
--resume
# Continue most recent conversation in non-interactive mode
qwen
--continue
--p
"Run the tests again"
Run parallel Qwen Code sessions with Git worktrees
Suppose you need to work on multiple tasks simultaneously with complete code isolation between Qwen Code instances.
1. Understand Git worktrees
Git worktrees allow you to check out multiple branches from the same repository into separate directories. Each worktree has its own working directory with isolated files, while sharing the same Git history. Learn more in the
official Git worktree documentation
.
2. Create a new worktree
# Create a new worktree with a new branch
git
worktree
add
../project-feature-a
-b
feature-a
# Or create a worktree with an existing branch
git
worktree
add
../project-bugfix
bugfix-123
This creates a new directory with a separate working copy of your repository.
3. Run Qwen Code in each worktree
# Navigate to your worktree
cd
../project-feature-a
# Run Qwen Code in this isolated environment
qwen
4. Run Qwen Code in another worktree
cd
../project-bugfix
qwen
5. Manage your worktrees
# List all worktrees
git
worktree
list
# Remove a worktree when done
git
worktree
remove
../project-feature-a
Tip
Each worktree has its own independent file state, making it perfect for parallel Qwen Code sessions
Changes made in one worktree won’t affect others, preventing Qwen Code instances from interfering with each other
All worktrees share the same Git history and remote connections
For long-running tasks, you can have Qwen Code working in one worktree while you continue development in another
Use descriptive directory names to easily identify which task each worktree is for
Remember to initialize your development environment in each new worktree according to your project’s setup. Depending on your stack, this might include:
JavaScript projects: Running dependency installation (
npm install
,
yarn
)
Python projects: Setting up virtual environments or installing with package managers
Other languages: Following your project’s standard setup process
Use Qwen Code as a unix-style utility
Add Qwen Code to your verification process
Suppose you want to use Qwen Code as a linter or code reviewer.
Add Qwen Code to your build script:
// package.json
{
...
"scripts"
: {
...
"lint:Qwen Code"
:
"qwen -p 'you are a linter. please look at the changes vs. main and report any issues related to typos. report the filename and line number on one line, and a description of the issue on the second line. do not return any other text.'"
}
}
Tip
Use Qwen Code for automated code review in your CI/CD pipeline
Customize the prompt to check for specific issues relevant to your project
Consider creating multiple scripts for different types of verification
Pipe in, pipe out
Suppose you want to pipe data into Qwen Code, and get back data in a structured format.
Pipe data through Qwen Code:
cat
build-error.txt
|
qwen
-p
'concisely explain the root cause of this build error'
>
output.txt
Tip
Use pipes to integrate Qwen-Code into existing shell scripts
Combine with other Unix tools for powerful workflows
Consider using —output-format for structured output
Control output format
Suppose you need Qwen Code’s output in a specific format, especially when integrating Qwen Code into scripts or other tools.
1. Use text format (default)
cat
data.txt
|
qwen
-p
'summarize this data'
--output-format
text
>
summary.txt
This outputs just Qwen Code’s plain text response (default behavior).
2. Use JSON format
cat
code.py
|
qwen
-p
'analyze this code for bugs'
--output-format
json
>
analysis.json
This outputs a JSON array of messages with metadata including cost and duration.
3. Use streaming JSON format
cat
log.txt
|
qwen
-p
'parse this log file for errors'
--output-format
stream-json
This outputs a series of JSON objects in real-time as Qwen Code processes the request. Each message is a valid JSON object, but the entire output is not valid JSON if concatenated.
Tip
Use
--output-format text
for simple integrations where you just need Qwen Code’s response
Use
--output-format json
when you need the full conversation log
Use
--output-format stream-json
for real-time output of each conversation turn
Ask Qwen Code about its capabilities
Qwen Code has built-in access to its documentation and can answer questions about its own features and limitations.
Example questions
can Qwen Code create pull requests?
how does Qwen Code handle permissions?
what slash commands are available?
how do I use MCP with Qwen Code?
how do I configure Qwen Code for Amazon Bedrock?
what are the limitations of Qwen Code?
Note
Qwen Code provides documentation-based answers to these questions. For executable examples and hands-on demonstrations, refer to the specific workflow sections above.
Tip
Qwen Code always has access to the latest Qwen Code documentation, regardless of the version you’re using
Ask specific questions to get detailed answers
Qwen Code can explain complex features like MCP integration, enterprise configurations, and advanced workflows
Last updated on
May 18, 2026
QuickStart
Visual Studio Code</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/common-workflow/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Agent Arena</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/arena/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/arena/</guid>
  <pubDate>Mon, 09 Sep 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Agent Arena
Agent Arena
Dispatch multiple AI models simultaneously to execute the same task, compare their solutions side-by-side, and select the best result to apply to your workspace.
[!warning]
Agent Arena is experimental. It has
known limitations
around display modes and session management.
Agent Arena lets you pit multiple AI models against each other on the same task. Each model runs as a fully independent agent in its own isolated Git worktree, so file operations never...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Agent Arena
Agent Arena
Dispatch multiple AI models simultaneously to execute the same task, compare their solutions side-by-side, and select the best result to apply to your workspace.
[!warning]
Agent Arena is experimental. It has
known limitations
around display modes and session management.
Agent Arena lets you pit multiple AI models against each other on the same task. Each model runs as a fully independent agent in its own isolated Git worktree, so file operations never interfere. When all agents finish, you compare results and select a winner to merge back into your main workspace.
Unlike
subagents
, which delegate focused subtasks within a single session, Arena agents are complete, top-level agent instances — each with its own model, context window, and full tool access.
This page covers:
When to use Agent Arena
Starting an arena session
Interacting with agents
, including display modes and navigation
Comparing results and selecting a winner
Best practices
When to use Agent Arena
Agent Arena is most effective when you want to
evaluate or compare
how different models tackle the same problem. The strongest use cases are:
Model benchmarking
: Evaluate different models’ capabilities on real tasks in your actual codebase, not synthetic benchmarks
Best-of-N selection
: Get multiple independent solutions and pick the best implementation
Exploring approaches
: See how different models reason about and solve the same problem — useful for learning and insight
Risk reduction
: For critical changes, validate that multiple models converge on a similar approach before committing
Agent Arena uses significantly more tokens than a single session (each agent has its own context window and model calls). It works best when the value of comparison justifies the cost. For routine tasks where you trust your default model, a single session is more efficient.
Start an arena session
Use the
/arena
slash command to launch a session. Specify the models you want to compete and the task:
/arena --models qwen3.5-plus,glm-5,kimi-k2.5 "Refactor the authentication module to use JWT tokens"
If you omit
--models
, an interactive model selection dialog appears, letting you pick from your configured providers.
What happens when you start
Worktree setup
: Qwen Code creates isolated Git worktrees for each agent at
~/.qwen/arena/<session-id>/worktrees/<model-name>/
. Each worktree mirrors your current working directory state exactly — including staged changes, unstaged changes, and untracked files.
Agent spawning
: Each agent starts in its own worktree with full tool access and its configured model. Agents are launched sequentially but execute in parallel.
Execution
: All agents work on the task independently with no shared state or communication. You can monitor their progress and interact with any of them.
Completion
: When all agents finish (or fail), you enter the result comparison phase.
Interact with agents
Display modes
Agent Arena currently supports
in-process mode
, where all agents run asynchronously within the same terminal process. A tab bar at the bottom of the terminal lets you switch between agents.
[!note]
Split-pane display modes are planned for the future.
We intend to support tmux-based and iTerm2-based split-pane layouts, where each agent gets its own terminal pane for true side-by-side viewing. Currently, only in-process tab switching is available.
Navigate between agents
In in-process mode, use keyboard shortcuts to switch between agent views:
Shortcut
Action
Right
Switch to the next agent tab
Left
Switch to the previous agent tab
Up
Switch focus to the input box
Down
Switch focus to the agent tab bar
The tab bar shows each agent’s current status:
Indicator
Meaning
●
Running or idle
✓
Completed successfully
✗
Failed
○
Cancelled
Interact with individual agents
When viewing an agent’s tab, you can:
Send messages
— type in the input area to give the agent additional instructions
Approve tool calls
— if an agent requests tool approval, the confirmation dialog appears in its tab
View full history
— scroll through the agent’s complete conversation, including model output, tool calls, and results
Each agent is a full, independent session. Anything you can do with the main agent, you can do with an arena agent.
Compare results and select a winner
When all agents complete, the Arena enters the result comparison phase. You’ll see:
Status summary
: Which agents succeeded, failed, or were cancelled
Execution metrics
: Duration, rounds of reasoning, token usage, and tool call counts for each agent
Arena comparison summary
: Files changed in common vs. by one agent only, line-change counts, token efficiency, and a high-level approach summary generated from each agent’s diff, metrics, and conversation history
A selection dialog presents the successful agents. Choose one to apply its changes to your main workspace, or discard all results. Press
p
to toggle a quick preview for the highlighted agent, or
d
to toggle that agent’s detailed diff before selecting a winner.
What happens when you select a winner
The winning agent’s changes are extracted as a diff against the baseline
The diff is applied to your main working directory
All worktrees and temporary branches are cleaned up automatically
If you want to inspect the complete reasoning path before deciding, each agent’s full conversation history is still available via the tab bar while the selection dialog is active.
Configuration
Arena behavior can be customized in
settings.json
:
{
"arena"
: {
"worktreeBaseDir"
:
"~/.qwen/arena"
,
"maxRoundsPerAgent"
:
50
,
"timeoutSeconds"
:
600
}
}
Setting
Description
Default
arena.worktreeBaseDir
Base directory for arena worktrees
~/.qwen/arena
arena.maxRoundsPerAgent
Maximum reasoning rounds per agent
50
arena.timeoutSeconds
Timeout for each agent in seconds
600
Best practices
Choose models that complement each other
Arena is most valuable when you compare models with meaningfully different strengths. For example:
/arena --models qwen3.5-plus,glm-5,kimi-k2.5 "Optimize the database query layer"
Comparing three versions of the same model family yields less insight than comparing across providers.
Keep tasks self-contained
Arena agents work independently with no communication. Tasks should be fully describable in the prompt without requiring back-and-forth:
Good
: “Refactor the payment module to use the strategy pattern. Update all tests.”
Less effective
: “Let’s discuss how to improve the payment module” — this benefits from conversation, which is better suited to a single session.
Limit the number of agents
Up to 5 agents can run simultaneously. In practice, 2-3 agents provide the best balance of comparison value to resource cost. More agents means:
Higher token costs (each agent has its own context window)
Longer total execution time
More results to compare
Start with 2-3 and scale up only when the comparison value justifies it.
Use Arena for high-impact decisions
Arena shines when the stakes justify running multiple models:
Choosing an architecture for a new module
Selecting an approach for a complex refactor
Validating a critical bug fix from multiple angles
For routine changes like renaming a variable or updating a config file, a single session is faster and cheaper.
Troubleshooting
Agents failing to start
Verify that each model in
--models
is properly configured with valid API credentials
Check that your working directory is a Git repository (worktrees require Git)
Ensure you have write access to the worktree base directory (
~/.qwen/arena/
by default)
Worktree creation fails
Run
git worktree list
to check for stale worktrees from previous sessions
Clean up stale worktrees with
git worktree prune
Ensure your Git version supports worktrees (
git --version
, requires Git 2.5+)
Agent takes too long
Increase the timeout: set
arena.timeoutSeconds
in settings
Reduce task complexity — Arena tasks should be focused and well-defined
Lower
arena.maxRoundsPerAgent
if agents are spending too many rounds
Applying winner fails
Check for uncommitted changes in your main working directory that might conflict
The diff is applied as a patch — merge conflicts are possible if your working directory changed during the session
Limitations
Agent Arena is experimental. Current limitations:
In-process mode only
: Split-pane display via tmux or iTerm2 is not yet available. All agents run within a single terminal window with tab switching.
No diff preview before selection
: You can view each agent’s conversation history, but there is no unified diff viewer to compare solutions side-by-side before picking a winner.
No worktree retention
: Worktrees are always cleaned up after selection. There is no option to preserve them for further inspection.
No session resumption
: Arena sessions cannot be resumed after exiting. If you close the terminal mid-session, worktrees remain on disk and must be cleaned up manually via
git worktree prune
.
Maximum 5 agents
: The hard limit of 5 concurrent agents cannot be changed.
Git repository required
: Arena requires a Git repository for worktree isolation. It cannot be used in non-Git directories.
Comparison with other multi-agent modes
Agent Arena is one of several planned multi-agent modes in Qwen Code.
Agent Team
and
Agent Swarm
are not yet implemented — the table below describes their intended design for reference.
Agent Arena
Agent Team
(planned)
Agent Swarm
(planned)
Goal
Competitive: Find the best solution to the
same
task
Collaborative: Tackle
different
aspects together
Batch parallel: Dynamically spawn workers for bulk tasks
Agents
Pre-configured models compete independently
Teammates collaborate with assigned roles
Workers spawned on-the-fly, destroyed on completion
Communication
No inter-agent communication
Direct peer-to-peer messaging
One-way: results aggregated by parent
Isolation
Full: separate Git worktrees
Independent sessions with shared task list
Lightweight ephemeral context per worker
Output
One selected solution applied to workspace
Synthesized results from multiple perspectives
Aggregated results from parallel processing
Best for
Benchmarking, choosing between model approaches
Research, complex collaboration, cross-layer work
Batch operations, data processing, map-reduce tasks
Next steps
Explore related approaches for parallel and delegated work:
Lightweight delegation
:
Subagents
handle focused subtasks within your session — better when you don’t need model comparison
Manual parallel sessions
: Run multiple Qwen Code sessions yourself in separate terminals with
Git worktrees
for full manual control
Last updated on
May 18, 2026
SubAgents
Skills</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/arena/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Agent Arena
Agent Arena
Dispatch multiple AI models simultaneously to execute the same task, compare their solutions side-by-side, and select the best result to apply to your workspace.
[!warning]
Agent Arena is experimental. It has
known limitations
around display modes and session management.
Agent Arena lets you pit multiple AI models against each other on the same task. Each model runs as a fully independent agent in its own isolated Git worktree, so file operations never...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Agent Arena
Agent Arena
Dispatch multiple AI models simultaneously to execute the same task, compare their solutions side-by-side, and select the best result to apply to your workspace.
[!warning]
Agent Arena is experimental. It has
known limitations
around display modes and session management.
Agent Arena lets you pit multiple AI models against each other on the same task. Each model runs as a fully independent agent in its own isolated Git worktree, so file operations never interfere. When all agents finish, you compare results and select a winner to merge back into your main workspace.
Unlike
subagents
, which delegate focused subtasks within a single session, Arena agents are complete, top-level agent instances — each with its own model, context window, and full tool access.
This page covers:
When to use Agent Arena
Starting an arena session
Interacting with agents
, including display modes and navigation
Comparing results and selecting a winner
Best practices
When to use Agent Arena
Agent Arena is most effective when you want to
evaluate or compare
how different models tackle the same problem. The strongest use cases are:
Model benchmarking
: Evaluate different models’ capabilities on real tasks in your actual codebase, not synthetic benchmarks
Best-of-N selection
: Get multiple independent solutions and pick the best implementation
Exploring approaches
: See how different models reason about and solve the same problem — useful for learning and insight
Risk reduction
: For critical changes, validate that multiple models converge on a similar approach before committing
Agent Arena uses significantly more tokens than a single session (each agent has its own context window and model calls). It works best when the value of comparison justifies the cost. For routine tasks where you trust your default model, a single session is more efficient.
Start an arena session
Use the
/arena
slash command to launch a session. Specify the models you want to compete and the task:
/arena --models qwen3.5-plus,glm-5,kimi-k2.5 "Refactor the authentication module to use JWT tokens"
If you omit
--models
, an interactive model selection dialog appears, letting you pick from your configured providers.
What happens when you start
Worktree setup
: Qwen Code creates isolated Git worktrees for each agent at
~/.qwen/arena/<session-id>/worktrees/<model-name>/
. Each worktree mirrors your current working directory state exactly — including staged changes, unstaged changes, and untracked files.
Agent spawning
: Each agent starts in its own worktree with full tool access and its configured model. Agents are launched sequentially but execute in parallel.
Execution
: All agents work on the task independently with no shared state or communication. You can monitor their progress and interact with any of them.
Completion
: When all agents finish (or fail), you enter the result comparison phase.
Interact with agents
Display modes
Agent Arena currently supports
in-process mode
, where all agents run asynchronously within the same terminal process. A tab bar at the bottom of the terminal lets you switch between agents.
[!note]
Split-pane display modes are planned for the future.
We intend to support tmux-based and iTerm2-based split-pane layouts, where each agent gets its own terminal pane for true side-by-side viewing. Currently, only in-process tab switching is available.
Navigate between agents
In in-process mode, use keyboard shortcuts to switch between agent views:
Shortcut
Action
Right
Switch to the next agent tab
Left
Switch to the previous agent tab
Up
Switch focus to the input box
Down
Switch focus to the agent tab bar
The tab bar shows each agent’s current status:
Indicator
Meaning
●
Running or idle
✓
Completed successfully
✗
Failed
○
Cancelled
Interact with individual agents
When viewing an agent’s tab, you can:
Send messages
— type in the input area to give the agent additional instructions
Approve tool calls
— if an agent requests tool approval, the confirmation dialog appears in its tab
View full history
— scroll through the agent’s complete conversation, including model output, tool calls, and results
Each agent is a full, independent session. Anything you can do with the main agent, you can do with an arena agent.
Compare results and select a winner
When all agents complete, the Arena enters the result comparison phase. You’ll see:
Status summary
: Which agents succeeded, failed, or were cancelled
Execution metrics
: Duration, rounds of reasoning, token usage, and tool call counts for each agent
Arena comparison summary
: Files changed in common vs. by one agent only, line-change counts, token efficiency, and a high-level approach summary generated from each agent’s diff, metrics, and conversation history
A selection dialog presents the successful agents. Choose one to apply its changes to your main workspace, or discard all results. Press
p
to toggle a quick preview for the highlighted agent, or
d
to toggle that agent’s detailed diff before selecting a winner.
What happens when you select a winner
The winning agent’s changes are extracted as a diff against the baseline
The diff is applied to your main working directory
All worktrees and temporary branches are cleaned up automatically
If you want to inspect the complete reasoning path before deciding, each agent’s full conversation history is still available via the tab bar while the selection dialog is active.
Configuration
Arena behavior can be customized in
settings.json
:
{
"arena"
: {
"worktreeBaseDir"
:
"~/.qwen/arena"
,
"maxRoundsPerAgent"
:
50
,
"timeoutSeconds"
:
600
}
}
Setting
Description
Default
arena.worktreeBaseDir
Base directory for arena worktrees
~/.qwen/arena
arena.maxRoundsPerAgent
Maximum reasoning rounds per agent
50
arena.timeoutSeconds
Timeout for each agent in seconds
600
Best practices
Choose models that complement each other
Arena is most valuable when you compare models with meaningfully different strengths. For example:
/arena --models qwen3.5-plus,glm-5,kimi-k2.5 "Optimize the database query layer"
Comparing three versions of the same model family yields less insight than comparing across providers.
Keep tasks self-contained
Arena agents work independently with no communication. Tasks should be fully describable in the prompt without requiring back-and-forth:
Good
: “Refactor the payment module to use the strategy pattern. Update all tests.”
Less effective
: “Let’s discuss how to improve the payment module” — this benefits from conversation, which is better suited to a single session.
Limit the number of agents
Up to 5 agents can run simultaneously. In practice, 2-3 agents provide the best balance of comparison value to resource cost. More agents means:
Higher token costs (each agent has its own context window)
Longer total execution time
More results to compare
Start with 2-3 and scale up only when the comparison value justifies it.
Use Arena for high-impact decisions
Arena shines when the stakes justify running multiple models:
Choosing an architecture for a new module
Selecting an approach for a complex refactor
Validating a critical bug fix from multiple angles
For routine changes like renaming a variable or updating a config file, a single session is faster and cheaper.
Troubleshooting
Agents failing to start
Verify that each model in
--models
is properly configured with valid API credentials
Check that your working directory is a Git repository (worktrees require Git)
Ensure you have write access to the worktree base directory (
~/.qwen/arena/
by default)
Worktree creation fails
Run
git worktree list
to check for stale worktrees from previous sessions
Clean up stale worktrees with
git worktree prune
Ensure your Git version supports worktrees (
git --version
, requires Git 2.5+)
Agent takes too long
Increase the timeout: set
arena.timeoutSeconds
in settings
Reduce task complexity — Arena tasks should be focused and well-defined
Lower
arena.maxRoundsPerAgent
if agents are spending too many rounds
Applying winner fails
Check for uncommitted changes in your main working directory that might conflict
The diff is applied as a patch — merge conflicts are possible if your working directory changed during the session
Limitations
Agent Arena is experimental. Current limitations:
In-process mode only
: Split-pane display via tmux or iTerm2 is not yet available. All agents run within a single terminal window with tab switching.
No diff preview before selection
: You can view each agent’s conversation history, but there is no unified diff viewer to compare solutions side-by-side before picking a winner.
No worktree retention
: Worktrees are always cleaned up after selection. There is no option to preserve them for further inspection.
No session resumption
: Arena sessions cannot be resumed after exiting. If you close the terminal mid-session, worktrees remain on disk and must be cleaned up manually via
git worktree prune
.
Maximum 5 agents
: The hard limit of 5 concurrent agents cannot be changed.
Git repository required
: Arena requires a Git repository for worktree isolation. It cannot be used in non-Git directories.
Comparison with other multi-agent modes
Agent Arena is one of several planned multi-agent modes in Qwen Code.
Agent Team
and
Agent Swarm
are not yet implemented — the table below describes their intended design for reference.
Agent Arena
Agent Team
(planned)
Agent Swarm
(planned)
Goal
Competitive: Find the best solution to the
same
task
Collaborative: Tackle
different
aspects together
Batch parallel: Dynamically spawn workers for bulk tasks
Agents
Pre-configured models compete independently
Teammates collaborate with assigned roles
Workers spawned on-the-fly, destroyed on completion
Communication
No inter-agent communication
Direct peer-to-peer messaging
One-way: results aggregated by parent
Isolation
Full: separate Git worktrees
Independent sessions with shared task list
Lightweight ephemeral context per worker
Output
One selected solution applied to workspace
Synthesized results from multiple perspectives
Aggregated results from parallel processing
Best for
Benchmarking, choosing between model approaches
Research, complex collaboration, cross-layer work
Batch operations, data processing, map-reduce tasks
Next steps
Explore related approaches for parallel and delegated work:
Lightweight delegation
:
Subagents
handle focused subtasks within your session — better when you don’t need model comparison
Manual parallel sessions
: Run multiple Qwen Code sessions yourself in separate terminals with
Git worktrees
for full manual control
Last updated on
May 18, 2026
SubAgents
Skills</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/arena/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Agent Skills</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/skills/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/skills/</guid>
  <pubDate>Sat, 07 Sep 2024 00:00:00 +0000</pubDate>
  <category>Skills</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Skills
Agent Skills
Create, manage, and share Skills to extend Qwen Code’s capabilities.
This guide shows you how to create, use, and manage Agent Skills in
Qwen Code
. Skills are modular capabilities that extend the model’s effectiveness through organized folders containing instructions (and optionally scripts/resources).
Prerequisites
Qwen Code (recent version)
Basic familiarity with Qwen Code (
Quickstart
)
What are Agent Skills?
Agent Skills package expertise into discove...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Skills
Agent Skills
Create, manage, and share Skills to extend Qwen Code’s capabilities.
This guide shows you how to create, use, and manage Agent Skills in
Qwen Code
. Skills are modular capabilities that extend the model’s effectiveness through organized folders containing instructions (and optionally scripts/resources).
Prerequisites
Qwen Code (recent version)
Basic familiarity with Qwen Code (
Quickstart
)
What are Agent Skills?
Agent Skills package expertise into discoverable capabilities. Each Skill consists of a
SKILL.md
file with instructions that the model can load when relevant, plus optional supporting files like scripts and templates.
How Skills are invoked
Skills are
model-invoked
— the model autonomously decides when to use them based on your request and the Skill’s description. This is different from slash commands, which are
user-invoked
(you explicitly type
/command
).
If you want to invoke a Skill explicitly, use the
/skills
slash command:
/skills
<
skill-nam
e
>
Use autocomplete to browse available Skills and descriptions.
Benefits
Extend Qwen Code for your workflows
Share expertise across your team via git
Reduce repetitive prompting
Compose multiple Skills for complex tasks
Create a Skill
Skills are stored as directories containing a
SKILL.md
file.
Personal Skills
Personal Skills are available across all your projects. Store them in
~/.qwen/skills/
:
mkdir
-p
~/.qwen/skills/my-skill-name
Use personal Skills for:
Your individual workflows and preferences
Skills you’re developing
Personal productivity helpers
Project Skills
Project Skills are shared with your team. Store them in
.qwen/skills/
within your project:
mkdir
-p
.qwen/skills/my-skill-name
Use project Skills for:
Team workflows and conventions
Project-specific expertise
Shared utilities and scripts
Project Skills can be checked into git and automatically become available to teammates.
Write
SKILL.md
Create a
SKILL.md
file with YAML frontmatter and Markdown content:
---
name
:
your-skill-name
description
:
Brief description of what this Skill does and when to use it
---
# Your Skill Name
## Instructions
Provide clear, step-by-step guidance for Qwen Code.
## Examples
Show concrete examples of using this Skill.
Field requirements
Qwen Code currently validates that:
name
is a non-empty string matching
/^[\p{L}\p{N}_:.-]+$/u
— Unicode letters and digits (CJK / Cyrillic / accented Latin all OK), plus
_
,
:
,
.
,
-
. Whitespace, slashes, brackets and other structurally unsafe characters are rejected at parse time.
description
is a non-empty string
Recommended conventions:
Prefer lowercase ASCII with hyphens for shareable names (e.g.
tsx-helper
)
Make
description
specific: include both
what
the Skill does and
when
to use it (key words users will naturally mention)
Optional: gate a Skill on file paths (
paths:
)
For Skills that only matter to specific parts of a codebase, add a
paths:
list of glob patterns. The Skill stays out of the model’s available-skills listing until a tool call touches a matching file:
---
name
:
tsx-helper
description
:
React TSX component helper
paths
:
-
'src/**/*.tsx'
-
'packages/*/src/**/*.tsx'
---
Notes:
Globs are matched relative to the project root with
picomatch
; files outside the project root never trigger activation.
A path-gated Skill
stays activated for the rest of the session
once a matching file is touched. A new session, or a
refreshCache
triggered by editing any Skill file, resets activations.
paths:
only gates
model
discovery, and only at the SkillTool listing level. You can always invoke a path-gated Skill yourself via
/<skill-name>
or the
/skills
picker — that user path runs the Skill body regardless of activation state. The model side, however, stays gated until a matching file is touched: a slash invocation does
not
unlock model-side activation, so if you want the model to chain off your invocation (call
Skill { skill: ... }
itself), also access a file matching the skill’s
paths:
first.
Combining
paths:
with
disable-model-invocation: true
is allowed but the gate has no effect — the Skill is hidden from the model regardless, so path activation never advertises it.
Add supporting files
Create additional files alongside
SKILL.md
:
my-skill/
├── SKILL.md (required)
├── reference.md (optional documentation)
├── examples.md (optional examples)
├── scripts/
│   └── helper.py (optional utility)
└── templates/
└── template.txt (optional template)
Reference these files from
SKILL.md
:
For advanced usage, see [
reference.md
](
reference.md
).
Run the helper script:
```bash
python
scripts/helper.py
input.txt
```
View available Skills
Qwen Code discovers Skills from:
Personal Skills:
~/.qwen/skills/
Project Skills:
.qwen/skills/
Extension Skills: Skills provided by installed extensions
Extension Skills
Extensions can provide custom skills that become available when the extension is enabled. These skills are stored in the extension’s
skills/
directory and follow the same format as personal and project skills.
Extension skills are automatically discovered and loaded when the extension is installed and enabled.
To see which extensions provide skills, check the extension’s
qwen-extension.json
file for a
skills
field.
To view available Skills, ask Qwen Code directly:
What Skills are available?
Heads up — model vs. user view.
Asking the model only surfaces Skills the model can currently see. If a Skill uses
paths:
(see “Optional: gate a Skill on file paths” above), it stays out of that listing until a matching file has been touched. The full set is always visible to you via the
/skills
slash command and on disk.
Or browse the full list with the slash command (always shows every Skill, including path-gated ones that have not activated yet):
/skills
Or inspect the filesystem:
# List personal Skills
ls
~/.qwen/skills/
# List project Skills (if in a project directory)
ls
.qwen/skills/
# View a specific Skill's content
cat
~/.qwen/skills/my-skill/SKILL.md
Test a Skill
After creating a Skill, test it by asking questions that match your description.
Example: if your description mentions “PDF files”:
Can you help me extract text from this PDF?
The model autonomously decides to use your Skill if it matches the request — you don’t need to explicitly invoke it.
Debug a Skill
If Qwen Code doesn’t use your Skill, check these common issues:
Make the description specific
Too vague:
description
:
Helps with documents
Specific:
description
:
Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDFs, forms, or document extraction.
Verify file path
Personal Skills:
~/.qwen/skills/<skill-name>/SKILL.md
Project Skills:
.qwen/skills/<skill-name>/SKILL.md
# Personal
ls
~/.qwen/skills/my-skill/SKILL.md
# Project
ls
.qwen/skills/my-skill/SKILL.md
Check YAML syntax
Invalid YAML prevents the Skill metadata from loading correctly.
cat
SKILL.md
|
head
-n
15
Ensure:
Opening
---
on line 1
Closing
---
before Markdown content
Valid YAML syntax (no tabs, correct indentation)
View errors
Run Qwen Code with debug mode to see Skill loading errors:
qwen
--debug
Share Skills with your team
You can share Skills through project repositories:
Add the Skill under
.qwen/skills/
Commit and push
Teammates pull the changes
git
add
.qwen/skills/
git
commit
-m
"Add team Skill for PDF processing"
git
push
Update a Skill
Edit
SKILL.md
directly:
# Personal Skill
code
~/.qwen/skills/my-skill/SKILL.md
# Project Skill
code
.qwen/skills/my-skill/SKILL.md
Changes take effect the next time you start Qwen Code. If Qwen Code is already running, restart it to load the updates.
Remove a Skill
Delete the Skill directory:
# Personal
rm
-rf
~/.qwen/skills/my-skill
# Project
rm
-rf
.qwen/skills/my-skill
git
commit
-m
"Remove unused Skill"
Best practices
Keep Skills focused
One Skill should address one capability:
Focused: “PDF form filling”, “Excel analysis”, “Git commit messages”
Too broad: “Document processing” (split into smaller Skills)
Write clear descriptions
Help the model discover when to use Skills by including specific triggers:
description
:
Analyze Excel spreadsheets, create pivot tables, and generate charts. Use when working with Excel files, spreadsheets, or .xlsx data.
Test with your team
Does the Skill activate when expected?
Are the instructions clear?
Are there missing examples or edge cases?
Last updated on
May 18, 2026
Agent Arena
Memory</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/skills/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Skills
Agent Skills
Create, manage, and share Skills to extend Qwen Code’s capabilities.
This guide shows you how to create, use, and manage Agent Skills in
Qwen Code
. Skills are modular capabilities that extend the model’s effectiveness through organized folders containing instructions (and optionally scripts/resources).
Prerequisites
Qwen Code (recent version)
Basic familiarity with Qwen Code (
Quickstart
)
What are Agent Skills?
Agent Skills package expertise into discove...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Skills
Agent Skills
Create, manage, and share Skills to extend Qwen Code’s capabilities.
This guide shows you how to create, use, and manage Agent Skills in
Qwen Code
. Skills are modular capabilities that extend the model’s effectiveness through organized folders containing instructions (and optionally scripts/resources).
Prerequisites
Qwen Code (recent version)
Basic familiarity with Qwen Code (
Quickstart
)
What are Agent Skills?
Agent Skills package expertise into discoverable capabilities. Each Skill consists of a
SKILL.md
file with instructions that the model can load when relevant, plus optional supporting files like scripts and templates.
How Skills are invoked
Skills are
model-invoked
— the model autonomously decides when to use them based on your request and the Skill’s description. This is different from slash commands, which are
user-invoked
(you explicitly type
/command
).
If you want to invoke a Skill explicitly, use the
/skills
slash command:
/skills
<
skill-nam
e
>
Use autocomplete to browse available Skills and descriptions.
Benefits
Extend Qwen Code for your workflows
Share expertise across your team via git
Reduce repetitive prompting
Compose multiple Skills for complex tasks
Create a Skill
Skills are stored as directories containing a
SKILL.md
file.
Personal Skills
Personal Skills are available across all your projects. Store them in
~/.qwen/skills/
:
mkdir
-p
~/.qwen/skills/my-skill-name
Use personal Skills for:
Your individual workflows and preferences
Skills you’re developing
Personal productivity helpers
Project Skills
Project Skills are shared with your team. Store them in
.qwen/skills/
within your project:
mkdir
-p
.qwen/skills/my-skill-name
Use project Skills for:
Team workflows and conventions
Project-specific expertise
Shared utilities and scripts
Project Skills can be checked into git and automatically become available to teammates.
Write
SKILL.md
Create a
SKILL.md
file with YAML frontmatter and Markdown content:
---
name
:
your-skill-name
description
:
Brief description of what this Skill does and when to use it
---
# Your Skill Name
## Instructions
Provide clear, step-by-step guidance for Qwen Code.
## Examples
Show concrete examples of using this Skill.
Field requirements
Qwen Code currently validates that:
name
is a non-empty string matching
/^[\p{L}\p{N}_:.-]+$/u
— Unicode letters and digits (CJK / Cyrillic / accented Latin all OK), plus
_
,
:
,
.
,
-
. Whitespace, slashes, brackets and other structurally unsafe characters are rejected at parse time.
description
is a non-empty string
Recommended conventions:
Prefer lowercase ASCII with hyphens for shareable names (e.g.
tsx-helper
)
Make
description
specific: include both
what
the Skill does and
when
to use it (key words users will naturally mention)
Optional: gate a Skill on file paths (
paths:
)
For Skills that only matter to specific parts of a codebase, add a
paths:
list of glob patterns. The Skill stays out of the model’s available-skills listing until a tool call touches a matching file:
---
name
:
tsx-helper
description
:
React TSX component helper
paths
:
-
'src/**/*.tsx'
-
'packages/*/src/**/*.tsx'
---
Notes:
Globs are matched relative to the project root with
picomatch
; files outside the project root never trigger activation.
A path-gated Skill
stays activated for the rest of the session
once a matching file is touched. A new session, or a
refreshCache
triggered by editing any Skill file, resets activations.
paths:
only gates
model
discovery, and only at the SkillTool listing level. You can always invoke a path-gated Skill yourself via
/<skill-name>
or the
/skills
picker — that user path runs the Skill body regardless of activation state. The model side, however, stays gated until a matching file is touched: a slash invocation does
not
unlock model-side activation, so if you want the model to chain off your invocation (call
Skill { skill: ... }
itself), also access a file matching the skill’s
paths:
first.
Combining
paths:
with
disable-model-invocation: true
is allowed but the gate has no effect — the Skill is hidden from the model regardless, so path activation never advertises it.
Add supporting files
Create additional files alongside
SKILL.md
:
my-skill/
├── SKILL.md (required)
├── reference.md (optional documentation)
├── examples.md (optional examples)
├── scripts/
│   └── helper.py (optional utility)
└── templates/
└── template.txt (optional template)
Reference these files from
SKILL.md
:
For advanced usage, see [
reference.md
](
reference.md
).
Run the helper script:
```bash
python
scripts/helper.py
input.txt
```
View available Skills
Qwen Code discovers Skills from:
Personal Skills:
~/.qwen/skills/
Project Skills:
.qwen/skills/
Extension Skills: Skills provided by installed extensions
Extension Skills
Extensions can provide custom skills that become available when the extension is enabled. These skills are stored in the extension’s
skills/
directory and follow the same format as personal and project skills.
Extension skills are automatically discovered and loaded when the extension is installed and enabled.
To see which extensions provide skills, check the extension’s
qwen-extension.json
file for a
skills
field.
To view available Skills, ask Qwen Code directly:
What Skills are available?
Heads up — model vs. user view.
Asking the model only surfaces Skills the model can currently see. If a Skill uses
paths:
(see “Optional: gate a Skill on file paths” above), it stays out of that listing until a matching file has been touched. The full set is always visible to you via the
/skills
slash command and on disk.
Or browse the full list with the slash command (always shows every Skill, including path-gated ones that have not activated yet):
/skills
Or inspect the filesystem:
# List personal Skills
ls
~/.qwen/skills/
# List project Skills (if in a project directory)
ls
.qwen/skills/
# View a specific Skill's content
cat
~/.qwen/skills/my-skill/SKILL.md
Test a Skill
After creating a Skill, test it by asking questions that match your description.
Example: if your description mentions “PDF files”:
Can you help me extract text from this PDF?
The model autonomously decides to use your Skill if it matches the request — you don’t need to explicitly invoke it.
Debug a Skill
If Qwen Code doesn’t use your Skill, check these common issues:
Make the description specific
Too vague:
description
:
Helps with documents
Specific:
description
:
Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDFs, forms, or document extraction.
Verify file path
Personal Skills:
~/.qwen/skills/<skill-name>/SKILL.md
Project Skills:
.qwen/skills/<skill-name>/SKILL.md
# Personal
ls
~/.qwen/skills/my-skill/SKILL.md
# Project
ls
.qwen/skills/my-skill/SKILL.md
Check YAML syntax
Invalid YAML prevents the Skill metadata from loading correctly.
cat
SKILL.md
|
head
-n
15
Ensure:
Opening
---
on line 1
Closing
---
before Markdown content
Valid YAML syntax (no tabs, correct indentation)
View errors
Run Qwen Code with debug mode to see Skill loading errors:
qwen
--debug
Share Skills with your team
You can share Skills through project repositories:
Add the Skill under
.qwen/skills/
Commit and push
Teammates pull the changes
git
add
.qwen/skills/
git
commit
-m
"Add team Skill for PDF processing"
git
push
Update a Skill
Edit
SKILL.md
directly:
# Personal Skill
code
~/.qwen/skills/my-skill/SKILL.md
# Project Skill
code
.qwen/skills/my-skill/SKILL.md
Changes take effect the next time you start Qwen Code. If Qwen Code is already running, restart it to load the updates.
Remove a Skill
Delete the Skill directory:
# Personal
rm
-rf
~/.qwen/skills/my-skill
# Project
rm
-rf
.qwen/skills/my-skill
git
commit
-m
"Remove unused Skill"
Best practices
Keep Skills focused
One Skill should address one capability:
Focused: “PDF form filling”, “Excel analysis”, “Git commit messages”
Too broad: “Document processing” (split into smaller Skills)
Write clear descriptions
Help the model discover when to use Skills by including specific triggers:
description
:
Analyze Excel spreadsheets, create pivot tables, and generate charts. Use when working with Excel files, spreadsheets, or .xlsx data.
Test with your team
Does the Skill activate when expected?
Are the instructions clear?
Are there missing examples or edge cases?
Last updated on
May 18, 2026
Agent Arena
Memory</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/skills/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Keyboard Shortcuts</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/reference/keyboard-shortcuts/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/reference/keyboard-shortcuts/</guid>
  <pubDate>Mon, 02 Sep 2024 00:00:00 +0000</pubDate>
  <category>Reference</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Reference
Keyboard Shortcuts
Qwen Code Keyboard Shortcuts
This document lists the available keyboard shortcuts in Qwen Code.
General
Shortcut
Description
Esc
Close dialogs and suggestions.
Ctrl+C
Cancel the ongoing request and clear the input. Press twice to exit the application.
Ctrl+D
Exit the application if the input is empty. Press twice to confirm.
Ctrl+L
Clear the screen.
Ctrl+O
Toggle compact mode (hide/show tool output and thinking).
Ctrl+S
Allows long responses to print fully...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Reference
Keyboard Shortcuts
Qwen Code Keyboard Shortcuts
This document lists the available keyboard shortcuts in Qwen Code.
General
Shortcut
Description
Esc
Close dialogs and suggestions.
Ctrl+C
Cancel the ongoing request and clear the input. Press twice to exit the application.
Ctrl+D
Exit the application if the input is empty. Press twice to confirm.
Ctrl+L
Clear the screen.
Ctrl+O
Toggle compact mode (hide/show tool output and thinking).
Ctrl+S
Allows long responses to print fully, disabling truncation. Use your terminal’s scrollback to view the entire output.
Ctrl+T
Toggle the display of tool descriptions.
Shift+Tab
(
Tab
on Windows)
Cycle approval modes (
plan
→
default
→
auto-edit
→
yolo
)
Input Prompt
Shortcut
Description
!
Toggle shell mode when the input is empty.
?
Toggle keyboard shortcuts display when the input is empty.
\
(at end of line) +
Enter
Insert a newline.
Down Arrow
Navigate down through the input history.
Enter
Submit the current prompt.
Meta+Delete
/
Ctrl+Delete
Delete the word to the right of the cursor.
Tab
Autocomplete the current suggestion if one exists.
Up Arrow
Navigate up through the input history.
Ctrl+A
/
Home
Move the cursor to the beginning of the line.
Ctrl+B
/
Left Arrow
Move the cursor one character to the left.
Ctrl+C
Clear the input prompt
Esc
(double press)
Clear the input prompt.
Ctrl+D
/
Delete
Delete the character to the right of the cursor.
Ctrl+E
/
End
Move the cursor to the end of the line.
Ctrl+F
/
Right Arrow
Move the cursor one character to the right.
Ctrl+H
/
Backspace
Delete the character to the left of the cursor.
Ctrl+K
Delete from the cursor to the end of the line.
Ctrl+Left Arrow
/
Meta+Left Arrow
/
Meta+B
Move the cursor one word to the left.
Ctrl+N
Navigate down through the input history.
Ctrl+P
Navigate up through the input history.
Ctrl+R
Reverse search through input/shell history.
Ctrl+Y
Retry the last failed request.
Ctrl+Right Arrow
/
Meta+Right Arrow
/
Meta+F
Move the cursor one word to the right.
Ctrl+U
Delete from the cursor to the beginning of the line.
Ctrl+V
(Windows:
Alt+V
)
Paste clipboard content. If the clipboard contains an image, it will be saved and a reference to it will be inserted in the prompt.
Ctrl+W
/
Meta+Backspace
/
Ctrl+Backspace
Delete the word to the left of the cursor.
Ctrl+X
/
Meta+Enter
Open the current input in an external editor.
Suggestions
Shortcut
Description
Down Arrow
Navigate down through the suggestions.
Tab
/
Enter
Accept the selected suggestion.
Up Arrow
Navigate up through the suggestions.
Radio Button Select
Shortcut
Description
Down Arrow
/
j
Move selection down.
Enter
Confirm selection.
Up Arrow
/
k
Move selection up.
1-9
Select an item by its number.
(multi-digit)
For items with numbers greater than 9, press the digits in quick succession to select the corresponding item.
IDE Integration
Shortcut
Description
Ctrl+G
See context CLI received from IDE
Last updated on
May 18, 2026
Introduction
Troubleshooting</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/reference/keyboard-shortcuts/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Reference
Keyboard Shortcuts
Qwen Code Keyboard Shortcuts
This document lists the available keyboard shortcuts in Qwen Code.
General
Shortcut
Description
Esc
Close dialogs and suggestions.
Ctrl+C
Cancel the ongoing request and clear the input. Press twice to exit the application.
Ctrl+D
Exit the application if the input is empty. Press twice to confirm.
Ctrl+L
Clear the screen.
Ctrl+O
Toggle compact mode (hide/show tool output and thinking).
Ctrl+S
Allows long responses to print fully...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Reference
Keyboard Shortcuts
Qwen Code Keyboard Shortcuts
This document lists the available keyboard shortcuts in Qwen Code.
General
Shortcut
Description
Esc
Close dialogs and suggestions.
Ctrl+C
Cancel the ongoing request and clear the input. Press twice to exit the application.
Ctrl+D
Exit the application if the input is empty. Press twice to confirm.
Ctrl+L
Clear the screen.
Ctrl+O
Toggle compact mode (hide/show tool output and thinking).
Ctrl+S
Allows long responses to print fully, disabling truncation. Use your terminal’s scrollback to view the entire output.
Ctrl+T
Toggle the display of tool descriptions.
Shift+Tab
(
Tab
on Windows)
Cycle approval modes (
plan
→
default
→
auto-edit
→
yolo
)
Input Prompt
Shortcut
Description
!
Toggle shell mode when the input is empty.
?
Toggle keyboard shortcuts display when the input is empty.
\
(at end of line) +
Enter
Insert a newline.
Down Arrow
Navigate down through the input history.
Enter
Submit the current prompt.
Meta+Delete
/
Ctrl+Delete
Delete the word to the right of the cursor.
Tab
Autocomplete the current suggestion if one exists.
Up Arrow
Navigate up through the input history.
Ctrl+A
/
Home
Move the cursor to the beginning of the line.
Ctrl+B
/
Left Arrow
Move the cursor one character to the left.
Ctrl+C
Clear the input prompt
Esc
(double press)
Clear the input prompt.
Ctrl+D
/
Delete
Delete the character to the right of the cursor.
Ctrl+E
/
End
Move the cursor to the end of the line.
Ctrl+F
/
Right Arrow
Move the cursor one character to the right.
Ctrl+H
/
Backspace
Delete the character to the left of the cursor.
Ctrl+K
Delete from the cursor to the end of the line.
Ctrl+Left Arrow
/
Meta+Left Arrow
/
Meta+B
Move the cursor one word to the left.
Ctrl+N
Navigate down through the input history.
Ctrl+P
Navigate up through the input history.
Ctrl+R
Reverse search through input/shell history.
Ctrl+Y
Retry the last failed request.
Ctrl+Right Arrow
/
Meta+Right Arrow
/
Meta+F
Move the cursor one word to the right.
Ctrl+U
Delete from the cursor to the beginning of the line.
Ctrl+V
(Windows:
Alt+V
)
Paste clipboard content. If the clipboard contains an image, it will be saved and a reference to it will be inserted in the prompt.
Ctrl+W
/
Meta+Backspace
/
Ctrl+Backspace
Delete the word to the left of the cursor.
Ctrl+X
/
Meta+Enter
Open the current input in an external editor.
Suggestions
Shortcut
Description
Down Arrow
Navigate down through the suggestions.
Tab
/
Enter
Accept the selected suggestion.
Up Arrow
Navigate up through the suggestions.
Radio Button Select
Shortcut
Description
Down Arrow
/
j
Move selection down.
Enter
Confirm selection.
Up Arrow
/
k
Move selection up.
1-9
Select an item by its number.
(multi-digit)
For items with numbers greater than 9, press the digits in quick succession to select the corresponding item.
IDE Integration
Shortcut
Description
Ctrl+G
See context CLI received from IDE
Last updated on
May 18, 2026
Introduction
Troubleshooting</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/reference/keyboard-shortcuts/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Sandbox</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/sandbox/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/sandbox/</guid>
  <pubDate>Fri, 30 Aug 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Sandboxing
Sandbox
This document explains how to run Qwen Code inside a sandbox to reduce risk when tools execute shell commands or modify files.
Prerequisites
Before using sandboxing, you need to install and set up Qwen Code:
npm
install
-g
@qwen-code/qwen-code
To verify the installation
qwen
--version
Overview of sandboxing
Sandboxing isolates potentially dangerous operations (such as shell commands or file modifications) from your host system, providing a security barrier ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Sandboxing
Sandbox
This document explains how to run Qwen Code inside a sandbox to reduce risk when tools execute shell commands or modify files.
Prerequisites
Before using sandboxing, you need to install and set up Qwen Code:
npm
install
-g
@qwen-code/qwen-code
To verify the installation
qwen
--version
Overview of sandboxing
Sandboxing isolates potentially dangerous operations (such as shell commands or file modifications) from your host system, providing a security barrier between the CLI and your environment.
The benefits of sandboxing include:
Security
: Prevent accidental system damage or data loss.
Isolation
: Limit file system access to project directory.
Consistency
: Ensure reproducible environments across different systems.
Safety
: Reduce risk when working with untrusted code or experimental commands.
Note
Naming note:
Some sandbox-related environment variables may have used the
GEMINI_*
prefix historically. All new environment variables use the
QWEN_*
prefix.
Sandboxing methods
Your ideal method of sandboxing may differ depending on your platform and your preferred container solution.
1. macOS Seatbelt (macOS only)
Lightweight, built-in sandboxing using
sandbox-exec
.
Default profile
:
permissive-open
- restricts writes outside the project directory, but allows most other operations and outbound network access.
Best for
: Fast, no Docker required, strong guardrails for file writes.
2. Container-based (Docker/Podman)
Cross-platform sandboxing with complete process isolation.
By default, Qwen Code uses a published sandbox image (configured in the CLI package) and will pull it as needed.
The container sandbox mounts your workspace and your
~/.qwen
directory into the container so auth and settings persist between runs.
Best for
: Strong isolation on any OS, consistent tooling inside a known image.
Choosing a method
On macOS
:
Use Seatbelt when you want lightweight sandboxing (recommended for most users).
Use Docker/Podman when you need a full Linux userland (e.g., tools that require Linux binaries).
On Linux/Windows
:
Use Docker or Podman.
Quickstart
# Enable sandboxing with command flag
qwen
-s
-p
"analyze the code structure"
# Or enable sandboxing for your shell session (recommended for CI / scripts)
export
QWEN_SANDBOX
=
true
# true auto-picks a provider (see notes below)
qwen
-p
"run the test suite"
# Configure in settings.json
{
"tools"
:
{
"sandbox"
:
true
}
}
Tip
Provider selection notes:
On
macOS
,
QWEN_SANDBOX=true
typically selects
sandbox-exec
(Seatbelt) if available.
On
Linux/Windows
,
QWEN_SANDBOX=true
requires
docker
or
podman
to be installed.
To force a provider, set
QWEN_SANDBOX=docker|podman|sandbox-exec
.
Configuration
Enable sandboxing (in order of precedence)
Environment variable
:
QWEN_SANDBOX=true|false|docker|podman|sandbox-exec
Command flag / argument
:
-s
,
--sandbox
, or
--sandbox=<provider>
Settings file
:
tools.sandbox
in your
settings.json
(e.g.,
{"tools": {"sandbox": true}}
).
Important
If
QWEN_SANDBOX
is set, it
overrides
the CLI flag and
settings.json
.
Configure the sandbox image (Docker/Podman)
CLI flag
:
--sandbox-image <image>
Environment variable
:
QWEN_SANDBOX_IMAGE=<image>
Settings file
:
tools.sandboxImage
in your
settings.json
(e.g.,
{"tools": {"sandboxImage": "ghcr.io/qwenlm/qwen-code:0.14.1"}}
)
Priority order (highest to lowest):
--sandbox-image
QWEN_SANDBOX_IMAGE
tools.sandboxImage
Built-in default image from the CLI package (for example
ghcr.io/qwenlm/qwen-code:<version>
)
settings.env.QWEN_SANDBOX_IMAGE
also works as a generic env injection mechanism, but
tools.sandboxImage
is the preferred persistent setting.
macOS Seatbelt profiles
Built-in profiles (set via
SEATBELT_PROFILE
env var):
permissive-open
(default): Write restrictions, network allowed
permissive-closed
: Write restrictions, no network
permissive-proxied
: Write restrictions, network via proxy
restrictive-open
: Strict restrictions, network allowed
restrictive-closed
: Maximum restrictions
restrictive-proxied
: Strict restrictions, network via proxy
Tip
Start with
permissive-open
, then tighten to
restrictive-closed
if your workflow still works.
Custom Seatbelt profiles (macOS)
To use a custom Seatbelt profile:
Create a file named
.qwen/sandbox-macos-<profile_name>.sb
in your project.
Set
SEATBELT_PROFILE=<profile_name>
.
Custom Sandbox Flags
For container-based sandboxing, you can inject custom flags into the
docker
or
podman
command using the
SANDBOX_FLAGS
environment variable. This is useful for advanced configurations, such as disabling security features for specific use cases.
Example (Podman)
:
To disable SELinux labeling for volume mounts, you can set the following:
export
SANDBOX_FLAGS
=
"--security-opt label=disable"
Multiple flags can be provided as a space-separated string:
export
SANDBOX_FLAGS
=
"--flag1 --flag2=value"
Network proxying (all sandbox methods)
If you want to restrict outbound network access to an allowlist, you can run a local proxy alongside the sandbox:
Set
QWEN_SANDBOX_PROXY_COMMAND=<command>
The command must start a proxy server that listens on
:::8877
This is especially useful with
*-proxied
Seatbelt profiles.
For a working allowlist-style proxy example, see:
Example Proxy Script
.
Linux UID/GID handling
On Linux, Qwen Code defaults to enabling UID/GID mapping so the sandbox runs as your user (and reuses the mounted
~/.qwen
). Override with:
export
SANDBOX_SET_UID_GID
=
true
# Force host UID/GID
export
SANDBOX_SET_UID_GID
=
false
# Disable UID/GID mapping
Troubleshooting
Common issues
“Operation not permitted”
Operation requires access outside sandbox.
On macOS Seatbelt: try a more permissive
SEATBELT_PROFILE
.
On Docker/Podman: verify the workspace is mounted and your command doesn’t require access outside the project directory.
Missing commands
Container sandbox: add them via
.qwen/sandbox.Dockerfile
or
.qwen/sandbox.bashrc
.
Seatbelt: your host binaries are used, but the sandbox may restrict access to some paths.
Java not available in Docker sandbox
The official Qwen Code Docker image is intentionally minimal to keep the image small, secure, and fast to pull. Different users require different language runtimes (Java, Python, Node.js, etc.), and bundling all environments into a single image is not practical. Therefore, Java is
not included by default
in the Docker sandbox.
If your workflow requires Java, you can extend the base image by creating a
.qwen/sandbox.Dockerfile
in your project:
FROM
ghcr.io/qwenlm/qwen-code:latest
RUN
apt-get update && \
apt-get install -y openjdk-17-jre && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Then rebuild the sandbox image:
QWEN_SANDBOX
=
docker
BUILD_SANDBOX
=
1
qwen
-s
For more details on customizing the sandbox, see
Customizing the sandbox environment
.
Network issues
Check sandbox profile allows network.
Verify proxy configuration.
Debug mode
DEBUG
=
1
qwen
-s
-p
"debug command"
Note:
If you have
DEBUG=true
in a project’s
.env
file, it won’t affect the CLI due to automatic exclusion. Use
.qwen/.env
files for Qwen Code-specific debug settings.
Inspect sandbox
# Check environment
qwen
-s
-p
"run shell command: env | grep SANDBOX"
# List mounts
qwen
-s
-p
"run shell command: mount | grep workspace"
Security notes
Sandboxing reduces but doesn’t eliminate all risks.
Use the most restrictive profile that allows your work.
Container overhead is minimal after the first pull/build.
GUI applications may not work in sandboxes.
Related documentation
Configuration
: Full configuration options.
Commands
: Available commands.
Troubleshooting
: General troubleshooting.
Last updated on
May 18, 2026
Token Caching
i18n</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/sandbox/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Sandboxing
Sandbox
This document explains how to run Qwen Code inside a sandbox to reduce risk when tools execute shell commands or modify files.
Prerequisites
Before using sandboxing, you need to install and set up Qwen Code:
npm
install
-g
@qwen-code/qwen-code
To verify the installation
qwen
--version
Overview of sandboxing
Sandboxing isolates potentially dangerous operations (such as shell commands or file modifications) from your host system, providing a security barrier ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Sandboxing
Sandbox
This document explains how to run Qwen Code inside a sandbox to reduce risk when tools execute shell commands or modify files.
Prerequisites
Before using sandboxing, you need to install and set up Qwen Code:
npm
install
-g
@qwen-code/qwen-code
To verify the installation
qwen
--version
Overview of sandboxing
Sandboxing isolates potentially dangerous operations (such as shell commands or file modifications) from your host system, providing a security barrier between the CLI and your environment.
The benefits of sandboxing include:
Security
: Prevent accidental system damage or data loss.
Isolation
: Limit file system access to project directory.
Consistency
: Ensure reproducible environments across different systems.
Safety
: Reduce risk when working with untrusted code or experimental commands.
Note
Naming note:
Some sandbox-related environment variables may have used the
GEMINI_*
prefix historically. All new environment variables use the
QWEN_*
prefix.
Sandboxing methods
Your ideal method of sandboxing may differ depending on your platform and your preferred container solution.
1. macOS Seatbelt (macOS only)
Lightweight, built-in sandboxing using
sandbox-exec
.
Default profile
:
permissive-open
- restricts writes outside the project directory, but allows most other operations and outbound network access.
Best for
: Fast, no Docker required, strong guardrails for file writes.
2. Container-based (Docker/Podman)
Cross-platform sandboxing with complete process isolation.
By default, Qwen Code uses a published sandbox image (configured in the CLI package) and will pull it as needed.
The container sandbox mounts your workspace and your
~/.qwen
directory into the container so auth and settings persist between runs.
Best for
: Strong isolation on any OS, consistent tooling inside a known image.
Choosing a method
On macOS
:
Use Seatbelt when you want lightweight sandboxing (recommended for most users).
Use Docker/Podman when you need a full Linux userland (e.g., tools that require Linux binaries).
On Linux/Windows
:
Use Docker or Podman.
Quickstart
# Enable sandboxing with command flag
qwen
-s
-p
"analyze the code structure"
# Or enable sandboxing for your shell session (recommended for CI / scripts)
export
QWEN_SANDBOX
=
true
# true auto-picks a provider (see notes below)
qwen
-p
"run the test suite"
# Configure in settings.json
{
"tools"
:
{
"sandbox"
:
true
}
}
Tip
Provider selection notes:
On
macOS
,
QWEN_SANDBOX=true
typically selects
sandbox-exec
(Seatbelt) if available.
On
Linux/Windows
,
QWEN_SANDBOX=true
requires
docker
or
podman
to be installed.
To force a provider, set
QWEN_SANDBOX=docker|podman|sandbox-exec
.
Configuration
Enable sandboxing (in order of precedence)
Environment variable
:
QWEN_SANDBOX=true|false|docker|podman|sandbox-exec
Command flag / argument
:
-s
,
--sandbox
, or
--sandbox=<provider>
Settings file
:
tools.sandbox
in your
settings.json
(e.g.,
{"tools": {"sandbox": true}}
).
Important
If
QWEN_SANDBOX
is set, it
overrides
the CLI flag and
settings.json
.
Configure the sandbox image (Docker/Podman)
CLI flag
:
--sandbox-image <image>
Environment variable
:
QWEN_SANDBOX_IMAGE=<image>
Settings file
:
tools.sandboxImage
in your
settings.json
(e.g.,
{"tools": {"sandboxImage": "ghcr.io/qwenlm/qwen-code:0.14.1"}}
)
Priority order (highest to lowest):
--sandbox-image
QWEN_SANDBOX_IMAGE
tools.sandboxImage
Built-in default image from the CLI package (for example
ghcr.io/qwenlm/qwen-code:<version>
)
settings.env.QWEN_SANDBOX_IMAGE
also works as a generic env injection mechanism, but
tools.sandboxImage
is the preferred persistent setting.
macOS Seatbelt profiles
Built-in profiles (set via
SEATBELT_PROFILE
env var):
permissive-open
(default): Write restrictions, network allowed
permissive-closed
: Write restrictions, no network
permissive-proxied
: Write restrictions, network via proxy
restrictive-open
: Strict restrictions, network allowed
restrictive-closed
: Maximum restrictions
restrictive-proxied
: Strict restrictions, network via proxy
Tip
Start with
permissive-open
, then tighten to
restrictive-closed
if your workflow still works.
Custom Seatbelt profiles (macOS)
To use a custom Seatbelt profile:
Create a file named
.qwen/sandbox-macos-<profile_name>.sb
in your project.
Set
SEATBELT_PROFILE=<profile_name>
.
Custom Sandbox Flags
For container-based sandboxing, you can inject custom flags into the
docker
or
podman
command using the
SANDBOX_FLAGS
environment variable. This is useful for advanced configurations, such as disabling security features for specific use cases.
Example (Podman)
:
To disable SELinux labeling for volume mounts, you can set the following:
export
SANDBOX_FLAGS
=
"--security-opt label=disable"
Multiple flags can be provided as a space-separated string:
export
SANDBOX_FLAGS
=
"--flag1 --flag2=value"
Network proxying (all sandbox methods)
If you want to restrict outbound network access to an allowlist, you can run a local proxy alongside the sandbox:
Set
QWEN_SANDBOX_PROXY_COMMAND=<command>
The command must start a proxy server that listens on
:::8877
This is especially useful with
*-proxied
Seatbelt profiles.
For a working allowlist-style proxy example, see:
Example Proxy Script
.
Linux UID/GID handling
On Linux, Qwen Code defaults to enabling UID/GID mapping so the sandbox runs as your user (and reuses the mounted
~/.qwen
). Override with:
export
SANDBOX_SET_UID_GID
=
true
# Force host UID/GID
export
SANDBOX_SET_UID_GID
=
false
# Disable UID/GID mapping
Troubleshooting
Common issues
“Operation not permitted”
Operation requires access outside sandbox.
On macOS Seatbelt: try a more permissive
SEATBELT_PROFILE
.
On Docker/Podman: verify the workspace is mounted and your command doesn’t require access outside the project directory.
Missing commands
Container sandbox: add them via
.qwen/sandbox.Dockerfile
or
.qwen/sandbox.bashrc
.
Seatbelt: your host binaries are used, but the sandbox may restrict access to some paths.
Java not available in Docker sandbox
The official Qwen Code Docker image is intentionally minimal to keep the image small, secure, and fast to pull. Different users require different language runtimes (Java, Python, Node.js, etc.), and bundling all environments into a single image is not practical. Therefore, Java is
not included by default
in the Docker sandbox.
If your workflow requires Java, you can extend the base image by creating a
.qwen/sandbox.Dockerfile
in your project:
FROM
ghcr.io/qwenlm/qwen-code:latest
RUN
apt-get update && \
apt-get install -y openjdk-17-jre && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Then rebuild the sandbox image:
QWEN_SANDBOX
=
docker
BUILD_SANDBOX
=
1
qwen
-s
For more details on customizing the sandbox, see
Customizing the sandbox environment
.
Network issues
Check sandbox profile allows network.
Verify proxy configuration.
Debug mode
DEBUG
=
1
qwen
-s
-p
"debug command"
Note:
If you have
DEBUG=true
in a project’s
.env
file, it won’t affect the CLI due to automatic exclusion. Use
.qwen/.env
files for Qwen Code-specific debug settings.
Inspect sandbox
# Check environment
qwen
-s
-p
"run shell command: env | grep SANDBOX"
# List mounts
qwen
-s
-p
"run shell command: mount | grep workspace"
Security notes
Sandboxing reduces but doesn’t eliminate all risks.
Use the most restrictive profile that allows your work.
Container overhead is minimal after the first pull/build.
GUI applications may not work in sandboxes.
Related documentation
Configuration
: Full configuration options.
Commands
: Available commands.
Troubleshooting
: General troubleshooting.
Last updated on
May 18, 2026
Token Caching
i18n</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/sandbox/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Java SDK</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-java/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-java/</guid>
  <pubDate>Sun, 18 Aug 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Java SDK (alpha)
Qwen Code Java SDK
The Qwen Code Java SDK is a minimum experimental SDK for programmatic access to Qwen Code functionality. It provides a Java interface to interact with the Qwen Code CLI, allowing developers to integrate Qwen Code capabilities into their Java applications.
Requirements
Java &gt;= 1.8
Maven &gt;= 3.6.0 (for building from source)
qwen-code &gt;= 0.5.0
Dependencies
Logging
: ch.qos.logback:logback-classic
Utilities
: org.apache.commons:commons-lang3
JSON Pr...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Java SDK (alpha)
Qwen Code Java SDK
The Qwen Code Java SDK is a minimum experimental SDK for programmatic access to Qwen Code functionality. It provides a Java interface to interact with the Qwen Code CLI, allowing developers to integrate Qwen Code capabilities into their Java applications.
Requirements
Java >= 1.8
Maven >= 3.6.0 (for building from source)
qwen-code >= 0.5.0
Dependencies
Logging
: ch.qos.logback:logback-classic
Utilities
: org.apache.commons:commons-lang3
JSON Processing
: com.alibaba.fastjson2:fastjson2
Testing
: JUnit 5 (org.junit.jupiter:junit-jupiter)
Installation
Add the following dependency to your Maven
pom.xml
:
<
dependency
>
<
groupId
>com.alibaba</
groupId
>
<
artifactId
>qwencode-sdk</
artifactId
>
<
version
>{$version}</
version
>
</
dependency
>
Or if using Gradle, add to your
build.gradle
:
implementation 'com.alibaba:qwencode-sdk:{$version}'
Building and Running
Build Commands
# Compile the project
mvn
compile
# Run tests
mvn
test
# Package the JAR
mvn
package
# Install to local repository
mvn
install
Quick Start
The simplest way to use the SDK is through the
QwenCodeCli.simpleQuery()
method:
public
static
void
runSimpleExample
() {
List<
String
> result
=
QwenCodeCli.
simpleQuery
(
"hello world"
);
result.
forEach
(logger
::
info);
}
For more advanced usage with custom transport options:
public
static
void
runTransportOptionsExample
() {
TransportOptions options
=
new
TransportOptions
()
.
setModel
(
"qwen3-coder-flash"
)
.
setPermissionMode
(PermissionMode.AUTO_EDIT)
.
setCwd
(
"./"
)
.
setEnv
(
new
HashMap<
String
,
String
>() {{
put
(
"CUSTOM_VAR"
,
"value"
);}})
.
setIncludePartialMessages
(
true
)
.
setTurnTimeout
(
new
Timeout
(
120L
, TimeUnit.SECONDS))
.
setMessageTimeout
(
new
Timeout
(
90L
, TimeUnit.SECONDS))
.
setAllowedTools
(Arrays.
asList
(
"read_file"
,
"write_file"
,
"list_directory"
));
List<
String
> result
=
QwenCodeCli.
simpleQuery
(
"who are you, what are your capabilities?"
, options);
result.
forEach
(logger
::
info);
}
For streaming content handling with custom content consumers:
public
static
void
runStreamingExample
() {
QwenCodeCli.
simpleQuery
(
"who are you, what are your capabilities?"
,
new
TransportOptions
().
setMessageTimeout
(
new
Timeout
(
10L
, TimeUnit.SECONDS)),
new
AssistantContentSimpleConsumers
() {
@
Override
public
void
onText
(Session
session
, TextAssistantContent
textAssistantContent
) {
logger.
info
(
"Text content received: {}"
, textAssistantContent.
getText
());
}
@
Override
public
void
onThinking
(Session
session
, ThinkingAssistantContent
thinkingAssistantContent
) {
logger.
info
(
"Thinking content received: {}"
, thinkingAssistantContent.
getThinking
());
}
@
Override
public
void
onToolUse
(Session
session
, ToolUseAssistantContent
toolUseContent
) {
logger.
info
(
"Tool use content received: {} with arguments: {}"
,
toolUseContent, toolUseContent.
getInput
());
}
@
Override
public
void
onToolResult
(Session
session
, ToolResultAssistantContent
toolResultContent
) {
logger.
info
(
"Tool result content received: {}"
, toolResultContent.
getContent
());
}
@
Override
public
void
onOtherContent
(Session
session
, AssistantContent<
?
>
other
) {
logger.
info
(
"Other content received: {}"
, other);
}
@
Override
public
void
onUsage
(Session
session
, AssistantUsage
assistantUsage
) {
logger.
info
(
"Usage information received: Input tokens: {}, Output tokens: {}"
,
assistantUsage.
getUsage
().
getInputTokens
(), assistantUsage.
getUsage
().
getOutputTokens
());
}
}.
setDefaultPermissionOperation
(Operation.allow));
logger.
info
(
"Streaming example completed."
);
}
other examples see src/test/java/com/alibaba/qwen/code/cli/example
Architecture
The SDK follows a layered architecture:
API Layer
: Provides the main entry points through
QwenCodeCli
class with simple static methods for basic usage
Session Layer
: Manages communication sessions with the Qwen Code CLI through the
Session
class
Transport Layer
: Handles the communication mechanism between the SDK and CLI process (currently using process transport via
ProcessTransport
)
Protocol Layer
: Defines data structures for communication based on the CLI protocol
Utils
: Common utilities for concurrent execution, timeout handling, and error management
Key Features
Permission Modes
The SDK supports different permission modes for controlling tool execution:
default
: Write tools are denied unless approved via
canUseTool
callback or in
allowedTools
. Read-only tools execute without confirmation.
plan
: Blocks all write tools, instructing AI to present a plan first.
auto-edit
: Auto-approve edit tools (edit, write_file) while other tools require confirmation.
yolo
: All tools execute automatically without confirmation.
Session Event Consumers and Assistant Content Consumers
The SDK provides two key interfaces for handling events and content from the CLI:
SessionEventConsumers Interface
The
SessionEventConsumers
interface provides callbacks for different types of messages during a session:
onSystemMessage
: Handles system messages from the CLI (receives Session and SDKSystemMessage)
onResultMessage
: Handles result messages from the CLI (receives Session and SDKResultMessage)
onAssistantMessage
: Handles assistant messages (AI responses) (receives Session and SDKAssistantMessage)
onPartialAssistantMessage
: Handles partial assistant messages during streaming (receives Session and SDKPartialAssistantMessage)
onUserMessage
: Handles user messages (receives Session and SDKUserMessage)
onOtherMessage
: Handles other types of messages (receives Session and String message)
onControlResponse
: Handles control responses (receives Session and CLIControlResponse)
onControlRequest
: Handles control requests (receives Session and CLIControlRequest, returns CLIControlResponse)
onPermissionRequest
: Handles permission requests (receives Session and CLIControlRequest
, returns Behavior)
AssistantContentConsumers Interface
The
AssistantContentConsumers
interface handles different types of content within assistant messages:
onText
: Handles text content (receives Session and TextAssistantContent)
onThinking
: Handles thinking content (receives Session and ThinkingAssistantContent)
onToolUse
: Handles tool use content (receives Session and ToolUseAssistantContent)
onToolResult
: Handles tool result content (receives Session and ToolResultAssistantContent)
onOtherContent
: Handles other content types (receives Session and AssistantContent)
onUsage
: Handles usage information (receives Session and AssistantUsage)
onPermissionRequest
: Handles permission requests (receives Session and CLIControlPermissionRequest, returns Behavior)
onOtherControlRequest
: Handles other control requests (receives Session and ControlRequestPayload, returns ControlResponsePayload)
Relationship Between the Interfaces
Important Note on Event Hierarchy:
SessionEventConsumers
is the
high-level
event processor that handles different message types (system, assistant, user, etc.)
AssistantContentConsumers
is the
low-level
content processor that handles different types of content within assistant messages (text, tools, thinking, etc.)
Processor Relationship:
SessionEventConsumers
→
AssistantContentConsumers
(SessionEventConsumers uses AssistantContentConsumers to process content within assistant messages)
Event Derivation Relationships:
onAssistantMessage
→
onText
,
onThinking
,
onToolUse
,
onToolResult
,
onOtherContent
,
onUsage
onPartialAssistantMessage
→
onText
,
onThinking
,
onToolUse
,
onToolResult
,
onOtherContent
onControlRequest
→
onPermissionRequest
,
onOtherControlRequest
Event Timeout Relationships:
Each event handler method has a corresponding timeout method that allows customizing the timeout behavior for that specific event:
onSystemMessage
↔
onSystemMessageTimeout
onResultMessage
↔
onResultMessageTimeout
onAssistantMessage
↔
onAssistantMessageTimeout
onPartialAssistantMessage
↔
onPartialAssistantMessageTimeout
onUserMessage
↔
onUserMessageTimeout
onOtherMessage
↔
onOtherMessageTimeout
onControlResponse
↔
onControlResponseTimeout
onControlRequest
↔
onControlRequestTimeout
For AssistantContentConsumers timeout methods:
onText
↔
onTextTimeout
onThinking
↔
onThinkingTimeout
onToolUse
↔
onToolUseTimeout
onToolResult
↔
onToolResultTimeout
onOtherContent
↔
onOtherContentTimeout
onPermissionRequest
↔
onPermissionRequestTimeout
onOtherControlRequest
↔
onOtherControlRequestTimeout
Default Timeout Values:
SessionEventSimpleConsumers
default timeout: 180 seconds (Timeout.TIMEOUT_180_SECONDS)
AssistantContentSimpleConsumers
default timeout: 60 seconds (Timeout.TIMEOUT_60_SECONDS)
Timeout Hierarchy Requirements:
For proper operation, the following timeout relationships should be maintained:
onAssistantMessageTimeout
return value should be greater than
onTextTimeout
,
onThinkingTimeout
,
onToolUseTimeout
,
onToolResultTimeout
, and
onOtherContentTimeout
return values
onControlRequestTimeout
return value should be greater than
onPermissionRequestTimeout
and
onOtherControlRequestTimeout
return values
Transport Options
The
TransportOptions
class allows configuration of how the SDK communicates with the Qwen Code CLI:
pathToQwenExecutable
: Path to the Qwen Code CLI executable
cwd
: Working directory for the CLI process
model
: AI model to use for the session
permissionMode
: Permission mode that controls tool execution
env
: Environment variables to pass to the CLI process
maxSessionTurns
: Limits the number of conversation turns in a session
coreTools
: List of core tools that should be available to the AI
excludeTools
: List of tools to exclude from being available to the AI
allowedTools
: List of tools that are pre-approved for use without additional confirmation
authType
: Authentication type to use for the session
includePartialMessages
: Enables receiving partial messages during streaming responses
turnTimeout
: Timeout for a complete turn of conversation
messageTimeout
: Timeout for individual messages within a turn
resumeSessionId
: ID of a previous session to resume
otherOptions
: Additional command-line options to pass to the CLI
Session Control Features
Session creation
: Use
QwenCodeCli.newSession()
to create a new session with custom options
Session management
: The
Session
class provides methods to send prompts, handle responses, and manage session state
Session cleanup
: Always close sessions using
session.close()
to properly terminate the CLI process
Session resumption
: Use
setResumeSessionId()
in
TransportOptions
to resume a previous session
Session interruption
: Use
session.interrupt()
to interrupt a currently running prompt
Dynamic model switching
: Use
session.setModel()
to change the model during a session
Dynamic permission mode switching
: Use
session.setPermissionMode()
to change the permission mode during a session
Thread Pool Configuration
The SDK uses a thread pool for managing concurrent operations with the following default configuration:
Core Pool Size
: 30 threads
Maximum Pool Size
: 100 threads
Keep-Alive Time
: 60 seconds
Queue Capacity
: 300 tasks (using LinkedBlockingQueue)
Thread Naming
: “qwen_code_cli-pool-{number}”
Daemon Threads
: false
Rejected Execution Handler
: CallerRunsPolicy
Error Handling
The SDK provides specific exception types for different error scenarios:
SessionControlException
: Thrown when there’s an issue with session control (creation, initialization, etc.)
SessionSendPromptException
: Thrown when there’s an issue sending a prompt or receiving a response
SessionClosedException
: Thrown when attempting to use a closed session
FAQ / Troubleshooting
Q: Do I need to install the Qwen CLI separately?
A: yes, requires Qwen CLI 0.5.5 or higher.
Q: What Java versions are supported?
A: The SDK requires Java 1.8 or higher.
Q: How do I handle long-running requests?
A: The SDK includes timeout utilities. You can configure timeouts using the
Timeout
class in
TransportOptions
.
Q: Why are some tools not executing?
A: This is likely due to permission modes. Check your permission mode settings and consider using
allowedTools
to pre-approve certain tools.
Q: How do I resume a previous session?
A: Use the
setResumeSessionId()
method in
TransportOptions
to resume a previous session.
Q: Can I customize the environment for the CLI process?
A: Yes, use the
setEnv()
method in
TransportOptions
to pass environment variables to the CLI process.
License
Apache-2.0 - see
LICENSE
for details.
Last updated on
May 18, 2026
Python SDK (alpha)
Channel Plugin Guide</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-java/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Java SDK (alpha)
Qwen Code Java SDK
The Qwen Code Java SDK is a minimum experimental SDK for programmatic access to Qwen Code functionality. It provides a Java interface to interact with the Qwen Code CLI, allowing developers to integrate Qwen Code capabilities into their Java applications.
Requirements
Java &gt;= 1.8
Maven &gt;= 3.6.0 (for building from source)
qwen-code &gt;= 0.5.0
Dependencies
Logging
: ch.qos.logback:logback-classic
Utilities
: org.apache.commons:commons-lang3
JSON Pr...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Java SDK (alpha)
Qwen Code Java SDK
The Qwen Code Java SDK is a minimum experimental SDK for programmatic access to Qwen Code functionality. It provides a Java interface to interact with the Qwen Code CLI, allowing developers to integrate Qwen Code capabilities into their Java applications.
Requirements
Java >= 1.8
Maven >= 3.6.0 (for building from source)
qwen-code >= 0.5.0
Dependencies
Logging
: ch.qos.logback:logback-classic
Utilities
: org.apache.commons:commons-lang3
JSON Processing
: com.alibaba.fastjson2:fastjson2
Testing
: JUnit 5 (org.junit.jupiter:junit-jupiter)
Installation
Add the following dependency to your Maven
pom.xml
:
<
dependency
>
<
groupId
>com.alibaba</
groupId
>
<
artifactId
>qwencode-sdk</
artifactId
>
<
version
>{$version}</
version
>
</
dependency
>
Or if using Gradle, add to your
build.gradle
:
implementation 'com.alibaba:qwencode-sdk:{$version}'
Building and Running
Build Commands
# Compile the project
mvn
compile
# Run tests
mvn
test
# Package the JAR
mvn
package
# Install to local repository
mvn
install
Quick Start
The simplest way to use the SDK is through the
QwenCodeCli.simpleQuery()
method:
public
static
void
runSimpleExample
() {
List<
String
> result
=
QwenCodeCli.
simpleQuery
(
"hello world"
);
result.
forEach
(logger
::
info);
}
For more advanced usage with custom transport options:
public
static
void
runTransportOptionsExample
() {
TransportOptions options
=
new
TransportOptions
()
.
setModel
(
"qwen3-coder-flash"
)
.
setPermissionMode
(PermissionMode.AUTO_EDIT)
.
setCwd
(
"./"
)
.
setEnv
(
new
HashMap<
String
,
String
>() {{
put
(
"CUSTOM_VAR"
,
"value"
);}})
.
setIncludePartialMessages
(
true
)
.
setTurnTimeout
(
new
Timeout
(
120L
, TimeUnit.SECONDS))
.
setMessageTimeout
(
new
Timeout
(
90L
, TimeUnit.SECONDS))
.
setAllowedTools
(Arrays.
asList
(
"read_file"
,
"write_file"
,
"list_directory"
));
List<
String
> result
=
QwenCodeCli.
simpleQuery
(
"who are you, what are your capabilities?"
, options);
result.
forEach
(logger
::
info);
}
For streaming content handling with custom content consumers:
public
static
void
runStreamingExample
() {
QwenCodeCli.
simpleQuery
(
"who are you, what are your capabilities?"
,
new
TransportOptions
().
setMessageTimeout
(
new
Timeout
(
10L
, TimeUnit.SECONDS)),
new
AssistantContentSimpleConsumers
() {
@
Override
public
void
onText
(Session
session
, TextAssistantContent
textAssistantContent
) {
logger.
info
(
"Text content received: {}"
, textAssistantContent.
getText
());
}
@
Override
public
void
onThinking
(Session
session
, ThinkingAssistantContent
thinkingAssistantContent
) {
logger.
info
(
"Thinking content received: {}"
, thinkingAssistantContent.
getThinking
());
}
@
Override
public
void
onToolUse
(Session
session
, ToolUseAssistantContent
toolUseContent
) {
logger.
info
(
"Tool use content received: {} with arguments: {}"
,
toolUseContent, toolUseContent.
getInput
());
}
@
Override
public
void
onToolResult
(Session
session
, ToolResultAssistantContent
toolResultContent
) {
logger.
info
(
"Tool result content received: {}"
, toolResultContent.
getContent
());
}
@
Override
public
void
onOtherContent
(Session
session
, AssistantContent<
?
>
other
) {
logger.
info
(
"Other content received: {}"
, other);
}
@
Override
public
void
onUsage
(Session
session
, AssistantUsage
assistantUsage
) {
logger.
info
(
"Usage information received: Input tokens: {}, Output tokens: {}"
,
assistantUsage.
getUsage
().
getInputTokens
(), assistantUsage.
getUsage
().
getOutputTokens
());
}
}.
setDefaultPermissionOperation
(Operation.allow));
logger.
info
(
"Streaming example completed."
);
}
other examples see src/test/java/com/alibaba/qwen/code/cli/example
Architecture
The SDK follows a layered architecture:
API Layer
: Provides the main entry points through
QwenCodeCli
class with simple static methods for basic usage
Session Layer
: Manages communication sessions with the Qwen Code CLI through the
Session
class
Transport Layer
: Handles the communication mechanism between the SDK and CLI process (currently using process transport via
ProcessTransport
)
Protocol Layer
: Defines data structures for communication based on the CLI protocol
Utils
: Common utilities for concurrent execution, timeout handling, and error management
Key Features
Permission Modes
The SDK supports different permission modes for controlling tool execution:
default
: Write tools are denied unless approved via
canUseTool
callback or in
allowedTools
. Read-only tools execute without confirmation.
plan
: Blocks all write tools, instructing AI to present a plan first.
auto-edit
: Auto-approve edit tools (edit, write_file) while other tools require confirmation.
yolo
: All tools execute automatically without confirmation.
Session Event Consumers and Assistant Content Consumers
The SDK provides two key interfaces for handling events and content from the CLI:
SessionEventConsumers Interface
The
SessionEventConsumers
interface provides callbacks for different types of messages during a session:
onSystemMessage
: Handles system messages from the CLI (receives Session and SDKSystemMessage)
onResultMessage
: Handles result messages from the CLI (receives Session and SDKResultMessage)
onAssistantMessage
: Handles assistant messages (AI responses) (receives Session and SDKAssistantMessage)
onPartialAssistantMessage
: Handles partial assistant messages during streaming (receives Session and SDKPartialAssistantMessage)
onUserMessage
: Handles user messages (receives Session and SDKUserMessage)
onOtherMessage
: Handles other types of messages (receives Session and String message)
onControlResponse
: Handles control responses (receives Session and CLIControlResponse)
onControlRequest
: Handles control requests (receives Session and CLIControlRequest, returns CLIControlResponse)
onPermissionRequest
: Handles permission requests (receives Session and CLIControlRequest
, returns Behavior)
AssistantContentConsumers Interface
The
AssistantContentConsumers
interface handles different types of content within assistant messages:
onText
: Handles text content (receives Session and TextAssistantContent)
onThinking
: Handles thinking content (receives Session and ThinkingAssistantContent)
onToolUse
: Handles tool use content (receives Session and ToolUseAssistantContent)
onToolResult
: Handles tool result content (receives Session and ToolResultAssistantContent)
onOtherContent
: Handles other content types (receives Session and AssistantContent)
onUsage
: Handles usage information (receives Session and AssistantUsage)
onPermissionRequest
: Handles permission requests (receives Session and CLIControlPermissionRequest, returns Behavior)
onOtherControlRequest
: Handles other control requests (receives Session and ControlRequestPayload, returns ControlResponsePayload)
Relationship Between the Interfaces
Important Note on Event Hierarchy:
SessionEventConsumers
is the
high-level
event processor that handles different message types (system, assistant, user, etc.)
AssistantContentConsumers
is the
low-level
content processor that handles different types of content within assistant messages (text, tools, thinking, etc.)
Processor Relationship:
SessionEventConsumers
→
AssistantContentConsumers
(SessionEventConsumers uses AssistantContentConsumers to process content within assistant messages)
Event Derivation Relationships:
onAssistantMessage
→
onText
,
onThinking
,
onToolUse
,
onToolResult
,
onOtherContent
,
onUsage
onPartialAssistantMessage
→
onText
,
onThinking
,
onToolUse
,
onToolResult
,
onOtherContent
onControlRequest
→
onPermissionRequest
,
onOtherControlRequest
Event Timeout Relationships:
Each event handler method has a corresponding timeout method that allows customizing the timeout behavior for that specific event:
onSystemMessage
↔
onSystemMessageTimeout
onResultMessage
↔
onResultMessageTimeout
onAssistantMessage
↔
onAssistantMessageTimeout
onPartialAssistantMessage
↔
onPartialAssistantMessageTimeout
onUserMessage
↔
onUserMessageTimeout
onOtherMessage
↔
onOtherMessageTimeout
onControlResponse
↔
onControlResponseTimeout
onControlRequest
↔
onControlRequestTimeout
For AssistantContentConsumers timeout methods:
onText
↔
onTextTimeout
onThinking
↔
onThinkingTimeout
onToolUse
↔
onToolUseTimeout
onToolResult
↔
onToolResultTimeout
onOtherContent
↔
onOtherContentTimeout
onPermissionRequest
↔
onPermissionRequestTimeout
onOtherControlRequest
↔
onOtherControlRequestTimeout
Default Timeout Values:
SessionEventSimpleConsumers
default timeout: 180 seconds (Timeout.TIMEOUT_180_SECONDS)
AssistantContentSimpleConsumers
default timeout: 60 seconds (Timeout.TIMEOUT_60_SECONDS)
Timeout Hierarchy Requirements:
For proper operation, the following timeout relationships should be maintained:
onAssistantMessageTimeout
return value should be greater than
onTextTimeout
,
onThinkingTimeout
,
onToolUseTimeout
,
onToolResultTimeout
, and
onOtherContentTimeout
return values
onControlRequestTimeout
return value should be greater than
onPermissionRequestTimeout
and
onOtherControlRequestTimeout
return values
Transport Options
The
TransportOptions
class allows configuration of how the SDK communicates with the Qwen Code CLI:
pathToQwenExecutable
: Path to the Qwen Code CLI executable
cwd
: Working directory for the CLI process
model
: AI model to use for the session
permissionMode
: Permission mode that controls tool execution
env
: Environment variables to pass to the CLI process
maxSessionTurns
: Limits the number of conversation turns in a session
coreTools
: List of core tools that should be available to the AI
excludeTools
: List of tools to exclude from being available to the AI
allowedTools
: List of tools that are pre-approved for use without additional confirmation
authType
: Authentication type to use for the session
includePartialMessages
: Enables receiving partial messages during streaming responses
turnTimeout
: Timeout for a complete turn of conversation
messageTimeout
: Timeout for individual messages within a turn
resumeSessionId
: ID of a previous session to resume
otherOptions
: Additional command-line options to pass to the CLI
Session Control Features
Session creation
: Use
QwenCodeCli.newSession()
to create a new session with custom options
Session management
: The
Session
class provides methods to send prompts, handle responses, and manage session state
Session cleanup
: Always close sessions using
session.close()
to properly terminate the CLI process
Session resumption
: Use
setResumeSessionId()
in
TransportOptions
to resume a previous session
Session interruption
: Use
session.interrupt()
to interrupt a currently running prompt
Dynamic model switching
: Use
session.setModel()
to change the model during a session
Dynamic permission mode switching
: Use
session.setPermissionMode()
to change the permission mode during a session
Thread Pool Configuration
The SDK uses a thread pool for managing concurrent operations with the following default configuration:
Core Pool Size
: 30 threads
Maximum Pool Size
: 100 threads
Keep-Alive Time
: 60 seconds
Queue Capacity
: 300 tasks (using LinkedBlockingQueue)
Thread Naming
: “qwen_code_cli-pool-{number}”
Daemon Threads
: false
Rejected Execution Handler
: CallerRunsPolicy
Error Handling
The SDK provides specific exception types for different error scenarios:
SessionControlException
: Thrown when there’s an issue with session control (creation, initialization, etc.)
SessionSendPromptException
: Thrown when there’s an issue sending a prompt or receiving a response
SessionClosedException
: Thrown when attempting to use a closed session
FAQ / Troubleshooting
Q: Do I need to install the Qwen CLI separately?
A: yes, requires Qwen CLI 0.5.5 or higher.
Q: What Java versions are supported?
A: The SDK requires Java 1.8 or higher.
Q: How do I handle long-running requests?
A: The SDK includes timeout utilities. You can configure timeouts using the
Timeout
class in
TransportOptions
.
Q: Why are some tools not executing?
A: This is likely due to permission modes. Check your permission mode settings and consider using
allowedTools
to pre-approve certain tools.
Q: How do I resume a previous session?
A: Use the
setResumeSessionId()
method in
TransportOptions
to resume a previous session.
Q: Can I customize the environment for the CLI process?
A: Yes, use the
setEnv()
method in
TransportOptions
to pass environment variables to the CLI process.
License
Apache-2.0 - see
LICENSE
for details.
Last updated on
May 18, 2026
Python SDK (alpha)
Channel Plugin Guide</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-java/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Extensions</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension/</guid>
  <pubDate>Mon, 12 Aug 2024 00:00:00 +0000</pubDate>
  <category>Extensions</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Extensions
Qwen Code Extensions
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extension management
We offer a suite of extension management tools using
qwen extensions
commands.
Note that these commands are not supported from within t...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Extensions
Qwen Code Extensions
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extension management
We offer a suite of extension management tools using
qwen extensions
commands.
Note that these commands are not supported from within the CLI, although you can list installed extensions using the
/extensions list
subcommand.
Note that all of these commands will only be reflected in active CLI sessions on restart.
Installing an extension
You can install an extension using
qwen extensions install
with either a GitHub URL or a local path`.
Note that we create a copy of the installed extension, so you will need to run
qwen extensions update
to pull in changes from both locally-defined extensions and those on GitHub.
qwen extensions install https://github.com/qwen-cli-extensions/security
This will install the Qwen Code Security extension, which offers support for a
/security:analyze
command.
Uninstalling an extension
To uninstall, run
qwen extensions uninstall extension-name
, so, in the case of the install example:
qwen extensions uninstall qwen-cli-security
Disabling an extension
Extensions are, by default, enabled across all workspaces. You can disable an extension entirely or for specific workspace.
For example,
qwen extensions disable extension-name
will disable the extension at the user level, so it will be disabled everywhere.
qwen extensions disable extension-name --scope=workspace
will only disable the extension in the current workspace.
Enabling an extension
You can enable extensions using
qwen extensions enable extension-name
. You can also enable an extension for a specific workspace using
qwen extensions enable extension-name --scope=workspace
from within that workspace.
This is useful if you have an extension disabled at the top-level and only enabled in specific places.
Updating an extension
For extensions installed from a local path or a git repository, you can explicitly update to the latest version (as reflected in the
qwen-extension.json
version
field) with
qwen extensions update extension-name
.
You can update all extensions with:
qwen extensions update --all
Extension creation
We offer commands to make extension development easier.
Create a boilerplate extension
We offer several example extensions
context
,
custom-commands
,
exclude-tools
and
mcp-server
. You can view these examples
here
.
To copy one of these examples into a development directory using the type of your choosing, run:
qwen extensions new path/to/directory custom-commands
Link a local extension
The
qwen extensions link
command will create a symbolic link from the extension installation directory to the development path.
This is useful so you don’t have to run
qwen extensions update
every time you make changes you’d like to test.
qwen extensions link path/to/directory
How it works
On startup, Qwen Code looks for extensions in
<home>/.qwen/extensions
Extensions exist as a directory that contains a
qwen-extension.json
file. For example:
<home>/.qwen/extensions/my-extension/qwen-extension.json
qwen-extension.json
The
qwen-extension.json
file contains the configuration for the extension. The file has the following structure:
{
"name"
:
"my-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"my-server"
: {
"command"
:
"node my-server.js"
}
},
"contextFileName"
:
"QWEN.md"
,
"excludeTools"
: [
"run_shell_command"
]
}
name
: The name of the extension. This is used to uniquely identify the extension and for conflict resolution when extension commands have the same name as user or project commands. The name should be lowercase or numbers and use dashes instead of underscores or spaces. This is how users will refer to your extension in the CLI. Note that we expect this name to match the extension directory name.
version
: The version of the extension.
mcpServers
: A map of MCP servers to configure. The key is the name of the server, and the value is the server configuration. These servers will be loaded on startup just like MCP servers configured in a
settings.json
file
. If both an extension and a
settings.json
file configure an MCP server with the same name, the server defined in the
settings.json
file takes precedence.
Note that all MCP server configuration options are supported except for
trust
.
contextFileName
: The name of the file that contains the context for the extension. This will be used to load the context from the extension directory. If this property is not used but a
QWEN.md
file is present in your extension directory, then that file will be loaded.
excludeTools
: An array of tool names to exclude from the model. You can also specify command-specific restrictions for tools that support it, like the
run_shell_command
tool. For example,
"excludeTools": ["run_shell_command(rm -rf)"]
will block the
rm -rf
command. Note that this differs from the MCP server
excludeTools
functionality, which can be listed in the MCP server config.
Important:
Tools specified in
excludeTools
will be disabled for the entire conversation context and will affect all subsequent queries in the current session.
When Qwen Code starts, it loads all the extensions and merges their configurations. If there are any conflicts, the workspace configuration takes precedence.
Custom commands
Extensions can provide
custom commands
by placing TOML files in a
commands/
subdirectory within the extension directory. These commands follow the same format as user and project custom commands and use standard naming conventions.
Example
An extension named
gcp
with the following structure:
.qwen/extensions/gcp/
├── qwen-extension.json
└── commands/
├── deploy.toml
└── gcs/
└── sync.toml
Would provide these commands:
/deploy
- Shows as
[gcp] Custom command from deploy.toml
in help
/gcs:sync
- Shows as
[gcp] Custom command from sync.toml
in help
Conflict resolution
Extension commands have the lowest precedence. When a conflict occurs with user or project commands:
No conflict
: Extension command uses its natural name (e.g.,
/deploy
)
With conflict
: Extension command is renamed with the extension prefix (e.g.,
/gcp.deploy
)
For example, if both a user and the
gcp
extension define a
deploy
command:
/deploy
- Executes the user’s deploy command
/gcp.deploy
- Executes the extension’s deploy command (marked with
[gcp]
tag)
Variables
Qwen Code extensions allow variable substitution in
qwen-extension.json
. This can be useful if e.g., you need the current directory to run an MCP server using
"cwd": "${extensionPath}${/}run.ts"
.
Supported variables:
variable
description
${extensionPath}
The fully-qualified path of the extension in the user’s filesystem e.g., ‘/Users/username/.qwen/extensions/example-extension’. This will not unwrap symlinks.
${workspacePath}
The fully-qualified path of the current workspace.
${/} or ${pathSeparator}
The path separator (differs per OS).
Last updated on
May 18, 2026
Issue and PR Automation
Extension Releasing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Extensions
Qwen Code Extensions
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extension management
We offer a suite of extension management tools using
qwen extensions
commands.
Note that these commands are not supported from within t...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Extensions
Qwen Code Extensions
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extension management
We offer a suite of extension management tools using
qwen extensions
commands.
Note that these commands are not supported from within the CLI, although you can list installed extensions using the
/extensions list
subcommand.
Note that all of these commands will only be reflected in active CLI sessions on restart.
Installing an extension
You can install an extension using
qwen extensions install
with either a GitHub URL or a local path`.
Note that we create a copy of the installed extension, so you will need to run
qwen extensions update
to pull in changes from both locally-defined extensions and those on GitHub.
qwen extensions install https://github.com/qwen-cli-extensions/security
This will install the Qwen Code Security extension, which offers support for a
/security:analyze
command.
Uninstalling an extension
To uninstall, run
qwen extensions uninstall extension-name
, so, in the case of the install example:
qwen extensions uninstall qwen-cli-security
Disabling an extension
Extensions are, by default, enabled across all workspaces. You can disable an extension entirely or for specific workspace.
For example,
qwen extensions disable extension-name
will disable the extension at the user level, so it will be disabled everywhere.
qwen extensions disable extension-name --scope=workspace
will only disable the extension in the current workspace.
Enabling an extension
You can enable extensions using
qwen extensions enable extension-name
. You can also enable an extension for a specific workspace using
qwen extensions enable extension-name --scope=workspace
from within that workspace.
This is useful if you have an extension disabled at the top-level and only enabled in specific places.
Updating an extension
For extensions installed from a local path or a git repository, you can explicitly update to the latest version (as reflected in the
qwen-extension.json
version
field) with
qwen extensions update extension-name
.
You can update all extensions with:
qwen extensions update --all
Extension creation
We offer commands to make extension development easier.
Create a boilerplate extension
We offer several example extensions
context
,
custom-commands
,
exclude-tools
and
mcp-server
. You can view these examples
here
.
To copy one of these examples into a development directory using the type of your choosing, run:
qwen extensions new path/to/directory custom-commands
Link a local extension
The
qwen extensions link
command will create a symbolic link from the extension installation directory to the development path.
This is useful so you don’t have to run
qwen extensions update
every time you make changes you’d like to test.
qwen extensions link path/to/directory
How it works
On startup, Qwen Code looks for extensions in
<home>/.qwen/extensions
Extensions exist as a directory that contains a
qwen-extension.json
file. For example:
<home>/.qwen/extensions/my-extension/qwen-extension.json
qwen-extension.json
The
qwen-extension.json
file contains the configuration for the extension. The file has the following structure:
{
"name"
:
"my-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"my-server"
: {
"command"
:
"node my-server.js"
}
},
"contextFileName"
:
"QWEN.md"
,
"excludeTools"
: [
"run_shell_command"
]
}
name
: The name of the extension. This is used to uniquely identify the extension and for conflict resolution when extension commands have the same name as user or project commands. The name should be lowercase or numbers and use dashes instead of underscores or spaces. This is how users will refer to your extension in the CLI. Note that we expect this name to match the extension directory name.
version
: The version of the extension.
mcpServers
: A map of MCP servers to configure. The key is the name of the server, and the value is the server configuration. These servers will be loaded on startup just like MCP servers configured in a
settings.json
file
. If both an extension and a
settings.json
file configure an MCP server with the same name, the server defined in the
settings.json
file takes precedence.
Note that all MCP server configuration options are supported except for
trust
.
contextFileName
: The name of the file that contains the context for the extension. This will be used to load the context from the extension directory. If this property is not used but a
QWEN.md
file is present in your extension directory, then that file will be loaded.
excludeTools
: An array of tool names to exclude from the model. You can also specify command-specific restrictions for tools that support it, like the
run_shell_command
tool. For example,
"excludeTools": ["run_shell_command(rm -rf)"]
will block the
rm -rf
command. Note that this differs from the MCP server
excludeTools
functionality, which can be listed in the MCP server config.
Important:
Tools specified in
excludeTools
will be disabled for the entire conversation context and will affect all subsequent queries in the current session.
When Qwen Code starts, it loads all the extensions and merges their configurations. If there are any conflicts, the workspace configuration takes precedence.
Custom commands
Extensions can provide
custom commands
by placing TOML files in a
commands/
subdirectory within the extension directory. These commands follow the same format as user and project custom commands and use standard naming conventions.
Example
An extension named
gcp
with the following structure:
.qwen/extensions/gcp/
├── qwen-extension.json
└── commands/
├── deploy.toml
└── gcs/
└── sync.toml
Would provide these commands:
/deploy
- Shows as
[gcp] Custom command from deploy.toml
in help
/gcs:sync
- Shows as
[gcp] Custom command from sync.toml
in help
Conflict resolution
Extension commands have the lowest precedence. When a conflict occurs with user or project commands:
No conflict
: Extension command uses its natural name (e.g.,
/deploy
)
With conflict
: Extension command is renamed with the extension prefix (e.g.,
/gcp.deploy
)
For example, if both a user and the
gcp
extension define a
deploy
command:
/deploy
- Executes the user’s deploy command
/gcp.deploy
- Executes the extension’s deploy command (marked with
[gcp]
tag)
Variables
Qwen Code extensions allow variable substitution in
qwen-extension.json
. This can be useful if e.g., you need the current directory to run an MCP server using
"cwd": "${extensionPath}${/}run.ts"
.
Supported variables:
variable
description
${extensionPath}
The fully-qualified path of the extension in the user’s filesystem e.g., ‘/Users/username/.qwen/extensions/example-extension’. This will not unwrap symlinks.
${workspacePath}
The fully-qualified path of the current workspace.
${/} or ${pathSeparator}
The path separator (differs per OS).
Last updated on
May 18, 2026
Issue and PR Automation
Extension Releasing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/extension/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Status Line</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/status-line/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/status-line/</guid>
  <pubDate>Sun, 11 Aug 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Status Line
Status Line
Display custom information in the footer using a shell command.
The status line lets you run a shell command whose output is displayed in the footer’s left section. The command receives structured JSON context via stdin, so it can show session-aware information like the current model, token usage, git branch, or anything else you can script.
Single-line status (default approval mode — 1 row):
┌───────────────────────────────────────────────────────────...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Status Line
Status Line
Display custom information in the footer using a shell command.
The status line lets you run a shell command whose output is displayed in the footer’s left section. The command receives structured JSON context via stdin, so it can show session-aware information like the current model, token usage, git branch, or anything else you can script.
Single-line status (default approval mode — 1 row):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line
└─────────────────────────────────────────────────────────────────┘
Multi-line status (up to 2 lines — 2 rows):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line 1
│  ████████░░░░░░░░░░ 34% context                                │  ← status line 2
└─────────────────────────────────────────────────────────────────┘
Multi-line status + non-default mode (3 rows max):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line 1
│  ████████░░░░░░░░░░ 34% context                                │  ← status line 2
│  auto-accept edits (shift + tab to cycle)                       │  ← mode indicator
└─────────────────────────────────────────────────────────────────┘
When configured, the status line replaces the default ”? for shortcuts” hint. High-priority messages (Ctrl+C/D exit prompts, Esc, vim INSERT mode) temporarily override the status line. The status line text is truncated to fit within the available width.
Prerequisites
jq
is recommended for parsing the JSON input (install via
brew install jq
,
apt install jq
, etc.)
Simple commands that don’t need JSON data (e.g.
git branch --show-current
) work without
jq
Quick setup
The easiest way to configure a status line is the
/statusline
command. It launches a setup agent that reads your shell PS1 configuration and generates a matching status line:
/statusline
You can also give it specific instructions:
/statusline show model name and context usage percentage
Manual configuration
Add a
statusLine
object under the
ui
key in
~/.qwen/settings.json
:
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); model=$(echo
\"
$input
\"
| jq -r '.model.display_name'); pct=$(echo
\"
$input
\"
| jq -r '.context_window.used_percentage'); echo
\"
$model  ctx:${pct}%
\"
"
}
}
}
Field
Type
Required
Description
type
"command"
Yes
Must be
"command"
command
string
Yes
Shell command to execute. Receives JSON via stdin, stdout is displayed (up to 2 lines).
refreshInterval
number
No
Re-run the command every N seconds (minimum 1). Useful for data that changes without an Agent state event (clock, quota, uptime).
JSON input
The command receives a JSON object via stdin with the following fields:
{
"session_id"
:
"abc-123"
,
"version"
:
"0.14.1"
,
"model"
: {
"display_name"
:
"qwen-3-235b"
},
"context_window"
: {
"context_window_size"
:
131072
,
"used_percentage"
:
34.3
,
"remaining_percentage"
:
65.7
,
"current_usage"
:
45000
,
"total_input_tokens"
:
30000
,
"total_output_tokens"
:
5000
},
"workspace"
: {
"current_dir"
:
"/home/user/project"
},
"git"
: {
"branch"
:
"main"
},
"metrics"
: {
"models"
: {
"qwen-3-235b"
: {
"api"
: {
"total_requests"
:
10
,
"total_errors"
:
0
,
"total_latency_ms"
:
5000
},
"tokens"
: {
"prompt"
:
30000
,
"completion"
:
5000
,
"total"
:
35000
,
"cached"
:
10000
,
"thoughts"
:
2000
}
}
},
"files"
: {
"total_lines_added"
:
120
,
"total_lines_removed"
:
30
}
},
"vim"
: {
"mode"
:
"INSERT"
}
}
Field
Type
Description
session_id
string
Unique session identifier
version
string
Qwen Code version
model.display_name
string
Current model name
context_window.context_window_size
number
Total context window size in tokens
context_window.used_percentage
number
Context window usage as percentage (0–100)
context_window.remaining_percentage
number
Context window remaining as percentage (0–100)
context_window.current_usage
number
Token count from the last API call (current context size)
context_window.total_input_tokens
number
Total input tokens consumed this session
context_window.total_output_tokens
number
Total output tokens consumed this session
workspace.current_dir
string
Current working directory
git
object | absent
Present only inside a git repository.
git.branch
string
Current branch name
metrics.models.<id>.api
object
Per-model API stats:
total_requests
,
total_errors
,
total_latency_ms
metrics.models.<id>.tokens
object
Per-model token usage:
prompt
,
completion
,
total
,
cached
,
thoughts
metrics.files
object
File change stats:
total_lines_added
,
total_lines_removed
vim
object | absent
Present only when vim mode is enabled. Contains
mode
(
"INSERT"
or
"NORMAL"
).
Important:
stdin can only be read once. Always store it in a variable first:
input=$(cat)
.
Examples
Model and token usage
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); model=$(echo
\"
$input
\"
| jq -r '.model.display_name'); pct=$(echo
\"
$input
\"
| jq -r '.context_window.used_percentage'); echo
\"
$model  ctx:${pct}%
\"
"
}
}
}
Output:
qwen-3-235b  ctx:34%
Git branch + directory
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); branch=$(echo
\"
$input
\"
| jq -r '.git.branch // empty'); dir=$(basename
\"
$(echo
\"
$input
\"
| jq -r '.workspace.current_dir')
\"
); echo
\"
$dir${branch:+ ($branch)}
\"
"
}
}
}
Output:
my-project (main)
Note: The
git.branch
field is provided directly in the JSON input — no need to shell out to
git
.
File change stats
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); added=$(echo
\"
$input
\"
| jq -r '.metrics.files.total_lines_added'); removed=$(echo
\"
$input
\"
| jq -r '.metrics.files.total_lines_removed'); echo
\"
+$added/-$removed lines
\"
"
}
}
}
Output:
+120/-30 lines
Live clock and git branch
Use
refreshInterval
when the statusline shows data that changes without an Agent event (e.g. the clock, uptime, or rate-limit counters):
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); branch=$(echo
\"
$input
\"
| jq -r '.git.branch //
\"
no-git
\"
'); echo
\"
$(date +%H:%M:%S)  ($branch)
\"
"
,
"refreshInterval"
:
1
}
}
}
Output (refreshed every second):
14:32:07  (main)
Script file for complex commands
For longer commands, save a script file at
~/.qwen/statusline-command.sh
:
#!/bin/bash
input
=
$(
cat
)
model
=
$(
echo
"
$input
"
|
jq
-r
'.model.display_name'
)
pct
=
$(
echo
"
$input
"
|
jq
-r
'.context_window.used_percentage'
)
branch
=
$(
echo
"
$input
"
|
jq
-r
'.git.branch // empty'
)
added
=
$(
echo
"
$input
"
|
jq
-r
'.metrics.files.total_lines_added'
)
removed
=
$(
echo
"
$input
"
|
jq
-r
'.metrics.files.total_lines_removed'
)
parts
=
()
[
-n
"
$model
"
] && parts
+=
(
"
$model
"
)
[
-n
"
$branch
"
] && parts
+=
(
"(
$branch
)"
)
[
"
$pct
"
!=
"0"
]
2>
/dev/null && parts
+=
(
"ctx:${
pct
}%"
)
([
"
$added
"
-gt
0
]
||
[
"
$removed
"
-gt
0
])
2
>
/dev/null
&& parts
+=
(
"+${
added
}/-${
removed
}"
)
echo
"${
parts
[
*
]}"
Then reference it in settings:
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"bash ~/.qwen/statusline-command.sh"
}
}
}
Behavior
Update triggers
: The status line updates when the model changes, a new message is sent (token count changes), vim mode is toggled, git branch changes, tool calls complete, or file changes occur. Updates are debounced (300ms). Set
refreshInterval
(seconds) to additionally re-run the command on a timer — useful for data that changes without an Agent event (clock, rate limits, build status).
Timeout
: Commands that take longer than 5 seconds are killed. The status line clears on failure.
Output
: Multi-line output is supported (up to 2 lines; extra lines are discarded). Each line is rendered as a separate row with dimmed colors in the footer’s left section. Lines that exceed the available width are truncated.
Hot reload
: Changes to
ui.statusLine
in settings take effect immediately — no restart required.
Shell
: Commands run via
/bin/sh
on macOS/Linux. On Windows,
cmd.exe
is used by default — wrap POSIX commands with
bash -c "..."
or point to a bash script (e.g.
bash ~/.qwen/statusline-command.sh
).
Removal
: Delete the
ui.statusLine
key from settings to disable. The ”? for shortcuts” hint returns.
Troubleshooting
Problem
Cause
Fix
Status line not showing
Config at wrong path
Must be under
ui.statusLine
, not root-level
statusLine
Empty output
Command fails silently
Test manually:
echo '{"session_id":"test","version":"0.14.1","model":{"display_name":"test"},"context_window":{"context_window_size":0,"used_percentage":0,"remaining_percentage":100,"current_usage":0,"total_input_tokens":0,"total_output_tokens":0},"workspace":{"current_dir":"/tmp"},"metrics":{"models":{},"files":{"total_lines_added":0,"total_lines_removed":0}}}' | sh -c 'your_command'
Stale data
No trigger fired
Send a message or switch models to trigger an update — or set
refreshInterval
to re-run the command on a timer
Command too slow
Complex script
Optimize the script or move heavy work to a background cache
Last updated on
May 18, 2026
Hooks
Scheduled Tasks</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/status-line/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Status Line
Status Line
Display custom information in the footer using a shell command.
The status line lets you run a shell command whose output is displayed in the footer’s left section. The command receives structured JSON context via stdin, so it can show session-aware information like the current model, token usage, git branch, or anything else you can script.
Single-line status (default approval mode — 1 row):
┌───────────────────────────────────────────────────────────...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Status Line
Status Line
Display custom information in the footer using a shell command.
The status line lets you run a shell command whose output is displayed in the footer’s left section. The command receives structured JSON context via stdin, so it can show session-aware information like the current model, token usage, git branch, or anything else you can script.
Single-line status (default approval mode — 1 row):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line
└─────────────────────────────────────────────────────────────────┘
Multi-line status (up to 2 lines — 2 rows):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line 1
│  ████████░░░░░░░░░░ 34% context                                │  ← status line 2
└─────────────────────────────────────────────────────────────────┘
Multi-line status + non-default mode (3 rows max):
┌─────────────────────────────────────────────────────────────────┐
│  user@host ~/project (main) ctx:34%   🔒 docker | Debug | 67%  │  ← status line 1
│  ████████░░░░░░░░░░ 34% context                                │  ← status line 2
│  auto-accept edits (shift + tab to cycle)                       │  ← mode indicator
└─────────────────────────────────────────────────────────────────┘
When configured, the status line replaces the default ”? for shortcuts” hint. High-priority messages (Ctrl+C/D exit prompts, Esc, vim INSERT mode) temporarily override the status line. The status line text is truncated to fit within the available width.
Prerequisites
jq
is recommended for parsing the JSON input (install via
brew install jq
,
apt install jq
, etc.)
Simple commands that don’t need JSON data (e.g.
git branch --show-current
) work without
jq
Quick setup
The easiest way to configure a status line is the
/statusline
command. It launches a setup agent that reads your shell PS1 configuration and generates a matching status line:
/statusline
You can also give it specific instructions:
/statusline show model name and context usage percentage
Manual configuration
Add a
statusLine
object under the
ui
key in
~/.qwen/settings.json
:
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); model=$(echo
\"
$input
\"
| jq -r '.model.display_name'); pct=$(echo
\"
$input
\"
| jq -r '.context_window.used_percentage'); echo
\"
$model  ctx:${pct}%
\"
"
}
}
}
Field
Type
Required
Description
type
"command"
Yes
Must be
"command"
command
string
Yes
Shell command to execute. Receives JSON via stdin, stdout is displayed (up to 2 lines).
refreshInterval
number
No
Re-run the command every N seconds (minimum 1). Useful for data that changes without an Agent state event (clock, quota, uptime).
JSON input
The command receives a JSON object via stdin with the following fields:
{
"session_id"
:
"abc-123"
,
"version"
:
"0.14.1"
,
"model"
: {
"display_name"
:
"qwen-3-235b"
},
"context_window"
: {
"context_window_size"
:
131072
,
"used_percentage"
:
34.3
,
"remaining_percentage"
:
65.7
,
"current_usage"
:
45000
,
"total_input_tokens"
:
30000
,
"total_output_tokens"
:
5000
},
"workspace"
: {
"current_dir"
:
"/home/user/project"
},
"git"
: {
"branch"
:
"main"
},
"metrics"
: {
"models"
: {
"qwen-3-235b"
: {
"api"
: {
"total_requests"
:
10
,
"total_errors"
:
0
,
"total_latency_ms"
:
5000
},
"tokens"
: {
"prompt"
:
30000
,
"completion"
:
5000
,
"total"
:
35000
,
"cached"
:
10000
,
"thoughts"
:
2000
}
}
},
"files"
: {
"total_lines_added"
:
120
,
"total_lines_removed"
:
30
}
},
"vim"
: {
"mode"
:
"INSERT"
}
}
Field
Type
Description
session_id
string
Unique session identifier
version
string
Qwen Code version
model.display_name
string
Current model name
context_window.context_window_size
number
Total context window size in tokens
context_window.used_percentage
number
Context window usage as percentage (0–100)
context_window.remaining_percentage
number
Context window remaining as percentage (0–100)
context_window.current_usage
number
Token count from the last API call (current context size)
context_window.total_input_tokens
number
Total input tokens consumed this session
context_window.total_output_tokens
number
Total output tokens consumed this session
workspace.current_dir
string
Current working directory
git
object | absent
Present only inside a git repository.
git.branch
string
Current branch name
metrics.models.<id>.api
object
Per-model API stats:
total_requests
,
total_errors
,
total_latency_ms
metrics.models.<id>.tokens
object
Per-model token usage:
prompt
,
completion
,
total
,
cached
,
thoughts
metrics.files
object
File change stats:
total_lines_added
,
total_lines_removed
vim
object | absent
Present only when vim mode is enabled. Contains
mode
(
"INSERT"
or
"NORMAL"
).
Important:
stdin can only be read once. Always store it in a variable first:
input=$(cat)
.
Examples
Model and token usage
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); model=$(echo
\"
$input
\"
| jq -r '.model.display_name'); pct=$(echo
\"
$input
\"
| jq -r '.context_window.used_percentage'); echo
\"
$model  ctx:${pct}%
\"
"
}
}
}
Output:
qwen-3-235b  ctx:34%
Git branch + directory
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); branch=$(echo
\"
$input
\"
| jq -r '.git.branch // empty'); dir=$(basename
\"
$(echo
\"
$input
\"
| jq -r '.workspace.current_dir')
\"
); echo
\"
$dir${branch:+ ($branch)}
\"
"
}
}
}
Output:
my-project (main)
Note: The
git.branch
field is provided directly in the JSON input — no need to shell out to
git
.
File change stats
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); added=$(echo
\"
$input
\"
| jq -r '.metrics.files.total_lines_added'); removed=$(echo
\"
$input
\"
| jq -r '.metrics.files.total_lines_removed'); echo
\"
+$added/-$removed lines
\"
"
}
}
}
Output:
+120/-30 lines
Live clock and git branch
Use
refreshInterval
when the statusline shows data that changes without an Agent event (e.g. the clock, uptime, or rate-limit counters):
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"input=$(cat); branch=$(echo
\"
$input
\"
| jq -r '.git.branch //
\"
no-git
\"
'); echo
\"
$(date +%H:%M:%S)  ($branch)
\"
"
,
"refreshInterval"
:
1
}
}
}
Output (refreshed every second):
14:32:07  (main)
Script file for complex commands
For longer commands, save a script file at
~/.qwen/statusline-command.sh
:
#!/bin/bash
input
=
$(
cat
)
model
=
$(
echo
"
$input
"
|
jq
-r
'.model.display_name'
)
pct
=
$(
echo
"
$input
"
|
jq
-r
'.context_window.used_percentage'
)
branch
=
$(
echo
"
$input
"
|
jq
-r
'.git.branch // empty'
)
added
=
$(
echo
"
$input
"
|
jq
-r
'.metrics.files.total_lines_added'
)
removed
=
$(
echo
"
$input
"
|
jq
-r
'.metrics.files.total_lines_removed'
)
parts
=
()
[
-n
"
$model
"
] && parts
+=
(
"
$model
"
)
[
-n
"
$branch
"
] && parts
+=
(
"(
$branch
)"
)
[
"
$pct
"
!=
"0"
]
2>
/dev/null && parts
+=
(
"ctx:${
pct
}%"
)
([
"
$added
"
-gt
0
]
||
[
"
$removed
"
-gt
0
])
2
>
/dev/null
&& parts
+=
(
"+${
added
}/-${
removed
}"
)
echo
"${
parts
[
*
]}"
Then reference it in settings:
{
"ui"
: {
"statusLine"
: {
"type"
:
"command"
,
"command"
:
"bash ~/.qwen/statusline-command.sh"
}
}
}
Behavior
Update triggers
: The status line updates when the model changes, a new message is sent (token count changes), vim mode is toggled, git branch changes, tool calls complete, or file changes occur. Updates are debounced (300ms). Set
refreshInterval
(seconds) to additionally re-run the command on a timer — useful for data that changes without an Agent event (clock, rate limits, build status).
Timeout
: Commands that take longer than 5 seconds are killed. The status line clears on failure.
Output
: Multi-line output is supported (up to 2 lines; extra lines are discarded). Each line is rendered as a separate row with dimmed colors in the footer’s left section. Lines that exceed the available width are truncated.
Hot reload
: Changes to
ui.statusLine
in settings take effect immediately — no restart required.
Shell
: Commands run via
/bin/sh
on macOS/Linux. On Windows,
cmd.exe
is used by default — wrap POSIX commands with
bash -c "..."
or point to a bash script (e.g.
bash ~/.qwen/statusline-command.sh
).
Removal
: Delete the
ui.statusLine
key from settings to disable. The ”? for shortcuts” hint returns.
Troubleshooting
Problem
Cause
Fix
Status line not showing
Config at wrong path
Must be under
ui.statusLine
, not root-level
statusLine
Empty output
Command fails silently
Test manually:
echo '{"session_id":"test","version":"0.14.1","model":{"display_name":"test"},"context_window":{"context_window_size":0,"used_percentage":0,"remaining_percentage":100,"current_usage":0,"total_input_tokens":0,"total_output_tokens":0},"workspace":{"current_dir":"/tmp"},"metrics":{"models":{},"files":{"total_lines_added":0,"total_lines_removed":0}}}' | sh -c 'your_command'
Stale data
No trigger fired
Send a message or switch models to trigger an update — or set
refreshInterval
to re-run the command on a timer
Command too slow
Complex script
Optimize the script or move heavy work to a background cache
Last updated on
May 18, 2026
Hooks
Scheduled Tasks</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/status-line/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Quickstart</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/quickstart/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/quickstart/</guid>
  <pubDate>Fri, 02 Aug 2024 00:00:00 +0000</pubDate>
  <category>Getting Started</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
QuickStart
Quickstart
👏 Welcome to Qwen Code!
This quickstart guide will have you using AI-powered coding assistance in just a few minutes. By the end, you’ll understand how to use Qwen Code for common development tasks.
Before you begin
Make sure you have:
A
terminal
or command prompt open
A code project to work with
An API key from Alibaba Cloud Model Studio (
Beijing
/
intl
), or an Alibaba Cloud Coding Plan (
Beijing
/
intl
) subscription
Step 1: Install Qwen Code
To install Qwen ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
QuickStart
Quickstart
👏 Welcome to Qwen Code!
This quickstart guide will have you using AI-powered coding assistance in just a few minutes. By the end, you’ll understand how to use Qwen Code for common development tasks.
Before you begin
Make sure you have:
A
terminal
or command prompt open
A code project to work with
An API key from Alibaba Cloud Model Studio (
Beijing
/
intl
), or an Alibaba Cloud Coding Plan (
Beijing
/
intl
) subscription
Step 1: Install Qwen Code
To install Qwen Code, use one of the following methods:
Quick Install (Recommended)
Linux / macOS
curl
-fsSL
https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh
|
bash
Windows (Run as Administrator)
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
Step 2: Set up authentication
When you start an interactive session with the
qwen
command, you’ll be prompted to configure authentication:
# You'll be prompted to set up authentication on first use
qwen
# Or run /auth anytime to change authentication method
/auth
Choose your preferred authentication method:
Alibaba Cloud Coding Plan
: Select
Alibaba Cloud Coding Plan
for a fixed monthly fee with diverse model options. See the
Coding Plan guide
(
intl
) for setup instructions.
API Key
: Select
API Key
, then enter your API key from Alibaba Cloud Model Studio (
Beijing
/
intl
). See the API setup guide (
Beijing
/
intl
) for details.
⚠️
Note
: Qwen OAuth was discontinued on April 15, 2026. If you were previously using Qwen OAuth, please switch to one of the methods above.
Note
When you first authenticate Qwen Code with your Qwen account, a workspace called “.qwen” is automatically created for you. This workspace provides centralized cost tracking and management for all Qwen Code usage in your organization.
Tip
You can also configure authentication directly from the terminal without starting a session by running
qwen auth
. Use
qwen auth status
to check your current configuration at any time. See the
Authentication
page for details.
Step 3: Start your first session
Open your terminal in any project directory and start Qwen Code:
# optiona
cd
/path/to/your/project
# start qwen
qwen
You’ll see the Qwen Code welcome screen with your session information, recent conversations, and latest updates. Type
/help
for available commands.
Chat with Qwen Code
Ask your first question
Qwen Code will analyze your files and provide a summary. You can also ask more specific questions:
explain the folder structure
You can also ask Qwen Code about its own capabilities:
what can Qwen Code do?
Note
Qwen Code reads your files as needed - you don’t have to manually add context. Qwen Code also has access to its own documentation and can answer questions about its features and capabilities.
Make your first code change
Now let’s make Qwen Code do some actual coding. Try a simple task:
add a hello world function to the main file
Qwen Code will:
Find the appropriate file
Show you the proposed changes
Ask for your approval
Make the edit
Note
Qwen Code always asks for permission before modifying files. You can approve individual changes or enable “Accept all” mode for a session.
Use Git with Qwen Code
Qwen Code makes Git operations conversational:
what files have I changed?
commit my changes with a descriptive message
You can also prompt for more complex Git operations:
create a new branch called feature/quickstart
show me the last 5 commits
help me resolve merge conflicts
Fix a bug or add a feature
Qwen Code is proficient at debugging and feature implementation.
Describe what you want in natural language:
add input validation to the user registration form
Or fix existing issues:
there's a bug where users can submit empty forms - fix it
Qwen Code will:
Locate the relevant code
Understand the context
Implement a solution
Run tests if available
Test out other common workflows
There are a number of ways to work with Qwen Code:
Refactor code
refactor the authentication module to use async/await instead of callbacks
Write tests
write unit tests for the calculator functions
Update documentation
update the README with installation instructions
Code review
review my changes and suggest improvements
Tip
Remember
: Qwen Code is your AI pair programmer. Talk to it like you would a helpful colleague - describe what you want to achieve, and it will help you get there.
Essential commands
Here are the most important commands for daily use:
Command
What it does
Example
qwen
start Qwen Code
qwen
/auth
Change authentication method (in session)
/auth
qwen auth
Configure authentication from the terminal
qwen auth
qwen auth api-key
Configure API key authentication
qwen auth api-key
qwen auth status
Check current authentication status
qwen auth status
/help
Display help information for available commands
/help
or
/?
/compress
Replace chat history with summary to save Tokens
/compress
/clear
Clear terminal screen content
/clear
(shortcut:
Ctrl+L
)
/theme
Change Qwen Code visual theme
/theme
/language
View or change language settings
/language
→
ui [language]
Set UI interface language
/language ui zh-CN
→
output [language]
Set LLM output language
/language output Chinese
/quit
Exit Qwen Code immediately
/quit
or
/exit
See the
CLI reference
for a complete list of commands.
Pro tips for beginners
Be specific with your requests
Instead of: “fix the bug”
Try: “fix the login bug where users see a blank screen after entering wrong credentials”
Use step-by-step instructions
Break complex tasks into steps:
1. create a new database table for user profiles
2. create an API endpoint to get and update user profiles
3. build a webpage that allows users to see and edit their information
Let Qwen Code explore first
Before making changes, let Qwen Code understand your code:
analyze the database schema
build a dashboard showing products that are most frequently returned by our UK customers
Save time with shortcuts
Press
?
to see all available keyboard shortcuts
Use Tab for command completion
Press ↑ for command history
Type
/
to see all slash commands
Getting help
In Qwen Code
: Type
/help
or ask “how do I…”
Documentation
: You’re here! Browse other guides
Community
: Join our
GitHub Discussion
for tips and support
Last updated on
May 18, 2026
Overview
Command Workflows</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/quickstart/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
QuickStart
Quickstart
👏 Welcome to Qwen Code!
This quickstart guide will have you using AI-powered coding assistance in just a few minutes. By the end, you’ll understand how to use Qwen Code for common development tasks.
Before you begin
Make sure you have:
A
terminal
or command prompt open
A code project to work with
An API key from Alibaba Cloud Model Studio (
Beijing
/
intl
), or an Alibaba Cloud Coding Plan (
Beijing
/
intl
) subscription
Step 1: Install Qwen Code
To install Qwen ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
QuickStart
Quickstart
👏 Welcome to Qwen Code!
This quickstart guide will have you using AI-powered coding assistance in just a few minutes. By the end, you’ll understand how to use Qwen Code for common development tasks.
Before you begin
Make sure you have:
A
terminal
or command prompt open
A code project to work with
An API key from Alibaba Cloud Model Studio (
Beijing
/
intl
), or an Alibaba Cloud Coding Plan (
Beijing
/
intl
) subscription
Step 1: Install Qwen Code
To install Qwen Code, use one of the following methods:
Quick Install (Recommended)
Linux / macOS
curl
-fsSL
https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh
|
bash
Windows (Run as Administrator)
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
Step 2: Set up authentication
When you start an interactive session with the
qwen
command, you’ll be prompted to configure authentication:
# You'll be prompted to set up authentication on first use
qwen
# Or run /auth anytime to change authentication method
/auth
Choose your preferred authentication method:
Alibaba Cloud Coding Plan
: Select
Alibaba Cloud Coding Plan
for a fixed monthly fee with diverse model options. See the
Coding Plan guide
(
intl
) for setup instructions.
API Key
: Select
API Key
, then enter your API key from Alibaba Cloud Model Studio (
Beijing
/
intl
). See the API setup guide (
Beijing
/
intl
) for details.
⚠️
Note
: Qwen OAuth was discontinued on April 15, 2026. If you were previously using Qwen OAuth, please switch to one of the methods above.
Note
When you first authenticate Qwen Code with your Qwen account, a workspace called “.qwen” is automatically created for you. This workspace provides centralized cost tracking and management for all Qwen Code usage in your organization.
Tip
You can also configure authentication directly from the terminal without starting a session by running
qwen auth
. Use
qwen auth status
to check your current configuration at any time. See the
Authentication
page for details.
Step 3: Start your first session
Open your terminal in any project directory and start Qwen Code:
# optiona
cd
/path/to/your/project
# start qwen
qwen
You’ll see the Qwen Code welcome screen with your session information, recent conversations, and latest updates. Type
/help
for available commands.
Chat with Qwen Code
Ask your first question
Qwen Code will analyze your files and provide a summary. You can also ask more specific questions:
explain the folder structure
You can also ask Qwen Code about its own capabilities:
what can Qwen Code do?
Note
Qwen Code reads your files as needed - you don’t have to manually add context. Qwen Code also has access to its own documentation and can answer questions about its features and capabilities.
Make your first code change
Now let’s make Qwen Code do some actual coding. Try a simple task:
add a hello world function to the main file
Qwen Code will:
Find the appropriate file
Show you the proposed changes
Ask for your approval
Make the edit
Note
Qwen Code always asks for permission before modifying files. You can approve individual changes or enable “Accept all” mode for a session.
Use Git with Qwen Code
Qwen Code makes Git operations conversational:
what files have I changed?
commit my changes with a descriptive message
You can also prompt for more complex Git operations:
create a new branch called feature/quickstart
show me the last 5 commits
help me resolve merge conflicts
Fix a bug or add a feature
Qwen Code is proficient at debugging and feature implementation.
Describe what you want in natural language:
add input validation to the user registration form
Or fix existing issues:
there's a bug where users can submit empty forms - fix it
Qwen Code will:
Locate the relevant code
Understand the context
Implement a solution
Run tests if available
Test out other common workflows
There are a number of ways to work with Qwen Code:
Refactor code
refactor the authentication module to use async/await instead of callbacks
Write tests
write unit tests for the calculator functions
Update documentation
update the README with installation instructions
Code review
review my changes and suggest improvements
Tip
Remember
: Qwen Code is your AI pair programmer. Talk to it like you would a helpful colleague - describe what you want to achieve, and it will help you get there.
Essential commands
Here are the most important commands for daily use:
Command
What it does
Example
qwen
start Qwen Code
qwen
/auth
Change authentication method (in session)
/auth
qwen auth
Configure authentication from the terminal
qwen auth
qwen auth api-key
Configure API key authentication
qwen auth api-key
qwen auth status
Check current authentication status
qwen auth status
/help
Display help information for available commands
/help
or
/?
/compress
Replace chat history with summary to save Tokens
/compress
/clear
Clear terminal screen content
/clear
(shortcut:
Ctrl+L
)
/theme
Change Qwen Code visual theme
/theme
/language
View or change language settings
/language
→
ui [language]
Set UI interface language
/language ui zh-CN
→
output [language]
Set LLM output language
/language output Chinese
/quit
Exit Qwen Code immediately
/quit
or
/exit
See the
CLI reference
for a complete list of commands.
Pro tips for beginners
Be specific with your requests
Instead of: “fix the bug”
Try: “fix the login bug where users see a blank screen after entering wrong credentials”
Use step-by-step instructions
Break complex tasks into steps:
1. create a new database table for user profiles
2. create an API endpoint to get and update user profiles
3. build a webpage that allows users to see and edit their information
Let Qwen Code explore first
Before making changes, let Qwen Code understand your code:
analyze the database schema
build a dashboard showing products that are most frequently returned by our UK customers
Save time with shortcuts
Press
?
to see all available keyboard shortcuts
Use Tab for command completion
Press ↑ for command history
Type
/
to see all slash commands
Getting help
In Qwen Code
: Type
/help
or ask “how do I…”
Documentation
: You’re here! Browse other guides
Community
: Join our
GitHub Discussion
for tips and support
Last updated on
May 18, 2026
Overview
Command Workflows</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/quickstart/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Authentication</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/configuration/auth/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/configuration/auth/</guid>
  <pubDate>Fri, 02 Aug 2024 00:00:00 +0000</pubDate>
  <category>Configuration</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Authentication
Authentication
Qwen Code supports three authentication methods. Pick the one that matches how you want to run the CLI:
Qwen OAuth
: sign in with your
qwen.ai
account in a browser.
Free tier discontinued on 2026-04-15
— switch to another method.
Alibaba Cloud Coding Plan
: use an API key from Alibaba Cloud. Paid subscription with diverse model options and higher quotas.
API Key
: bring your own API key. Flexible to your own needs — supports OpenAI, Anthropi...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Authentication
Authentication
Qwen Code supports three authentication methods. Pick the one that matches how you want to run the CLI:
Qwen OAuth
: sign in with your
qwen.ai
account in a browser.
Free tier discontinued on 2026-04-15
— switch to another method.
Alibaba Cloud Coding Plan
: use an API key from Alibaba Cloud. Paid subscription with diverse model options and higher quotas.
API Key
: bring your own API key. Flexible to your own needs — supports OpenAI, Anthropic, Gemini, and other compatible endpoints.
Option 1: Qwen OAuth (Discontinued)
Warning
The Qwen OAuth free tier was discontinued on 2026-04-15. Existing cached tokens may continue working briefly, but new requests will be rejected. Please switch to Alibaba Cloud Coding Plan,
OpenRouter
,
Fireworks AI
, or another provider. Run
qwen auth
to configure.
How it works
: on first start, Qwen Code opens a browser login page. After you finish, credentials are cached locally so you usually won’t need to log in again.
Requirements
: a
qwen.ai
account + internet access (at least for the first login).
Benefits
: no API key management, automatic credential refresh.
Cost & quota
: the free tier has been discontinued as of 2026-04-15.
Start the CLI and follow the browser flow:
qwen
Or authenticate directly without starting a session:
qwen
auth
qwen-oauth
Note
In non-interactive or headless environments (e.g., CI, SSH, containers), you typically
cannot
complete the OAuth browser login flow.
In these cases, please use the Alibaba Cloud Coding Plan or API Key authentication method.
💳 Option 2: Alibaba Cloud Coding Plan
Use this if you want predictable costs with diverse model options and higher usage quotas.
How it works
: Subscribe to the Coding Plan with a fixed monthly fee, then configure Qwen Code to use the dedicated endpoint and your subscription API key.
Requirements
: Obtain an active Coding Plan subscription from
Alibaba Cloud ModelStudio(Beijing)
or
Alibaba Cloud ModelStudio(intl)
, depending on the region of your account.
Benefits
: Diverse model options, higher usage quotas, predictable monthly costs, access to a wide range of models (Qwen, GLM, Kimi, Minimax and more).
Cost & quota
: View Aliyun ModelStudio Coding Plan documentation
Beijing
intl
.
Alibaba Cloud Coding Plan is available in two regions:
Region
Console URL
Aliyun ModelStudio (Beijing)
bailian.console.aliyun.com
Alibaba Cloud (intl)
bailian.console.alibabacloud.com
Interactive setup
You can set up Coding Plan authentication in two ways:
Option A: From the terminal (recommended for first-time setup)
# Interactive — prompts for region and API key
qwen
auth
coding-plan
# Or non-interactive — pass region and key directly
qwen
auth
coding-plan
--region
china
--key
sk-sp-xxxxxxxxx
Option B: Inside a Qwen Code session
Enter
qwen
in the terminal to launch Qwen Code, then run the
/auth
command and select
Alibaba Cloud Coding Plan
. Choose your region, then enter your
sk-sp-xxxxxxxxx
key.
After authentication, use the
/model
command to switch between all Alibaba Cloud Coding Plan supported models (including qwen3.5-plus, qwen3-coder-plus, qwen3-coder-next, qwen3-max, glm-4.7, and kimi-k2.5).
Alternative: configure via
settings.json
If you prefer to skip the interactive
/auth
flow, add the following to
~/.qwen/settings.json
:
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen3-coder-plus"
,
"name"
:
"qwen3-coder-plus (Coding Plan)"
,
"baseUrl"
:
"https://coding.dashscope.aliyuncs.com/v1"
,
"description"
:
"qwen3-coder-plus from Alibaba Cloud Coding Plan"
,
"envKey"
:
"BAILIAN_CODING_PLAN_API_KEY"
}
]
},
"env"
: {
"BAILIAN_CODING_PLAN_API_KEY"
:
"sk-sp-xxxxxxxxx"
},
"security"
: {
"auth"
: {
"selectedType"
:
"openai"
}
},
"model"
: {
"name"
:
"qwen3-coder-plus"
}
}
Note
The Coding Plan uses a dedicated endpoint (
https://coding.dashscope.aliyuncs.com/v1
) that is different from the standard Dashscope endpoint. Make sure to use the correct
baseUrl
.
🚀 Option 3: API Key (flexible)
Use this if you want to connect to third-party providers such as OpenAI, Anthropic, Google, Azure OpenAI, OpenRouter, ModelScope, or a self-hosted endpoint. Supports multiple protocols and providers.
Recommended: One-file setup via
settings.json
The simplest way to get started with API Key authentication is to put everything in a single
~/.qwen/settings.json
file. Here’s a complete, ready-to-use example:
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen3-coder-plus"
,
"name"
:
"qwen3-coder-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-coder-plus"
}
}
What each field does:
Field
Description
modelProviders
Declares which models are available and how to connect to them. Keys (
openai
,
anthropic
,
gemini
) represent the API protocol.
env
Stores API keys directly in
settings.json
as a fallback (lowest priority — shell
export
and
.env
files take precedence).
security.auth.selectedType
Tells Qwen Code which protocol to use on startup (e.g.
openai
,
anthropic
,
gemini
). Without this, you’d need to run
/auth
interactively.
model.name
The default model to activate when Qwen Code starts. Must match one of the
id
values in your
modelProviders
.
After saving the file, just run
qwen
— no interactive
/auth
setup needed.
Tip
The sections below explain each part in more detail. If the quick example above works for you, feel free to skip ahead to
Security notes
.
The key concept is
Model Providers
(
modelProviders
): Qwen Code supports multiple API protocols, not just OpenAI. You configure which providers and models are available by editing
~/.qwen/settings.json
, then switch between them at runtime with the
/model
command.
Supported protocols
Protocol
modelProviders
key
Environment variables
Providers
OpenAI-compatible
openai
OPENAI_API_KEY
,
OPENAI_BASE_URL
,
OPENAI_MODEL
OpenAI, Azure OpenAI, OpenRouter, ModelScope, Alibaba Cloud, any OpenAI-compatible endpoint
Anthropic
anthropic
ANTHROPIC_API_KEY
,
ANTHROPIC_BASE_URL
,
ANTHROPIC_MODEL
Anthropic Claude
Google GenAI
gemini
GEMINI_API_KEY
,
GEMINI_MODEL
Google Gemini
Step 1: Configure models and providers in
~/.qwen/settings.json
Define which models are available for each protocol. Each model entry requires at minimum an
id
and an
envKey
(the environment variable name that holds your API key).
Important
It is recommended to define
modelProviders
in the user-scope
~/.qwen/settings.json
to avoid merge conflicts between project and user settings.
Edit
~/.qwen/settings.json
(create it if it doesn’t exist). You can mix multiple protocols in a single file — here is a multi-provider example showing just the
modelProviders
section:
{
"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"
}
]
}
}
Tip
Don’t forget to also set
env
,
security.auth.selectedType
, and
model.name
alongside
modelProviders
— see the
complete example above
for reference.
ModelConfig
fields (each entry inside
modelProviders
):
Field
Required
Description
id
Yes
Model ID sent to the API (e.g.
gpt-4o
,
claude-sonnet-4-20250514
)
name
No
Display name in the
/model
picker (defaults to
id
)
envKey
Yes
Environment variable name for the API key (e.g.
OPENAI_API_KEY
)
baseUrl
No
API endpoint override (useful for proxies or custom endpoints)
generationConfig
No
Fine-tune
timeout
,
maxRetries
,
samplingParams
, etc.
Note
When using the
env
field in
settings.json
, credentials are stored in plain text. For better security, prefer
.env
files or shell
export
— see
Step 2
.
For the full
modelProviders
schema and advanced options like
generationConfig
,
customHeaders
, and
extra_body
, see
Model Providers Reference
.
Step 2: Set environment variables
Qwen Code reads API keys from environment variables (specified by
envKey
in your model config). There are multiple ways to provide them, listed below from
highest to lowest priority
:
1. Shell environment /
export
(highest priority)
Set directly in your shell profile (
~/.zshrc
,
~/.bashrc
, etc.) or inline before launching:
# Alibaba Dashscope
export
DASHSCOPE_API_KEY
=
"sk-..."
# OpenAI / OpenAI-compatible
export
OPENAI_API_KEY
=
"sk-..."
# Anthropic
export
ANTHROPIC_API_KEY
=
"sk-ant-..."
# Google GenAI
export
GEMINI_API_KEY
=
"AIza..."
2.
.env
files
Qwen Code auto-loads the
first
.env
file it finds (variables are
not merged
across multiple files). Only variables not already present in
process.env
are loaded.
Search order (from the current directory, walking upward toward
/
):
.qwen/.env
(preferred — keeps Qwen Code variables isolated from other tools)
.env
If nothing is found, it falls back to your
home directory
:
~/.qwen/.env
~/.env
Tip
.qwen/.env
is recommended over
.env
to avoid conflicts with other tools. Some variables (like
DEBUG
and
DEBUG_MODE
) are excluded from project-level
.env
files to avoid interfering with Qwen Code behavior.
3.
settings.json
→
env
field (lowest priority)
You can also define API keys directly in
~/.qwen/settings.json
under the
env
key. These are loaded as the
lowest-priority fallback
— only applied when a variable is not already set by the system environment or
.env
files.
{
"env"
: {
"DASHSCOPE_API_KEY"
:
"sk-..."
,
"OPENAI_API_KEY"
:
"sk-..."
,
"ANTHROPIC_API_KEY"
:
"sk-ant-..."
}
}
This is the approach used in the
one-file setup example
above. It’s convenient for keeping everything in one place, but be mindful that
settings.json
may be shared or synced — prefer
.env
files for sensitive secrets.
Priority summary:
Priority
Source
Override behavior
1 (highest)
CLI flags (
--openai-api-key
)
Always wins
2
System env (
export
, inline)
Overrides
.env
and
settings.json
→
env
3
.env
file
Only sets if not in system env
4 (lowest)
settings.json
→
env
Only sets if not in system env or
.env
Step 3: Switch models with
/model
After launching Qwen Code, use the
/model
command to switch between all configured models. Models are grouped by protocol:
/model
The picker will show all models from your
modelProviders
configuration, grouped by their protocol (e.g.
openai
,
anthropic
,
gemini
). Your selection is persisted across sessions.
You can also switch models directly with a command-line argument, which is convenient when working across multiple terminals.
# In one terminal
qwen
--model
"qwen3-coder-plus"
# In another terminal
qwen
--model
"qwen3.5-plus"
qwen auth
CLI command
In addition to the in-session
/auth
slash command, Qwen Code provides a standalone
qwen auth
CLI command for managing authentication directly from the terminal — without starting an interactive session first.
Interactive mode
Run
qwen auth
without arguments to get an interactive menu:
qwen
auth
You’ll see a selector with arrow-key navigation:
Select authentication method:
Alibaba Cloud Coding Plan - Paid · Up to 6,000 requests/5 hrs · All Alibaba Cloud Coding Plan Models
API Key - Bring your own API key
Qwen OAuth - Discontinued — switch to Coding Plan or API Key
(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)
Subcommands
Command
Description
qwen auth
Interactive authentication setup
qwen auth coding-plan
Authenticate with Alibaba Cloud Coding Plan
qwen auth coding-plan --region china --key sk-sp-…
Non-interactive Coding Plan setup (for scripting)
qwen auth api-key
Authenticate with an API key
qwen auth qwen-oauth
Authenticate with Qwen OAuth (discontinued)
qwen auth status
Show current authentication status
Examples:
# Authenticate with Qwen OAuth directly
qwen
auth
qwen-oauth
# Set up Coding Plan interactively (prompts for region and key)
qwen
auth
coding-plan
# Set up Coding Plan non-interactively (useful for CI/scripting)
qwen
auth
coding-plan
--region
china
--key
sk-sp-xxxxxxxxx
# Set up API key (ModelStudio Standard or custom provider)
qwen
auth
api-key
# Check your current auth configuration
qwen
auth
status
Security notes
Don’t commit API keys to version control.
Prefer
.qwen/.env
for project-local secrets (and keep it out of git).
Treat your terminal output as sensitive if it prints credentials for verification.
Last updated on
May 18, 2026
Settings
Ignoring Files</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/auth/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Authentication
Authentication
Qwen Code supports three authentication methods. Pick the one that matches how you want to run the CLI:
Qwen OAuth
: sign in with your
qwen.ai
account in a browser.
Free tier discontinued on 2026-04-15
— switch to another method.
Alibaba Cloud Coding Plan
: use an API key from Alibaba Cloud. Paid subscription with diverse model options and higher quotas.
API Key
: bring your own API key. Flexible to your own needs — supports OpenAI, Anthropi...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Authentication
Authentication
Qwen Code supports three authentication methods. Pick the one that matches how you want to run the CLI:
Qwen OAuth
: sign in with your
qwen.ai
account in a browser.
Free tier discontinued on 2026-04-15
— switch to another method.
Alibaba Cloud Coding Plan
: use an API key from Alibaba Cloud. Paid subscription with diverse model options and higher quotas.
API Key
: bring your own API key. Flexible to your own needs — supports OpenAI, Anthropic, Gemini, and other compatible endpoints.
Option 1: Qwen OAuth (Discontinued)
Warning
The Qwen OAuth free tier was discontinued on 2026-04-15. Existing cached tokens may continue working briefly, but new requests will be rejected. Please switch to Alibaba Cloud Coding Plan,
OpenRouter
,
Fireworks AI
, or another provider. Run
qwen auth
to configure.
How it works
: on first start, Qwen Code opens a browser login page. After you finish, credentials are cached locally so you usually won’t need to log in again.
Requirements
: a
qwen.ai
account + internet access (at least for the first login).
Benefits
: no API key management, automatic credential refresh.
Cost & quota
: the free tier has been discontinued as of 2026-04-15.
Start the CLI and follow the browser flow:
qwen
Or authenticate directly without starting a session:
qwen
auth
qwen-oauth
Note
In non-interactive or headless environments (e.g., CI, SSH, containers), you typically
cannot
complete the OAuth browser login flow.
In these cases, please use the Alibaba Cloud Coding Plan or API Key authentication method.
💳 Option 2: Alibaba Cloud Coding Plan
Use this if you want predictable costs with diverse model options and higher usage quotas.
How it works
: Subscribe to the Coding Plan with a fixed monthly fee, then configure Qwen Code to use the dedicated endpoint and your subscription API key.
Requirements
: Obtain an active Coding Plan subscription from
Alibaba Cloud ModelStudio(Beijing)
or
Alibaba Cloud ModelStudio(intl)
, depending on the region of your account.
Benefits
: Diverse model options, higher usage quotas, predictable monthly costs, access to a wide range of models (Qwen, GLM, Kimi, Minimax and more).
Cost & quota
: View Aliyun ModelStudio Coding Plan documentation
Beijing
intl
.
Alibaba Cloud Coding Plan is available in two regions:
Region
Console URL
Aliyun ModelStudio (Beijing)
bailian.console.aliyun.com
Alibaba Cloud (intl)
bailian.console.alibabacloud.com
Interactive setup
You can set up Coding Plan authentication in two ways:
Option A: From the terminal (recommended for first-time setup)
# Interactive — prompts for region and API key
qwen
auth
coding-plan
# Or non-interactive — pass region and key directly
qwen
auth
coding-plan
--region
china
--key
sk-sp-xxxxxxxxx
Option B: Inside a Qwen Code session
Enter
qwen
in the terminal to launch Qwen Code, then run the
/auth
command and select
Alibaba Cloud Coding Plan
. Choose your region, then enter your
sk-sp-xxxxxxxxx
key.
After authentication, use the
/model
command to switch between all Alibaba Cloud Coding Plan supported models (including qwen3.5-plus, qwen3-coder-plus, qwen3-coder-next, qwen3-max, glm-4.7, and kimi-k2.5).
Alternative: configure via
settings.json
If you prefer to skip the interactive
/auth
flow, add the following to
~/.qwen/settings.json
:
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen3-coder-plus"
,
"name"
:
"qwen3-coder-plus (Coding Plan)"
,
"baseUrl"
:
"https://coding.dashscope.aliyuncs.com/v1"
,
"description"
:
"qwen3-coder-plus from Alibaba Cloud Coding Plan"
,
"envKey"
:
"BAILIAN_CODING_PLAN_API_KEY"
}
]
},
"env"
: {
"BAILIAN_CODING_PLAN_API_KEY"
:
"sk-sp-xxxxxxxxx"
},
"security"
: {
"auth"
: {
"selectedType"
:
"openai"
}
},
"model"
: {
"name"
:
"qwen3-coder-plus"
}
}
Note
The Coding Plan uses a dedicated endpoint (
https://coding.dashscope.aliyuncs.com/v1
) that is different from the standard Dashscope endpoint. Make sure to use the correct
baseUrl
.
🚀 Option 3: API Key (flexible)
Use this if you want to connect to third-party providers such as OpenAI, Anthropic, Google, Azure OpenAI, OpenRouter, ModelScope, or a self-hosted endpoint. Supports multiple protocols and providers.
Recommended: One-file setup via
settings.json
The simplest way to get started with API Key authentication is to put everything in a single
~/.qwen/settings.json
file. Here’s a complete, ready-to-use example:
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen3-coder-plus"
,
"name"
:
"qwen3-coder-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-coder-plus"
}
}
What each field does:
Field
Description
modelProviders
Declares which models are available and how to connect to them. Keys (
openai
,
anthropic
,
gemini
) represent the API protocol.
env
Stores API keys directly in
settings.json
as a fallback (lowest priority — shell
export
and
.env
files take precedence).
security.auth.selectedType
Tells Qwen Code which protocol to use on startup (e.g.
openai
,
anthropic
,
gemini
). Without this, you’d need to run
/auth
interactively.
model.name
The default model to activate when Qwen Code starts. Must match one of the
id
values in your
modelProviders
.
After saving the file, just run
qwen
— no interactive
/auth
setup needed.
Tip
The sections below explain each part in more detail. If the quick example above works for you, feel free to skip ahead to
Security notes
.
The key concept is
Model Providers
(
modelProviders
): Qwen Code supports multiple API protocols, not just OpenAI. You configure which providers and models are available by editing
~/.qwen/settings.json
, then switch between them at runtime with the
/model
command.
Supported protocols
Protocol
modelProviders
key
Environment variables
Providers
OpenAI-compatible
openai
OPENAI_API_KEY
,
OPENAI_BASE_URL
,
OPENAI_MODEL
OpenAI, Azure OpenAI, OpenRouter, ModelScope, Alibaba Cloud, any OpenAI-compatible endpoint
Anthropic
anthropic
ANTHROPIC_API_KEY
,
ANTHROPIC_BASE_URL
,
ANTHROPIC_MODEL
Anthropic Claude
Google GenAI
gemini
GEMINI_API_KEY
,
GEMINI_MODEL
Google Gemini
Step 1: Configure models and providers in
~/.qwen/settings.json
Define which models are available for each protocol. Each model entry requires at minimum an
id
and an
envKey
(the environment variable name that holds your API key).
Important
It is recommended to define
modelProviders
in the user-scope
~/.qwen/settings.json
to avoid merge conflicts between project and user settings.
Edit
~/.qwen/settings.json
(create it if it doesn’t exist). You can mix multiple protocols in a single file — here is a multi-provider example showing just the
modelProviders
section:
{
"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"
}
]
}
}
Tip
Don’t forget to also set
env
,
security.auth.selectedType
, and
model.name
alongside
modelProviders
— see the
complete example above
for reference.
ModelConfig
fields (each entry inside
modelProviders
):
Field
Required
Description
id
Yes
Model ID sent to the API (e.g.
gpt-4o
,
claude-sonnet-4-20250514
)
name
No
Display name in the
/model
picker (defaults to
id
)
envKey
Yes
Environment variable name for the API key (e.g.
OPENAI_API_KEY
)
baseUrl
No
API endpoint override (useful for proxies or custom endpoints)
generationConfig
No
Fine-tune
timeout
,
maxRetries
,
samplingParams
, etc.
Note
When using the
env
field in
settings.json
, credentials are stored in plain text. For better security, prefer
.env
files or shell
export
— see
Step 2
.
For the full
modelProviders
schema and advanced options like
generationConfig
,
customHeaders
, and
extra_body
, see
Model Providers Reference
.
Step 2: Set environment variables
Qwen Code reads API keys from environment variables (specified by
envKey
in your model config). There are multiple ways to provide them, listed below from
highest to lowest priority
:
1. Shell environment /
export
(highest priority)
Set directly in your shell profile (
~/.zshrc
,
~/.bashrc
, etc.) or inline before launching:
# Alibaba Dashscope
export
DASHSCOPE_API_KEY
=
"sk-..."
# OpenAI / OpenAI-compatible
export
OPENAI_API_KEY
=
"sk-..."
# Anthropic
export
ANTHROPIC_API_KEY
=
"sk-ant-..."
# Google GenAI
export
GEMINI_API_KEY
=
"AIza..."
2.
.env
files
Qwen Code auto-loads the
first
.env
file it finds (variables are
not merged
across multiple files). Only variables not already present in
process.env
are loaded.
Search order (from the current directory, walking upward toward
/
):
.qwen/.env
(preferred — keeps Qwen Code variables isolated from other tools)
.env
If nothing is found, it falls back to your
home directory
:
~/.qwen/.env
~/.env
Tip
.qwen/.env
is recommended over
.env
to avoid conflicts with other tools. Some variables (like
DEBUG
and
DEBUG_MODE
) are excluded from project-level
.env
files to avoid interfering with Qwen Code behavior.
3.
settings.json
→
env
field (lowest priority)
You can also define API keys directly in
~/.qwen/settings.json
under the
env
key. These are loaded as the
lowest-priority fallback
— only applied when a variable is not already set by the system environment or
.env
files.
{
"env"
: {
"DASHSCOPE_API_KEY"
:
"sk-..."
,
"OPENAI_API_KEY"
:
"sk-..."
,
"ANTHROPIC_API_KEY"
:
"sk-ant-..."
}
}
This is the approach used in the
one-file setup example
above. It’s convenient for keeping everything in one place, but be mindful that
settings.json
may be shared or synced — prefer
.env
files for sensitive secrets.
Priority summary:
Priority
Source
Override behavior
1 (highest)
CLI flags (
--openai-api-key
)
Always wins
2
System env (
export
, inline)
Overrides
.env
and
settings.json
→
env
3
.env
file
Only sets if not in system env
4 (lowest)
settings.json
→
env
Only sets if not in system env or
.env
Step 3: Switch models with
/model
After launching Qwen Code, use the
/model
command to switch between all configured models. Models are grouped by protocol:
/model
The picker will show all models from your
modelProviders
configuration, grouped by their protocol (e.g.
openai
,
anthropic
,
gemini
). Your selection is persisted across sessions.
You can also switch models directly with a command-line argument, which is convenient when working across multiple terminals.
# In one terminal
qwen
--model
"qwen3-coder-plus"
# In another terminal
qwen
--model
"qwen3.5-plus"
qwen auth
CLI command
In addition to the in-session
/auth
slash command, Qwen Code provides a standalone
qwen auth
CLI command for managing authentication directly from the terminal — without starting an interactive session first.
Interactive mode
Run
qwen auth
without arguments to get an interactive menu:
qwen
auth
You’ll see a selector with arrow-key navigation:
Select authentication method:
Alibaba Cloud Coding Plan - Paid · Up to 6,000 requests/5 hrs · All Alibaba Cloud Coding Plan Models
API Key - Bring your own API key
Qwen OAuth - Discontinued — switch to Coding Plan or API Key
(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)
Subcommands
Command
Description
qwen auth
Interactive authentication setup
qwen auth coding-plan
Authenticate with Alibaba Cloud Coding Plan
qwen auth coding-plan --region china --key sk-sp-…
Non-interactive Coding Plan setup (for scripting)
qwen auth api-key
Authenticate with an API key
qwen auth qwen-oauth
Authenticate with Qwen OAuth (discontinued)
qwen auth status
Show current authentication status
Examples:
# Authenticate with Qwen OAuth directly
qwen
auth
qwen-oauth
# Set up Coding Plan interactively (prompts for region and key)
qwen
auth
coding-plan
# Set up Coding Plan non-interactively (useful for CI/scripting)
qwen
auth
coding-plan
--region
china
--key
sk-sp-xxxxxxxxx
# Set up API key (ModelStudio Standard or custom provider)
qwen
auth
api-key
# Check your current auth configuration
qwen
auth
status
Security notes
Don’t commit API keys to version control.
Prefer
.qwen/.env
for project-local secrets (and keep it out of git).
Treat your terminal output as sensitive if it prints credentials for verification.
Last updated on
May 18, 2026
Settings
Ignoring Files</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/auth/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>How to Contribute</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/contributing/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/contributing/</guid>
  <pubDate>Tue, 30 Jul 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Contributing Guide
How to Contribute
We would love to accept your patches and contributions to this project.
Contribution Process
Code Reviews
All submissions, including submissions by project members, require review. We
use
GitHub pull requests
for this purpose.
Pull Request Guidelines
To help us review and merge your PRs quickly, please follow these guidelines. PRs that do not meet these standards may be closed.
1. Link to an Existing Issue
All PRs should be linked to an existi...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Contributing Guide
How to Contribute
We would love to accept your patches and contributions to this project.
Contribution Process
Code Reviews
All submissions, including submissions by project members, require review. We
use
GitHub pull requests
for this purpose.
Pull Request Guidelines
To help us review and merge your PRs quickly, please follow these guidelines. PRs that do not meet these standards may be closed.
1. Link to an Existing Issue
All PRs should be linked to an existing issue in our tracker. This ensures that every change has been discussed and is aligned with the project’s goals before any code is written.
For bug fixes:
The PR should be linked to the bug report issue.
For features:
The PR should be linked to the feature request or proposal issue that has been approved by a maintainer.
If an issue for your change doesn’t exist, please
open one first
and wait for feedback before you start coding.
2. Keep It Small and Focused
We favor small, atomic PRs that address a single issue or add a single, self-contained feature.
Do:
Create a PR that fixes one specific bug or adds one specific feature.
Don’t:
Bundle multiple unrelated changes (e.g., a bug fix, a new feature, and a refactor) into a single PR.
Large changes should be broken down into a series of smaller, logical PRs that can be reviewed and merged independently.
3. Use Draft PRs for Work in Progress
If you’d like to get early feedback on your work, please use GitHub’s
Draft Pull Request
feature. This signals to the maintainers that the PR is not yet ready for a formal review but is open for discussion and initial feedback.
4. Ensure All Checks Pass
Before submitting your PR, ensure that all automated checks are passing by running
npm run preflight
. This command runs all tests, linting, and other style checks.
5. Update Documentation
If your PR introduces a user-facing change (e.g., a new command, a modified flag, or a change in behavior), you must also update the relevant documentation in the
/docs
directory.
6. Write Clear Commit Messages and a Good PR Description
Your PR should have a clear, descriptive title and a detailed description of the changes. Follow the
Conventional Commits
standard for your commit messages.
Good PR Title:
feat(cli): Add --json flag to 'config get' command
Bad PR Title:
Made some changes
In the PR description, explain the “why” behind your changes and link to the relevant issue (e.g.,
Fixes #123
).
Development Setup and Workflow
This section guides contributors on how to build, modify, and understand the development setup of this project.
Setting Up the Development Environment
Prerequisites:
Node.js
:
Development:
Please use Node.js
~20.19.0
. This specific version is required due to an upstream development dependency issue. You can use a tool like
nvm
to manage Node.js versions.
Production:
For running the CLI in a production environment, any version of Node.js
>=20
is acceptable.
Git
Build Process
To clone the repository:
git
clone
https://github.com/QwenLM/qwen-code.git
# Or your fork's URL
cd
qwen-code
To install dependencies defined in
package.json
as well as root dependencies:
npm
install
To build the entire project (all packages):
npm
run
build
This command typically compiles TypeScript to JavaScript, bundles assets, and prepares the packages for execution. Refer to
scripts/build.js
and
package.json
scripts for more details on what happens during the build.
Enabling Sandboxing
Sandboxing
is highly recommended and requires, at a minimum, setting
QWEN_SANDBOX=true
in your
~/.env
and ensuring a sandboxing provider (e.g.
macOS Seatbelt
,
docker
, or
podman
) is available. See
Sandboxing
for details.
To build both the
qwen-code
CLI utility and the sandbox container, run
build:all
from the root directory:
npm
run
build:all
To skip building the sandbox container, you can use
npm run build
instead.
Running
To start the Qwen Code application from the source code (after building), run the following command from the root directory:
npm
start
If you’d like to run the source build outside of the qwen-code folder, you can utilize
npm link path/to/qwen-code/packages/cli
(see:
docs
) to run with
qwen-code
Running Tests
This project contains two types of tests: unit tests and integration tests.
Unit Tests
To execute the unit test suite for the project:
npm
run
test
This will run tests located in the
packages/core
and
packages/cli
directories. Ensure tests pass before submitting any changes. For a more comprehensive check, it is recommended to run
npm run preflight
.
Integration Tests
The integration tests are designed to validate the end-to-end functionality of Qwen Code. They are not run as part of the default
npm run test
command.
To run the integration tests, use the following command:
npm
run
test:e2e
For more detailed information on the integration testing framework, please see the
Integration Tests documentation
.
Linting and Preflight Checks
To ensure code quality and formatting consistency, run the preflight check:
npm
run
preflight
This command will run ESLint, Prettier, all tests, and other checks as defined in the project’s
package.json
.
ProTip
after cloning create a git precommit hook file to ensure your commits are always clean.
echo
"
# Run npm build and check for errors
if ! npm run preflight; then
echo "npm
build
failed.
Commit
aborted."
exit 1
fi
"
>
.git/hooks/pre-commit
&&
chmod
+x
.git/hooks/pre-commit
Formatting
To separately format the code in this project by running the following command from the root directory:
npm
run
format
This command uses Prettier to format the code according to the project’s style guidelines.
Linting
To separately lint the code in this project, run the following command from the root directory:
npm
run
lint
Coding Conventions
Please adhere to the coding style, patterns, and conventions used throughout the existing codebase.
Imports:
Pay special attention to import paths. The project uses ESLint to enforce restrictions on relative imports between packages.
Project Structure
packages/
: Contains the individual sub-packages of the project.
cli/
: The command-line interface.
core/
: The core backend logic for Qwen Code.
docs/
: Contains all project documentation.
scripts/
: Utility scripts for building, testing, and development tasks.
For more detailed architecture, see
docs/architecture.md
.
Documentation Development
This section describes how to develop and preview the documentation locally.
Prerequisites
Ensure you have Node.js (version 18+) installed
Have npm or yarn available
Setup Documentation Site Locally
To work on the documentation and preview changes locally:
Navigate to the
docs-site
directory:
cd
docs-site
Install dependencies:
npm
install
Link the documentation content from the main
docs
directory:
npm
run
link
This creates a symbolic link from
../docs
to
content
in the docs-site project, allowing the documentation content to be served by the Next.js site.
Start the development server:
npm
run
dev
Open
http://localhost:3000
in your browser to see the documentation site with live updates as you make changes.
Any changes made to the documentation files in the main
docs
directory will be reflected immediately in the documentation site.
Debugging
VS Code:
Run the CLI to interactively debug in VS Code with
F5
Start the CLI in debug mode from the root directory:
npm
run
debug
This command runs
node --inspect-brk dist/index.js
within the
packages/cli
directory, pausing execution until a debugger attaches. You can then open
chrome://inspect
in your Chrome browser to connect to the debugger.
In VS Code, use the “Attach” launch configuration (found in
.vscode/launch.json
).
Alternatively, you can use the “Launch Program” configuration in VS Code if you prefer to launch the currently open file directly, but ‘F5’ is generally recommended.
To hit a breakpoint inside the sandbox container run:
DEBUG
=
1
qwen-code
Note:
If you have
DEBUG=true
in a project’s
.env
file, it won’t affect qwen-code due to automatic exclusion. Use
.qwen-code/.env
files for qwen-code specific debug settings.
React DevTools
To debug the CLI’s React-based UI, you can use React DevTools. Ink, the library used for the CLI’s interface, is compatible with React DevTools version 4.x.
Start the Qwen Code application in development mode:
DEV
=
true
npm
start
Install and run React DevTools version 4.28.5 (or the latest compatible 4.x version):
You can either install it globally:
npm
install
-g
react-devtools@4.28.5
react-devtools
Or run it directly using npx:
npx
react-devtools@4.28.5
Your running CLI application should then connect to React DevTools.
Sandboxing
TBD
Manual Publish
We publish an artifact for each commit to our internal registry. But if you need to manually cut a local build, then run the following commands:
npm run clean
npm install
npm run auth
npm run prerelease:dev
npm publish --workspaces
Last updated on
May 18, 2026
Roadmap
Typescript SDK</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/contributing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Contributing Guide
How to Contribute
We would love to accept your patches and contributions to this project.
Contribution Process
Code Reviews
All submissions, including submissions by project members, require review. We
use
GitHub pull requests
for this purpose.
Pull Request Guidelines
To help us review and merge your PRs quickly, please follow these guidelines. PRs that do not meet these standards may be closed.
1. Link to an Existing Issue
All PRs should be linked to an existi...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Contributing Guide
How to Contribute
We would love to accept your patches and contributions to this project.
Contribution Process
Code Reviews
All submissions, including submissions by project members, require review. We
use
GitHub pull requests
for this purpose.
Pull Request Guidelines
To help us review and merge your PRs quickly, please follow these guidelines. PRs that do not meet these standards may be closed.
1. Link to an Existing Issue
All PRs should be linked to an existing issue in our tracker. This ensures that every change has been discussed and is aligned with the project’s goals before any code is written.
For bug fixes:
The PR should be linked to the bug report issue.
For features:
The PR should be linked to the feature request or proposal issue that has been approved by a maintainer.
If an issue for your change doesn’t exist, please
open one first
and wait for feedback before you start coding.
2. Keep It Small and Focused
We favor small, atomic PRs that address a single issue or add a single, self-contained feature.
Do:
Create a PR that fixes one specific bug or adds one specific feature.
Don’t:
Bundle multiple unrelated changes (e.g., a bug fix, a new feature, and a refactor) into a single PR.
Large changes should be broken down into a series of smaller, logical PRs that can be reviewed and merged independently.
3. Use Draft PRs for Work in Progress
If you’d like to get early feedback on your work, please use GitHub’s
Draft Pull Request
feature. This signals to the maintainers that the PR is not yet ready for a formal review but is open for discussion and initial feedback.
4. Ensure All Checks Pass
Before submitting your PR, ensure that all automated checks are passing by running
npm run preflight
. This command runs all tests, linting, and other style checks.
5. Update Documentation
If your PR introduces a user-facing change (e.g., a new command, a modified flag, or a change in behavior), you must also update the relevant documentation in the
/docs
directory.
6. Write Clear Commit Messages and a Good PR Description
Your PR should have a clear, descriptive title and a detailed description of the changes. Follow the
Conventional Commits
standard for your commit messages.
Good PR Title:
feat(cli): Add --json flag to 'config get' command
Bad PR Title:
Made some changes
In the PR description, explain the “why” behind your changes and link to the relevant issue (e.g.,
Fixes #123
).
Development Setup and Workflow
This section guides contributors on how to build, modify, and understand the development setup of this project.
Setting Up the Development Environment
Prerequisites:
Node.js
:
Development:
Please use Node.js
~20.19.0
. This specific version is required due to an upstream development dependency issue. You can use a tool like
nvm
to manage Node.js versions.
Production:
For running the CLI in a production environment, any version of Node.js
>=20
is acceptable.
Git
Build Process
To clone the repository:
git
clone
https://github.com/QwenLM/qwen-code.git
# Or your fork's URL
cd
qwen-code
To install dependencies defined in
package.json
as well as root dependencies:
npm
install
To build the entire project (all packages):
npm
run
build
This command typically compiles TypeScript to JavaScript, bundles assets, and prepares the packages for execution. Refer to
scripts/build.js
and
package.json
scripts for more details on what happens during the build.
Enabling Sandboxing
Sandboxing
is highly recommended and requires, at a minimum, setting
QWEN_SANDBOX=true
in your
~/.env
and ensuring a sandboxing provider (e.g.
macOS Seatbelt
,
docker
, or
podman
) is available. See
Sandboxing
for details.
To build both the
qwen-code
CLI utility and the sandbox container, run
build:all
from the root directory:
npm
run
build:all
To skip building the sandbox container, you can use
npm run build
instead.
Running
To start the Qwen Code application from the source code (after building), run the following command from the root directory:
npm
start
If you’d like to run the source build outside of the qwen-code folder, you can utilize
npm link path/to/qwen-code/packages/cli
(see:
docs
) to run with
qwen-code
Running Tests
This project contains two types of tests: unit tests and integration tests.
Unit Tests
To execute the unit test suite for the project:
npm
run
test
This will run tests located in the
packages/core
and
packages/cli
directories. Ensure tests pass before submitting any changes. For a more comprehensive check, it is recommended to run
npm run preflight
.
Integration Tests
The integration tests are designed to validate the end-to-end functionality of Qwen Code. They are not run as part of the default
npm run test
command.
To run the integration tests, use the following command:
npm
run
test:e2e
For more detailed information on the integration testing framework, please see the
Integration Tests documentation
.
Linting and Preflight Checks
To ensure code quality and formatting consistency, run the preflight check:
npm
run
preflight
This command will run ESLint, Prettier, all tests, and other checks as defined in the project’s
package.json
.
ProTip
after cloning create a git precommit hook file to ensure your commits are always clean.
echo
"
# Run npm build and check for errors
if ! npm run preflight; then
echo "npm
build
failed.
Commit
aborted."
exit 1
fi
"
>
.git/hooks/pre-commit
&&
chmod
+x
.git/hooks/pre-commit
Formatting
To separately format the code in this project by running the following command from the root directory:
npm
run
format
This command uses Prettier to format the code according to the project’s style guidelines.
Linting
To separately lint the code in this project, run the following command from the root directory:
npm
run
lint
Coding Conventions
Please adhere to the coding style, patterns, and conventions used throughout the existing codebase.
Imports:
Pay special attention to import paths. The project uses ESLint to enforce restrictions on relative imports between packages.
Project Structure
packages/
: Contains the individual sub-packages of the project.
cli/
: The command-line interface.
core/
: The core backend logic for Qwen Code.
docs/
: Contains all project documentation.
scripts/
: Utility scripts for building, testing, and development tasks.
For more detailed architecture, see
docs/architecture.md
.
Documentation Development
This section describes how to develop and preview the documentation locally.
Prerequisites
Ensure you have Node.js (version 18+) installed
Have npm or yarn available
Setup Documentation Site Locally
To work on the documentation and preview changes locally:
Navigate to the
docs-site
directory:
cd
docs-site
Install dependencies:
npm
install
Link the documentation content from the main
docs
directory:
npm
run
link
This creates a symbolic link from
../docs
to
content
in the docs-site project, allowing the documentation content to be served by the Next.js site.
Start the development server:
npm
run
dev
Open
http://localhost:3000
in your browser to see the documentation site with live updates as you make changes.
Any changes made to the documentation files in the main
docs
directory will be reflected immediately in the documentation site.
Debugging
VS Code:
Run the CLI to interactively debug in VS Code with
F5
Start the CLI in debug mode from the root directory:
npm
run
debug
This command runs
node --inspect-brk dist/index.js
within the
packages/cli
directory, pausing execution until a debugger attaches. You can then open
chrome://inspect
in your Chrome browser to connect to the debugger.
In VS Code, use the “Attach” launch configuration (found in
.vscode/launch.json
).
Alternatively, you can use the “Launch Program” configuration in VS Code if you prefer to launch the currently open file directly, but ‘F5’ is generally recommended.
To hit a breakpoint inside the sandbox container run:
DEBUG
=
1
qwen-code
Note:
If you have
DEBUG=true
in a project’s
.env
file, it won’t affect qwen-code due to automatic exclusion. Use
.qwen-code/.env
files for qwen-code specific debug settings.
React DevTools
To debug the CLI’s React-based UI, you can use React DevTools. Ink, the library used for the CLI’s interface, is compatible with React DevTools version 4.x.
Start the Qwen Code application in development mode:
DEV
=
true
npm
start
Install and run React DevTools version 4.28.5 (or the latest compatible 4.x version):
You can either install it globally:
npm
install
-g
react-devtools@4.28.5
react-devtools
Or run it directly using npx:
npx
react-devtools@4.28.5
Your running CLI application should then connect to React DevTools.
Sandboxing
TBD
Manual Publish
We publish an artifact for each commit to our internal registry. But if you need to manually cut a local build, then run the following commands:
npm run clean
npm install
npm run auth
npm run prerelease:dev
npm publish --workspaces
Last updated on
May 18, 2026
Roadmap
Typescript SDK</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/contributing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>WeChat (Weixin)</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/weixin/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/weixin/</guid>
  <pubDate>Wed, 24 Jul 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
WeChat
WeChat (Weixin)
This guide covers setting up a Qwen Code channel on WeChat via the official iLink Bot API.
Prerequisites
A WeChat account that can scan QR codes (mobile app)
Access to the iLink Bot platform (WeChat’s official bot API)
Setup
1. Log in via QR code
WeChat uses QR code authentication instead of a static bot token. Run the login command:
qwen
channel
configure-weixin
This will display a QR code URL. Scan it with your WeChat mobile app to authentica...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
WeChat
WeChat (Weixin)
This guide covers setting up a Qwen Code channel on WeChat via the official iLink Bot API.
Prerequisites
A WeChat account that can scan QR codes (mobile app)
Access to the iLink Bot platform (WeChat’s official bot API)
Setup
1. Log in via QR code
WeChat uses QR code authentication instead of a static bot token. Run the login command:
qwen
channel
configure-weixin
This will display a QR code URL. Scan it with your WeChat mobile app to authenticate. Your credentials are saved to
~/.qwen/channels/weixin/account.json
.
2. Configure the channel
Add the channel to
~/.qwen/settings.json
:
{
"channels"
: {
"my-weixin"
: {
"type"
:
"weixin"
,
"senderPolicy"
:
"pairing"
,
"allowedUsers"
: [],
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/your/project"
,
"model"
:
"qwen3.5-plus"
,
"instructions"
:
"You are a concise coding assistant responding via WeChat. Keep responses under 500 characters. Use plain text only."
}
}
}
Note: WeChat channels do not use a
token
field — credentials come from the QR login step.
3. Start the channel
# Start only the WeChat channel
qwen
channel
start
my-weixin
# Or start all configured channels together
qwen
channel
start
Open WeChat and send a message to the bot. You should see a typing indicator (”…”) while the agent processes, followed by the response.
Images and Files
You can send photos and documents to the bot, not just text.
Photos:
Send an image (screenshot, photo, etc.) and the agent will analyze it using its vision capabilities. This requires a multimodal model — add
"model": "qwen3.5-plus"
(or another vision-capable model) to your channel config. A typing indicator shows while the image is being downloaded and processed.
Files:
Send a PDF, code file, or any document. The bot downloads and decrypts it from WeChat’s CDN, saves it locally, and the agent reads it with its file tools. This works with any model.
Configuration Options
WeChat channels support all the standard channel options (see
Channel Overview
), plus:
Option
Description
baseUrl
Override the iLink Bot API base URL (default:
https://ilinkai.weixin.qq.com
)
Key Differences from Telegram
Authentication:
QR code login instead of a static bot token. Sessions can expire — the channel will pause and log a message if this happens.
Formatting:
WeChat only supports plain text. Markdown in agent responses is automatically stripped.
Typing indicator:
WeChat has a native ”…” typing indicator instead of a “Working…” text message.
Groups:
WeChat iLink Bot is DM-only — group chats are not supported.
Media encryption:
Images and files are encrypted on WeChat’s CDN with AES-128-ECB. The channel handles decryption transparently.
Tips
Use plain text instructions
— Since WeChat strips all markdown, add instructions like “Use plain text only” to avoid the agent producing formatted responses that look messy.
Keep responses short
— WeChat message bubbles work best with concise text. Adding a character limit to your instructions helps (e.g., “Keep responses under 500 characters”).
Session expiry
— If you see “Session expired (errcode -14)” in the logs, your WeChat login has expired. Stop the channel and re-run
qwen channel configure-weixin
to log in again.
Restrict access
— Use
senderPolicy: "pairing"
or
"allowlist"
to control who can talk to the bot. See
DM Pairing
for details.
Troubleshooting
”WeChat account not configured”
Run
qwen channel configure-weixin
to log in via QR code first.
”Session expired (errcode -14)”
Your WeChat login session has expired. Stop the channel and run
qwen channel configure-weixin
again.
Bot doesn’t respond
Check the terminal output for errors
Verify the channel is running (
qwen channel start my-weixin
)
If using
senderPolicy: "allowlist"
, make sure your WeChat user ID is in
allowedUsers
Images not working
Make sure your channel config has a
model
that supports vision (e.g.,
qwen3.5-plus
)
Check the terminal for CDN download errors — these may indicate a network issue
Last updated on
May 18, 2026
Telegram
DingTalk</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/weixin/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
WeChat
WeChat (Weixin)
This guide covers setting up a Qwen Code channel on WeChat via the official iLink Bot API.
Prerequisites
A WeChat account that can scan QR codes (mobile app)
Access to the iLink Bot platform (WeChat’s official bot API)
Setup
1. Log in via QR code
WeChat uses QR code authentication instead of a static bot token. Run the login command:
qwen
channel
configure-weixin
This will display a QR code URL. Scan it with your WeChat mobile app to authentica...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
WeChat
WeChat (Weixin)
This guide covers setting up a Qwen Code channel on WeChat via the official iLink Bot API.
Prerequisites
A WeChat account that can scan QR codes (mobile app)
Access to the iLink Bot platform (WeChat’s official bot API)
Setup
1. Log in via QR code
WeChat uses QR code authentication instead of a static bot token. Run the login command:
qwen
channel
configure-weixin
This will display a QR code URL. Scan it with your WeChat mobile app to authenticate. Your credentials are saved to
~/.qwen/channels/weixin/account.json
.
2. Configure the channel
Add the channel to
~/.qwen/settings.json
:
{
"channels"
: {
"my-weixin"
: {
"type"
:
"weixin"
,
"senderPolicy"
:
"pairing"
,
"allowedUsers"
: [],
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/your/project"
,
"model"
:
"qwen3.5-plus"
,
"instructions"
:
"You are a concise coding assistant responding via WeChat. Keep responses under 500 characters. Use plain text only."
}
}
}
Note: WeChat channels do not use a
token
field — credentials come from the QR login step.
3. Start the channel
# Start only the WeChat channel
qwen
channel
start
my-weixin
# Or start all configured channels together
qwen
channel
start
Open WeChat and send a message to the bot. You should see a typing indicator (”…”) while the agent processes, followed by the response.
Images and Files
You can send photos and documents to the bot, not just text.
Photos:
Send an image (screenshot, photo, etc.) and the agent will analyze it using its vision capabilities. This requires a multimodal model — add
"model": "qwen3.5-plus"
(or another vision-capable model) to your channel config. A typing indicator shows while the image is being downloaded and processed.
Files:
Send a PDF, code file, or any document. The bot downloads and decrypts it from WeChat’s CDN, saves it locally, and the agent reads it with its file tools. This works with any model.
Configuration Options
WeChat channels support all the standard channel options (see
Channel Overview
), plus:
Option
Description
baseUrl
Override the iLink Bot API base URL (default:
https://ilinkai.weixin.qq.com
)
Key Differences from Telegram
Authentication:
QR code login instead of a static bot token. Sessions can expire — the channel will pause and log a message if this happens.
Formatting:
WeChat only supports plain text. Markdown in agent responses is automatically stripped.
Typing indicator:
WeChat has a native ”…” typing indicator instead of a “Working…” text message.
Groups:
WeChat iLink Bot is DM-only — group chats are not supported.
Media encryption:
Images and files are encrypted on WeChat’s CDN with AES-128-ECB. The channel handles decryption transparently.
Tips
Use plain text instructions
— Since WeChat strips all markdown, add instructions like “Use plain text only” to avoid the agent producing formatted responses that look messy.
Keep responses short
— WeChat message bubbles work best with concise text. Adding a character limit to your instructions helps (e.g., “Keep responses under 500 characters”).
Session expiry
— If you see “Session expired (errcode -14)” in the logs, your WeChat login has expired. Stop the channel and re-run
qwen channel configure-weixin
to log in again.
Restrict access
— Use
senderPolicy: "pairing"
or
"allowlist"
to control who can talk to the bot. See
DM Pairing
for details.
Troubleshooting
”WeChat account not configured”
Run
qwen channel configure-weixin
to log in via QR code first.
”Session expired (errcode -14)”
Your WeChat login session has expired. Stop the channel and run
qwen channel configure-weixin
again.
Bot doesn’t respond
Check the terminal output for errors
Verify the channel is running (
qwen channel start my-weixin
)
If using
senderPolicy: "allowlist"
, make sure your WeChat user ID is in
allowedUsers
Images not working
Make sure your channel config has a
model
that supports vision (e.g.,
qwen3.5-plus
)
Check the terminal for CDN download errors — these may indicate a network issue
Last updated on
May 18, 2026
Telegram
DingTalk</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/weixin/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Prompt Suggestion (NES) Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-design/</guid>
  <pubDate>Mon, 15 Jul 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Prompt Suggestion
Prompt Suggestion (NES) Design
Prompt Suggestion (NES) Design
Predicts what the user would naturally type next after the AI completes a response, showing it as ghost text in the input prompt.
Implementation status:
prompt-suggestion-implementation.md
. Speculation engine:
speculation-design.md
.
Overview
A
prompt suggestion
(Next-step Suggestion / NES) is a short prediction (2-12 words) of the user’s next input, generated by an LLM call after each AI response. It appears...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Prompt Suggestion
Prompt Suggestion (NES) Design
Prompt Suggestion (NES) Design
Predicts what the user would naturally type next after the AI completes a response, showing it as ghost text in the input prompt.
Implementation status:
prompt-suggestion-implementation.md
. Speculation engine:
speculation-design.md
.
Overview
A
prompt suggestion
(Next-step Suggestion / NES) is a short prediction (2-12 words) of the user’s next input, generated by an LLM call after each AI response. It appears as ghost text in the input prompt. The user can accept it with Tab/Enter/Right Arrow or dismiss it by typing.
Architecture
┌─────────────────────────────────────────────────────────────┐
│  AppContainer (CLI)                                         │
│                                                             │
│  Responding → Idle transition                               │
│       │                                                     │
│       ▼                                                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Guard Conditions (11 categories)                    │    │
│  │  settings, interactive, sdk, plan mode, dialogs,    │    │
│  │  elicitation, API error                             │    │
│  └────────────────────┬────────────────────────────────┘    │
│                       │                                     │
│                       ▼                                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  generatePromptSuggestion()                         │    │
│  │                                                     │    │
│  │  ┌─── CacheSafeParams available? ───┐               │    │
│  │  │                                  │               │    │
│  │  ▼ YES                         NO ▼                 │    │
│  │  runForkedQuery()      BaseLlmClient.generateJson() │    │
│  │  (cache-aware)         (standalone fallback)        │    │
│  │                                                     │    │
│  │  ──── SUGGESTION_PROMPT ────                        │    │
│  │  ──── 12 filter rules ──────                        │    │
│  │  ──── getFilterReason() ────                        │    │
│  └────────────────────┬────────────────────────────────┘    │
│                       │                                     │
│                       ▼                                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  FollowupController (framework-agnostic)            │    │
│  │  300ms delay → show as ghost text                   │    │
│  │                                                     │    │
│  │  Tab    → accept (fill input)                       │    │
│  │  Enter  → accept + submit                           │    │
│  │  Right  → accept (fill input)                       │    │
│  │  Type   → dismiss + abort speculation               │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Telemetry (PromptSuggestionEvent)                  │    │
│  │  outcome, accept_method, timing, similarity,        │    │
│  │  keystroke, focus, suppression reason, prompt_id     │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
Suggestion Generation
LLM Prompt
[SUGGESTION MODE: Suggest what the user might naturally type next.]
FIRST: Read the LAST FEW LINES of the assistant's most recent message — that's where
next-step hints, tips, and actionable suggestions usually appear. Then check the user's
recent messages and original request.
Your job is to predict what THEY would type - not what you think they should do.
THE TEST: Would they think "I was just about to type that"?
PRIORITY: If the assistant's last message contains a tip or hint like "Tip: type X to ..."
or "type X to ...", extract X as the suggestion. These are explicit next-step hints.
EXAMPLES:
Assistant says "Tip: type post comments to publish findings" → "post comments"
Assistant says "type /review to start" → "/review"
User asked "fix the bug and run tests", bug is fixed → "run the tests"
After code written → "try it out"
Task complete, obvious follow-up → "commit this" or "push it"
Format: 2-12 words, match the user's style. Or nothing.
Reply with ONLY the suggestion, no quotes or explanation.
Filter Rules (12)
Rule
Example blocked
done
”done”
meta_text
”nothing found”, “no suggestion”, “silence”
meta_wrapped
”(silence)”, “[no suggestion]“
error_message
”api error: 500”
prefixed_label
”Suggestion: commit”
too_few_words
”hmm” (but allows “yes”, “commit”, “push” etc.)
too_many_words
> 12 words
too_long
>= 100 chars
multiple_sentences
”Run tests. Then commit.”
has_formatting
newlines, markdown bold
evaluative
”looks good”, “thanks” (with \b word boundaries)
ai_voice
”Let me…”, “I’ll…”, “Here’s…”
Guard Conditions
AppContainer useEffect (13 checks in code):
Guard
Check
Settings toggle
enableFollowupSuggestions
Non-interactive
config.isInteractive()
SDK mode
!config.getSdkMode()
Streaming transition
Responding → Idle
(2 checks)
API error (history)
historyManager.history[last]?.type !== 'error'
API error (pending)
!pendingGeminiHistoryItems.some(type === 'error')
Confirmation dialogs
shell + general + loop detection (3 checks)
Permission dialog
isPermissionsDialogOpen
Elicitation
settingInputRequests.length === 0
Plan mode
ApprovalMode.PLAN
Inside generatePromptSuggestion():
Guard
Check
Early conversation
modelTurns < 2
Separate feature flags (not in guard block):
Flag
Controls
enableCacheSharing
Whether to use forked query or fallback to generateJson
enableSpeculation
Whether to start speculation on suggestion display
State Management
FollowupState
interface
FollowupState
{
suggestion
:
string
|
null
;
isVisible
:
boolean
;
shownAt
:
number
;
// timestamp for telemetry
}
FollowupController
Framework-agnostic controller shared by CLI (Ink) and WebUI (React):
setSuggestion(text)
— 300ms delayed show, null clears immediately
accept(method)
— clears state, fires
onAccept
via microtask, 100ms debounce lock
dismiss()
— clears state, logs
ignored
telemetry
clear()
— hard reset all state + timers
Object.freeze(INITIAL_FOLLOWUP_STATE)
prevents accidental mutation
Keyboard Interaction
Key
CLI
WebUI
Tab
Fill input (no submit)
Fill input (no submit)
Enter
Fill + submit
Fill + submit (
explicitText
param)
Right Arrow
Fill input (no submit)
Fill input (no submit)
Typing
Dismiss + abort speculation
Dismiss
Paste
Dismiss + abort speculation
Dismiss
Key Binding Note
The Tab handler uses
key.name === 'tab'
explicitly (not
ACCEPT_SUGGESTION
matcher) because
ACCEPT_SUGGESTION
also matches Enter, which must fall through to the SUBMIT handler.
Telemetry
PromptSuggestionEvent
Field
Type
Description
outcome
accepted/ignored/suppressed
Final outcome
prompt_id
string
Default: ‘user_intent’
accept_method
tab/enter/right
How user accepted
time_to_accept_ms
number
Time from shown to accept
time_to_ignore_ms
number
Time from shown to dismiss
time_to_first_keystroke_ms
number
Time to first keystroke while shown
suggestion_length
number
Character count
similarity
number
1.0 for accept, 0.0 for ignore
was_focused_when_shown
boolean
Terminal had focus
reason
string
For suppressed: filter rule name
SpeculationEvent
Field
Type
Description
outcome
accepted/aborted/failed
Speculation result
turns_used
number
API round-trips
files_written
number
Files in overlay
tool_use_count
number
Tools executed
duration_ms
number
Wall-clock time
boundary_type
string
What stopped speculation
had_pipelined_suggestion
boolean
Next suggestion generated
Feature Flags and Settings
Setting
Type
Default
Description
enableFollowupSuggestions
boolean
true
Master toggle for prompt suggestions
enableCacheSharing
boolean
true
Use cache-aware forked queries
enableSpeculation
boolean
false
Predictive execution engine
fastModel
(top-level)
string
""
Model for all background tasks (empty = use main model). Set via
/model --fast
Internal Prompt ID Filtering
Background operations use dedicated prompt IDs (
INTERNAL_PROMPT_IDS
in
utils/internalPromptIds.ts
) to prevent their API traffic and tool calls from appearing in the user-visible UI:
Prompt ID
Used by
prompt_suggestion
Suggestion generation
forked_query
Cache-aware forked queries
speculation
Speculation engine
Filtering applied:
loggingContentGenerator
— skips
logApiRequest
and OpenAI interaction logging for internal IDs
logApiResponse
/
logApiError
— skips
chatRecordingService.recordUiTelemetryEvent
logToolCall
— skips
chatRecordingService.recordUiTelemetryEvent
uiTelemetryService.addEvent
—
not filtered
(ensures
/stats
token tracking works)
Thinking Mode
Thinking/reasoning is explicitly disabled (
thinkingConfig: { includeThoughts: false }
) for all background task paths:
Forked query path
(
createForkedChat
) — overrides
thinkingConfig
in the cloned
generationConfig
, covering both suggestion generation and speculation
BaseLlm fallback path
(
generateViaBaseLlm
) — per-request config overrides base content generator’s thinking settings
This is safe because:
Cache prefix is determined by systemInstruction + tools + history, not
thinkingConfig
— cache hits are unaffected
All backends (Gemini, OpenAI-compatible, Anthropic) handle
includeThoughts: false
by omitting the thinking field — no API errors on models without thinking support
Suggestion generation and speculation don’t benefit from reasoning tokens
Last updated on
May 18, 2026
OpenRouter Auth and Model Management Design
Prompt Suggestion Implementation Status</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Prompt Suggestion
Prompt Suggestion (NES) Design
Prompt Suggestion (NES) Design
Predicts what the user would naturally type next after the AI completes a response, showing it as ghost text in the input prompt.
Implementation status:
prompt-suggestion-implementation.md
. Speculation engine:
speculation-design.md
.
Overview
A
prompt suggestion
(Next-step Suggestion / NES) is a short prediction (2-12 words) of the user’s next input, generated by an LLM call after each AI response. It appears...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Prompt Suggestion
Prompt Suggestion (NES) Design
Prompt Suggestion (NES) Design
Predicts what the user would naturally type next after the AI completes a response, showing it as ghost text in the input prompt.
Implementation status:
prompt-suggestion-implementation.md
. Speculation engine:
speculation-design.md
.
Overview
A
prompt suggestion
(Next-step Suggestion / NES) is a short prediction (2-12 words) of the user’s next input, generated by an LLM call after each AI response. It appears as ghost text in the input prompt. The user can accept it with Tab/Enter/Right Arrow or dismiss it by typing.
Architecture
┌─────────────────────────────────────────────────────────────┐
│  AppContainer (CLI)                                         │
│                                                             │
│  Responding → Idle transition                               │
│       │                                                     │
│       ▼                                                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Guard Conditions (11 categories)                    │    │
│  │  settings, interactive, sdk, plan mode, dialogs,    │    │
│  │  elicitation, API error                             │    │
│  └────────────────────┬────────────────────────────────┘    │
│                       │                                     │
│                       ▼                                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  generatePromptSuggestion()                         │    │
│  │                                                     │    │
│  │  ┌─── CacheSafeParams available? ───┐               │    │
│  │  │                                  │               │    │
│  │  ▼ YES                         NO ▼                 │    │
│  │  runForkedQuery()      BaseLlmClient.generateJson() │    │
│  │  (cache-aware)         (standalone fallback)        │    │
│  │                                                     │    │
│  │  ──── SUGGESTION_PROMPT ────                        │    │
│  │  ──── 12 filter rules ──────                        │    │
│  │  ──── getFilterReason() ────                        │    │
│  └────────────────────┬────────────────────────────────┘    │
│                       │                                     │
│                       ▼                                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  FollowupController (framework-agnostic)            │    │
│  │  300ms delay → show as ghost text                   │    │
│  │                                                     │    │
│  │  Tab    → accept (fill input)                       │    │
│  │  Enter  → accept + submit                           │    │
│  │  Right  → accept (fill input)                       │    │
│  │  Type   → dismiss + abort speculation               │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Telemetry (PromptSuggestionEvent)                  │    │
│  │  outcome, accept_method, timing, similarity,        │    │
│  │  keystroke, focus, suppression reason, prompt_id     │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
Suggestion Generation
LLM Prompt
[SUGGESTION MODE: Suggest what the user might naturally type next.]
FIRST: Read the LAST FEW LINES of the assistant's most recent message — that's where
next-step hints, tips, and actionable suggestions usually appear. Then check the user's
recent messages and original request.
Your job is to predict what THEY would type - not what you think they should do.
THE TEST: Would they think "I was just about to type that"?
PRIORITY: If the assistant's last message contains a tip or hint like "Tip: type X to ..."
or "type X to ...", extract X as the suggestion. These are explicit next-step hints.
EXAMPLES:
Assistant says "Tip: type post comments to publish findings" → "post comments"
Assistant says "type /review to start" → "/review"
User asked "fix the bug and run tests", bug is fixed → "run the tests"
After code written → "try it out"
Task complete, obvious follow-up → "commit this" or "push it"
Format: 2-12 words, match the user's style. Or nothing.
Reply with ONLY the suggestion, no quotes or explanation.
Filter Rules (12)
Rule
Example blocked
done
”done”
meta_text
”nothing found”, “no suggestion”, “silence”
meta_wrapped
”(silence)”, “[no suggestion]“
error_message
”api error: 500”
prefixed_label
”Suggestion: commit”
too_few_words
”hmm” (but allows “yes”, “commit”, “push” etc.)
too_many_words
> 12 words
too_long
>= 100 chars
multiple_sentences
”Run tests. Then commit.”
has_formatting
newlines, markdown bold
evaluative
”looks good”, “thanks” (with \b word boundaries)
ai_voice
”Let me…”, “I’ll…”, “Here’s…”
Guard Conditions
AppContainer useEffect (13 checks in code):
Guard
Check
Settings toggle
enableFollowupSuggestions
Non-interactive
config.isInteractive()
SDK mode
!config.getSdkMode()
Streaming transition
Responding → Idle
(2 checks)
API error (history)
historyManager.history[last]?.type !== 'error'
API error (pending)
!pendingGeminiHistoryItems.some(type === 'error')
Confirmation dialogs
shell + general + loop detection (3 checks)
Permission dialog
isPermissionsDialogOpen
Elicitation
settingInputRequests.length === 0
Plan mode
ApprovalMode.PLAN
Inside generatePromptSuggestion():
Guard
Check
Early conversation
modelTurns < 2
Separate feature flags (not in guard block):
Flag
Controls
enableCacheSharing
Whether to use forked query or fallback to generateJson
enableSpeculation
Whether to start speculation on suggestion display
State Management
FollowupState
interface
FollowupState
{
suggestion
:
string
|
null
;
isVisible
:
boolean
;
shownAt
:
number
;
// timestamp for telemetry
}
FollowupController
Framework-agnostic controller shared by CLI (Ink) and WebUI (React):
setSuggestion(text)
— 300ms delayed show, null clears immediately
accept(method)
— clears state, fires
onAccept
via microtask, 100ms debounce lock
dismiss()
— clears state, logs
ignored
telemetry
clear()
— hard reset all state + timers
Object.freeze(INITIAL_FOLLOWUP_STATE)
prevents accidental mutation
Keyboard Interaction
Key
CLI
WebUI
Tab
Fill input (no submit)
Fill input (no submit)
Enter
Fill + submit
Fill + submit (
explicitText
param)
Right Arrow
Fill input (no submit)
Fill input (no submit)
Typing
Dismiss + abort speculation
Dismiss
Paste
Dismiss + abort speculation
Dismiss
Key Binding Note
The Tab handler uses
key.name === 'tab'
explicitly (not
ACCEPT_SUGGESTION
matcher) because
ACCEPT_SUGGESTION
also matches Enter, which must fall through to the SUBMIT handler.
Telemetry
PromptSuggestionEvent
Field
Type
Description
outcome
accepted/ignored/suppressed
Final outcome
prompt_id
string
Default: ‘user_intent’
accept_method
tab/enter/right
How user accepted
time_to_accept_ms
number
Time from shown to accept
time_to_ignore_ms
number
Time from shown to dismiss
time_to_first_keystroke_ms
number
Time to first keystroke while shown
suggestion_length
number
Character count
similarity
number
1.0 for accept, 0.0 for ignore
was_focused_when_shown
boolean
Terminal had focus
reason
string
For suppressed: filter rule name
SpeculationEvent
Field
Type
Description
outcome
accepted/aborted/failed
Speculation result
turns_used
number
API round-trips
files_written
number
Files in overlay
tool_use_count
number
Tools executed
duration_ms
number
Wall-clock time
boundary_type
string
What stopped speculation
had_pipelined_suggestion
boolean
Next suggestion generated
Feature Flags and Settings
Setting
Type
Default
Description
enableFollowupSuggestions
boolean
true
Master toggle for prompt suggestions
enableCacheSharing
boolean
true
Use cache-aware forked queries
enableSpeculation
boolean
false
Predictive execution engine
fastModel
(top-level)
string
""
Model for all background tasks (empty = use main model). Set via
/model --fast
Internal Prompt ID Filtering
Background operations use dedicated prompt IDs (
INTERNAL_PROMPT_IDS
in
utils/internalPromptIds.ts
) to prevent their API traffic and tool calls from appearing in the user-visible UI:
Prompt ID
Used by
prompt_suggestion
Suggestion generation
forked_query
Cache-aware forked queries
speculation
Speculation engine
Filtering applied:
loggingContentGenerator
— skips
logApiRequest
and OpenAI interaction logging for internal IDs
logApiResponse
/
logApiError
— skips
chatRecordingService.recordUiTelemetryEvent
logToolCall
— skips
chatRecordingService.recordUiTelemetryEvent
uiTelemetryService.addEvent
—
not filtered
(ensures
/stats
token tracking works)
Thinking Mode
Thinking/reasoning is explicitly disabled (
thinkingConfig: { includeThoughts: false }
) for all background task paths:
Forked query path
(
createForkedChat
) — overrides
thinkingConfig
in the cloned
generationConfig
, covering both suggestion generation and speculation
BaseLlm fallback path
(
generateViaBaseLlm
) — per-request config overrides base content generator’s thinking settings
This is safe because:
Cache prefix is determined by systemInstruction + tools + history, not
thinkingConfig
— cache hits are unaffected
All backends (Gemini, OpenAI-compatible, Anthropic) handle
includeThoughts: false
by omitting the thinking field — no API errors on models without thinking support
Suggestion generation and speculation don’t benefit from reasoning tokens
Last updated on
May 18, 2026
OpenRouter Auth and Model Management Design
Prompt Suggestion Implementation Status</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/prompt-suggestion/prompt-suggestion-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Visual Studio Code</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/integration-vscode/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/integration-vscode/</guid>
  <pubDate>Mon, 08 Jul 2024 00:00:00 +0000</pubDate>
  <category>Integrations</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Visual Studio Code
Visual Studio Code
The VS Code extension (Beta) lets you see Qwen’s changes in real-time through a native graphical interface integrated directly into your IDE, making it easier to access and interact with Qwen Code.
Your browser does not support the video tag.
Features
Native IDE experience
: Dedicated Qwen Code sidebar panel accessed via the Qwen icon
Auto-accept edits mode
: Automatically apply Qwen’s changes as they’re made
File management
: @-mention files or a...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Visual Studio Code
Visual Studio Code
The VS Code extension (Beta) lets you see Qwen’s changes in real-time through a native graphical interface integrated directly into your IDE, making it easier to access and interact with Qwen Code.
Your browser does not support the video tag.
Features
Native IDE experience
: Dedicated Qwen Code sidebar panel accessed via the Qwen icon
Auto-accept edits mode
: Automatically apply Qwen’s changes as they’re made
File management
: @-mention files or attach files and images using the system file picker
Conversation history
: Access to past conversations
Multiple sessions
: Run multiple Qwen Code sessions simultaneously
Requirements
VS Code 1.85.0 or higher
Installation
Download and install the extension from the
Visual Studio Code Extension Marketplace
.
Troubleshooting
Extension not installing
Ensure you have VS Code 1.85.0 or higher
Check that VS Code has permission to install extensions
Try installing directly from the Marketplace website
Qwen Code not responding
Check your internet connection
Start a new conversation to see if the issue persists
File an issue on GitHub
if the problem continues
Last updated on
May 18, 2026
Command Workflows
Zed IDE</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-vscode/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Visual Studio Code
Visual Studio Code
The VS Code extension (Beta) lets you see Qwen’s changes in real-time through a native graphical interface integrated directly into your IDE, making it easier to access and interact with Qwen Code.
Your browser does not support the video tag.
Features
Native IDE experience
: Dedicated Qwen Code sidebar panel accessed via the Qwen icon
Auto-accept edits mode
: Automatically apply Qwen’s changes as they’re made
File management
: @-mention files or a...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Visual Studio Code
Visual Studio Code
The VS Code extension (Beta) lets you see Qwen’s changes in real-time through a native graphical interface integrated directly into your IDE, making it easier to access and interact with Qwen Code.
Your browser does not support the video tag.
Features
Native IDE experience
: Dedicated Qwen Code sidebar panel accessed via the Qwen icon
Auto-accept edits mode
: Automatically apply Qwen’s changes as they’re made
File management
: @-mention files or attach files and images using the system file picker
Conversation history
: Access to past conversations
Multiple sessions
: Run multiple Qwen Code sessions simultaneously
Requirements
VS Code 1.85.0 or higher
Installation
Download and install the extension from the
Visual Studio Code Extension Marketplace
.
Troubleshooting
Extension not installing
Ensure you have VS Code 1.85.0 or higher
Check that VS Code has permission to install extensions
Try installing directly from the Marketplace website
Qwen Code not responding
Check your internet connection
Start a new conversation to see if the issue persists
File an issue on GitHub
if the problem continues
Last updated on
May 18, 2026
Command Workflows
Zed IDE</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-vscode/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Language Server Protocol (LSP) Support</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/lsp/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/lsp/</guid>
  <pubDate>Sun, 07 Jul 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
LSP (Language Server Protocol)
Language Server Protocol (LSP) Support
Qwen Code provides native Language Server Protocol (LSP) support, enabling advanced code intelligence features like go-to-definition, find references, diagnostics, and code actions. This integration allows the AI agent to understand your code more deeply and provide more accurate assistance.
Overview
LSP support in Qwen Code works by connecting to language servers that understand your code. Once you configu...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
LSP (Language Server Protocol)
Language Server Protocol (LSP) Support
Qwen Code provides native Language Server Protocol (LSP) support, enabling advanced code intelligence features like go-to-definition, find references, diagnostics, and code actions. This integration allows the AI agent to understand your code more deeply and provide more accurate assistance.
Overview
LSP support in Qwen Code works by connecting to language servers that understand your code. Once you configure servers via
.lsp.json
(or extensions), Qwen Code can start them and use them to:
Navigate to symbol definitions
Find all references to a symbol
Get hover information (documentation, type info)
View diagnostic messages (errors, warnings)
Access code actions (quick fixes, refactorings)
Analyze call hierarchies
Quick Start
LSP is an experimental feature in Qwen Code. To enable it, use the
--experimental-lsp
command line flag:
qwen
--experimental-lsp
LSP servers are configuration-driven. You must define them in
.lsp.json
(or via extensions) for Qwen Code to start them.
Prerequisites
You need to have the language server for your programming language installed:
Language
Language Server
Install Command
TypeScript/JavaScript
typescript-language-server
npm install -g typescript-language-server typescript
Python
pylsp
pip install python-lsp-server
Go
gopls
go install golang.org/x/tools/gopls@latest
Rust
rust-analyzer
Installation guide
C/C++
clangd
Install LLVM/clangd via your package manager
Java
jdtls
Install JDTLS and a JDK
Configuration
.lsp.json File
You can configure language servers using a
.lsp.json
file in your project root. Each top-level key is a language identifier, and its value is the server configuration object.
Basic format:
{
"typescript"
: {
"command"
:
"typescript-language-server"
,
"args"
: [
"--stdio"
],
"extensionToLanguage"
: {
".ts"
:
"typescript"
,
".tsx"
:
"typescriptreact"
,
".js"
:
"javascript"
,
".jsx"
:
"javascriptreact"
}
}
}
C/C++ (clangd) configuration
Dependencies:
clangd (LLVM) must be installed and available in PATH.
A compile database (
compile_commands.json
) or
compile_flags.txt
is required for accurate results.
Example:
{
"cpp"
: {
"command"
:
"clangd"
,
"args"
: [
"--background-index"
,
"--clang-tidy"
,
"--header-insertion=iwyu"
,
"--completion-style=detailed"
]
}
}
Java (jdtls) configuration
Dependencies:
JDK installed and available in PATH (
java
).
JDTLS installed and available in PATH (
jdtls
).
Example:
{
"java"
: {
"command"
:
"jdtls"
,
"args"
: [
"-configuration"
,
".jdtls-config"
,
"-data"
,
".jdtls-workspace"
]
}
}
Configuration Options
Required Fields
Option
Type
Description
command
string
Command to start the LSP server. Supports bare command names resolved via
PATH
(e.g.
clangd
) and absolute paths (e.g.
/opt/llvm/bin/clangd
)
Optional Fields
Option
Type
Default
Description
args
string[]
[]
Command line arguments
transport
string
"stdio"
Transport type:
stdio
,
tcp
, or
socket
env
object
-
Environment variables
initializationOptions
object
-
LSP initialization options
settings
object
-
Server settings via
workspace/didChangeConfiguration
extensionToLanguage
object
-
Maps file extensions to language identifiers
workspaceFolder
string
-
Override workspace folder (must be within project root)
startupTimeout
number
10000
Startup timeout in milliseconds
shutdownTimeout
number
5000
Shutdown timeout in milliseconds
restartOnCrash
boolean
false
Auto-restart on crash
maxRestarts
number
3
Maximum restart attempts
trustRequired
boolean
true
Require trusted workspace
TCP/Socket Transport
For servers that use TCP or Unix socket transport:
{
"remote-lsp"
: {
"transport"
:
"tcp"
,
"socket"
: {
"host"
:
"127.0.0.1"
,
"port"
:
9999
},
"extensionToLanguage"
: {
".custom"
:
"custom"
}
}
}
Available LSP Operations
Qwen Code exposes LSP functionality through the unified
lsp
tool. Here are the available operations:
Location-based operations (
goToDefinition
,
findReferences
,
hover
,
goToImplementation
, and
prepareCallHierarchy
) require an exact
filePath
+
line
+
character
position. If you do not know the exact position, use
workspaceSymbol
or
documentSymbol
first to locate the symbol.
Code Navigation
Go to Definition
Find where a symbol is defined.
Operation: goToDefinition
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Find References
Find all references to a symbol.
Operation: findReferences
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
- includeDeclaration: Include the declaration itself (optional)
Go to Implementation
Find implementations of an interface or abstract method.
Operation: goToImplementation
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Symbol Information
Hover
Get documentation and type information for a symbol.
Operation: hover
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Document Symbols
Get all symbols in a document.
Operation: documentSymbol
Parameters:
- filePath: Path to the file
Workspace Symbol Search
Search for symbols across the workspace.
Operation: workspaceSymbol
Parameters:
- query: Search query string
- limit: Maximum results (optional)
Call Hierarchy
Prepare Call Hierarchy
Get the call hierarchy item at a position.
Operation: prepareCallHierarchy
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Incoming Calls
Find all functions that call the given function.
Operation: incomingCalls
Parameters:
- callHierarchyItem: Item from prepareCallHierarchy
Outgoing Calls
Find all functions called by the given function.
Operation: outgoingCalls
Parameters:
- callHierarchyItem: Item from prepareCallHierarchy
Diagnostics
File Diagnostics
Get diagnostic messages (errors, warnings) for a file.
Operation: diagnostics
Parameters:
- filePath: Path to the file
Workspace Diagnostics
Get all diagnostic messages across the workspace.
Operation: workspaceDiagnostics
Parameters:
- limit: Maximum results (optional)
Code Actions
Get Code Actions
Get available code actions (quick fixes, refactorings) at a location.
Operation: codeActions
Parameters:
- filePath: Path to the file
- line: Start line number (1-based)
- character: Start column number (1-based)
- endLine: End line number (optional, defaults to line)
- endCharacter: End column (optional, defaults to character)
- diagnostics: Diagnostics to get actions for (optional)
- codeActionKinds: Filter by action kind (optional)
Code action kinds:
quickfix
- Quick fixes for errors/warnings
refactor
- Refactoring operations
refactor.extract
- Extract to function/variable
refactor.inline
- Inline function/variable
source
- Source code actions
source.organizeImports
- Organize imports
source.fixAll
- Fix all auto-fixable issues
Security
LSP servers are only started in trusted workspaces by default. This is because language servers run with your user permissions and can execute code.
Trust Controls
Trusted Workspace
: LSP servers start if configured
Untrusted Workspace
: LSP servers won’t start unless
trustRequired: false
is set in the server configuration
To mark a workspace as trusted, use the
/trust
command.
Per-Server Trust Override
You can override trust requirements for specific servers in their configuration:
{
"safe-server"
: {
"command"
:
"safe-language-server"
,
"args"
: [
"--stdio"
],
"trustRequired"
:
false
,
"extensionToLanguage"
: {
".safe"
:
"safe"
}
}
}
Troubleshooting
Server Not Starting
Verify
--experimental-lsp
flag
: Make sure you’re using the flag when starting Qwen Code
Check if the server is installed
: Run the command manually (e.g.
clangd --version
) to verify
Check the command
: The server binary must be in your system
PATH
, or specified as an absolute path (e.g.
/opt/llvm/bin/clangd
). Relative paths that escape the workspace are blocked
Check workspace trust
: The workspace must be trusted for LSP (use
/trust
)
Check logs
: Look for
[LSP]
entries in the debug log (see Debugging section below)
Check the process
: Run
ps aux | grep <server-name>
to verify the server process is running
Slow Performance
Large projects
: Consider excluding
node_modules
and other large directories
Server timeout
: Increase
startupTimeout
in server configuration for slow servers
No Results
Server not ready
: The server may still be indexing. For C/C++ projects with clangd, ensure
--background-index
is in the args and a
compile_commands.json
(or
compile_flags.txt
) exists in the project root or a parent directory. Use
--compile-commands-dir=<path>
if it is in a build subdirectory
File not saved
: Save your file for the server to pick up changes
Wrong language
: Check if the correct server is running for your language
Check the process
: Run
ps aux | grep <server-name>
to verify the server is actually running
Debugging
LSP debug logs are automatically written to session log files in
~/.qwen/debug/
. To check LSP-related entries:
# View the latest session log
grep
'\[LSP\]'
~/.qwen/debug/latest
# Common error messages to look for:
#   "command path is unsafe"  → relative path escapes workspace, use absolute path or add to PATH
#   "command not found"       → server binary not installed or not in PATH
#   "requires trusted workspace" → run /trust first
You can also verify the server process is running:
ps
aux
|
grep
clangd
# or typescript-language-server, jdtls, etc.
Extension LSP Configuration
Extensions can provide LSP server configurations through the
lspServers
field in their
plugin.json
. This can be either an inline object or a path to a
.lsp.json
file. Qwen Code loads these configs when the extension is enabled. The format is the same language-keyed layout used in project
.lsp.json
files.
Best Practices
Install language servers globally
: This ensures they’re available in all projects
Use project-specific settings
: Configure server options per project when needed via
.lsp.json
Keep servers updated
: Update your language servers regularly for best results
Trust wisely
: Only trust workspaces from trusted sources
FAQ
Q: How do I enable LSP?
Use the
--experimental-lsp
flag when starting Qwen Code:
qwen
--experimental-lsp
Q: How do I know which language servers are running?
Check the debug log for
[LSP]
entries (
grep '\[LSP\]' ~/.qwen/debug/latest
), or verify the process directly with
ps aux | grep <server-name>
.
Q: Can I use multiple language servers for the same file type?
Yes, but only one will be used for each operation. The first server that returns results wins.
Q: Does LSP work in sandbox mode?
LSP servers run outside the sandbox to access your code. They’re subject to workspace trust controls.
Last updated on
May 18, 2026
MCP
Token Caching</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/lsp/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
LSP (Language Server Protocol)
Language Server Protocol (LSP) Support
Qwen Code provides native Language Server Protocol (LSP) support, enabling advanced code intelligence features like go-to-definition, find references, diagnostics, and code actions. This integration allows the AI agent to understand your code more deeply and provide more accurate assistance.
Overview
LSP support in Qwen Code works by connecting to language servers that understand your code. Once you configu...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
LSP (Language Server Protocol)
Language Server Protocol (LSP) Support
Qwen Code provides native Language Server Protocol (LSP) support, enabling advanced code intelligence features like go-to-definition, find references, diagnostics, and code actions. This integration allows the AI agent to understand your code more deeply and provide more accurate assistance.
Overview
LSP support in Qwen Code works by connecting to language servers that understand your code. Once you configure servers via
.lsp.json
(or extensions), Qwen Code can start them and use them to:
Navigate to symbol definitions
Find all references to a symbol
Get hover information (documentation, type info)
View diagnostic messages (errors, warnings)
Access code actions (quick fixes, refactorings)
Analyze call hierarchies
Quick Start
LSP is an experimental feature in Qwen Code. To enable it, use the
--experimental-lsp
command line flag:
qwen
--experimental-lsp
LSP servers are configuration-driven. You must define them in
.lsp.json
(or via extensions) for Qwen Code to start them.
Prerequisites
You need to have the language server for your programming language installed:
Language
Language Server
Install Command
TypeScript/JavaScript
typescript-language-server
npm install -g typescript-language-server typescript
Python
pylsp
pip install python-lsp-server
Go
gopls
go install golang.org/x/tools/gopls@latest
Rust
rust-analyzer
Installation guide
C/C++
clangd
Install LLVM/clangd via your package manager
Java
jdtls
Install JDTLS and a JDK
Configuration
.lsp.json File
You can configure language servers using a
.lsp.json
file in your project root. Each top-level key is a language identifier, and its value is the server configuration object.
Basic format:
{
"typescript"
: {
"command"
:
"typescript-language-server"
,
"args"
: [
"--stdio"
],
"extensionToLanguage"
: {
".ts"
:
"typescript"
,
".tsx"
:
"typescriptreact"
,
".js"
:
"javascript"
,
".jsx"
:
"javascriptreact"
}
}
}
C/C++ (clangd) configuration
Dependencies:
clangd (LLVM) must be installed and available in PATH.
A compile database (
compile_commands.json
) or
compile_flags.txt
is required for accurate results.
Example:
{
"cpp"
: {
"command"
:
"clangd"
,
"args"
: [
"--background-index"
,
"--clang-tidy"
,
"--header-insertion=iwyu"
,
"--completion-style=detailed"
]
}
}
Java (jdtls) configuration
Dependencies:
JDK installed and available in PATH (
java
).
JDTLS installed and available in PATH (
jdtls
).
Example:
{
"java"
: {
"command"
:
"jdtls"
,
"args"
: [
"-configuration"
,
".jdtls-config"
,
"-data"
,
".jdtls-workspace"
]
}
}
Configuration Options
Required Fields
Option
Type
Description
command
string
Command to start the LSP server. Supports bare command names resolved via
PATH
(e.g.
clangd
) and absolute paths (e.g.
/opt/llvm/bin/clangd
)
Optional Fields
Option
Type
Default
Description
args
string[]
[]
Command line arguments
transport
string
"stdio"
Transport type:
stdio
,
tcp
, or
socket
env
object
-
Environment variables
initializationOptions
object
-
LSP initialization options
settings
object
-
Server settings via
workspace/didChangeConfiguration
extensionToLanguage
object
-
Maps file extensions to language identifiers
workspaceFolder
string
-
Override workspace folder (must be within project root)
startupTimeout
number
10000
Startup timeout in milliseconds
shutdownTimeout
number
5000
Shutdown timeout in milliseconds
restartOnCrash
boolean
false
Auto-restart on crash
maxRestarts
number
3
Maximum restart attempts
trustRequired
boolean
true
Require trusted workspace
TCP/Socket Transport
For servers that use TCP or Unix socket transport:
{
"remote-lsp"
: {
"transport"
:
"tcp"
,
"socket"
: {
"host"
:
"127.0.0.1"
,
"port"
:
9999
},
"extensionToLanguage"
: {
".custom"
:
"custom"
}
}
}
Available LSP Operations
Qwen Code exposes LSP functionality through the unified
lsp
tool. Here are the available operations:
Location-based operations (
goToDefinition
,
findReferences
,
hover
,
goToImplementation
, and
prepareCallHierarchy
) require an exact
filePath
+
line
+
character
position. If you do not know the exact position, use
workspaceSymbol
or
documentSymbol
first to locate the symbol.
Code Navigation
Go to Definition
Find where a symbol is defined.
Operation: goToDefinition
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Find References
Find all references to a symbol.
Operation: findReferences
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
- includeDeclaration: Include the declaration itself (optional)
Go to Implementation
Find implementations of an interface or abstract method.
Operation: goToImplementation
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Symbol Information
Hover
Get documentation and type information for a symbol.
Operation: hover
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Document Symbols
Get all symbols in a document.
Operation: documentSymbol
Parameters:
- filePath: Path to the file
Workspace Symbol Search
Search for symbols across the workspace.
Operation: workspaceSymbol
Parameters:
- query: Search query string
- limit: Maximum results (optional)
Call Hierarchy
Prepare Call Hierarchy
Get the call hierarchy item at a position.
Operation: prepareCallHierarchy
Parameters:
- filePath: Path to the file
- line: Line number (1-based)
- character: Column number (1-based)
Incoming Calls
Find all functions that call the given function.
Operation: incomingCalls
Parameters:
- callHierarchyItem: Item from prepareCallHierarchy
Outgoing Calls
Find all functions called by the given function.
Operation: outgoingCalls
Parameters:
- callHierarchyItem: Item from prepareCallHierarchy
Diagnostics
File Diagnostics
Get diagnostic messages (errors, warnings) for a file.
Operation: diagnostics
Parameters:
- filePath: Path to the file
Workspace Diagnostics
Get all diagnostic messages across the workspace.
Operation: workspaceDiagnostics
Parameters:
- limit: Maximum results (optional)
Code Actions
Get Code Actions
Get available code actions (quick fixes, refactorings) at a location.
Operation: codeActions
Parameters:
- filePath: Path to the file
- line: Start line number (1-based)
- character: Start column number (1-based)
- endLine: End line number (optional, defaults to line)
- endCharacter: End column (optional, defaults to character)
- diagnostics: Diagnostics to get actions for (optional)
- codeActionKinds: Filter by action kind (optional)
Code action kinds:
quickfix
- Quick fixes for errors/warnings
refactor
- Refactoring operations
refactor.extract
- Extract to function/variable
refactor.inline
- Inline function/variable
source
- Source code actions
source.organizeImports
- Organize imports
source.fixAll
- Fix all auto-fixable issues
Security
LSP servers are only started in trusted workspaces by default. This is because language servers run with your user permissions and can execute code.
Trust Controls
Trusted Workspace
: LSP servers start if configured
Untrusted Workspace
: LSP servers won’t start unless
trustRequired: false
is set in the server configuration
To mark a workspace as trusted, use the
/trust
command.
Per-Server Trust Override
You can override trust requirements for specific servers in their configuration:
{
"safe-server"
: {
"command"
:
"safe-language-server"
,
"args"
: [
"--stdio"
],
"trustRequired"
:
false
,
"extensionToLanguage"
: {
".safe"
:
"safe"
}
}
}
Troubleshooting
Server Not Starting
Verify
--experimental-lsp
flag
: Make sure you’re using the flag when starting Qwen Code
Check if the server is installed
: Run the command manually (e.g.
clangd --version
) to verify
Check the command
: The server binary must be in your system
PATH
, or specified as an absolute path (e.g.
/opt/llvm/bin/clangd
). Relative paths that escape the workspace are blocked
Check workspace trust
: The workspace must be trusted for LSP (use
/trust
)
Check logs
: Look for
[LSP]
entries in the debug log (see Debugging section below)
Check the process
: Run
ps aux | grep <server-name>
to verify the server process is running
Slow Performance
Large projects
: Consider excluding
node_modules
and other large directories
Server timeout
: Increase
startupTimeout
in server configuration for slow servers
No Results
Server not ready
: The server may still be indexing. For C/C++ projects with clangd, ensure
--background-index
is in the args and a
compile_commands.json
(or
compile_flags.txt
) exists in the project root or a parent directory. Use
--compile-commands-dir=<path>
if it is in a build subdirectory
File not saved
: Save your file for the server to pick up changes
Wrong language
: Check if the correct server is running for your language
Check the process
: Run
ps aux | grep <server-name>
to verify the server is actually running
Debugging
LSP debug logs are automatically written to session log files in
~/.qwen/debug/
. To check LSP-related entries:
# View the latest session log
grep
'\[LSP\]'
~/.qwen/debug/latest
# Common error messages to look for:
#   "command path is unsafe"  → relative path escapes workspace, use absolute path or add to PATH
#   "command not found"       → server binary not installed or not in PATH
#   "requires trusted workspace" → run /trust first
You can also verify the server process is running:
ps
aux
|
grep
clangd
# or typescript-language-server, jdtls, etc.
Extension LSP Configuration
Extensions can provide LSP server configurations through the
lspServers
field in their
plugin.json
. This can be either an inline object or a path to a
.lsp.json
file. Qwen Code loads these configs when the extension is enabled. The format is the same language-keyed layout used in project
.lsp.json
files.
Best Practices
Install language servers globally
: This ensures they’re available in all projects
Use project-specific settings
: Configure server options per project when needed via
.lsp.json
Keep servers updated
: Update your language servers regularly for best results
Trust wisely
: Only trust workspaces from trusted sources
FAQ
Q: How do I enable LSP?
Use the
--experimental-lsp
flag when starting Qwen Code:
qwen
--experimental-lsp
Q: How do I know which language servers are running?
Check the debug log for
[LSP]
entries (
grep '\[LSP\]' ~/.qwen/debug/latest
), or verify the process directly with
ps aux | grep <server-name>
.
Q: Can I use multiple language servers for the same file type?
Yes, but only one will be used for each operation. The first server that returns results wins.
Q: Does LSP work in sandbox mode?
LSP servers run outside the sandbox to access your code. They’re subject to workspace trust controls.
Last updated on
May 18, 2026
MCP
Token Caching</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/lsp/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Checkpointing</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/checkpointing/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/checkpointing/</guid>
  <pubDate>Mon, 01 Jul 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Checkpointing
Checkpointing
Qwen Code includes a Checkpointing feature that automatically saves a snapshot of your project’s state before any file modifications are made by AI-powered tools. This allows you to safely experiment with and apply code changes, knowing you can instantly revert back to the state before the tool was run.
How It Works
When you approve a tool that modifies the file system (like
write_file
or
edit
), the CLI automatically creates a “checkpoint.” This c...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Checkpointing
Checkpointing
Qwen Code includes a Checkpointing feature that automatically saves a snapshot of your project’s state before any file modifications are made by AI-powered tools. This allows you to safely experiment with and apply code changes, knowing you can instantly revert back to the state before the tool was run.
How It Works
When you approve a tool that modifies the file system (like
write_file
or
edit
), the CLI automatically creates a “checkpoint.” This checkpoint includes:
A Git Snapshot:
A commit is made in a special, shadow Git repository located in your home directory (
~/.qwen/history/<project_hash>
). This snapshot captures the complete state of your project files at that moment. It does
not
interfere with your own project’s Git repository.
Conversation History:
The entire conversation you’ve had with the agent up to that point is saved.
The Tool Call:
The specific tool call that was about to be executed is also stored.
If you want to undo the change or simply go back, you can use the
/restore
command. Restoring a checkpoint will:
Revert all files in your project to the state captured in the snapshot.
Restore the conversation history in the CLI.
Re-propose the original tool call, allowing you to run it again, modify it, or simply ignore it.
All checkpoint data, including the Git snapshot and conversation history, is stored locally on your machine. The Git snapshot is stored in the shadow repository while the conversation history and tool calls are saved in a JSON file in your project’s temporary directory, typically located at
~/.qwen/tmp/<project_hash>/checkpoints
.
Enabling the Feature
The Checkpointing feature is disabled by default. To enable it, you can either use a command-line flag or edit your
settings.json
file.
Using the Command-Line Flag
You can enable checkpointing for the current session by using the
--checkpointing
flag when starting Qwen Code:
qwen
--checkpointing
Using the
settings.json
File
To enable checkpointing by default for all sessions, you need to edit your
settings.json
file.
Add the following key to your
settings.json
:
{
"general"
: {
"checkpointing"
: {
"enabled"
:
true
}
}
}
Using the
/restore
Command
Once enabled, checkpoints are created automatically. To manage them, you use the
/restore
command.
List Available Checkpoints
To see a list of all saved checkpoints for the current project, simply run:
/restore
The CLI will display a list of available checkpoint files. These file names are typically composed of a timestamp, the name of the file being modified, and the name of the tool that was about to be run (e.g.,
2025-06-22T10-00-00_000Z-my-file.txt-write_file
).
Restore a Specific Checkpoint
To restore your project to a specific checkpoint, use the checkpoint file from the list:
/restore <checkpoint_file>
For example:
/restore 2025-06-22T10-00-00_000Z-my-file.txt-write_file
After running the command, your files and conversation will be immediately restored to the state they were in when the checkpoint was created, and the original tool prompt will reappear.
Last updated on
May 18, 2026
Dual Output
MCP</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/checkpointing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Checkpointing
Checkpointing
Qwen Code includes a Checkpointing feature that automatically saves a snapshot of your project’s state before any file modifications are made by AI-powered tools. This allows you to safely experiment with and apply code changes, knowing you can instantly revert back to the state before the tool was run.
How It Works
When you approve a tool that modifies the file system (like
write_file
or
edit
), the CLI automatically creates a “checkpoint.” This c...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Checkpointing
Checkpointing
Qwen Code includes a Checkpointing feature that automatically saves a snapshot of your project’s state before any file modifications are made by AI-powered tools. This allows you to safely experiment with and apply code changes, knowing you can instantly revert back to the state before the tool was run.
How It Works
When you approve a tool that modifies the file system (like
write_file
or
edit
), the CLI automatically creates a “checkpoint.” This checkpoint includes:
A Git Snapshot:
A commit is made in a special, shadow Git repository located in your home directory (
~/.qwen/history/<project_hash>
). This snapshot captures the complete state of your project files at that moment. It does
not
interfere with your own project’s Git repository.
Conversation History:
The entire conversation you’ve had with the agent up to that point is saved.
The Tool Call:
The specific tool call that was about to be executed is also stored.
If you want to undo the change or simply go back, you can use the
/restore
command. Restoring a checkpoint will:
Revert all files in your project to the state captured in the snapshot.
Restore the conversation history in the CLI.
Re-propose the original tool call, allowing you to run it again, modify it, or simply ignore it.
All checkpoint data, including the Git snapshot and conversation history, is stored locally on your machine. The Git snapshot is stored in the shadow repository while the conversation history and tool calls are saved in a JSON file in your project’s temporary directory, typically located at
~/.qwen/tmp/<project_hash>/checkpoints
.
Enabling the Feature
The Checkpointing feature is disabled by default. To enable it, you can either use a command-line flag or edit your
settings.json
file.
Using the Command-Line Flag
You can enable checkpointing for the current session by using the
--checkpointing
flag when starting Qwen Code:
qwen
--checkpointing
Using the
settings.json
File
To enable checkpointing by default for all sessions, you need to edit your
settings.json
file.
Add the following key to your
settings.json
:
{
"general"
: {
"checkpointing"
: {
"enabled"
:
true
}
}
}
Using the
/restore
Command
Once enabled, checkpoints are created automatically. To manage them, you use the
/restore
command.
List Available Checkpoints
To see a list of all saved checkpoints for the current project, simply run:
/restore
The CLI will display a list of available checkpoint files. These file names are typically composed of a timestamp, the name of the file being modified, and the name of the tool that was about to be run (e.g.,
2025-06-22T10-00-00_000Z-my-file.txt-write_file
).
Restore a Specific Checkpoint
To restore your project to a specific checkpoint, use the checkpoint file from the list:
/restore <checkpoint_file>
For example:
/restore 2025-06-22T10-00-00_000Z-my-file.txt-write_file
After running the command, your files and conversation will be immediately restored to the state they were in when the checkpoint was created, and the original tool prompt will reappear.
Last updated on
May 18, 2026
Dual Output
MCP</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/checkpointing/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Extensions</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/extension/introduction/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/extension/introduction/</guid>
  <pubDate>Wed, 26 Jun 2024 00:00:00 +0000</pubDate>
  <category>Getting Started</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Extension
Introduction
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, subagents, skills and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extensions and plugins from
Gemini CLI Extensions Gallery
and
Claude Code Marketplace
can be directly installed into Qwen Code. This cross-platform comp...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Extension
Introduction
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, subagents, skills and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extensions and plugins from
Gemini CLI Extensions Gallery
and
Claude Code Marketplace
can be directly installed into Qwen Code. This cross-platform compatibility gives you access to a rich ecosystem of extensions and plugins, dramatically expanding Qwen Code’s capabilities without requiring extension authors to maintain separate versions.
Extension management
We offer a suite of extension management tools using both
qwen extensions
CLI commands and
/extensions
slash commands within the interactive CLI.
Runtime Extension Management (Slash Commands)
You can manage extensions at runtime within the interactive CLI using
/extensions
slash commands. These commands support hot-reloading, meaning changes take effect immediately without restarting the application.
Command
Description
/extensions
or
/extensions manage
Manage all installed extensions
/extensions install <source>
Install an extension from a git URL, local path, npm package, or marketplace
/extensions explore [source]
Open extensions source page(Gemini or ClaudeCode) in your browser
CLI Extension Management
You can also manage extensions using
qwen extensions
CLI commands. Note that changes made via CLI commands will be reflected in active CLI sessions on restart.
Installing an extension
You can install an extension using
qwen extensions install
from multiple sources:
From Claude Code Marketplace
Qwen Code also supports plugins from the
Claude Code Marketplace
. Install from a marketplace and choose a plugin:
qwen
extensions
install
<
marketplace-nam
e
>
# or
qwen
extensions
install
<
marketplace-github-ur
l
>
If you want to install a specific plugin, you can use the format with plugin name:
qwen
extensions
install
<
marketplace-nam
e
>
:
<
plugin-nam
e
>
# or
qwen
extensions
install
<
marketplace-github-ur
l
>
:
<
plugin-nam
e
>
For example, to install the
prompts.chat
plugin from the
f/awesome-chatgpt-prompts
marketplace:
qwen
extensions
install
f/awesome-chatgpt-prompts:prompts.chat
# or
qwen
extensions
install
https://github.com/f/awesome-chatgpt-prompts:prompts.chat
Claude plugins are automatically converted to Qwen Code format during installation:
claude-plugin.json
is converted to
qwen-extension.json
Agent configurations are converted to Qwen subagent format
Skill configurations are converted to Qwen skill format
Tool mappings are automatically handled
You can quickly browse available extensions from different marketplaces using the
/extensions explore
command:
# Open Gemini CLI Extensions marketplace
/extensions
explore
Gemini
# Open Claude Code marketplace
/extensions
explore
ClaudeCode
This command opens the respective marketplace in your default browser, allowing you to discover new extensions to enhance your Qwen Code experience.
Cross-Platform Compatibility
: This allows you to leverage the rich extension ecosystems from both Gemini CLI and Claude Code, dramatically expanding the available functionality for Qwen Code users.
From Gemini CLI Extensions
Qwen Code fully supports extensions from the
Gemini CLI Extensions Gallery
. Simply install them using the git URL:
qwen
extensions
install
<
gemini-cli-extension-github-ur
l
>
# or
qwen
extensions
install
<
owne
r
>
/
<
rep
o
>
Gemini extensions are automatically converted to Qwen Code format during installation:
gemini-extension.json
is converted to
qwen-extension.json
TOML command files are automatically migrated to Markdown format
MCP servers, context files, and settings are preserved
From npm Registry
Qwen Code supports installing extensions from npm registries using scoped package names. This is ideal for teams with private registries that already have auth, versioning, and publishing infrastructure in place.
# Install the latest version
qwen
extensions
install
@scope/my-extension
# Install a specific version
qwen
extensions
install
@scope/my-extension@1.2.0
# Install from a custom registry
qwen
extensions
install
@scope/my-extension
--registry
https://your-registry.com
Only scoped packages (
@scope/package-name
) are supported to avoid ambiguity with the
owner/repo
GitHub shorthand format.
Registry resolution
follows this priority:
--registry
CLI flag (explicit override)
Scoped registry from
.npmrc
(e.g.
@scope:registry=https://...
)
Default registry from
.npmrc
Fallback:
https://registry.npmjs.org/
Authentication
is handled automatically via the
NPM_TOKEN
environment variable or registry-specific
_authToken
entries in your
.npmrc
file.
Note:
npm extensions must include a
qwen-extension.json
file at the package root, following the same format as any other Qwen Code extension. See
Extension Releasing
for packaging details.
From Git Repository
qwen
extensions
install
https://github.com/github/github-mcp-server
This will install the github mcp server extension.
From Local Path
qwen
extensions
install
/path/to/your/extension
Note that we create a copy of the installed extension, so you will need to run
qwen extensions update
to pull in changes from both locally-defined extensions and those on GitHub.
Uninstalling an extension
To uninstall, run
qwen extensions uninstall extension-name
, so, in the case of the install example:
qwen extensions uninstall qwen-cli-security
Disabling an extension
Extensions are, by default, enabled across all workspaces. You can disable an extension entirely or for specific workspace.
For example,
qwen extensions disable extension-name
will disable the extension at the user level, so it will be disabled everywhere.
qwen extensions disable extension-name --scope=workspace
will only disable the extension in the current workspace.
Enabling an extension
You can enable extensions using
qwen extensions enable extension-name
. You can also enable an extension for a specific workspace using
qwen extensions enable extension-name --scope=workspace
from within that workspace.
This is useful if you have an extension disabled at the top-level and only enabled in specific places.
Updating an extension
For extensions installed from a local path, a git repository, or an npm registry, you can explicitly update to the latest version with
qwen extensions update extension-name
. For npm extensions installed without a version pin (e.g.
@scope/pkg
), updates check the
latest
dist-tag. For those installed with a specific dist-tag (e.g.
@scope/pkg@beta
), updates track that tag. Extensions pinned to an exact version (e.g.
@scope/pkg@1.2.0
) are always considered up-to-date.
You can update all extensions with:
qwen extensions update --all
How it works
On startup, Qwen Code looks for extensions in
<home>/.qwen/extensions
Extensions exist as a directory that contains a
qwen-extension.json
file. For example:
<home>/.qwen/extensions/my-extension/qwen-extension.json
qwen-extension.json
The
qwen-extension.json
file contains the configuration for the extension. The file has the following structure:
{
"name"
:
"my-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"my-server"
: {
"command"
:
"node my-server.js"
}
},
"channels"
: {
"my-platform"
: {
"entry"
:
"dist/index.js"
,
"displayName"
:
"My Platform Channel"
}
},
"contextFileName"
:
"QWEN.md"
,
"commands"
:
"commands"
,
"skills"
:
"skills"
,
"agents"
:
"agents"
,
"settings"
: [
{
"name"
:
"API Key"
,
"description"
:
"Your API key for the service"
,
"envVar"
:
"MY_API_KEY"
,
"sensitive"
:
true
}
]
}
name
: The name of the extension. This is used to uniquely identify the extension and for conflict resolution when extension commands have the same name as user or project commands. The name should be lowercase or numbers and use dashes instead of underscores or spaces. This is how users will refer to your extension in the CLI. Note that we expect this name to match the extension directory name.
version
: The version of the extension.
mcpServers
: A map of MCP servers to configure. The key is the name of the server, and the value is the server configuration. These servers will be loaded on startup just like MCP servers configured in a
settings.json
file
. If both an extension and a
settings.json
file configure an MCP server with the same name, the server defined in the
settings.json
file takes precedence.
Note that all MCP server configuration options are supported except for
trust
.
channels
: A map of custom channel adapters. The key is the channel type name, and the value has an
entry
(path to compiled JS entry point) and optional
displayName
. The entry point must export a
plugin
object conforming to the
ChannelPlugin
interface. See
Channel Plugins
for a full guide.
contextFileName
: The name of the file that contains the context for the extension. This will be used to load the context from the extension directory. If this property is not used but a
QWEN.md
file is present in your extension directory, then that file will be loaded.
commands
: The directory containing custom commands (default:
commands
). Commands are
.md
files that define prompts.
skills
: The directory containing custom skills (default:
skills
). Skills are discovered automatically and become available via the
/skills
command.
agents
: The directory containing custom subagents (default:
agents
). Subagents are
.yaml
or
.md
files that define specialized AI assistants.
settings
: An array of settings that the extension requires. When installing, users will be prompted to provide values for these settings. The values are stored securely and passed to MCP servers as environment variables.
Each setting has the following properties:
name
: Display name for the setting
description
: A description of what this setting is used for
envVar
: The environment variable name that will be set
sensitive
: Boolean indicating if the value should be hidden (e.g., API keys, passwords)
Managing Extension Settings
Extensions can require configuration through settings (such as API keys or credentials). These settings can be managed using the
qwen extensions settings
CLI command:
Set a setting value:
qwen
extensions
settings
set
<
extension-nam
e
>
<
setting-nam
e
>
[--scope
user
|
workspace]
List all settings for an extension:
qwen
extensions
settings
list
<
extension-nam
e
>
View current values (user and workspace):
qwen
extensions
settings
show
<
extension-nam
e
>
<
setting-nam
e
>
Remove a setting value:
qwen
extensions
settings
unset
<
extension-nam
e
>
<
setting-nam
e
>
[--scope
user
|
workspace]
Settings can be configured at two levels:
User level
(default): Settings apply across all projects (
~/.qwen/.env
)
Workspace level
: Settings apply only to the current project (
.qwen/.env
)
Workspace settings take precedence over user settings. Sensitive settings are stored securely and never displayed in plain text.
When Qwen Code starts, it loads all the extensions and merges their configurations. If there are any conflicts, the workspace configuration takes precedence.
Custom commands
Extensions can provide
custom commands
by placing Markdown files in a
commands/
subdirectory within the extension directory. These commands follow the same format as user and project custom commands and use standard naming conventions.
Note:
The command format has been updated from TOML to Markdown. TOML files are deprecated but still supported. You can migrate existing TOML commands using the automatic migration prompt that appears when TOML files are detected.
Example
An extension named
gcp
with the following structure:
.qwen/extensions/gcp/
├── qwen-extension.json
└── commands/
├── deploy.md
└── gcs/
└── sync.md
Would provide these commands:
/deploy
- Shows as
[gcp] Custom command from deploy.md
in help
/gcs:sync
- Shows as
[gcp] Custom command from sync.md
in help
Custom skills
Extensions can provide custom skills by placing skill files in a
skills/
subdirectory within the extension directory. Each skill should have a
SKILL.md
file with YAML frontmatter defining the skill’s name and description.
Example
.qwen/extensions/my-extension/
├── qwen-extension.json
└── skills/
└── pdf-processor/
└── SKILL.md
The skill will be available via the
/skills
command when the extension is active.
Custom subagents
Extensions can provide custom subagents by placing agent configuration files in an
agents/
subdirectory within the extension directory. Agents are defined using YAML or Markdown files.
Example
.qwen/extensions/my-extension/
├── qwen-extension.json
└── agents/
└── testing-expert.yaml
Extension subagents appear in the subagent manager dialog under “Extension Agents” section.
Conflict resolution
Extension commands have the lowest precedence. When a conflict occurs with user or project commands:
No conflict
: Extension command uses its natural name (e.g.,
/deploy
)
With conflict
: Extension command is renamed with the extension prefix (e.g.,
/gcp.deploy
)
For example, if both a user and the
gcp
extension define a
deploy
command:
/deploy
- Executes the user’s deploy command
/gcp.deploy
- Executes the extension’s deploy command (marked with
[gcp]
tag)
Variables
Qwen Code extensions allow variable substitution in
qwen-extension.json
. This can be useful if e.g., you need the current directory to run an MCP server using
"cwd": "${extensionPath}${/}run.ts"
.
Supported variables:
variable
description
${extensionPath}
The fully-qualified path of the extension in the user’s filesystem e.g., ‘/Users/username/.qwen/extensions/example-extension’. This will not unwrap symlinks.
${workspacePath}
The fully-qualified path of the current workspace.
${/} or ${pathSeparator}
The path separator (differs per OS).
Last updated on
May 18, 2026
Model Providers
Keyboard Shortcuts</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/extension/introduction/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Extension
Introduction
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, subagents, skills and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extensions and plugins from
Gemini CLI Extensions Gallery
and
Claude Code Marketplace
can be directly installed into Qwen Code. This cross-platform comp...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Extension
Introduction
Qwen Code Extensions
Qwen Code extensions package prompts, MCP servers, subagents, skills and custom commands into a familiar and user-friendly format. With extensions, you can expand the capabilities of Qwen Code and share those capabilities with others. They are designed to be easily installable and shareable.
Extensions and plugins from
Gemini CLI Extensions Gallery
and
Claude Code Marketplace
can be directly installed into Qwen Code. This cross-platform compatibility gives you access to a rich ecosystem of extensions and plugins, dramatically expanding Qwen Code’s capabilities without requiring extension authors to maintain separate versions.
Extension management
We offer a suite of extension management tools using both
qwen extensions
CLI commands and
/extensions
slash commands within the interactive CLI.
Runtime Extension Management (Slash Commands)
You can manage extensions at runtime within the interactive CLI using
/extensions
slash commands. These commands support hot-reloading, meaning changes take effect immediately without restarting the application.
Command
Description
/extensions
or
/extensions manage
Manage all installed extensions
/extensions install <source>
Install an extension from a git URL, local path, npm package, or marketplace
/extensions explore [source]
Open extensions source page(Gemini or ClaudeCode) in your browser
CLI Extension Management
You can also manage extensions using
qwen extensions
CLI commands. Note that changes made via CLI commands will be reflected in active CLI sessions on restart.
Installing an extension
You can install an extension using
qwen extensions install
from multiple sources:
From Claude Code Marketplace
Qwen Code also supports plugins from the
Claude Code Marketplace
. Install from a marketplace and choose a plugin:
qwen
extensions
install
<
marketplace-nam
e
>
# or
qwen
extensions
install
<
marketplace-github-ur
l
>
If you want to install a specific plugin, you can use the format with plugin name:
qwen
extensions
install
<
marketplace-nam
e
>
:
<
plugin-nam
e
>
# or
qwen
extensions
install
<
marketplace-github-ur
l
>
:
<
plugin-nam
e
>
For example, to install the
prompts.chat
plugin from the
f/awesome-chatgpt-prompts
marketplace:
qwen
extensions
install
f/awesome-chatgpt-prompts:prompts.chat
# or
qwen
extensions
install
https://github.com/f/awesome-chatgpt-prompts:prompts.chat
Claude plugins are automatically converted to Qwen Code format during installation:
claude-plugin.json
is converted to
qwen-extension.json
Agent configurations are converted to Qwen subagent format
Skill configurations are converted to Qwen skill format
Tool mappings are automatically handled
You can quickly browse available extensions from different marketplaces using the
/extensions explore
command:
# Open Gemini CLI Extensions marketplace
/extensions
explore
Gemini
# Open Claude Code marketplace
/extensions
explore
ClaudeCode
This command opens the respective marketplace in your default browser, allowing you to discover new extensions to enhance your Qwen Code experience.
Cross-Platform Compatibility
: This allows you to leverage the rich extension ecosystems from both Gemini CLI and Claude Code, dramatically expanding the available functionality for Qwen Code users.
From Gemini CLI Extensions
Qwen Code fully supports extensions from the
Gemini CLI Extensions Gallery
. Simply install them using the git URL:
qwen
extensions
install
<
gemini-cli-extension-github-ur
l
>
# or
qwen
extensions
install
<
owne
r
>
/
<
rep
o
>
Gemini extensions are automatically converted to Qwen Code format during installation:
gemini-extension.json
is converted to
qwen-extension.json
TOML command files are automatically migrated to Markdown format
MCP servers, context files, and settings are preserved
From npm Registry
Qwen Code supports installing extensions from npm registries using scoped package names. This is ideal for teams with private registries that already have auth, versioning, and publishing infrastructure in place.
# Install the latest version
qwen
extensions
install
@scope/my-extension
# Install a specific version
qwen
extensions
install
@scope/my-extension@1.2.0
# Install from a custom registry
qwen
extensions
install
@scope/my-extension
--registry
https://your-registry.com
Only scoped packages (
@scope/package-name
) are supported to avoid ambiguity with the
owner/repo
GitHub shorthand format.
Registry resolution
follows this priority:
--registry
CLI flag (explicit override)
Scoped registry from
.npmrc
(e.g.
@scope:registry=https://...
)
Default registry from
.npmrc
Fallback:
https://registry.npmjs.org/
Authentication
is handled automatically via the
NPM_TOKEN
environment variable or registry-specific
_authToken
entries in your
.npmrc
file.
Note:
npm extensions must include a
qwen-extension.json
file at the package root, following the same format as any other Qwen Code extension. See
Extension Releasing
for packaging details.
From Git Repository
qwen
extensions
install
https://github.com/github/github-mcp-server
This will install the github mcp server extension.
From Local Path
qwen
extensions
install
/path/to/your/extension
Note that we create a copy of the installed extension, so you will need to run
qwen extensions update
to pull in changes from both locally-defined extensions and those on GitHub.
Uninstalling an extension
To uninstall, run
qwen extensions uninstall extension-name
, so, in the case of the install example:
qwen extensions uninstall qwen-cli-security
Disabling an extension
Extensions are, by default, enabled across all workspaces. You can disable an extension entirely or for specific workspace.
For example,
qwen extensions disable extension-name
will disable the extension at the user level, so it will be disabled everywhere.
qwen extensions disable extension-name --scope=workspace
will only disable the extension in the current workspace.
Enabling an extension
You can enable extensions using
qwen extensions enable extension-name
. You can also enable an extension for a specific workspace using
qwen extensions enable extension-name --scope=workspace
from within that workspace.
This is useful if you have an extension disabled at the top-level and only enabled in specific places.
Updating an extension
For extensions installed from a local path, a git repository, or an npm registry, you can explicitly update to the latest version with
qwen extensions update extension-name
. For npm extensions installed without a version pin (e.g.
@scope/pkg
), updates check the
latest
dist-tag. For those installed with a specific dist-tag (e.g.
@scope/pkg@beta
), updates track that tag. Extensions pinned to an exact version (e.g.
@scope/pkg@1.2.0
) are always considered up-to-date.
You can update all extensions with:
qwen extensions update --all
How it works
On startup, Qwen Code looks for extensions in
<home>/.qwen/extensions
Extensions exist as a directory that contains a
qwen-extension.json
file. For example:
<home>/.qwen/extensions/my-extension/qwen-extension.json
qwen-extension.json
The
qwen-extension.json
file contains the configuration for the extension. The file has the following structure:
{
"name"
:
"my-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"my-server"
: {
"command"
:
"node my-server.js"
}
},
"channels"
: {
"my-platform"
: {
"entry"
:
"dist/index.js"
,
"displayName"
:
"My Platform Channel"
}
},
"contextFileName"
:
"QWEN.md"
,
"commands"
:
"commands"
,
"skills"
:
"skills"
,
"agents"
:
"agents"
,
"settings"
: [
{
"name"
:
"API Key"
,
"description"
:
"Your API key for the service"
,
"envVar"
:
"MY_API_KEY"
,
"sensitive"
:
true
}
]
}
name
: The name of the extension. This is used to uniquely identify the extension and for conflict resolution when extension commands have the same name as user or project commands. The name should be lowercase or numbers and use dashes instead of underscores or spaces. This is how users will refer to your extension in the CLI. Note that we expect this name to match the extension directory name.
version
: The version of the extension.
mcpServers
: A map of MCP servers to configure. The key is the name of the server, and the value is the server configuration. These servers will be loaded on startup just like MCP servers configured in a
settings.json
file
. If both an extension and a
settings.json
file configure an MCP server with the same name, the server defined in the
settings.json
file takes precedence.
Note that all MCP server configuration options are supported except for
trust
.
channels
: A map of custom channel adapters. The key is the channel type name, and the value has an
entry
(path to compiled JS entry point) and optional
displayName
. The entry point must export a
plugin
object conforming to the
ChannelPlugin
interface. See
Channel Plugins
for a full guide.
contextFileName
: The name of the file that contains the context for the extension. This will be used to load the context from the extension directory. If this property is not used but a
QWEN.md
file is present in your extension directory, then that file will be loaded.
commands
: The directory containing custom commands (default:
commands
). Commands are
.md
files that define prompts.
skills
: The directory containing custom skills (default:
skills
). Skills are discovered automatically and become available via the
/skills
command.
agents
: The directory containing custom subagents (default:
agents
). Subagents are
.yaml
or
.md
files that define specialized AI assistants.
settings
: An array of settings that the extension requires. When installing, users will be prompted to provide values for these settings. The values are stored securely and passed to MCP servers as environment variables.
Each setting has the following properties:
name
: Display name for the setting
description
: A description of what this setting is used for
envVar
: The environment variable name that will be set
sensitive
: Boolean indicating if the value should be hidden (e.g., API keys, passwords)
Managing Extension Settings
Extensions can require configuration through settings (such as API keys or credentials). These settings can be managed using the
qwen extensions settings
CLI command:
Set a setting value:
qwen
extensions
settings
set
<
extension-nam
e
>
<
setting-nam
e
>
[--scope
user
|
workspace]
List all settings for an extension:
qwen
extensions
settings
list
<
extension-nam
e
>
View current values (user and workspace):
qwen
extensions
settings
show
<
extension-nam
e
>
<
setting-nam
e
>
Remove a setting value:
qwen
extensions
settings
unset
<
extension-nam
e
>
<
setting-nam
e
>
[--scope
user
|
workspace]
Settings can be configured at two levels:
User level
(default): Settings apply across all projects (
~/.qwen/.env
)
Workspace level
: Settings apply only to the current project (
.qwen/.env
)
Workspace settings take precedence over user settings. Sensitive settings are stored securely and never displayed in plain text.
When Qwen Code starts, it loads all the extensions and merges their configurations. If there are any conflicts, the workspace configuration takes precedence.
Custom commands
Extensions can provide
custom commands
by placing Markdown files in a
commands/
subdirectory within the extension directory. These commands follow the same format as user and project custom commands and use standard naming conventions.
Note:
The command format has been updated from TOML to Markdown. TOML files are deprecated but still supported. You can migrate existing TOML commands using the automatic migration prompt that appears when TOML files are detected.
Example
An extension named
gcp
with the following structure:
.qwen/extensions/gcp/
├── qwen-extension.json
└── commands/
├── deploy.md
└── gcs/
└── sync.md
Would provide these commands:
/deploy
- Shows as
[gcp] Custom command from deploy.md
in help
/gcs:sync
- Shows as
[gcp] Custom command from sync.md
in help
Custom skills
Extensions can provide custom skills by placing skill files in a
skills/
subdirectory within the extension directory. Each skill should have a
SKILL.md
file with YAML frontmatter defining the skill’s name and description.
Example
.qwen/extensions/my-extension/
├── qwen-extension.json
└── skills/
└── pdf-processor/
└── SKILL.md
The skill will be available via the
/skills
command when the extension is active.
Custom subagents
Extensions can provide custom subagents by placing agent configuration files in an
agents/
subdirectory within the extension directory. Agents are defined using YAML or Markdown files.
Example
.qwen/extensions/my-extension/
├── qwen-extension.json
└── agents/
└── testing-expert.yaml
Extension subagents appear in the subagent manager dialog under “Extension Agents” section.
Conflict resolution
Extension commands have the lowest precedence. When a conflict occurs with user or project commands:
No conflict
: Extension command uses its natural name (e.g.,
/deploy
)
With conflict
: Extension command is renamed with the extension prefix (e.g.,
/gcp.deploy
)
For example, if both a user and the
gcp
extension define a
deploy
command:
/deploy
- Executes the user’s deploy command
/gcp.deploy
- Executes the extension’s deploy command (marked with
[gcp]
tag)
Variables
Qwen Code extensions allow variable substitution in
qwen-extension.json
. This can be useful if e.g., you need the current directory to run an MCP server using
"cwd": "${extensionPath}${/}run.ts"
.
Supported variables:
variable
description
${extensionPath}
The fully-qualified path of the extension in the user’s filesystem e.g., ‘/Users/username/.qwen/extensions/example-extension’. This will not unwrap symlinks.
${workspacePath}
The fully-qualified path of the current workspace.
${/} or ${pathSeparator}
The path separator (differs per OS).
Last updated on
May 18, 2026
Model Providers
Keyboard Shortcuts</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/extension/introduction/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Memory Tool ( save_memory )</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/memory/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/memory/</guid>
  <pubDate>Wed, 26 Jun 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Memory Tool (save_memory)
Memory Tool (
save_memory
)
This document describes the
save_memory
tool for Qwen Code.
Description
Use
save_memory
to save and recall information across your Qwen Code sessions. With
save_memory
, you can direct the CLI to remember key details across sessions, providing personalized and directed assistance.
Arguments
save_memory
takes one argument:
fact
(string, required): The specific fact or piece of information to remember. This should be a cle...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Memory Tool (save_memory)
Memory Tool (
save_memory
)
This document describes the
save_memory
tool for Qwen Code.
Description
Use
save_memory
to save and recall information across your Qwen Code sessions. With
save_memory
, you can direct the CLI to remember key details across sessions, providing personalized and directed assistance.
Arguments
save_memory
takes one argument:
fact
(string, required): The specific fact or piece of information to remember. This should be a clear, self-contained statement written in natural language.
How to use
save_memory
with Qwen Code
The tool appends the provided
fact
to your context file in the user’s home directory (
~/.qwen/QWEN.md
by default). This filename can be configured via
contextFileName
.
Once added, the facts are stored under a
## Qwen Added Memories
section. This file is loaded as context in subsequent sessions, allowing the CLI to recall the saved information.
Usage:
save_memory(fact="Your fact here.")
save_memory
examples
Remember a user preference:
save_memory(fact="My preferred programming language is Python.")
Store a project-specific detail:
save_memory(fact="The project I'm currently working on is called 'qwen-code'.")
Important notes
General usage:
This tool should be used for concise, important facts. It is not intended for storing large amounts of data or conversational history.
Memory file:
The memory file is a plain text Markdown file, so you can view and edit it manually if needed.
Last updated on
May 18, 2026
Sandboxing
Example Proxy Script</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/memory/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Memory Tool (save_memory)
Memory Tool (
save_memory
)
This document describes the
save_memory
tool for Qwen Code.
Description
Use
save_memory
to save and recall information across your Qwen Code sessions. With
save_memory
, you can direct the CLI to remember key details across sessions, providing personalized and directed assistance.
Arguments
save_memory
takes one argument:
fact
(string, required): The specific fact or piece of information to remember. This should be a cle...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Memory Tool (save_memory)
Memory Tool (
save_memory
)
This document describes the
save_memory
tool for Qwen Code.
Description
Use
save_memory
to save and recall information across your Qwen Code sessions. With
save_memory
, you can direct the CLI to remember key details across sessions, providing personalized and directed assistance.
Arguments
save_memory
takes one argument:
fact
(string, required): The specific fact or piece of information to remember. This should be a clear, self-contained statement written in natural language.
How to use
save_memory
with Qwen Code
The tool appends the provided
fact
to your context file in the user’s home directory (
~/.qwen/QWEN.md
by default). This filename can be configured via
contextFileName
.
Once added, the facts are stored under a
## Qwen Added Memories
section. This file is loaded as context in subsequent sessions, allowing the CLI to recall the saved information.
Usage:
save_memory(fact="Your fact here.")
save_memory
examples
Remember a user preference:
save_memory(fact="My preferred programming language is Python.")
Store a project-specific detail:
save_memory(fact="The project I'm currently working on is called 'qwen-code'.")
Important notes
General usage:
This tool should be used for concise, important facts. It is not intended for storing large amounts of data or conversational history.
Memory file:
The memory file is a plain text Markdown file, so you can view and edit it manually if needed.
Last updated on
May 18, 2026
Sandboxing
Example Proxy Script</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/memory/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Internationalization (i18n) &amp; Language</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/language/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/language/</guid>
  <pubDate>Tue, 25 Jun 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
i18n
Internationalization (i18n) &amp; Language
Qwen Code is built for multilingual workflows: it supports UI localization (i18n/l10n) in the CLI, lets you choose the assistant output language, and allows custom UI language packs.
Overview
From a user point of view, Qwen Code’s “internationalization” spans multiple layers:
Capability / Setting
What it controls
Where stored
/language ui
Terminal UI text (menus, system messages, prompts)
~/.qwen/settings.json
/language output
Langu...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
i18n
Internationalization (i18n) & Language
Qwen Code is built for multilingual workflows: it supports UI localization (i18n/l10n) in the CLI, lets you choose the assistant output language, and allows custom UI language packs.
Overview
From a user point of view, Qwen Code’s “internationalization” spans multiple layers:
Capability / Setting
What it controls
Where stored
/language ui
Terminal UI text (menus, system messages, prompts)
~/.qwen/settings.json
/language output
Language the AI responds in (an output preference, not UI translation)
~/.qwen/output-language.md
Custom UI language packs
Overrides/extends built-in UI translations
~/.qwen/locales/*.js
UI Language
This is the CLI’s UI localization layer (i18n/l10n): it controls the language of menus, prompts, and system messages.
Setting the UI Language
Use the
/language ui
command:
/language
ui
zh-CN
# Chinese
/language
ui
en-US
# English
/language
ui
ru-RU
# Russian
/language
ui
de-DE
# German
/language
ui
ja-JP
# Japanese
Aliases are also supported:
/language
ui
zh
# Chinese
/language
ui
en
# English
/language
ui
ru
# Russian
/language
ui
de
# German
/language
ui
ja
# Japanese
Auto-detection
On first startup, Qwen Code detects your system locale and sets the UI language automatically.
Detection priority:
QWEN_CODE_LANG
environment variable
LANG
environment variable
System locale via JavaScript Intl API
Default: English
LLM Output Language
The LLM output language controls what language the AI assistant responds in, regardless of what language you type your questions in.
How It Works
The LLM output language is controlled by a rule file at
~/.qwen/output-language.md
. This file is automatically included in the LLM’s context during startup, instructing it to respond in the specified language.
Auto-detection
On first startup, if no
output-language.md
file exists, Qwen Code automatically creates one based on your system locale. For example:
System locale
zh
creates a rule for Chinese responses
System locale
en
creates a rule for English responses
System locale
ru
creates a rule for Russian responses
System locale
de
creates a rule for German responses
System locale
ja
creates a rule for Japanese responses
Manual Setting
Use
/language output <language>
to change:
/language
output
Chinese
/language
output
English
/language
output
Japanese
/language
output
German
Any language name works. The LLM will be instructed to respond in that language.
Note
After changing the output language, restart Qwen Code for the change to take effect.
File Location
~/.qwen/output-language.md
Configuration
Via Settings Dialog
Run
/settings
Find “Language” under General
Select your preferred UI language
Via Environment Variable
export
QWEN_CODE_LANG
=
zh
This influences auto-detection on first startup (if you haven’t set a UI language and no
output-language.md
file exists yet).
Custom Language Packs
For UI translations, you can create custom language packs in
~/.qwen/locales/
:
Example:
~/.qwen/locales/es.js
for Spanish
Example:
~/.qwen/locales/fr.js
for French
User directory takes precedence over built-in translations.
Tip
Contributions are welcome! If you’d like to improve built-in translations or add new languages.
For a concrete example, see
PR #1238: feat(i18n): add Russian language support
.
Language Pack Format
// ~/.qwen/locales/es.js
export
default
{
Hello:
'Hola'
,
Settings:
'Configuracion'
,
// ... more translations
};
Related Commands
/language
- Show current language settings
/language ui [lang]
- Set UI language
/language output <language>
- Set LLM output language
/settings
- Open settings dialog
Last updated on
May 18, 2026
Sandboxing
Overview</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/language/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
i18n
Internationalization (i18n) &amp; Language
Qwen Code is built for multilingual workflows: it supports UI localization (i18n/l10n) in the CLI, lets you choose the assistant output language, and allows custom UI language packs.
Overview
From a user point of view, Qwen Code’s “internationalization” spans multiple layers:
Capability / Setting
What it controls
Where stored
/language ui
Terminal UI text (menus, system messages, prompts)
~/.qwen/settings.json
/language output
Langu...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
i18n
Internationalization (i18n) & Language
Qwen Code is built for multilingual workflows: it supports UI localization (i18n/l10n) in the CLI, lets you choose the assistant output language, and allows custom UI language packs.
Overview
From a user point of view, Qwen Code’s “internationalization” spans multiple layers:
Capability / Setting
What it controls
Where stored
/language ui
Terminal UI text (menus, system messages, prompts)
~/.qwen/settings.json
/language output
Language the AI responds in (an output preference, not UI translation)
~/.qwen/output-language.md
Custom UI language packs
Overrides/extends built-in UI translations
~/.qwen/locales/*.js
UI Language
This is the CLI’s UI localization layer (i18n/l10n): it controls the language of menus, prompts, and system messages.
Setting the UI Language
Use the
/language ui
command:
/language
ui
zh-CN
# Chinese
/language
ui
en-US
# English
/language
ui
ru-RU
# Russian
/language
ui
de-DE
# German
/language
ui
ja-JP
# Japanese
Aliases are also supported:
/language
ui
zh
# Chinese
/language
ui
en
# English
/language
ui
ru
# Russian
/language
ui
de
# German
/language
ui
ja
# Japanese
Auto-detection
On first startup, Qwen Code detects your system locale and sets the UI language automatically.
Detection priority:
QWEN_CODE_LANG
environment variable
LANG
environment variable
System locale via JavaScript Intl API
Default: English
LLM Output Language
The LLM output language controls what language the AI assistant responds in, regardless of what language you type your questions in.
How It Works
The LLM output language is controlled by a rule file at
~/.qwen/output-language.md
. This file is automatically included in the LLM’s context during startup, instructing it to respond in the specified language.
Auto-detection
On first startup, if no
output-language.md
file exists, Qwen Code automatically creates one based on your system locale. For example:
System locale
zh
creates a rule for Chinese responses
System locale
en
creates a rule for English responses
System locale
ru
creates a rule for Russian responses
System locale
de
creates a rule for German responses
System locale
ja
creates a rule for Japanese responses
Manual Setting
Use
/language output <language>
to change:
/language
output
Chinese
/language
output
English
/language
output
Japanese
/language
output
German
Any language name works. The LLM will be instructed to respond in that language.
Note
After changing the output language, restart Qwen Code for the change to take effect.
File Location
~/.qwen/output-language.md
Configuration
Via Settings Dialog
Run
/settings
Find “Language” under General
Select your preferred UI language
Via Environment Variable
export
QWEN_CODE_LANG
=
zh
This influences auto-detection on first startup (if you haven’t set a UI language and no
output-language.md
file exists yet).
Custom Language Packs
For UI translations, you can create custom language packs in
~/.qwen/locales/
:
Example:
~/.qwen/locales/es.js
for Spanish
Example:
~/.qwen/locales/fr.js
for French
User directory takes precedence over built-in translations.
Tip
Contributions are welcome! If you’d like to improve built-in translations or add new languages.
For a concrete example, see
PR #1238: feat(i18n): add Russian language support
.
Language Pack Format
// ~/.qwen/locales/es.js
export
default
{
Hello:
'Hola'
,
Settings:
'Configuracion'
,
// ... more translations
};
Related Commands
/language
- Show current language settings
/language ui [lang]
- Set UI language
/language output <language>
- Set LLM output language
/settings
- Open settings dialog
Last updated on
May 18, 2026
Sandboxing
Overview</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/language/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Token Caching and Cost Optimization</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/token-caching/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/token-caching/</guid>
  <pubDate>Tue, 25 Jun 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Token Caching
Token Caching and Cost Optimization
Qwen Code automatically optimizes API costs through token caching when using API key authentication. This feature stores frequently used content like system instructions and conversation history to reduce the number of tokens processed in subsequent requests.
How It Benefits You
Cost reduction
: Less tokens mean lower API costs
Faster responses
: Cached content is retrieved more quickly
Automatic optimization
: No configuratio...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Token Caching
Token Caching and Cost Optimization
Qwen Code automatically optimizes API costs through token caching when using API key authentication. This feature stores frequently used content like system instructions and conversation history to reduce the number of tokens processed in subsequent requests.
How It Benefits You
Cost reduction
: Less tokens mean lower API costs
Faster responses
: Cached content is retrieved more quickly
Automatic optimization
: No configuration needed - it works behind the scenes
Token caching is available for
API key users (Qwen API key, OpenAI-compatible providers)
Monitoring Your Savings
Use the
/stats
command to see your cached token savings:
When active, the stats display shows how many tokens were served from cache
You’ll see both the absolute number and percentage of cached tokens
Example: “10,500 (90.4%) of input tokens were served from the cache, reducing costs.”
This information is only displayed when cached tokens are being used, which occurs with API key authentication but not with OAuth authentication.
Example Stats Display
The above image shows an example of the
/stats
command output, highlighting the cached token savings information.
Last updated on
May 18, 2026
LSP (Language Server Protocol)
Sandboxing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/token-caching/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Token Caching
Token Caching and Cost Optimization
Qwen Code automatically optimizes API costs through token caching when using API key authentication. This feature stores frequently used content like system instructions and conversation history to reduce the number of tokens processed in subsequent requests.
How It Benefits You
Cost reduction
: Less tokens mean lower API costs
Faster responses
: Cached content is retrieved more quickly
Automatic optimization
: No configuratio...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Token Caching
Token Caching and Cost Optimization
Qwen Code automatically optimizes API costs through token caching when using API key authentication. This feature stores frequently used content like system instructions and conversation history to reduce the number of tokens processed in subsequent requests.
How It Benefits You
Cost reduction
: Less tokens mean lower API costs
Faster responses
: Cached content is retrieved more quickly
Automatic optimization
: No configuration needed - it works behind the scenes
Token caching is available for
API key users (Qwen API key, OpenAI-compatible providers)
Monitoring Your Savings
Use the
/stats
command to see your cached token savings:
When active, the stats display shows how many tokens were served from cache
You’ll see both the absolute number and percentage of cached tokens
Example: “10,500 (90.4%) of input tokens were served from the cache, reducing costs.”
This information is only displayed when cached tokens are being used, which occurs with API key authentication but not with OAuth authentication.
Example Stats Display
The above image shows an example of the
/stats
command output, highlighting the cached token savings information.
Last updated on
May 18, 2026
LSP (Language Server Protocol)
Sandboxing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/token-caching/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Configuration</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/configuration/settings/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/configuration/settings/</guid>
  <pubDate>Sat, 15 Jun 2024 00:00:00 +0000</pubDate>
  <category>Configuration</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Settings
Qwen Code Configuration
Tip
Authentication / API keys:
Authentication (API Key, Alibaba Cloud Coding Plan) and auth-related environment variables (like
OPENAI_API_KEY
) are documented in
Authentication
.
Note
Note on New Configuration Format
: The format of the
settings.json
file has been updated to a new, more organized structure. The old format will be migrated automatically.
Qwen Code offers several ways to configure its behavior, including environment variab...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Settings
Qwen Code Configuration
Tip
Authentication / API keys:
Authentication (API Key, Alibaba Cloud Coding Plan) and auth-related environment variables (like
OPENAI_API_KEY
) are documented in
Authentication
.
Note
Note on New Configuration Format
: The format of the
settings.json
file has been updated to a new, more organized structure. The old format will be migrated automatically.
Qwen Code offers several ways to configure its behavior, including environment variables, command-line arguments, and settings files. This document outlines the different configuration methods and available settings.
Configuration layers
Configuration is applied in the following order of precedence (lower numbers are overridden by higher numbers):
Level
Configuration Source
Description
1
Default values
Hardcoded defaults within the application
2
System defaults file
System-wide default settings that can be overridden by other settings files
3
User settings file
Global settings for the current user
4
Project settings file
Project-specific settings
5
System settings file
System-wide settings that override all other settings files
6
Environment variables
System-wide or session-specific variables, potentially loaded from
.env
files
7
Command-line arguments
Values passed when launching the CLI
Settings files
Qwen Code uses JSON settings files for persistent configuration. There are four locations for these files:
File Type
Location
Scope
System defaults file
Linux:
/etc/qwen-code/system-defaults.json
Windows:
C:\ProgramData\qwen-code\system-defaults.json
macOS:
/Library/Application Support/QwenCode/system-defaults.json
The path can be overridden using the
QWEN_CODE_SYSTEM_DEFAULTS_PATH
environment variable.
Provides a base layer of system-wide default settings. These settings have the lowest precedence and are intended to be overridden by user, project, or system override settings.
User settings file
~/.qwen/settings.json
(where
~
is your home directory).
Applies to all Qwen Code sessions for the current user.
Project settings file
.qwen/settings.json
within your project’s root directory.
Applies only when running Qwen Code from that specific project. Project settings override user settings.
System settings file
Linux：
/etc/qwen-code/settings.json
Windows:
C:\ProgramData\qwen-code\settings.json
macOS:
/Library/Application Support/QwenCode/settings.json
The path can be overridden using the
QWEN_CODE_SYSTEM_SETTINGS_PATH
environment variable.
Applies to all Qwen Code sessions on the system, for all users. System settings override user and project settings. May be useful for system administrators at enterprises to have controls over users’ Qwen Code setups.
Note
Note on environment variables in settings:
String values within your
settings.json
files can reference environment variables using either
$VAR_NAME
or
${VAR_NAME}
syntax. These variables will be automatically resolved when the settings are loaded. For example, if you have an environment variable
MY_API_TOKEN
, you could use it in
settings.json
like this:
"apiKey": "$MY_API_TOKEN"
.
The
.qwen
directory in your project
In addition to a project settings file, a project’s
.qwen
directory can contain other project-specific files related to Qwen Code’s operation, such as:
Custom sandbox profiles
(e.g.
.qwen/sandbox-macos-custom.sb
,
.qwen/sandbox.Dockerfile
).
Agent Skills
under
.qwen/skills/
(each Skill is a directory containing a
SKILL.md
).
Configuration migration
Qwen Code automatically migrates legacy configuration settings to the new format. Old settings files are backed up before migration. The following settings have been renamed from negative (
disable*
) to positive (
enable*
) naming:
Old Setting
New Setting
Notes
disableAutoUpdate
+
disableUpdateNag
general.enableAutoUpdate
Consolidated into a single setting
disableLoadingPhrases
ui.accessibility.enableLoadingPhrases
disableFuzzySearch
context.fileFiltering.enableFuzzySearch
disableCacheControl
model.generationConfig.enableCacheControl
Note
Boolean value inversion:
When migrating, boolean values are inverted (e.g.,
disableAutoUpdate: true
becomes
enableAutoUpdate: false
).
Consolidation policy for
disableAutoUpdate
and
disableUpdateNag
When both legacy settings are present with different values, the migration follows this policy: if
either
disableAutoUpdate
or
disableUpdateNag
is
true
, then
enableAutoUpdate
becomes
false
:
disableAutoUpdate
disableUpdateNag
Migrated
enableAutoUpdate
false
false
true
false
true
false
true
false
false
true
true
false
Available settings in
settings.json
Settings are organized into categories. Most settings should be placed within their corresponding top-level category object in your
settings.json
file. A few compatibility settings, such as
proxy
, are top-level keys.
top-level
Setting
Type
Description
Default
proxy
string
Proxy URL for CLI HTTP requests. Precedence is
--proxy
>
proxy
in
settings.json
>
HTTPS_PROXY
/
https_proxy
/
HTTP_PROXY
/
http_proxy
environment variables.
undefined
general
Setting
Type
Description
Default
general.preferredEditor
string
The preferred editor to open files in.
undefined
general.vimMode
boolean
Enable Vim keybindings.
false
general.enableAutoUpdate
boolean
Enable automatic update checks and installations on startup.
true
general.showSessionRecap
boolean
Auto-show a one-line “where you left off” recap when returning to the terminal after being away. Off by default. Use
/recap
to trigger manually regardless of this setting.
false
general.sessionRecapAwayThresholdMinutes
number
Minutes the terminal must be blurred before an auto-recap fires on focus-in. Only used when
showSessionRecap
is enabled.
5
general.gitCoAuthor
boolean
Automatically add a Co-authored-by trailer to git commit messages when commits are made through Qwen Code.
true
general.checkpointing.enabled
boolean
Enable session checkpointing for recovery.
false
general.defaultFileEncoding
string
Default encoding for new files. Use
"utf-8"
(default) for UTF-8 without BOM, or
"utf-8-bom"
for UTF-8 with BOM. Only change this if your project specifically requires BOM.
"utf-8"
output
Setting
Type
Description
Default
Possible Values
output.format
string
The format of the CLI output.
"text"
"text"
,
"json"
ui
Setting
Type
Description
Default
ui.theme
string
The color theme for the UI. See
Themes
for available options.
undefined
ui.customThemes
object
Custom theme definitions.
{}
ui.statusLine
object
Custom status line configuration. A shell command whose output is shown in the footer’s left section. See
Status Line
.
undefined
ui.hideWindowTitle
boolean
Hide the window title bar.
false
ui.hideTips
boolean
Hide all tips (startup and post-response) in the UI. See
Contextual Tips
.
false
ui.hideBanner
boolean
Hide the application banner.
false
ui.hideFooter
boolean
Hide the footer from the UI.
false
ui.showMemoryUsage
boolean
Display memory usage information in the UI.
false
ui.showLineNumbers
boolean
Show line numbers in code blocks in the CLI output.
true
ui.showCitations
boolean
Show citations for generated text in the chat.
true
ui.compactMode
boolean
Hide tool output and thinking for a cleaner view. Toggle with
Ctrl+O
during a session or via the Settings dialog. Tool approval prompts are never hidden, even in compact mode. The setting persists across sessions.
false
ui.shellOutputMaxLines
number
Max number of shell output lines shown inline. Set to
0
to disable the cap and show full output. Hidden lines are surfaced via the
+N lines
indicator. Errors,
!
-prefix user-initiated commands, confirming tools, and focused embedded shells always show full output.
5
enableWelcomeBack
boolean
Show welcome back dialog when returning to a project with conversation history. When enabled, Qwen Code will automatically detect if you’re returning to a project with a previously generated project summary (
.qwen/PROJECT_SUMMARY.md
) and show a dialog allowing you to continue your previous conversation or start fresh. If you choose
Start new chat session
, that choice is remembered for the current project until the project summary changes. This feature integrates with the
/summary
command and quit confirmation dialog.
true
ui.accessibility.enableLoadingPhrases
boolean
Enable loading phrases (disable for accessibility).
true
ui.accessibility.screenReader
boolean
Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers.
false
ui.customWittyPhrases
array of strings
A list of custom phrases to display during loading states. When provided, the CLI will cycle through these phrases instead of the default ones.
[]
ui.enableFollowupSuggestions
boolean
Enable
followup suggestions
that predict what you want to type next after the model responds. Suggestions appear as ghost text and can be accepted with Tab, Enter, or Right Arrow.
true
ui.enableCacheSharing
boolean
Use cache-aware forked queries for suggestion generation. Reduces cost on providers that support prefix caching (experimental).
true
ui.enableSpeculation
boolean
Speculatively execute accepted suggestions before submission. Results appear instantly when you accept (experimental).
false
experimental.emitToolUseSummaries
boolean
Generate short LLM-based labels summarizing each tool-call batch. See
Tool-Use Summaries
. Requires
fastModel
to be configured; silently skipped otherwise. Can be overridden per-session with
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
or
=1
.
true
ide
Setting
Type
Description
Default
ide.enabled
boolean
Enable IDE integration mode.
false
ide.hasSeenNudge
boolean
Whether the user has seen the IDE integration nudge.
false
privacy
Setting
Type
Description
Default
privacy.usageStatisticsEnabled
boolean
Enable collection of usage statistics.
true
model
Setting
Type
Description
Default
model.name
string
The Qwen model to use for conversations.
undefined
model.maxSessionTurns
number
Maximum number of user/model/tool turns to keep in a session. -1 means unlimited.
-1
model.generationConfig
object
Advanced overrides passed to the underlying content generator. Supports request controls such as
timeout
,
maxRetries
,
enableCacheControl
,
splitToolMedia
(set
true
for strict OpenAI-compatible servers like LM Studio that reject non-text content on
role: "tool"
messages — splits media into a follow-up user message),
contextWindowSize
(override model’s context window size),
modalities
(override auto-detected input modalities),
customHeaders
(custom HTTP headers for API requests),
extra_body
(additional body parameters for OpenAI-compatible API requests only), and
reasoning
(
{ effort: 'low' | 'medium' | 'high' | 'max', budget_tokens?: number }
to control thinking intensity, or
false
to disable;
'max'
is a DeepSeek extension — see
Reasoning / thinking configuration
for per-provider behavior.
Note:
when
samplingParams
is set on an OpenAI-compatible provider, the pipeline ships those keys verbatim and the separate top-level
reasoning
field is dropped — put
reasoning_effort
inside
samplingParams
(or
extra_body
) instead in that case), along with fine-tuning knobs under
samplingParams
(for example
temperature
,
top_p
,
max_tokens
). Leave unset to rely on provider defaults.
undefined
model.chatCompression.contextPercentageThreshold
number
Sets the threshold for chat history compression as a percentage of the model’s total token limit. This is a value between 0 and 1 that applies to both automatic compression and the manual
/compress
command. For example, a value of
0.6
will trigger compression when the chat history exceeds 60% of the token limit. Use
0
to disable compression entirely.
0.7
model.skipNextSpeakerCheck
boolean
Skip the next speaker check.
false
model.skipLoopDetection
boolean
Disables loop detection checks. Loop detection prevents infinite loops in AI responses but can generate false positives that interrupt legitimate workflows. Enable this option if you experience frequent false positive loop detection interruptions.
false
model.skipStartupContext
boolean
Skips sending the startup workspace context (environment summary and acknowledgement) at the beginning of each session. Enable this if you prefer to provide context manually or want to save tokens on startup.
false
model.enableOpenAILogging
boolean
Enables logging of OpenAI API calls for debugging and analysis. When enabled, API requests and responses are logged to JSON files.
false
model.openAILoggingDir
string
Custom directory path for OpenAI API logs. If not specified, defaults to
logs/openai
in the current working directory. Supports absolute paths, relative paths (resolved from current working directory), and
~
expansion (home directory).
undefined
Example model.generationConfig:
{
"model"
: {
"generationConfig"
: {
"timeout"
:
60000
,
"contextWindowSize"
:
128000
,
"modalities"
: {
"image"
:
true
},
"enableCacheControl"
:
true
,
"customHeaders"
: {
"X-Client-Request-ID"
:
"req-123"
},
"extra_body"
: {
"enable_thinking"
:
true
},
"samplingParams"
: {
"temperature"
:
0.2
,
"top_p"
:
0.8
,
"max_tokens"
:
1024
}
}
}
}
max_tokens (adaptive output tokens):
When
samplingParams.max_tokens
is not set, Qwen Code uses an adaptive output token strategy to optimize GPU resource usage:
Requests start with a default limit of
8K
output tokens
If the response is truncated (the model hits the limit), Qwen Code automatically retries with
64K
tokens
The partial output is discarded and replaced with the full response from the retry
This is transparent to users — you may briefly see a retry indicator if escalation occurs. Since 99% of responses are under 5K tokens, the retry happens rarely (<1% of requests).
To override this behavior, either set
samplingParams.max_tokens
in your settings or use the
QWEN_CODE_MAX_OUTPUT_TOKENS
environment variable.
contextWindowSize:
Overrides the default context window size for the selected model. Qwen Code determines the context window using built-in defaults based on model name matching, with a constant fallback value. Use this setting when a provider’s effective context limit differs from Qwen Code’s default. This value defines the model’s assumed maximum context capacity, not a per-request token limit.
modalities:
Overrides the auto-detected input modalities for the selected model. Qwen Code automatically detects supported modalities (image, PDF, audio, video) based on model name pattern matching. Use this setting when the auto-detection is incorrect — for example, to enable
pdf
for a model that supports it but isn’t recognized. Format:
{ "image": true, "pdf": true, "audio": true, "video": true }
. Omit a key or set it to
false
for unsupported types.
customHeaders:
Allows you to add custom HTTP headers to all API requests. This is useful for request tracing, monitoring, API gateway routing, or when different models require different headers. If
customHeaders
is defined in
modelProviders[].generationConfig.customHeaders
, it will be used directly; otherwise, headers from
model.generationConfig.customHeaders
will be used. No merging occurs between the two levels.
The
extra_body
field allows you to add custom parameters to the request body sent to the API. This is useful for provider-specific options that are not covered by the standard configuration fields.
Note: This field is only supported for OpenAI-compatible providers (
openai
,
qwen-oauth
). It is ignored for Anthropic and Gemini providers.
If
extra_body
is defined in
modelProviders[].generationConfig.extra_body
, it will be used directly; otherwise, values from
model.generationConfig.extra_body
will be used.
model.openAILoggingDir examples:
"~/qwen-logs"
- Logs to
~/qwen-logs
directory
"./custom-logs"
- Logs to
./custom-logs
relative to current directory
"/tmp/openai-logs"
- Logs to absolute path
/tmp/openai-logs
fastModel
Setting
Type
Description
Default
fastModel
string
Model used for generating
prompt suggestions
and speculative execution. Leave empty to use the main model. A smaller/faster model (e.g.,
qwen3-coder-flash
) reduces latency and cost. Can also be set via
/model --fast
.
""
context
Setting
Type
Description
Default
context.fileName
string or array of strings
The name of the context file(s).
undefined
context.importFormat
string
The format to use when importing memory.
undefined
context.includeDirectories
array
Additional directories to include in the workspace context. Specifies an array of additional absolute or relative paths to include in the workspace context. Missing directories will be skipped with a warning by default. Paths can use
~
to refer to the user’s home directory. This setting can be combined with the
--include-directories
command-line flag.
[]
context.loadFromIncludeDirectories
boolean
Controls the behavior of the
/memory refresh
command. If set to
true
,
QWEN.md
files should be loaded from all directories that are added. If set to
false
,
QWEN.md
should only be loaded from the current directory.
false
context.fileFiltering.respectGitIgnore
boolean
Respect .gitignore files when searching.
true
context.fileFiltering.respectQwenIgnore
boolean
Respect .qwenignore files when searching.
true
context.fileFiltering.enableRecursiveFileSearch
boolean
Whether to enable searching recursively for filenames under the current tree when completing
@
prefixes in the prompt.
true
context.fileFiltering.enableFuzzySearch
boolean
When
true
, enables fuzzy search capabilities when searching for files. Set to
false
to improve performance on projects with a large number of files.
true
context.clearContextOnIdle.toolResultsThresholdMinutes
number
Minutes of inactivity before clearing old tool result content. Use
-1
to disable.
60
context.clearContextOnIdle.toolResultsNumToKeep
number
Number of most-recent compactable tool results to preserve when clearing. Floor at 1.
5
Troubleshooting File Search Performance
If you are experiencing performance issues with file searching (e.g., with
@
completions), especially in projects with a very large number of files, here are a few things you can try in order of recommendation:
Use
.qwenignore
:
Create a
.qwenignore
file in your project root to exclude directories that contain a large number of files that you don’t need to reference (e.g., build artifacts, logs,
node_modules
). Reducing the total number of files crawled is the most effective way to improve performance.
Disable Fuzzy Search:
If ignoring files is not enough, you can disable fuzzy search by setting
enableFuzzySearch
to
false
in your
settings.json
file. This will use a simpler, non-fuzzy matching algorithm, which can be faster.
Disable Recursive File Search:
As a last resort, you can disable recursive file search entirely by setting
enableRecursiveFileSearch
to
false
. This will be the fastest option as it avoids a recursive crawl of your project. However, it means you will need to type the full path to files when using
@
completions.
tools
Setting
Type
Description
Default
Notes
tools.sandbox
boolean or string
Sandbox execution environment (can be a boolean or a path string).
undefined
tools.sandboxImage
string
Sandbox image URI used by Docker/Podman when
--sandbox-image
and
QWEN_SANDBOX_IMAGE
are not set.
undefined
tools.shell.enableInteractiveShell
boolean
Use
node-pty
for an interactive shell experience. Fallback to
child_process
still applies.
false
tools.core
array of strings
Deprecated.
Will be removed in next version. Use
permissions.allow
+
permissions.deny
instead. Restricts built-in tools to an allowlist. All tools not in the list are disabled.
undefined
tools.exclude
array of strings
Deprecated.
Use
permissions.deny
instead. Tool names to exclude from discovery. Automatically migrated to the
permissions
format on first load.
undefined
tools.allowed
array of strings
Deprecated.
Use
permissions.allow
instead. Tool names that bypass the confirmation dialog. Automatically migrated to the
permissions
format on first load.
undefined
tools.approvalMode
string
Sets the default approval mode for tool usage.
default
Possible values:
plan
(analyze only, do not modify files or execute commands),
default
(require approval before file edits or shell commands run),
auto-edit
(automatically approve file edits),
yolo
(automatically approve all tool calls)
tools.discoveryCommand
string
Command to run for tool discovery.
undefined
tools.callCommand
string
Defines a custom shell command for calling a specific tool that was discovered using
tools.discoveryCommand
. The shell command must meet the following criteria: It must take function
name
(exactly as in
function declaration
) as first command line argument. It must read function arguments as JSON on
stdin
, analogous to
functionCall.args
. It must return function output as JSON on
stdout
, analogous to
functionResponse.response.content
.
undefined
tools.useRipgrep
boolean
Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.
true
tools.useBuiltinRipgrep
boolean
Use the bundled ripgrep binary. When set to
false
, the system-level
rg
command will be used instead. This setting is only effective when
tools.useRipgrep
is
true
.
true
tools.truncateToolOutputThreshold
number
Truncate tool output if it is larger than this many characters. Applies to Shell, Grep, Glob, ReadFile and ReadManyFiles tools.
25000
Requires restart: Yes
tools.truncateToolOutputLines
number
Maximum lines or entries kept when truncating tool output. Applies to Shell, Grep, Glob, ReadFile and ReadManyFiles tools.
1000
Requires restart: Yes
Note
Migrating from
tools.core
/
tools.exclude
/
tools.allowed
:
These legacy settings are
deprecated
and automatically migrated to the new
permissions
format on first load. Prefer configuring
permissions.allow
/
permissions.deny
directly. Use
/permissions
to manage rules interactively.
memory
Setting
Type
Description
Default
memory.enableManagedAutoMemory
boolean
Enable background extraction of memories from conversations.
true
memory.enableManagedAutoDream
boolean
Enable automatic consolidation (deduplication and cleanup) of collected memories.
false
See
Memory
for details on how auto-memory works and how to use the
/memory
,
/remember
, and
/dream
commands.
permissions
The permissions system provides fine-grained control over which tools can run, which require confirmation, and which are blocked.
Decision priority (highest first):
deny
>
ask
>
allow
>
(default/interactive mode)
The first matching rule wins. Rules use the format
"ToolName"
or
"ToolName(specifier)"
.
Setting
Type
Description
Default
permissions.allow
array of strings
Rules for auto-approved tool calls (no confirmation needed). Merged across all scopes (user + project + system).
undefined
permissions.ask
array of strings
Rules for tool calls that always require user confirmation. Takes priority over
allow
.
undefined
permissions.deny
array of strings
Rules for blocked tool calls. Highest priority — overrides both
allow
and
ask
.
undefined
Tool name aliases (any of these work in rules):
Alias
Canonical tool
Notes
Bash
,
Shell
run_shell_command
Read
,
ReadFile
read_file
Meta-category — see below
Edit
,
EditFile
edit
Meta-category — see below
Write
,
WriteFile
write_file
Grep
,
SearchFiles
grep_search
Glob
,
FindFiles
glob
ListFiles
list_directory
WebFetch
web_fetch
Agent
task
Skill
skill
Meta-categories:
Some rule names automatically cover multiple tools:
Rule name
Tools covered
Read
read_file
,
grep_search
,
glob
,
list_directory
Edit
edit
,
write_file
[!important]
Read(/path/**)
matches
all four
read tools (file read, grep, glob, and directory listing).
To restrict only file reading, use
ReadFile(/path/**)
or
read_file(/path/**)
.
Rule syntax examples:
Rule
Meaning
"Bash"
All shell commands
"Bash(git *)"
Shell commands starting with
git
(word boundary: NOT
gitk
)
"Bash(git push *)"
Shell commands like
git push origin main
"Bash(npm run *)"
Any
npm run
script
"Read"
All file read operations (read, grep, glob, list)
"Read(./secrets/**)"
Read any file under
./secrets/
recursively
"Edit(/src/**/*.ts)"
Edit TypeScript files under project root
/src/
"WebFetch(api.example.com)"
Fetch from
api.example.com
and all its subdomains
"mcp__puppeteer"
All tools from the puppeteer MCP server
Path pattern prefixes:
Prefix
Meaning
Example
//
Absolute path from filesystem root
//etc/passwd
~/
Relative to home directory
~/Documents/*.pdf
/
Relative to project root
/src/**/*.ts
./
Relative to current working directory
./secrets/**
(none)
Same as
./
secrets/**
Shell command bypass prevention:
Permission rules for
Read
,
Edit
, and
WebFetch
are also enforced when the agent runs equivalent shell commands. For example, if
Read(./.env)
is in
deny
, the agent cannot bypass it via
cat .env
in a shell command. Supported shell commands include
cat
,
grep
,
curl
,
wget
,
cp
,
mv
,
rm
,
chmod
, and many more. Unknown/safe commands (e.g.
git
) are unaffected by file/network rules.
Migrating from legacy settings:
Legacy setting
Equivalent
permissions
rule
Notes
tools.allowed
permissions.allow
Auto-migrated on first load
tools.exclude
permissions.deny
Auto-migrated on first load
tools.core
permissions.allow
(allowlist)
Auto-migrated; unlisted tools are disabled at registry level
Example configuration:
{
"permissions"
: {
"allow"
: [
"Bash(git *)"
,
"Bash(npm run *)"
,
"Read(//Users/alice/code/**)"
],
"ask"
: [
"Bash(git push *)"
,
"Edit"
],
"deny"
: [
"Bash(rm -rf *)"
,
"Read(.env)"
,
"WebFetch(malicious.com)"
]
}
}
[!tip]
Use
/permissions
in the interactive CLI to view, add, and remove rules without editing
settings.json
directly.
slashCommands
Controls which slash commands are available in the CLI. Useful for locking down
the command surface in multi-tenant or enterprise deployments.
Setting
Type
Description
Default
slashCommands.disabled
array of strings
Slash command names to hide and refuse to execute. Matched case-insensitively against the final command name (for extension commands this is the disambiguated form, e.g.
myext.deploy
).
Merged as a union across scopes
, so workspace settings can add to but not remove entries defined in user or system settings.
undefined
The same denylist can also be provided via the
--disabled-slash-commands
CLI
flag (comma-separated or repeated) and the
QWEN_DISABLED_SLASH_COMMANDS
environment variable; values from all three sources are unioned together.
Example — lock down built-ins for a sandboxed deployment:
{
"slashCommands"
: {
"disabled"
: [
"auth"
,
"mcp"
,
"extensions"
,
"ide"
,
"quit"
]
}
}
With these values in a system-level
settings.json
(
/etc/qwen-code/settings.json
or
QWEN_CODE_SYSTEM_SETTINGS_PATH
), users cannot shrink the denylist from
their own scope, and the disabled commands will not appear in autocomplete or
execute when typed.
[!note]
This setting only gates slash commands (e.g.
/auth
,
/mcp
). It does not
affect tool permissions — see
permissions.deny
for that. It also does not
intercept keyboard shortcuts such as
Ctrl+C
or
Esc
.
mcp
Setting
Type
Description
Default
mcp.serverCommand
string
Command to start an MCP server.
undefined
mcp.allowed
array of strings
An allowlist of MCP servers to allow. Allows you to specify a list of MCP server names that should be made available to the model. This can be used to restrict the set of MCP servers to connect to. Note that this will be ignored if
--allowed-mcp-server-names
is set.
undefined
mcp.excluded
array of strings
A denylist of MCP servers to exclude. A server listed in both
mcp.excluded
and
mcp.allowed
is excluded. Note that this will be ignored if
--allowed-mcp-server-names
is set.
undefined
Note
Security Note for MCP servers:
These settings use simple string matching on MCP server names, which can be modified. If you’re a system administrator looking to prevent users from bypassing this, consider configuring the
mcpServers
at the system settings level such that the user will not be able to configure any MCP servers of their own. This should not be used as an airtight security mechanism.
lsp
[!warning]
Experimental Feature
: LSP support is currently experimental and disabled by default. Enable it using the
--experimental-lsp
command line flag.
Language Server Protocol (LSP) provides code intelligence features like go-to-definition, find references, and diagnostics.
LSP server configuration is done through
.lsp.json
files in your project root directory, not through
settings.json
. See the
LSP documentation
for configuration details and examples.
security
Setting
Type
Description
Default
security.folderTrust.enabled
boolean
Setting to track whether Folder trust is enabled.
false
security.auth.selectedType
string
The currently selected authentication type.
undefined
security.auth.enforcedType
string
The required auth type (useful for enterprises).
undefined
security.auth.useExternal
boolean
Whether to use an external authentication flow.
undefined
advanced
Setting
Type
Description
Default
advanced.autoConfigureMemory
boolean
Automatically configure Node.js memory limits.
false
advanced.dnsResolutionOrder
string
The DNS resolution order.
undefined
advanced.excludedEnvVars
array of strings
Environment variables to exclude from project context. Specifies environment variables that should be excluded from being loaded from project
.env
files. This prevents project-specific environment variables (like
DEBUG=true
) from interfering with the CLI behavior. Variables from
.qwen/.env
files are never excluded.
["DEBUG","DEBUG_MODE"]
advanced.bugCommand
object
Configuration for the bug report command. Overrides the default URL for the
/bug
command. Properties:
urlTemplate
(string): A URL that can contain
{title}
and
{info}
placeholders. Example:
"bugCommand": { "urlTemplate": "https://bug.example.com/new?title={title}&info={info}" }
undefined
mcpServers
Configures connections to one or more Model-Context Protocol (MCP) servers for discovering and using custom tools. Qwen Code attempts to connect to each configured MCP server to discover available tools. If multiple MCP servers expose a tool with the same name, the tool names will be prefixed with the server alias you defined in the configuration (e.g.,
serverAlias__actualToolName
) to avoid conflicts. Note that the system might strip certain schema properties from MCP tool definitions for compatibility. At least one of
command
,
url
, or
httpUrl
must be provided. If multiple are specified, the order of precedence is
httpUrl
, then
url
, then
command
.
Property
Type
Description
Optional
mcpServers.<SERVER_NAME>.command
string
The command to execute to start the MCP server via standard I/O.
Yes
mcpServers.<SERVER_NAME>.args
array of strings
Arguments to pass to the command.
Yes
mcpServers.<SERVER_NAME>.env
object
Environment variables to set for the server process.
Yes
mcpServers.<SERVER_NAME>.cwd
string
The working directory in which to start the server.
Yes
mcpServers.<SERVER_NAME>.url
string
The URL of an MCP server that uses Server-Sent Events (SSE) for communication.
Yes
mcpServers.<SERVER_NAME>.httpUrl
string
The URL of an MCP server that uses streamable HTTP for communication.
Yes
mcpServers.<SERVER_NAME>.headers
object
A map of HTTP headers to send with requests to
url
or
httpUrl
.
Yes
mcpServers.<SERVER_NAME>.timeout
number
Timeout in milliseconds for requests to this MCP server.
Yes
mcpServers.<SERVER_NAME>.trust
boolean
Trust this server and bypass all tool call confirmations.
Yes
mcpServers.<SERVER_NAME>.description
string
A brief description of the server, which may be used for display purposes.
Yes
mcpServers.<SERVER_NAME>.includeTools
array of strings
List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.
Yes
mcpServers.<SERVER_NAME>.excludeTools
array of strings
List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server.
Note:
excludeTools
takes precedence over
includeTools
- if a tool is in both lists, it will be excluded.
Yes
telemetry
Configures logging and metrics collection for Qwen Code. For more information, see
telemetry
.
Setting
Type
Description
Default
telemetry.enabled
boolean
Whether or not telemetry is enabled.
telemetry.target
string
The destination for collected telemetry. Supported values are
local
and
gcp
.
telemetry.otlpEndpoint
string
The endpoint for the OTLP Exporter.
telemetry.otlpProtocol
string
The protocol for the OTLP Exporter (
grpc
or
http
).
telemetry.logPrompts
boolean
Whether or not to include the content of user prompts in the logs.
telemetry.outfile
string
The file to write telemetry to when
target
is
local
.
telemetry.useCollector
boolean
Whether to use an external OTLP collector.
Example
settings.json
Here is an example of a
settings.json
file with the nested structure, new as of v0.3.0:
{
"proxy": "http://localhost:7890",
"general": {
"vimMode": true,
"preferredEditor": "code"
},
"ui": {
"theme": "GitHub",
"hideTips": false,
"customWittyPhrases": [
"You forget a thousand things every day. Make sure this is one of 'em",
"Connecting to AGI"
]
},
"tools": {
"approvalMode": "yolo",
"sandbox": "docker",
"sandboxImage": "ghcr.io/qwenlm/qwen-code:0.14.1",
"discoveryCommand": "bin/get_tools",
"callCommand": "bin/call_tool",
"exclude": ["write_file"]
},
"mcpServers": {
"mainServer": {
"command": "bin/mcp_server.py"
},
"anotherServer": {
"command": "node",
"args": ["mcp_server.js", "--verbose"]
}
},
"telemetry": {
"enabled": true,
"target": "local",
"otlpEndpoint": "http://localhost:4317",
"logPrompts": true
},
"privacy": {
"usageStatisticsEnabled": true
},
"model": {
"name": "qwen3-coder-plus",
"maxSessionTurns": 10,
"enableOpenAILogging": false,
"openAILoggingDir": "~/qwen-logs",
},
"context": {
"fileName": ["CONTEXT.md", "QWEN.md"],
"includeDirectories": ["path/to/dir1", "~/path/to/dir2", "../path/to/dir3"],
"loadFromIncludeDirectories": true,
"fileFiltering": {
"respectGitIgnore": false
}
},
"advanced": {
"excludedEnvVars": ["DEBUG", "DEBUG_MODE", "NODE_ENV"]
}
}
Shell History
The CLI keeps a history of shell commands you run. To avoid conflicts between different projects, this history is stored in a project-specific directory within your user’s home folder.
Location:
~/.qwen/tmp/<project_hash>/shell_history
<project_hash>
is a unique identifier generated from your project’s root path.
The history is stored in a file named
shell_history
.
Environment Variables &
.env
Files
Environment variables are a common way to configure applications, especially for sensitive information (like tokens) or for settings that might change between environments.
Qwen Code can automatically load environment variables from
.env
files.
For authentication-related variables (like
OPENAI_*
) and the recommended
.qwen/.env
approach, see
Authentication
.
Tip
Environment Variable Exclusion:
Some environment variables (like
DEBUG
and
DEBUG_MODE
) are automatically excluded from project
.env
files by default to prevent interference with the CLI behavior. Variables from
.qwen/.env
files are never excluded. You can customize this behavior using the
advanced.excludedEnvVars
setting in your
settings.json
file.
Environment Variables Table
Variable
Description
Notes
QWEN_TELEMETRY_ENABLED
Set to
true
or
1
to enable telemetry. Any other value is treated as disabling it.
Overrides the
telemetry.enabled
setting.
QWEN_TELEMETRY_TARGET
Sets the telemetry target (
local
or
gcp
).
Overrides the
telemetry.target
setting.
QWEN_TELEMETRY_OTLP_ENDPOINT
Sets the OTLP endpoint for telemetry.
Overrides the
telemetry.otlpEndpoint
setting.
QWEN_TELEMETRY_OTLP_PROTOCOL
Sets the OTLP protocol (
grpc
or
http
).
Overrides the
telemetry.otlpProtocol
setting.
QWEN_TELEMETRY_LOG_PROMPTS
Set to
true
or
1
to enable or disable logging of user prompts. Any other value is treated as disabling it.
Overrides the
telemetry.logPrompts
setting.
QWEN_TELEMETRY_OUTFILE
Sets the file path to write telemetry to when the target is
local
.
Overrides the
telemetry.outfile
setting.
QWEN_TELEMETRY_USE_COLLECTOR
Set to
true
or
1
to enable or disable using an external OTLP collector. Any other value is treated as disabling it.
Overrides the
telemetry.useCollector
setting.
QWEN_SANDBOX
Alternative to the
sandbox
setting in
settings.json
.
Accepts
true
,
false
,
docker
,
podman
, or a custom command string.
QWEN_SANDBOX_IMAGE
Overrides sandbox image selection for Docker/Podman.
Takes precedence over
tools.sandboxImage
.
SEATBELT_PROFILE
(macOS specific) Switches the Seatbelt (
sandbox-exec
) profile on macOS.
permissive-open
: (Default) Restricts writes to the project folder (and a few other folders, see
packages/cli/src/utils/sandbox-macos-permissive-open.sb
) but allows other operations.
strict
: Uses a strict profile that declines operations by default.
<profile_name>
: Uses a custom profile. To define a custom profile, create a file named
sandbox-macos-<profile_name>.sb
in your project’s
.qwen/
directory (e.g.,
my-project/.qwen/sandbox-macos-custom.sb
).
DEBUG
or
DEBUG_MODE
(often used by underlying libraries or the CLI itself) Set to
true
or
1
to enable verbose debug logging, which can be helpful for troubleshooting.
Note:
These variables are automatically excluded from project
.env
files by default to prevent interference with the CLI behavior. Use
.qwen/.env
files if you need to set these for Qwen Code specifically.
NO_COLOR
Set to any value to disable all color output in the CLI.
CLI_TITLE
Set to a string to customize the title of the CLI.
CODE_ASSIST_ENDPOINT
Specifies the endpoint for the code assist server.
This is useful for development and testing.
QWEN_CODE_MAX_OUTPUT_TOKENS
Overrides the default maximum output tokens per response. When not set, Qwen Code uses an adaptive strategy: starts with 8K tokens and automatically retries with 64K if the response is truncated. Set this to a specific value (e.g.,
16000
) to use a fixed limit instead.
Takes precedence over the capped default (8K) but is overridden by
samplingParams.max_tokens
in settings. Disables automatic escalation when set. Example:
export QWEN_CODE_MAX_OUTPUT_TOKENS=16000
QWEN_CODE_UNATTENDED_RETRY
Set to
true
or
1
to enable persistent retry mode. When enabled, transient API capacity errors (HTTP 429 Rate Limit and 529 Overloaded) are retried indefinitely with exponential backoff (capped at 5 minutes per retry) and heartbeat keepalives every 30 seconds on stderr.
Designed for CI/CD pipelines and background automation where long-running tasks should survive temporary API outages. Must be set explicitly —
CI=true
alone does
not
activate this mode. See
Headless Mode
for details. Example:
export QWEN_CODE_UNATTENDED_RETRY=1
QWEN_CODE_PROFILE_STARTUP
Set to
1
to enable startup performance profiling. Writes a JSON timing report to
~/.qwen/startup-perf/
with per-phase durations.
Only active inside the sandbox child process. Zero overhead when not set. Example:
export QWEN_CODE_PROFILE_STARTUP=1
Command-Line Arguments
Arguments passed directly when running the CLI can override other configurations for that specific session.
For sandbox image selection, precedence is:
--sandbox-image
>
QWEN_SANDBOX_IMAGE
>
tools.sandboxImage
> built-in default image.
Command-Line Arguments Table
Argument
Alias
Description
Possible Values
Notes
--model
-m
Specifies the Qwen model to use for this session.
Model name
Example:
npm start -- --model qwen3-coder-plus
--prompt
-p
Used to pass a prompt directly to the command. This invokes Qwen Code in a non-interactive mode.
Your prompt text
For scripting examples, use the
--output-format json
flag to get structured output.
--prompt-interactive
-i
Starts an interactive session with the provided prompt as the initial input.
Your prompt text
The prompt is processed within the interactive session, not before it. Cannot be used when piping input from stdin. Example:
qwen -i "explain this code"
--system-prompt
Overrides the built-in main session system prompt for this run.
Your prompt text
Loaded context files such as
QWEN.md
are still appended after this override. Can be combined with
--append-system-prompt
.
--append-system-prompt
Appends extra instructions to the main session system prompt for this run.
Your prompt text
Applied after the built-in prompt and loaded context files. Can be combined with
--system-prompt
. See
Headless Mode
for examples.
--output-format
-o
Specifies the format of the CLI output for non-interactive mode.
text
,
json
,
stream-json
text
: (Default) The standard human-readable output.
json
: A machine-readable JSON output emitted at the end of execution.
stream-json
: Streaming JSON messages emitted as they occur during execution. For structured output and scripting, use the
--output-format json
or
--output-format stream-json
flag. See
Headless Mode
for detailed information.
--input-format
Specifies the format consumed from standard input.
text
,
stream-json
text
: (Default) Standard text input from stdin or command-line arguments.
stream-json
: JSON message protocol via stdin for bidirectional communication. Requirement:
--input-format stream-json
requires
--output-format stream-json
to be set. When using
stream-json
, stdin is reserved for protocol messages. See
Headless Mode
for detailed information.
--include-partial-messages
Include partial assistant messages when using
stream-json
output format. When enabled, emits stream events (message_start, content_block_delta, etc.) as they occur during streaming.
Default:
false
. Requirement: Requires
--output-format stream-json
to be set. See
Headless Mode
for detailed information about stream events.
--sandbox
-s
Enables sandbox mode for this session.
--sandbox-image
Sets the sandbox image URI.
--debug
-d
Enables debug mode for this session, providing more verbose output.
--all-files
-a
If set, recursively includes all files within the current directory as context for the prompt.
--help
-h
Displays help information about command-line arguments.
--show-memory-usage
Displays the current memory usage.
--yolo
Enables YOLO mode, which automatically approves all tool calls.
--approval-mode
Sets the approval mode for tool calls.
plan
,
default
,
auto-edit
,
yolo
Supported modes:
plan
: Analyze only—do not modify files or execute commands.
default
: Require approval for file edits or shell commands (default behavior).
auto-edit
: Automatically approve edit tools (edit, write_file) while prompting for others.
yolo
: Automatically approve all tool calls (equivalent to
--yolo
). Cannot be used together with
--yolo
. Use
--approval-mode=yolo
instead of
--yolo
for the new unified approach. Example:
qwen --approval-mode auto-edit
See more about
Approval Mode
.
--allowed-tools
A comma-separated list of tool names that will bypass the confirmation dialog.
Tool names
Example:
qwen --allowed-tools "Shell(git status)"
--disabled-slash-commands
Slash command names to hide/disable (comma-separated or repeated). Unioned with the
slashCommands.disabled
setting and the
QWEN_DISABLED_SLASH_COMMANDS
environment variable. Matched case-insensitively against the final command name.
Command names
Example:
qwen --disabled-slash-commands "auth,mcp,extensions"
--telemetry
Enables
telemetry
.
--telemetry-target
Sets the telemetry target.
See
telemetry
for more information.
--telemetry-otlp-endpoint
Sets the OTLP endpoint for telemetry.
See
telemetry
for more information.
--telemetry-otlp-protocol
Sets the OTLP protocol for telemetry (
grpc
or
http
).
Defaults to
grpc
. See
telemetry
for more information.
--telemetry-log-prompts
Enables logging of prompts for telemetry.
See
telemetry
for more information.
--checkpointing
Enables
checkpointing
.
--acp
Enables ACP mode (Agent Client Protocol). Useful for IDE/editor integrations like
Zed
.
Stable. Replaces the deprecated
--experimental-acp
flag.
--experimental-lsp
Enables experimental
LSP (Language Server Protocol)
feature for code intelligence (go-to-definition, find references, diagnostics, etc.).
Experimental. Requires language servers to be installed.
--extensions
-e
Specifies a list of extensions to use for the session.
Extension names
If not provided, all available extensions are used. Use the special term
qwen -e none
to disable all extensions. Example:
qwen -e my-extension -e my-other-extension
--list-extensions
-l
Lists all available extensions and exits.
--proxy
Sets the proxy for the CLI.
Proxy URL
Example:
--proxy http://localhost:7890
.
--include-directories
Includes additional directories in the workspace for multi-directory support.
Directory paths
Can be specified multiple times or as comma-separated values. 5 directories can be added at maximum. Example:
--include-directories /path/to/project1,/path/to/project2
or
--include-directories /path/to/project1 --include-directories /path/to/project2
--screen-reader
Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers.
--version
Displays the version of the CLI.
--openai-logging
Enables logging of OpenAI API calls for debugging and analysis.
This flag overrides the
enableOpenAILogging
setting in
settings.json
.
--openai-logging-dir
Sets a custom directory path for OpenAI API logs.
Directory path
This flag overrides the
openAILoggingDir
setting in
settings.json
. Supports absolute paths, relative paths, and
~
expansion. Example:
qwen --openai-logging-dir "~/qwen-logs" --openai-logging
Context Files (Hierarchical Instructional Context)
While not strictly configuration for the CLI’s
behavior
, context files (defaulting to
QWEN.md
but configurable via the
context.fileName
setting) are crucial for configuring the
instructional context
(also referred to as “memory”). This powerful feature allows you to give project-specific instructions, coding style guides, or any relevant background information to the AI, making its responses more tailored and accurate to your needs. The CLI includes UI elements, such as an indicator in the footer showing the number of loaded context files, to keep you informed about the active context.
Purpose:
These Markdown files contain instructions, guidelines, or context that you want the Qwen model to be aware of during your interactions. The system is designed to manage this instructional context hierarchically.
Example Context File Content (e.g.
QWEN.md
)
Here’s a conceptual example of what a context file at the root of a TypeScript project might contain:
# Project: My Awesome TypeScript Library
## General Instructions:
- When generating new TypeScript code, please follow the existing coding style.
- Ensure all new functions and classes have JSDoc comments.
- Prefer functional programming paradigms where appropriate.
- All code should be compatible with TypeScript 5.0 and Node.js 20+.
## Coding Style:
- Use 2 spaces for indentation.
- Interface names should be prefixed with `I` (e.g., `IUserService`).
- Private class members should be prefixed with an underscore (`_`).
- Always use strict equality (`===` and `!==`).
## Specific Component: `src/api/client.ts`
- This file handles all outbound API requests.
- When adding new API call functions, ensure they include robust error handling and logging.
- Use the existing `fetchWithRetry` utility for all GET requests.
## Regarding Dependencies:
- Avoid introducing new external dependencies unless absolutely necessary.
- If a new dependency is required, please state the reason.
This example demonstrates how you can provide general project context, specific coding conventions, and even notes about particular files or components. The more relevant and precise your context files are, the better the AI can assist you. Project-specific context files are highly encouraged to establish conventions and context.
Hierarchical Loading and Precedence:
The CLI implements a hierarchical memory system by loading context files (e.g.,
QWEN.md
) from several locations. Content from files lower in this list (more specific) typically overrides or supplements content from files higher up (more general). The exact concatenation order and final context can be inspected using the
/memory show
command. The typical loading order is:
Global Context File:
Location:
~/.qwen/<configured-context-filename>
(e.g.,
~/.qwen/QWEN.md
in your user home directory).
Scope: Provides default instructions for all your projects.
Project Root & Ancestors Context Files:
Location: The CLI searches for the configured context file in the current working directory and then in each parent directory up to either the project root (identified by a
.git
folder) or your home directory.
Scope: Provides context relevant to the entire project or a significant portion of it.
Concatenation & UI Indication:
The contents of all found context files are concatenated (with separators indicating their origin and path) and provided as part of the system prompt. The CLI footer displays the count of loaded context files, giving you a quick visual cue about the active instructional context.
Importing Content:
You can modularize your context files by importing other Markdown files using the
@path/to/file.md
syntax. For more details, see the
Memory Import Processor documentation
.
Commands for Memory Management:
Use
/memory refresh
to force a re-scan and reload of all context files from all configured locations. This updates the AI’s instructional context.
Use
/memory show
to display the combined instructional context currently loaded, allowing you to verify the hierarchy and content being used by the AI.
See the
Commands documentation
for full details on the
/memory
command and its sub-commands (
show
and
refresh
).
By understanding and utilizing these configuration layers and the hierarchical nature of context files, you can effectively manage the AI’s memory and tailor Qwen Code’s responses to your specific needs and projects.
Sandbox
Qwen Code can execute potentially unsafe operations (like shell commands and file modifications) within a sandboxed environment to protect your system.
Sandbox
is disabled by default, but you can enable it in a few ways:
Using
--sandbox
or
-s
flag.
Setting
QWEN_SANDBOX
environment variable.
Sandbox is enabled when using
--yolo
or
--approval-mode=yolo
by default.
By default, it uses a pre-built
qwen-code-sandbox
Docker image.
For project-specific sandboxing needs, you can create a custom Dockerfile at
.qwen/sandbox.Dockerfile
in your project’s root directory. This Dockerfile can be based on the base sandbox image:
FROM qwen-code-sandbox
# Add your custom dependencies or configurations here
# For example:
# RUN apt-get update && apt-get install -y some-package
# COPY ./my-config /app/my-config
When
.qwen/sandbox.Dockerfile
exists, you can use
BUILD_SANDBOX
environment variable when running Qwen Code to automatically build the custom sandbox image:
BUILD_SANDBOX=1 qwen -s
Usage Statistics
To help us improve Qwen Code, we collect anonymized usage statistics. This data helps us understand how the CLI is used, identify common issues, and prioritize new features.
What we collect:
Tool Calls:
We log the names of the tools that are called, whether they succeed or fail, and how long they take to execute. We do not collect the arguments passed to the tools or any data returned by them.
API Requests:
We log the model used for each request, the duration of the request, and whether it was successful. We do not collect the content of the prompts or responses.
Session Information:
We collect information about the configuration of the CLI, such as the enabled tools and the approval mode.
What we DON’T collect:
Personally Identifiable Information (PII):
We do not collect any personal information, such as your name, email address, or API keys.
Prompt and Response Content:
We do not log the content of your prompts or the responses from the model.
File Content:
We do not log the content of any files that are read or written by the CLI.
How to opt out:
You can opt out of usage statistics collection at any time by setting the
usageStatisticsEnabled
property to
false
under the
privacy
category in your
settings.json
file:
{
"privacy": {
"usageStatisticsEnabled": false
}
}
Note
When usage statistics are enabled, events are sent to an Alibaba Cloud RUM collection endpoint.
Last updated on
May 18, 2026
Contextual Tips
Authentication</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/settings/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Settings
Qwen Code Configuration
Tip
Authentication / API keys:
Authentication (API Key, Alibaba Cloud Coding Plan) and auth-related environment variables (like
OPENAI_API_KEY
) are documented in
Authentication
.
Note
Note on New Configuration Format
: The format of the
settings.json
file has been updated to a new, more organized structure. The old format will be migrated automatically.
Qwen Code offers several ways to configure its behavior, including environment variab...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Settings
Qwen Code Configuration
Tip
Authentication / API keys:
Authentication (API Key, Alibaba Cloud Coding Plan) and auth-related environment variables (like
OPENAI_API_KEY
) are documented in
Authentication
.
Note
Note on New Configuration Format
: The format of the
settings.json
file has been updated to a new, more organized structure. The old format will be migrated automatically.
Qwen Code offers several ways to configure its behavior, including environment variables, command-line arguments, and settings files. This document outlines the different configuration methods and available settings.
Configuration layers
Configuration is applied in the following order of precedence (lower numbers are overridden by higher numbers):
Level
Configuration Source
Description
1
Default values
Hardcoded defaults within the application
2
System defaults file
System-wide default settings that can be overridden by other settings files
3
User settings file
Global settings for the current user
4
Project settings file
Project-specific settings
5
System settings file
System-wide settings that override all other settings files
6
Environment variables
System-wide or session-specific variables, potentially loaded from
.env
files
7
Command-line arguments
Values passed when launching the CLI
Settings files
Qwen Code uses JSON settings files for persistent configuration. There are four locations for these files:
File Type
Location
Scope
System defaults file
Linux:
/etc/qwen-code/system-defaults.json
Windows:
C:\ProgramData\qwen-code\system-defaults.json
macOS:
/Library/Application Support/QwenCode/system-defaults.json
The path can be overridden using the
QWEN_CODE_SYSTEM_DEFAULTS_PATH
environment variable.
Provides a base layer of system-wide default settings. These settings have the lowest precedence and are intended to be overridden by user, project, or system override settings.
User settings file
~/.qwen/settings.json
(where
~
is your home directory).
Applies to all Qwen Code sessions for the current user.
Project settings file
.qwen/settings.json
within your project’s root directory.
Applies only when running Qwen Code from that specific project. Project settings override user settings.
System settings file
Linux：
/etc/qwen-code/settings.json
Windows:
C:\ProgramData\qwen-code\settings.json
macOS:
/Library/Application Support/QwenCode/settings.json
The path can be overridden using the
QWEN_CODE_SYSTEM_SETTINGS_PATH
environment variable.
Applies to all Qwen Code sessions on the system, for all users. System settings override user and project settings. May be useful for system administrators at enterprises to have controls over users’ Qwen Code setups.
Note
Note on environment variables in settings:
String values within your
settings.json
files can reference environment variables using either
$VAR_NAME
or
${VAR_NAME}
syntax. These variables will be automatically resolved when the settings are loaded. For example, if you have an environment variable
MY_API_TOKEN
, you could use it in
settings.json
like this:
"apiKey": "$MY_API_TOKEN"
.
The
.qwen
directory in your project
In addition to a project settings file, a project’s
.qwen
directory can contain other project-specific files related to Qwen Code’s operation, such as:
Custom sandbox profiles
(e.g.
.qwen/sandbox-macos-custom.sb
,
.qwen/sandbox.Dockerfile
).
Agent Skills
under
.qwen/skills/
(each Skill is a directory containing a
SKILL.md
).
Configuration migration
Qwen Code automatically migrates legacy configuration settings to the new format. Old settings files are backed up before migration. The following settings have been renamed from negative (
disable*
) to positive (
enable*
) naming:
Old Setting
New Setting
Notes
disableAutoUpdate
+
disableUpdateNag
general.enableAutoUpdate
Consolidated into a single setting
disableLoadingPhrases
ui.accessibility.enableLoadingPhrases
disableFuzzySearch
context.fileFiltering.enableFuzzySearch
disableCacheControl
model.generationConfig.enableCacheControl
Note
Boolean value inversion:
When migrating, boolean values are inverted (e.g.,
disableAutoUpdate: true
becomes
enableAutoUpdate: false
).
Consolidation policy for
disableAutoUpdate
and
disableUpdateNag
When both legacy settings are present with different values, the migration follows this policy: if
either
disableAutoUpdate
or
disableUpdateNag
is
true
, then
enableAutoUpdate
becomes
false
:
disableAutoUpdate
disableUpdateNag
Migrated
enableAutoUpdate
false
false
true
false
true
false
true
false
false
true
true
false
Available settings in
settings.json
Settings are organized into categories. Most settings should be placed within their corresponding top-level category object in your
settings.json
file. A few compatibility settings, such as
proxy
, are top-level keys.
top-level
Setting
Type
Description
Default
proxy
string
Proxy URL for CLI HTTP requests. Precedence is
--proxy
>
proxy
in
settings.json
>
HTTPS_PROXY
/
https_proxy
/
HTTP_PROXY
/
http_proxy
environment variables.
undefined
general
Setting
Type
Description
Default
general.preferredEditor
string
The preferred editor to open files in.
undefined
general.vimMode
boolean
Enable Vim keybindings.
false
general.enableAutoUpdate
boolean
Enable automatic update checks and installations on startup.
true
general.showSessionRecap
boolean
Auto-show a one-line “where you left off” recap when returning to the terminal after being away. Off by default. Use
/recap
to trigger manually regardless of this setting.
false
general.sessionRecapAwayThresholdMinutes
number
Minutes the terminal must be blurred before an auto-recap fires on focus-in. Only used when
showSessionRecap
is enabled.
5
general.gitCoAuthor
boolean
Automatically add a Co-authored-by trailer to git commit messages when commits are made through Qwen Code.
true
general.checkpointing.enabled
boolean
Enable session checkpointing for recovery.
false
general.defaultFileEncoding
string
Default encoding for new files. Use
"utf-8"
(default) for UTF-8 without BOM, or
"utf-8-bom"
for UTF-8 with BOM. Only change this if your project specifically requires BOM.
"utf-8"
output
Setting
Type
Description
Default
Possible Values
output.format
string
The format of the CLI output.
"text"
"text"
,
"json"
ui
Setting
Type
Description
Default
ui.theme
string
The color theme for the UI. See
Themes
for available options.
undefined
ui.customThemes
object
Custom theme definitions.
{}
ui.statusLine
object
Custom status line configuration. A shell command whose output is shown in the footer’s left section. See
Status Line
.
undefined
ui.hideWindowTitle
boolean
Hide the window title bar.
false
ui.hideTips
boolean
Hide all tips (startup and post-response) in the UI. See
Contextual Tips
.
false
ui.hideBanner
boolean
Hide the application banner.
false
ui.hideFooter
boolean
Hide the footer from the UI.
false
ui.showMemoryUsage
boolean
Display memory usage information in the UI.
false
ui.showLineNumbers
boolean
Show line numbers in code blocks in the CLI output.
true
ui.showCitations
boolean
Show citations for generated text in the chat.
true
ui.compactMode
boolean
Hide tool output and thinking for a cleaner view. Toggle with
Ctrl+O
during a session or via the Settings dialog. Tool approval prompts are never hidden, even in compact mode. The setting persists across sessions.
false
ui.shellOutputMaxLines
number
Max number of shell output lines shown inline. Set to
0
to disable the cap and show full output. Hidden lines are surfaced via the
+N lines
indicator. Errors,
!
-prefix user-initiated commands, confirming tools, and focused embedded shells always show full output.
5
enableWelcomeBack
boolean
Show welcome back dialog when returning to a project with conversation history. When enabled, Qwen Code will automatically detect if you’re returning to a project with a previously generated project summary (
.qwen/PROJECT_SUMMARY.md
) and show a dialog allowing you to continue your previous conversation or start fresh. If you choose
Start new chat session
, that choice is remembered for the current project until the project summary changes. This feature integrates with the
/summary
command and quit confirmation dialog.
true
ui.accessibility.enableLoadingPhrases
boolean
Enable loading phrases (disable for accessibility).
true
ui.accessibility.screenReader
boolean
Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers.
false
ui.customWittyPhrases
array of strings
A list of custom phrases to display during loading states. When provided, the CLI will cycle through these phrases instead of the default ones.
[]
ui.enableFollowupSuggestions
boolean
Enable
followup suggestions
that predict what you want to type next after the model responds. Suggestions appear as ghost text and can be accepted with Tab, Enter, or Right Arrow.
true
ui.enableCacheSharing
boolean
Use cache-aware forked queries for suggestion generation. Reduces cost on providers that support prefix caching (experimental).
true
ui.enableSpeculation
boolean
Speculatively execute accepted suggestions before submission. Results appear instantly when you accept (experimental).
false
experimental.emitToolUseSummaries
boolean
Generate short LLM-based labels summarizing each tool-call batch. See
Tool-Use Summaries
. Requires
fastModel
to be configured; silently skipped otherwise. Can be overridden per-session with
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
or
=1
.
true
ide
Setting
Type
Description
Default
ide.enabled
boolean
Enable IDE integration mode.
false
ide.hasSeenNudge
boolean
Whether the user has seen the IDE integration nudge.
false
privacy
Setting
Type
Description
Default
privacy.usageStatisticsEnabled
boolean
Enable collection of usage statistics.
true
model
Setting
Type
Description
Default
model.name
string
The Qwen model to use for conversations.
undefined
model.maxSessionTurns
number
Maximum number of user/model/tool turns to keep in a session. -1 means unlimited.
-1
model.generationConfig
object
Advanced overrides passed to the underlying content generator. Supports request controls such as
timeout
,
maxRetries
,
enableCacheControl
,
splitToolMedia
(set
true
for strict OpenAI-compatible servers like LM Studio that reject non-text content on
role: "tool"
messages — splits media into a follow-up user message),
contextWindowSize
(override model’s context window size),
modalities
(override auto-detected input modalities),
customHeaders
(custom HTTP headers for API requests),
extra_body
(additional body parameters for OpenAI-compatible API requests only), and
reasoning
(
{ effort: 'low' | 'medium' | 'high' | 'max', budget_tokens?: number }
to control thinking intensity, or
false
to disable;
'max'
is a DeepSeek extension — see
Reasoning / thinking configuration
for per-provider behavior.
Note:
when
samplingParams
is set on an OpenAI-compatible provider, the pipeline ships those keys verbatim and the separate top-level
reasoning
field is dropped — put
reasoning_effort
inside
samplingParams
(or
extra_body
) instead in that case), along with fine-tuning knobs under
samplingParams
(for example
temperature
,
top_p
,
max_tokens
). Leave unset to rely on provider defaults.
undefined
model.chatCompression.contextPercentageThreshold
number
Sets the threshold for chat history compression as a percentage of the model’s total token limit. This is a value between 0 and 1 that applies to both automatic compression and the manual
/compress
command. For example, a value of
0.6
will trigger compression when the chat history exceeds 60% of the token limit. Use
0
to disable compression entirely.
0.7
model.skipNextSpeakerCheck
boolean
Skip the next speaker check.
false
model.skipLoopDetection
boolean
Disables loop detection checks. Loop detection prevents infinite loops in AI responses but can generate false positives that interrupt legitimate workflows. Enable this option if you experience frequent false positive loop detection interruptions.
false
model.skipStartupContext
boolean
Skips sending the startup workspace context (environment summary and acknowledgement) at the beginning of each session. Enable this if you prefer to provide context manually or want to save tokens on startup.
false
model.enableOpenAILogging
boolean
Enables logging of OpenAI API calls for debugging and analysis. When enabled, API requests and responses are logged to JSON files.
false
model.openAILoggingDir
string
Custom directory path for OpenAI API logs. If not specified, defaults to
logs/openai
in the current working directory. Supports absolute paths, relative paths (resolved from current working directory), and
~
expansion (home directory).
undefined
Example model.generationConfig:
{
"model"
: {
"generationConfig"
: {
"timeout"
:
60000
,
"contextWindowSize"
:
128000
,
"modalities"
: {
"image"
:
true
},
"enableCacheControl"
:
true
,
"customHeaders"
: {
"X-Client-Request-ID"
:
"req-123"
},
"extra_body"
: {
"enable_thinking"
:
true
},
"samplingParams"
: {
"temperature"
:
0.2
,
"top_p"
:
0.8
,
"max_tokens"
:
1024
}
}
}
}
max_tokens (adaptive output tokens):
When
samplingParams.max_tokens
is not set, Qwen Code uses an adaptive output token strategy to optimize GPU resource usage:
Requests start with a default limit of
8K
output tokens
If the response is truncated (the model hits the limit), Qwen Code automatically retries with
64K
tokens
The partial output is discarded and replaced with the full response from the retry
This is transparent to users — you may briefly see a retry indicator if escalation occurs. Since 99% of responses are under 5K tokens, the retry happens rarely (<1% of requests).
To override this behavior, either set
samplingParams.max_tokens
in your settings or use the
QWEN_CODE_MAX_OUTPUT_TOKENS
environment variable.
contextWindowSize:
Overrides the default context window size for the selected model. Qwen Code determines the context window using built-in defaults based on model name matching, with a constant fallback value. Use this setting when a provider’s effective context limit differs from Qwen Code’s default. This value defines the model’s assumed maximum context capacity, not a per-request token limit.
modalities:
Overrides the auto-detected input modalities for the selected model. Qwen Code automatically detects supported modalities (image, PDF, audio, video) based on model name pattern matching. Use this setting when the auto-detection is incorrect — for example, to enable
pdf
for a model that supports it but isn’t recognized. Format:
{ "image": true, "pdf": true, "audio": true, "video": true }
. Omit a key or set it to
false
for unsupported types.
customHeaders:
Allows you to add custom HTTP headers to all API requests. This is useful for request tracing, monitoring, API gateway routing, or when different models require different headers. If
customHeaders
is defined in
modelProviders[].generationConfig.customHeaders
, it will be used directly; otherwise, headers from
model.generationConfig.customHeaders
will be used. No merging occurs between the two levels.
The
extra_body
field allows you to add custom parameters to the request body sent to the API. This is useful for provider-specific options that are not covered by the standard configuration fields.
Note: This field is only supported for OpenAI-compatible providers (
openai
,
qwen-oauth
). It is ignored for Anthropic and Gemini providers.
If
extra_body
is defined in
modelProviders[].generationConfig.extra_body
, it will be used directly; otherwise, values from
model.generationConfig.extra_body
will be used.
model.openAILoggingDir examples:
"~/qwen-logs"
- Logs to
~/qwen-logs
directory
"./custom-logs"
- Logs to
./custom-logs
relative to current directory
"/tmp/openai-logs"
- Logs to absolute path
/tmp/openai-logs
fastModel
Setting
Type
Description
Default
fastModel
string
Model used for generating
prompt suggestions
and speculative execution. Leave empty to use the main model. A smaller/faster model (e.g.,
qwen3-coder-flash
) reduces latency and cost. Can also be set via
/model --fast
.
""
context
Setting
Type
Description
Default
context.fileName
string or array of strings
The name of the context file(s).
undefined
context.importFormat
string
The format to use when importing memory.
undefined
context.includeDirectories
array
Additional directories to include in the workspace context. Specifies an array of additional absolute or relative paths to include in the workspace context. Missing directories will be skipped with a warning by default. Paths can use
~
to refer to the user’s home directory. This setting can be combined with the
--include-directories
command-line flag.
[]
context.loadFromIncludeDirectories
boolean
Controls the behavior of the
/memory refresh
command. If set to
true
,
QWEN.md
files should be loaded from all directories that are added. If set to
false
,
QWEN.md
should only be loaded from the current directory.
false
context.fileFiltering.respectGitIgnore
boolean
Respect .gitignore files when searching.
true
context.fileFiltering.respectQwenIgnore
boolean
Respect .qwenignore files when searching.
true
context.fileFiltering.enableRecursiveFileSearch
boolean
Whether to enable searching recursively for filenames under the current tree when completing
@
prefixes in the prompt.
true
context.fileFiltering.enableFuzzySearch
boolean
When
true
, enables fuzzy search capabilities when searching for files. Set to
false
to improve performance on projects with a large number of files.
true
context.clearContextOnIdle.toolResultsThresholdMinutes
number
Minutes of inactivity before clearing old tool result content. Use
-1
to disable.
60
context.clearContextOnIdle.toolResultsNumToKeep
number
Number of most-recent compactable tool results to preserve when clearing. Floor at 1.
5
Troubleshooting File Search Performance
If you are experiencing performance issues with file searching (e.g., with
@
completions), especially in projects with a very large number of files, here are a few things you can try in order of recommendation:
Use
.qwenignore
:
Create a
.qwenignore
file in your project root to exclude directories that contain a large number of files that you don’t need to reference (e.g., build artifacts, logs,
node_modules
). Reducing the total number of files crawled is the most effective way to improve performance.
Disable Fuzzy Search:
If ignoring files is not enough, you can disable fuzzy search by setting
enableFuzzySearch
to
false
in your
settings.json
file. This will use a simpler, non-fuzzy matching algorithm, which can be faster.
Disable Recursive File Search:
As a last resort, you can disable recursive file search entirely by setting
enableRecursiveFileSearch
to
false
. This will be the fastest option as it avoids a recursive crawl of your project. However, it means you will need to type the full path to files when using
@
completions.
tools
Setting
Type
Description
Default
Notes
tools.sandbox
boolean or string
Sandbox execution environment (can be a boolean or a path string).
undefined
tools.sandboxImage
string
Sandbox image URI used by Docker/Podman when
--sandbox-image
and
QWEN_SANDBOX_IMAGE
are not set.
undefined
tools.shell.enableInteractiveShell
boolean
Use
node-pty
for an interactive shell experience. Fallback to
child_process
still applies.
false
tools.core
array of strings
Deprecated.
Will be removed in next version. Use
permissions.allow
+
permissions.deny
instead. Restricts built-in tools to an allowlist. All tools not in the list are disabled.
undefined
tools.exclude
array of strings
Deprecated.
Use
permissions.deny
instead. Tool names to exclude from discovery. Automatically migrated to the
permissions
format on first load.
undefined
tools.allowed
array of strings
Deprecated.
Use
permissions.allow
instead. Tool names that bypass the confirmation dialog. Automatically migrated to the
permissions
format on first load.
undefined
tools.approvalMode
string
Sets the default approval mode for tool usage.
default
Possible values:
plan
(analyze only, do not modify files or execute commands),
default
(require approval before file edits or shell commands run),
auto-edit
(automatically approve file edits),
yolo
(automatically approve all tool calls)
tools.discoveryCommand
string
Command to run for tool discovery.
undefined
tools.callCommand
string
Defines a custom shell command for calling a specific tool that was discovered using
tools.discoveryCommand
. The shell command must meet the following criteria: It must take function
name
(exactly as in
function declaration
) as first command line argument. It must read function arguments as JSON on
stdin
, analogous to
functionCall.args
. It must return function output as JSON on
stdout
, analogous to
functionResponse.response.content
.
undefined
tools.useRipgrep
boolean
Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.
true
tools.useBuiltinRipgrep
boolean
Use the bundled ripgrep binary. When set to
false
, the system-level
rg
command will be used instead. This setting is only effective when
tools.useRipgrep
is
true
.
true
tools.truncateToolOutputThreshold
number
Truncate tool output if it is larger than this many characters. Applies to Shell, Grep, Glob, ReadFile and ReadManyFiles tools.
25000
Requires restart: Yes
tools.truncateToolOutputLines
number
Maximum lines or entries kept when truncating tool output. Applies to Shell, Grep, Glob, ReadFile and ReadManyFiles tools.
1000
Requires restart: Yes
Note
Migrating from
tools.core
/
tools.exclude
/
tools.allowed
:
These legacy settings are
deprecated
and automatically migrated to the new
permissions
format on first load. Prefer configuring
permissions.allow
/
permissions.deny
directly. Use
/permissions
to manage rules interactively.
memory
Setting
Type
Description
Default
memory.enableManagedAutoMemory
boolean
Enable background extraction of memories from conversations.
true
memory.enableManagedAutoDream
boolean
Enable automatic consolidation (deduplication and cleanup) of collected memories.
false
See
Memory
for details on how auto-memory works and how to use the
/memory
,
/remember
, and
/dream
commands.
permissions
The permissions system provides fine-grained control over which tools can run, which require confirmation, and which are blocked.
Decision priority (highest first):
deny
>
ask
>
allow
>
(default/interactive mode)
The first matching rule wins. Rules use the format
"ToolName"
or
"ToolName(specifier)"
.
Setting
Type
Description
Default
permissions.allow
array of strings
Rules for auto-approved tool calls (no confirmation needed). Merged across all scopes (user + project + system).
undefined
permissions.ask
array of strings
Rules for tool calls that always require user confirmation. Takes priority over
allow
.
undefined
permissions.deny
array of strings
Rules for blocked tool calls. Highest priority — overrides both
allow
and
ask
.
undefined
Tool name aliases (any of these work in rules):
Alias
Canonical tool
Notes
Bash
,
Shell
run_shell_command
Read
,
ReadFile
read_file
Meta-category — see below
Edit
,
EditFile
edit
Meta-category — see below
Write
,
WriteFile
write_file
Grep
,
SearchFiles
grep_search
Glob
,
FindFiles
glob
ListFiles
list_directory
WebFetch
web_fetch
Agent
task
Skill
skill
Meta-categories:
Some rule names automatically cover multiple tools:
Rule name
Tools covered
Read
read_file
,
grep_search
,
glob
,
list_directory
Edit
edit
,
write_file
[!important]
Read(/path/**)
matches
all four
read tools (file read, grep, glob, and directory listing).
To restrict only file reading, use
ReadFile(/path/**)
or
read_file(/path/**)
.
Rule syntax examples:
Rule
Meaning
"Bash"
All shell commands
"Bash(git *)"
Shell commands starting with
git
(word boundary: NOT
gitk
)
"Bash(git push *)"
Shell commands like
git push origin main
"Bash(npm run *)"
Any
npm run
script
"Read"
All file read operations (read, grep, glob, list)
"Read(./secrets/**)"
Read any file under
./secrets/
recursively
"Edit(/src/**/*.ts)"
Edit TypeScript files under project root
/src/
"WebFetch(api.example.com)"
Fetch from
api.example.com
and all its subdomains
"mcp__puppeteer"
All tools from the puppeteer MCP server
Path pattern prefixes:
Prefix
Meaning
Example
//
Absolute path from filesystem root
//etc/passwd
~/
Relative to home directory
~/Documents/*.pdf
/
Relative to project root
/src/**/*.ts
./
Relative to current working directory
./secrets/**
(none)
Same as
./
secrets/**
Shell command bypass prevention:
Permission rules for
Read
,
Edit
, and
WebFetch
are also enforced when the agent runs equivalent shell commands. For example, if
Read(./.env)
is in
deny
, the agent cannot bypass it via
cat .env
in a shell command. Supported shell commands include
cat
,
grep
,
curl
,
wget
,
cp
,
mv
,
rm
,
chmod
, and many more. Unknown/safe commands (e.g.
git
) are unaffected by file/network rules.
Migrating from legacy settings:
Legacy setting
Equivalent
permissions
rule
Notes
tools.allowed
permissions.allow
Auto-migrated on first load
tools.exclude
permissions.deny
Auto-migrated on first load
tools.core
permissions.allow
(allowlist)
Auto-migrated; unlisted tools are disabled at registry level
Example configuration:
{
"permissions"
: {
"allow"
: [
"Bash(git *)"
,
"Bash(npm run *)"
,
"Read(//Users/alice/code/**)"
],
"ask"
: [
"Bash(git push *)"
,
"Edit"
],
"deny"
: [
"Bash(rm -rf *)"
,
"Read(.env)"
,
"WebFetch(malicious.com)"
]
}
}
[!tip]
Use
/permissions
in the interactive CLI to view, add, and remove rules without editing
settings.json
directly.
slashCommands
Controls which slash commands are available in the CLI. Useful for locking down
the command surface in multi-tenant or enterprise deployments.
Setting
Type
Description
Default
slashCommands.disabled
array of strings
Slash command names to hide and refuse to execute. Matched case-insensitively against the final command name (for extension commands this is the disambiguated form, e.g.
myext.deploy
).
Merged as a union across scopes
, so workspace settings can add to but not remove entries defined in user or system settings.
undefined
The same denylist can also be provided via the
--disabled-slash-commands
CLI
flag (comma-separated or repeated) and the
QWEN_DISABLED_SLASH_COMMANDS
environment variable; values from all three sources are unioned together.
Example — lock down built-ins for a sandboxed deployment:
{
"slashCommands"
: {
"disabled"
: [
"auth"
,
"mcp"
,
"extensions"
,
"ide"
,
"quit"
]
}
}
With these values in a system-level
settings.json
(
/etc/qwen-code/settings.json
or
QWEN_CODE_SYSTEM_SETTINGS_PATH
), users cannot shrink the denylist from
their own scope, and the disabled commands will not appear in autocomplete or
execute when typed.
[!note]
This setting only gates slash commands (e.g.
/auth
,
/mcp
). It does not
affect tool permissions — see
permissions.deny
for that. It also does not
intercept keyboard shortcuts such as
Ctrl+C
or
Esc
.
mcp
Setting
Type
Description
Default
mcp.serverCommand
string
Command to start an MCP server.
undefined
mcp.allowed
array of strings
An allowlist of MCP servers to allow. Allows you to specify a list of MCP server names that should be made available to the model. This can be used to restrict the set of MCP servers to connect to. Note that this will be ignored if
--allowed-mcp-server-names
is set.
undefined
mcp.excluded
array of strings
A denylist of MCP servers to exclude. A server listed in both
mcp.excluded
and
mcp.allowed
is excluded. Note that this will be ignored if
--allowed-mcp-server-names
is set.
undefined
Note
Security Note for MCP servers:
These settings use simple string matching on MCP server names, which can be modified. If you’re a system administrator looking to prevent users from bypassing this, consider configuring the
mcpServers
at the system settings level such that the user will not be able to configure any MCP servers of their own. This should not be used as an airtight security mechanism.
lsp
[!warning]
Experimental Feature
: LSP support is currently experimental and disabled by default. Enable it using the
--experimental-lsp
command line flag.
Language Server Protocol (LSP) provides code intelligence features like go-to-definition, find references, and diagnostics.
LSP server configuration is done through
.lsp.json
files in your project root directory, not through
settings.json
. See the
LSP documentation
for configuration details and examples.
security
Setting
Type
Description
Default
security.folderTrust.enabled
boolean
Setting to track whether Folder trust is enabled.
false
security.auth.selectedType
string
The currently selected authentication type.
undefined
security.auth.enforcedType
string
The required auth type (useful for enterprises).
undefined
security.auth.useExternal
boolean
Whether to use an external authentication flow.
undefined
advanced
Setting
Type
Description
Default
advanced.autoConfigureMemory
boolean
Automatically configure Node.js memory limits.
false
advanced.dnsResolutionOrder
string
The DNS resolution order.
undefined
advanced.excludedEnvVars
array of strings
Environment variables to exclude from project context. Specifies environment variables that should be excluded from being loaded from project
.env
files. This prevents project-specific environment variables (like
DEBUG=true
) from interfering with the CLI behavior. Variables from
.qwen/.env
files are never excluded.
["DEBUG","DEBUG_MODE"]
advanced.bugCommand
object
Configuration for the bug report command. Overrides the default URL for the
/bug
command. Properties:
urlTemplate
(string): A URL that can contain
{title}
and
{info}
placeholders. Example:
"bugCommand": { "urlTemplate": "https://bug.example.com/new?title={title}&info={info}" }
undefined
mcpServers
Configures connections to one or more Model-Context Protocol (MCP) servers for discovering and using custom tools. Qwen Code attempts to connect to each configured MCP server to discover available tools. If multiple MCP servers expose a tool with the same name, the tool names will be prefixed with the server alias you defined in the configuration (e.g.,
serverAlias__actualToolName
) to avoid conflicts. Note that the system might strip certain schema properties from MCP tool definitions for compatibility. At least one of
command
,
url
, or
httpUrl
must be provided. If multiple are specified, the order of precedence is
httpUrl
, then
url
, then
command
.
Property
Type
Description
Optional
mcpServers.<SERVER_NAME>.command
string
The command to execute to start the MCP server via standard I/O.
Yes
mcpServers.<SERVER_NAME>.args
array of strings
Arguments to pass to the command.
Yes
mcpServers.<SERVER_NAME>.env
object
Environment variables to set for the server process.
Yes
mcpServers.<SERVER_NAME>.cwd
string
The working directory in which to start the server.
Yes
mcpServers.<SERVER_NAME>.url
string
The URL of an MCP server that uses Server-Sent Events (SSE) for communication.
Yes
mcpServers.<SERVER_NAME>.httpUrl
string
The URL of an MCP server that uses streamable HTTP for communication.
Yes
mcpServers.<SERVER_NAME>.headers
object
A map of HTTP headers to send with requests to
url
or
httpUrl
.
Yes
mcpServers.<SERVER_NAME>.timeout
number
Timeout in milliseconds for requests to this MCP server.
Yes
mcpServers.<SERVER_NAME>.trust
boolean
Trust this server and bypass all tool call confirmations.
Yes
mcpServers.<SERVER_NAME>.description
string
A brief description of the server, which may be used for display purposes.
Yes
mcpServers.<SERVER_NAME>.includeTools
array of strings
List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.
Yes
mcpServers.<SERVER_NAME>.excludeTools
array of strings
List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server.
Note:
excludeTools
takes precedence over
includeTools
- if a tool is in both lists, it will be excluded.
Yes
telemetry
Configures logging and metrics collection for Qwen Code. For more information, see
telemetry
.
Setting
Type
Description
Default
telemetry.enabled
boolean
Whether or not telemetry is enabled.
telemetry.target
string
The destination for collected telemetry. Supported values are
local
and
gcp
.
telemetry.otlpEndpoint
string
The endpoint for the OTLP Exporter.
telemetry.otlpProtocol
string
The protocol for the OTLP Exporter (
grpc
or
http
).
telemetry.logPrompts
boolean
Whether or not to include the content of user prompts in the logs.
telemetry.outfile
string
The file to write telemetry to when
target
is
local
.
telemetry.useCollector
boolean
Whether to use an external OTLP collector.
Example
settings.json
Here is an example of a
settings.json
file with the nested structure, new as of v0.3.0:
{
"proxy": "http://localhost:7890",
"general": {
"vimMode": true,
"preferredEditor": "code"
},
"ui": {
"theme": "GitHub",
"hideTips": false,
"customWittyPhrases": [
"You forget a thousand things every day. Make sure this is one of 'em",
"Connecting to AGI"
]
},
"tools": {
"approvalMode": "yolo",
"sandbox": "docker",
"sandboxImage": "ghcr.io/qwenlm/qwen-code:0.14.1",
"discoveryCommand": "bin/get_tools",
"callCommand": "bin/call_tool",
"exclude": ["write_file"]
},
"mcpServers": {
"mainServer": {
"command": "bin/mcp_server.py"
},
"anotherServer": {
"command": "node",
"args": ["mcp_server.js", "--verbose"]
}
},
"telemetry": {
"enabled": true,
"target": "local",
"otlpEndpoint": "http://localhost:4317",
"logPrompts": true
},
"privacy": {
"usageStatisticsEnabled": true
},
"model": {
"name": "qwen3-coder-plus",
"maxSessionTurns": 10,
"enableOpenAILogging": false,
"openAILoggingDir": "~/qwen-logs",
},
"context": {
"fileName": ["CONTEXT.md", "QWEN.md"],
"includeDirectories": ["path/to/dir1", "~/path/to/dir2", "../path/to/dir3"],
"loadFromIncludeDirectories": true,
"fileFiltering": {
"respectGitIgnore": false
}
},
"advanced": {
"excludedEnvVars": ["DEBUG", "DEBUG_MODE", "NODE_ENV"]
}
}
Shell History
The CLI keeps a history of shell commands you run. To avoid conflicts between different projects, this history is stored in a project-specific directory within your user’s home folder.
Location:
~/.qwen/tmp/<project_hash>/shell_history
<project_hash>
is a unique identifier generated from your project’s root path.
The history is stored in a file named
shell_history
.
Environment Variables &
.env
Files
Environment variables are a common way to configure applications, especially for sensitive information (like tokens) or for settings that might change between environments.
Qwen Code can automatically load environment variables from
.env
files.
For authentication-related variables (like
OPENAI_*
) and the recommended
.qwen/.env
approach, see
Authentication
.
Tip
Environment Variable Exclusion:
Some environment variables (like
DEBUG
and
DEBUG_MODE
) are automatically excluded from project
.env
files by default to prevent interference with the CLI behavior. Variables from
.qwen/.env
files are never excluded. You can customize this behavior using the
advanced.excludedEnvVars
setting in your
settings.json
file.
Environment Variables Table
Variable
Description
Notes
QWEN_TELEMETRY_ENABLED
Set to
true
or
1
to enable telemetry. Any other value is treated as disabling it.
Overrides the
telemetry.enabled
setting.
QWEN_TELEMETRY_TARGET
Sets the telemetry target (
local
or
gcp
).
Overrides the
telemetry.target
setting.
QWEN_TELEMETRY_OTLP_ENDPOINT
Sets the OTLP endpoint for telemetry.
Overrides the
telemetry.otlpEndpoint
setting.
QWEN_TELEMETRY_OTLP_PROTOCOL
Sets the OTLP protocol (
grpc
or
http
).
Overrides the
telemetry.otlpProtocol
setting.
QWEN_TELEMETRY_LOG_PROMPTS
Set to
true
or
1
to enable or disable logging of user prompts. Any other value is treated as disabling it.
Overrides the
telemetry.logPrompts
setting.
QWEN_TELEMETRY_OUTFILE
Sets the file path to write telemetry to when the target is
local
.
Overrides the
telemetry.outfile
setting.
QWEN_TELEMETRY_USE_COLLECTOR
Set to
true
or
1
to enable or disable using an external OTLP collector. Any other value is treated as disabling it.
Overrides the
telemetry.useCollector
setting.
QWEN_SANDBOX
Alternative to the
sandbox
setting in
settings.json
.
Accepts
true
,
false
,
docker
,
podman
, or a custom command string.
QWEN_SANDBOX_IMAGE
Overrides sandbox image selection for Docker/Podman.
Takes precedence over
tools.sandboxImage
.
SEATBELT_PROFILE
(macOS specific) Switches the Seatbelt (
sandbox-exec
) profile on macOS.
permissive-open
: (Default) Restricts writes to the project folder (and a few other folders, see
packages/cli/src/utils/sandbox-macos-permissive-open.sb
) but allows other operations.
strict
: Uses a strict profile that declines operations by default.
<profile_name>
: Uses a custom profile. To define a custom profile, create a file named
sandbox-macos-<profile_name>.sb
in your project’s
.qwen/
directory (e.g.,
my-project/.qwen/sandbox-macos-custom.sb
).
DEBUG
or
DEBUG_MODE
(often used by underlying libraries or the CLI itself) Set to
true
or
1
to enable verbose debug logging, which can be helpful for troubleshooting.
Note:
These variables are automatically excluded from project
.env
files by default to prevent interference with the CLI behavior. Use
.qwen/.env
files if you need to set these for Qwen Code specifically.
NO_COLOR
Set to any value to disable all color output in the CLI.
CLI_TITLE
Set to a string to customize the title of the CLI.
CODE_ASSIST_ENDPOINT
Specifies the endpoint for the code assist server.
This is useful for development and testing.
QWEN_CODE_MAX_OUTPUT_TOKENS
Overrides the default maximum output tokens per response. When not set, Qwen Code uses an adaptive strategy: starts with 8K tokens and automatically retries with 64K if the response is truncated. Set this to a specific value (e.g.,
16000
) to use a fixed limit instead.
Takes precedence over the capped default (8K) but is overridden by
samplingParams.max_tokens
in settings. Disables automatic escalation when set. Example:
export QWEN_CODE_MAX_OUTPUT_TOKENS=16000
QWEN_CODE_UNATTENDED_RETRY
Set to
true
or
1
to enable persistent retry mode. When enabled, transient API capacity errors (HTTP 429 Rate Limit and 529 Overloaded) are retried indefinitely with exponential backoff (capped at 5 minutes per retry) and heartbeat keepalives every 30 seconds on stderr.
Designed for CI/CD pipelines and background automation where long-running tasks should survive temporary API outages. Must be set explicitly —
CI=true
alone does
not
activate this mode. See
Headless Mode
for details. Example:
export QWEN_CODE_UNATTENDED_RETRY=1
QWEN_CODE_PROFILE_STARTUP
Set to
1
to enable startup performance profiling. Writes a JSON timing report to
~/.qwen/startup-perf/
with per-phase durations.
Only active inside the sandbox child process. Zero overhead when not set. Example:
export QWEN_CODE_PROFILE_STARTUP=1
Command-Line Arguments
Arguments passed directly when running the CLI can override other configurations for that specific session.
For sandbox image selection, precedence is:
--sandbox-image
>
QWEN_SANDBOX_IMAGE
>
tools.sandboxImage
> built-in default image.
Command-Line Arguments Table
Argument
Alias
Description
Possible Values
Notes
--model
-m
Specifies the Qwen model to use for this session.
Model name
Example:
npm start -- --model qwen3-coder-plus
--prompt
-p
Used to pass a prompt directly to the command. This invokes Qwen Code in a non-interactive mode.
Your prompt text
For scripting examples, use the
--output-format json
flag to get structured output.
--prompt-interactive
-i
Starts an interactive session with the provided prompt as the initial input.
Your prompt text
The prompt is processed within the interactive session, not before it. Cannot be used when piping input from stdin. Example:
qwen -i "explain this code"
--system-prompt
Overrides the built-in main session system prompt for this run.
Your prompt text
Loaded context files such as
QWEN.md
are still appended after this override. Can be combined with
--append-system-prompt
.
--append-system-prompt
Appends extra instructions to the main session system prompt for this run.
Your prompt text
Applied after the built-in prompt and loaded context files. Can be combined with
--system-prompt
. See
Headless Mode
for examples.
--output-format
-o
Specifies the format of the CLI output for non-interactive mode.
text
,
json
,
stream-json
text
: (Default) The standard human-readable output.
json
: A machine-readable JSON output emitted at the end of execution.
stream-json
: Streaming JSON messages emitted as they occur during execution. For structured output and scripting, use the
--output-format json
or
--output-format stream-json
flag. See
Headless Mode
for detailed information.
--input-format
Specifies the format consumed from standard input.
text
,
stream-json
text
: (Default) Standard text input from stdin or command-line arguments.
stream-json
: JSON message protocol via stdin for bidirectional communication. Requirement:
--input-format stream-json
requires
--output-format stream-json
to be set. When using
stream-json
, stdin is reserved for protocol messages. See
Headless Mode
for detailed information.
--include-partial-messages
Include partial assistant messages when using
stream-json
output format. When enabled, emits stream events (message_start, content_block_delta, etc.) as they occur during streaming.
Default:
false
. Requirement: Requires
--output-format stream-json
to be set. See
Headless Mode
for detailed information about stream events.
--sandbox
-s
Enables sandbox mode for this session.
--sandbox-image
Sets the sandbox image URI.
--debug
-d
Enables debug mode for this session, providing more verbose output.
--all-files
-a
If set, recursively includes all files within the current directory as context for the prompt.
--help
-h
Displays help information about command-line arguments.
--show-memory-usage
Displays the current memory usage.
--yolo
Enables YOLO mode, which automatically approves all tool calls.
--approval-mode
Sets the approval mode for tool calls.
plan
,
default
,
auto-edit
,
yolo
Supported modes:
plan
: Analyze only—do not modify files or execute commands.
default
: Require approval for file edits or shell commands (default behavior).
auto-edit
: Automatically approve edit tools (edit, write_file) while prompting for others.
yolo
: Automatically approve all tool calls (equivalent to
--yolo
). Cannot be used together with
--yolo
. Use
--approval-mode=yolo
instead of
--yolo
for the new unified approach. Example:
qwen --approval-mode auto-edit
See more about
Approval Mode
.
--allowed-tools
A comma-separated list of tool names that will bypass the confirmation dialog.
Tool names
Example:
qwen --allowed-tools "Shell(git status)"
--disabled-slash-commands
Slash command names to hide/disable (comma-separated or repeated). Unioned with the
slashCommands.disabled
setting and the
QWEN_DISABLED_SLASH_COMMANDS
environment variable. Matched case-insensitively against the final command name.
Command names
Example:
qwen --disabled-slash-commands "auth,mcp,extensions"
--telemetry
Enables
telemetry
.
--telemetry-target
Sets the telemetry target.
See
telemetry
for more information.
--telemetry-otlp-endpoint
Sets the OTLP endpoint for telemetry.
See
telemetry
for more information.
--telemetry-otlp-protocol
Sets the OTLP protocol for telemetry (
grpc
or
http
).
Defaults to
grpc
. See
telemetry
for more information.
--telemetry-log-prompts
Enables logging of prompts for telemetry.
See
telemetry
for more information.
--checkpointing
Enables
checkpointing
.
--acp
Enables ACP mode (Agent Client Protocol). Useful for IDE/editor integrations like
Zed
.
Stable. Replaces the deprecated
--experimental-acp
flag.
--experimental-lsp
Enables experimental
LSP (Language Server Protocol)
feature for code intelligence (go-to-definition, find references, diagnostics, etc.).
Experimental. Requires language servers to be installed.
--extensions
-e
Specifies a list of extensions to use for the session.
Extension names
If not provided, all available extensions are used. Use the special term
qwen -e none
to disable all extensions. Example:
qwen -e my-extension -e my-other-extension
--list-extensions
-l
Lists all available extensions and exits.
--proxy
Sets the proxy for the CLI.
Proxy URL
Example:
--proxy http://localhost:7890
.
--include-directories
Includes additional directories in the workspace for multi-directory support.
Directory paths
Can be specified multiple times or as comma-separated values. 5 directories can be added at maximum. Example:
--include-directories /path/to/project1,/path/to/project2
or
--include-directories /path/to/project1 --include-directories /path/to/project2
--screen-reader
Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers.
--version
Displays the version of the CLI.
--openai-logging
Enables logging of OpenAI API calls for debugging and analysis.
This flag overrides the
enableOpenAILogging
setting in
settings.json
.
--openai-logging-dir
Sets a custom directory path for OpenAI API logs.
Directory path
This flag overrides the
openAILoggingDir
setting in
settings.json
. Supports absolute paths, relative paths, and
~
expansion. Example:
qwen --openai-logging-dir "~/qwen-logs" --openai-logging
Context Files (Hierarchical Instructional Context)
While not strictly configuration for the CLI’s
behavior
, context files (defaulting to
QWEN.md
but configurable via the
context.fileName
setting) are crucial for configuring the
instructional context
(also referred to as “memory”). This powerful feature allows you to give project-specific instructions, coding style guides, or any relevant background information to the AI, making its responses more tailored and accurate to your needs. The CLI includes UI elements, such as an indicator in the footer showing the number of loaded context files, to keep you informed about the active context.
Purpose:
These Markdown files contain instructions, guidelines, or context that you want the Qwen model to be aware of during your interactions. The system is designed to manage this instructional context hierarchically.
Example Context File Content (e.g.
QWEN.md
)
Here’s a conceptual example of what a context file at the root of a TypeScript project might contain:
# Project: My Awesome TypeScript Library
## General Instructions:
- When generating new TypeScript code, please follow the existing coding style.
- Ensure all new functions and classes have JSDoc comments.
- Prefer functional programming paradigms where appropriate.
- All code should be compatible with TypeScript 5.0 and Node.js 20+.
## Coding Style:
- Use 2 spaces for indentation.
- Interface names should be prefixed with `I` (e.g., `IUserService`).
- Private class members should be prefixed with an underscore (`_`).
- Always use strict equality (`===` and `!==`).
## Specific Component: `src/api/client.ts`
- This file handles all outbound API requests.
- When adding new API call functions, ensure they include robust error handling and logging.
- Use the existing `fetchWithRetry` utility for all GET requests.
## Regarding Dependencies:
- Avoid introducing new external dependencies unless absolutely necessary.
- If a new dependency is required, please state the reason.
This example demonstrates how you can provide general project context, specific coding conventions, and even notes about particular files or components. The more relevant and precise your context files are, the better the AI can assist you. Project-specific context files are highly encouraged to establish conventions and context.
Hierarchical Loading and Precedence:
The CLI implements a hierarchical memory system by loading context files (e.g.,
QWEN.md
) from several locations. Content from files lower in this list (more specific) typically overrides or supplements content from files higher up (more general). The exact concatenation order and final context can be inspected using the
/memory show
command. The typical loading order is:
Global Context File:
Location:
~/.qwen/<configured-context-filename>
(e.g.,
~/.qwen/QWEN.md
in your user home directory).
Scope: Provides default instructions for all your projects.
Project Root & Ancestors Context Files:
Location: The CLI searches for the configured context file in the current working directory and then in each parent directory up to either the project root (identified by a
.git
folder) or your home directory.
Scope: Provides context relevant to the entire project or a significant portion of it.
Concatenation & UI Indication:
The contents of all found context files are concatenated (with separators indicating their origin and path) and provided as part of the system prompt. The CLI footer displays the count of loaded context files, giving you a quick visual cue about the active instructional context.
Importing Content:
You can modularize your context files by importing other Markdown files using the
@path/to/file.md
syntax. For more details, see the
Memory Import Processor documentation
.
Commands for Memory Management:
Use
/memory refresh
to force a re-scan and reload of all context files from all configured locations. This updates the AI’s instructional context.
Use
/memory show
to display the combined instructional context currently loaded, allowing you to verify the hierarchy and content being used by the AI.
See the
Commands documentation
for full details on the
/memory
command and its sub-commands (
show
and
refresh
).
By understanding and utilizing these configuration layers and the hierarchical nature of context files, you can effectively manage the AI’s memory and tailor Qwen Code’s responses to your specific needs and projects.
Sandbox
Qwen Code can execute potentially unsafe operations (like shell commands and file modifications) within a sandboxed environment to protect your system.
Sandbox
is disabled by default, but you can enable it in a few ways:
Using
--sandbox
or
-s
flag.
Setting
QWEN_SANDBOX
environment variable.
Sandbox is enabled when using
--yolo
or
--approval-mode=yolo
by default.
By default, it uses a pre-built
qwen-code-sandbox
Docker image.
For project-specific sandboxing needs, you can create a custom Dockerfile at
.qwen/sandbox.Dockerfile
in your project’s root directory. This Dockerfile can be based on the base sandbox image:
FROM qwen-code-sandbox
# Add your custom dependencies or configurations here
# For example:
# RUN apt-get update && apt-get install -y some-package
# COPY ./my-config /app/my-config
When
.qwen/sandbox.Dockerfile
exists, you can use
BUILD_SANDBOX
environment variable when running Qwen Code to automatically build the custom sandbox image:
BUILD_SANDBOX=1 qwen -s
Usage Statistics
To help us improve Qwen Code, we collect anonymized usage statistics. This data helps us understand how the CLI is used, identify common issues, and prioritize new features.
What we collect:
Tool Calls:
We log the names of the tools that are called, whether they succeed or fail, and how long they take to execute. We do not collect the arguments passed to the tools or any data returned by them.
API Requests:
We log the model used for each request, the duration of the request, and whether it was successful. We do not collect the content of the prompts or responses.
Session Information:
We collect information about the configuration of the CLI, such as the enabled tools and the approval mode.
What we DON’T collect:
Personally Identifiable Information (PII):
We do not collect any personal information, such as your name, email address, or API keys.
Prompt and Response Content:
We do not log the content of your prompts or the responses from the model.
File Content:
We do not log the content of any files that are read or written by the CLI.
How to opt out:
You can opt out of usage statistics collection at any time by setting the
usageStatisticsEnabled
property to
false
under the
privacy
category in your
settings.json
file:
{
"privacy": {
"usageStatisticsEnabled": false
}
}
Note
When usage statistics are enabled, events are sent to an Alibaba Cloud RUM collection endpoint.
Last updated on
May 18, 2026
Contextual Tips
Authentication</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/settings/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Shell Tool ( run_shell_command )</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/shell/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/shell/</guid>
  <pubDate>Thu, 13 Jun 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Shell
Shell Tool (
run_shell_command
)
This document describes the
run_shell_command
tool for Qwen Code.
Description
Use
run_shell_command
to interact with the underlying system, run scripts, or perform command-line operations.
run_shell_command
executes a given shell command, including interactive commands that require user input (e.g.,
vim
,
git rebase -i
) if the
tools.shell.enableInteractiveShell
setting is set to
true
.
On Windows, commands are executed with
cmd.exe /c...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Shell
Shell Tool (
run_shell_command
)
This document describes the
run_shell_command
tool for Qwen Code.
Description
Use
run_shell_command
to interact with the underlying system, run scripts, or perform command-line operations.
run_shell_command
executes a given shell command, including interactive commands that require user input (e.g.,
vim
,
git rebase -i
) if the
tools.shell.enableInteractiveShell
setting is set to
true
.
On Windows, commands are executed with
cmd.exe /c
. On other platforms, they are executed with
bash -c
.
Arguments
run_shell_command
takes the following arguments:
command
(string, required): The exact shell command to execute.
description
(string, optional): A brief description of the command’s purpose, which will be shown to the user.
directory
(string, optional): The directory (relative to the project root) in which to execute the command. If not provided, the command runs in the project root.
is_background
(boolean, required): Whether to run the command in background. This parameter is required to ensure explicit decision-making about command execution mode. Set to true for long-running processes like development servers, watchers, or daemons that should continue running without blocking further commands. Set to false for one-time commands that should complete before proceeding.
How to use
run_shell_command
with Qwen Code
When using
run_shell_command
, the command is executed as a subprocess. You can control whether commands run in background or foreground using the
is_background
parameter, or by explicitly adding
&
to commands. The tool returns detailed information about the execution, including:
Required Background Parameter
The
is_background
parameter is
required
for all command executions. This design ensures that the LLM (and users) must explicitly decide whether each command should run in the background or foreground, promoting intentional and predictable command execution behavior. By making this parameter mandatory, we avoid unintended fallback to foreground execution, which could block subsequent operations when dealing with long-running processes.
Background vs Foreground Execution
The tool intelligently handles background and foreground execution based on your explicit choice:
Use background execution (
is_background: true
) for:
Long-running development servers:
npm run start
,
npm run dev
,
yarn dev
Build watchers:
npm run watch
,
webpack --watch
Database servers:
mongod
,
mysql
,
redis-server
Web servers:
python -m http.server
,
php -S localhost:8000
Any command expected to run indefinitely until manually stopped
Use foreground execution (
is_background: false
) for:
One-time commands:
ls
,
cat
,
grep
Build commands:
npm run build
,
make
Installation commands:
npm install
,
pip install
Git operations:
git commit
,
git push
Test runs:
npm test
,
pytest
Execution Information
The tool returns detailed information about the execution, including:
Command
: The command that was executed.
Directory
: The directory where the command was run.
Stdout
: Output from the standard output stream.
Stderr
: Output from the standard error stream.
Error
: Any error message reported by the subprocess.
Exit Code
: The exit code of the command.
Signal
: The signal number if the command was terminated by a signal.
Background PIDs
: A list of PIDs for any background processes started.
Usage:
run_shell_command(command
=
"Your commands."
,
description="Your description of the command.",
directory="Your execution directory.",
is_background=
false
)
Note:
The
is_background
parameter is required and must be explicitly specified for every command execution.
run_shell_command
examples
List files in the current directory:
run_shell_command(command
=
"ls -la"
,
is_background=
false
)
Run a script in a specific directory:
run_shell_command(command
=
"./my_script.sh"
,
directory="scripts",
description="Run my custom script",
is_background=
false
)
Start a background development server (recommended approach):
run_shell_command(command
=
"npm run dev"
,
description="Start development server in background",
is_background=
true
)
Start a background server (alternative with explicit &):
run_shell_command(command
=
"npm run dev &"
,
description="Start development server in background",
is_background=
false
)
Run a build command in foreground:
run_shell_command(command
=
"npm run build"
,
description="Build the project",
is_background=
false
)
Start multiple background services:
run_shell_command(command
=
"docker-compose up"
,
description="Start all services",
is_background=
true
)
Configuration
You can configure the behavior of the
run_shell_command
tool by modifying your
settings.json
file or by using the
/settings
command in the Qwen Code.
Enabling Interactive Commands
The
tools.shell.enableInteractiveShell
setting controls whether shell commands are executed via
node-pty
(interactive PTY) or the plain
child_process
backend. When enabled, interactive sessions such as
vim
,
git rebase -i
, and TUI programs work correctly.
This setting defaults to
true
on most platforms. On Windows builds
<= 19041
(before Windows 10 version 2004), it defaults to
false
because older ConPTY implementations have known reliability issues (missing output, hangs). This matches the same cutoff used by VS Code (
microsoft/vscode#123725
). If
node-pty
is not available at runtime, the tool falls back to
child_process
regardless of this setting.
To explicitly override the default, set the value in
settings.json
:
Example
settings.json
:
{
"tools"
: {
"shell"
: {
"enableInteractiveShell"
:
true
}
}
}
Showing Color in Output
To show color in the shell output, you need to set the
tools.shell.showColor
setting to
true
.
Note: This setting only applies when
tools.shell.enableInteractiveShell
is enabled.
Example
settings.json
:
{
"tools"
: {
"shell"
: {
"showColor"
:
true
}
}
}
Setting the Pager
You can set a custom pager for the shell output by setting the
tools.shell.pager
setting. The default pager is
cat
.
Note: This setting only applies when
tools.shell.enableInteractiveShell
is enabled.
Example
settings.json
:
{
"tools"
: {
"shell"
: {
"pager"
:
"less"
}
}
}
Interactive Commands
The
run_shell_command
tool now supports interactive commands by integrating a pseudo-terminal (pty). This allows you to run commands that require real-time user input, such as text editors (
vim
,
nano
), terminal-based UIs (
htop
), and interactive version control operations (
git rebase -i
).
When an interactive command is running, you can send input to it from the Qwen Code. To focus on the interactive shell, press
ctrl+f
. The terminal output, including complex TUIs, will be rendered correctly.
Important notes
Security:
Be cautious when executing commands, especially those constructed from user input, to prevent security vulnerabilities.
Error handling:
Check the
Stderr
,
Error
, and
Exit Code
fields to determine if a command executed successfully.
Background processes:
When
is_background=true
or when a command contains
&
, the tool will return immediately and the process will continue to run in the background. The
Background PIDs
field will contain the process ID of the background process.
Background execution choices:
The
is_background
parameter is required and provides explicit control over execution mode. You can also add
&
to the command for manual background execution, but the
is_background
parameter must still be specified. The parameter provides clearer intent and automatically handles the background execution setup.
Command descriptions:
When using
is_background=true
, the command description will include a
[background]
indicator to clearly show the execution mode.
Environment Variables
When
run_shell_command
executes a command, it sets the
QWEN_CODE=1
environment variable in the subprocess’s environment. This allows scripts or tools to detect if they are being run from within the CLI.
Command Restrictions
You can restrict the commands that can be executed by the
run_shell_command
tool by using the
tools.core
and
tools.exclude
settings in your configuration file.
tools.core
: To restrict
run_shell_command
to a specific set of commands, add entries to the
core
list under the
tools
category in the format
run_shell_command(<command>)
. For example,
"tools": {"core": ["run_shell_command(git)"]}
will only allow
git
commands. Including the generic
run_shell_command
acts as a wildcard, allowing any command not explicitly blocked.
tools.exclude
: To block specific commands, add entries to the
exclude
list under the
tools
category in the format
run_shell_command(<command>)
. For example,
"tools": {"exclude": ["run_shell_command(rm)"]}
will block
rm
commands.
The validation logic is designed to be secure and flexible:
Command Chaining Disabled
: The tool automatically splits commands chained with
&&
,
||
, or
;
and validates each part separately. If any part of the chain is disallowed, the entire command is blocked.
Prefix Matching
: The tool uses prefix matching. For example, if you allow
git
, you can run
git status
or
git log
.
Blocklist Precedence
: The
tools.exclude
list is always checked first. If a command matches a blocked prefix, it will be denied, even if it also matches an allowed prefix in
tools.core
.
Command Restriction Examples
Allow only specific command prefixes
To allow only
git
and
npm
commands, and block all others:
{
"tools"
: {
"core"
: [
"run_shell_command(git)"
,
"run_shell_command(npm)"
]
}
}
git status
: Allowed
npm install
: Allowed
ls -l
: Blocked
Block specific command prefixes
To block
rm
and allow all other commands:
{
"tools"
: {
"core"
: [
"run_shell_command"
],
"exclude"
: [
"run_shell_command(rm)"
]
}
}
rm -rf /
: Blocked
git status
: Allowed
npm install
: Allowed
Blocklist takes precedence
If a command prefix is in both
tools.core
and
tools.exclude
, it will be blocked.
{
"tools"
: {
"core"
: [
"run_shell_command(git)"
],
"exclude"
: [
"run_shell_command(git push)"
]
}
}
git push origin main
: Blocked
git status
: Allowed
Block all shell commands
To block all shell commands, add the
run_shell_command
wildcard to
tools.exclude
:
{
"tools"
: {
"exclude"
: [
"run_shell_command"
]
}
}
ls -l
: Blocked
any other command
: Blocked
Security Note for
excludeTools
Command-specific restrictions in
excludeTools
for
run_shell_command
are based on simple string matching and can be easily bypassed. This feature is
not a security mechanism
and should not be relied upon to safely execute untrusted code. It is recommended to use
coreTools
to explicitly select commands
that can be executed.
Last updated on
May 18, 2026
Multi-File Read
Todo Write</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/shell/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Shell
Shell Tool (
run_shell_command
)
This document describes the
run_shell_command
tool for Qwen Code.
Description
Use
run_shell_command
to interact with the underlying system, run scripts, or perform command-line operations.
run_shell_command
executes a given shell command, including interactive commands that require user input (e.g.,
vim
,
git rebase -i
) if the
tools.shell.enableInteractiveShell
setting is set to
true
.
On Windows, commands are executed with
cmd.exe /c...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Shell
Shell Tool (
run_shell_command
)
This document describes the
run_shell_command
tool for Qwen Code.
Description
Use
run_shell_command
to interact with the underlying system, run scripts, or perform command-line operations.
run_shell_command
executes a given shell command, including interactive commands that require user input (e.g.,
vim
,
git rebase -i
) if the
tools.shell.enableInteractiveShell
setting is set to
true
.
On Windows, commands are executed with
cmd.exe /c
. On other platforms, they are executed with
bash -c
.
Arguments
run_shell_command
takes the following arguments:
command
(string, required): The exact shell command to execute.
description
(string, optional): A brief description of the command’s purpose, which will be shown to the user.
directory
(string, optional): The directory (relative to the project root) in which to execute the command. If not provided, the command runs in the project root.
is_background
(boolean, required): Whether to run the command in background. This parameter is required to ensure explicit decision-making about command execution mode. Set to true for long-running processes like development servers, watchers, or daemons that should continue running without blocking further commands. Set to false for one-time commands that should complete before proceeding.
How to use
run_shell_command
with Qwen Code
When using
run_shell_command
, the command is executed as a subprocess. You can control whether commands run in background or foreground using the
is_background
parameter, or by explicitly adding
&
to commands. The tool returns detailed information about the execution, including:
Required Background Parameter
The
is_background
parameter is
required
for all command executions. This design ensures that the LLM (and users) must explicitly decide whether each command should run in the background or foreground, promoting intentional and predictable command execution behavior. By making this parameter mandatory, we avoid unintended fallback to foreground execution, which could block subsequent operations when dealing with long-running processes.
Background vs Foreground Execution
The tool intelligently handles background and foreground execution based on your explicit choice:
Use background execution (
is_background: true
) for:
Long-running development servers:
npm run start
,
npm run dev
,
yarn dev
Build watchers:
npm run watch
,
webpack --watch
Database servers:
mongod
,
mysql
,
redis-server
Web servers:
python -m http.server
,
php -S localhost:8000
Any command expected to run indefinitely until manually stopped
Use foreground execution (
is_background: false
) for:
One-time commands:
ls
,
cat
,
grep
Build commands:
npm run build
,
make
Installation commands:
npm install
,
pip install
Git operations:
git commit
,
git push
Test runs:
npm test
,
pytest
Execution Information
The tool returns detailed information about the execution, including:
Command
: The command that was executed.
Directory
: The directory where the command was run.
Stdout
: Output from the standard output stream.
Stderr
: Output from the standard error stream.
Error
: Any error message reported by the subprocess.
Exit Code
: The exit code of the command.
Signal
: The signal number if the command was terminated by a signal.
Background PIDs
: A list of PIDs for any background processes started.
Usage:
run_shell_command(command
=
"Your commands."
,
description="Your description of the command.",
directory="Your execution directory.",
is_background=
false
)
Note:
The
is_background
parameter is required and must be explicitly specified for every command execution.
run_shell_command
examples
List files in the current directory:
run_shell_command(command
=
"ls -la"
,
is_background=
false
)
Run a script in a specific directory:
run_shell_command(command
=
"./my_script.sh"
,
directory="scripts",
description="Run my custom script",
is_background=
false
)
Start a background development server (recommended approach):
run_shell_command(command
=
"npm run dev"
,
description="Start development server in background",
is_background=
true
)
Start a background server (alternative with explicit &):
run_shell_command(command
=
"npm run dev &"
,
description="Start development server in background",
is_background=
false
)
Run a build command in foreground:
run_shell_command(command
=
"npm run build"
,
description="Build the project",
is_background=
false
)
Start multiple background services:
run_shell_command(command
=
"docker-compose up"
,
description="Start all services",
is_background=
true
)
Configuration
You can configure the behavior of the
run_shell_command
tool by modifying your
settings.json
file or by using the
/settings
command in the Qwen Code.
Enabling Interactive Commands
The
tools.shell.enableInteractiveShell
setting controls whether shell commands are executed via
node-pty
(interactive PTY) or the plain
child_process
backend. When enabled, interactive sessions such as
vim
,
git rebase -i
, and TUI programs work correctly.
This setting defaults to
true
on most platforms. On Windows builds
<= 19041
(before Windows 10 version 2004), it defaults to
false
because older ConPTY implementations have known reliability issues (missing output, hangs). This matches the same cutoff used by VS Code (
microsoft/vscode#123725
). If
node-pty
is not available at runtime, the tool falls back to
child_process
regardless of this setting.
To explicitly override the default, set the value in
settings.json
:
Example
settings.json
:
{
"tools"
: {
"shell"
: {
"enableInteractiveShell"
:
true
}
}
}
Showing Color in Output
To show color in the shell output, you need to set the
tools.shell.showColor
setting to
true
.
Note: This setting only applies when
tools.shell.enableInteractiveShell
is enabled.
Example
settings.json
:
{
"tools"
: {
"shell"
: {
"showColor"
:
true
}
}
}
Setting the Pager
You can set a custom pager for the shell output by setting the
tools.shell.pager
setting. The default pager is
cat
.
Note: This setting only applies when
tools.shell.enableInteractiveShell
is enabled.
Example
settings.json
:
{
"tools"
: {
"shell"
: {
"pager"
:
"less"
}
}
}
Interactive Commands
The
run_shell_command
tool now supports interactive commands by integrating a pseudo-terminal (pty). This allows you to run commands that require real-time user input, such as text editors (
vim
,
nano
), terminal-based UIs (
htop
), and interactive version control operations (
git rebase -i
).
When an interactive command is running, you can send input to it from the Qwen Code. To focus on the interactive shell, press
ctrl+f
. The terminal output, including complex TUIs, will be rendered correctly.
Important notes
Security:
Be cautious when executing commands, especially those constructed from user input, to prevent security vulnerabilities.
Error handling:
Check the
Stderr
,
Error
, and
Exit Code
fields to determine if a command executed successfully.
Background processes:
When
is_background=true
or when a command contains
&
, the tool will return immediately and the process will continue to run in the background. The
Background PIDs
field will contain the process ID of the background process.
Background execution choices:
The
is_background
parameter is required and provides explicit control over execution mode. You can also add
&
to the command for manual background execution, but the
is_background
parameter must still be specified. The parameter provides clearer intent and automatically handles the background execution setup.
Command descriptions:
When using
is_background=true
, the command description will include a
[background]
indicator to clearly show the execution mode.
Environment Variables
When
run_shell_command
executes a command, it sets the
QWEN_CODE=1
environment variable in the subprocess’s environment. This allows scripts or tools to detect if they are being run from within the CLI.
Command Restrictions
You can restrict the commands that can be executed by the
run_shell_command
tool by using the
tools.core
and
tools.exclude
settings in your configuration file.
tools.core
: To restrict
run_shell_command
to a specific set of commands, add entries to the
core
list under the
tools
category in the format
run_shell_command(<command>)
. For example,
"tools": {"core": ["run_shell_command(git)"]}
will only allow
git
commands. Including the generic
run_shell_command
acts as a wildcard, allowing any command not explicitly blocked.
tools.exclude
: To block specific commands, add entries to the
exclude
list under the
tools
category in the format
run_shell_command(<command>)
. For example,
"tools": {"exclude": ["run_shell_command(rm)"]}
will block
rm
commands.
The validation logic is designed to be secure and flexible:
Command Chaining Disabled
: The tool automatically splits commands chained with
&&
,
||
, or
;
and validates each part separately. If any part of the chain is disallowed, the entire command is blocked.
Prefix Matching
: The tool uses prefix matching. For example, if you allow
git
, you can run
git status
or
git log
.
Blocklist Precedence
: The
tools.exclude
list is always checked first. If a command matches a blocked prefix, it will be denied, even if it also matches an allowed prefix in
tools.core
.
Command Restriction Examples
Allow only specific command prefixes
To allow only
git
and
npm
commands, and block all others:
{
"tools"
: {
"core"
: [
"run_shell_command(git)"
,
"run_shell_command(npm)"
]
}
}
git status
: Allowed
npm install
: Allowed
ls -l
: Blocked
Block specific command prefixes
To block
rm
and allow all other commands:
{
"tools"
: {
"core"
: [
"run_shell_command"
],
"exclude"
: [
"run_shell_command(rm)"
]
}
}
rm -rf /
: Blocked
git status
: Allowed
npm install
: Allowed
Blocklist takes precedence
If a command prefix is in both
tools.core
and
tools.exclude
, it will be blocked.
{
"tools"
: {
"core"
: [
"run_shell_command(git)"
],
"exclude"
: [
"run_shell_command(git push)"
]
}
}
git push origin main
: Blocked
git status
: Allowed
Block all shell commands
To block all shell commands, add the
run_shell_command
wildcard to
tools.exclude
:
{
"tools"
: {
"exclude"
: [
"run_shell_command"
]
}
}
ls -l
: Blocked
any other command
: Blocked
Security Note for
excludeTools
Command-specific restrictions in
excludeTools
for
run_shell_command
are based on simple string matching and can be easily bypassed. This feature is
not a security mechanism
and should not be relied upon to safely execute untrusted code. It is recommended to use
coreTools
to explicitly select commands
that can be executed.
Last updated on
May 18, 2026
Multi-File Read
Todo Write</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/shell/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Documentation</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/</guid>
  <pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Qwen Code Documentation
Welcome to the Qwen Code documentation. Qwen Code is an agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Documentation Sections
User Guide
Learn how to use Qwen Code as an end user. This section covers:
Basic installation and setup
Common usage patterns
Features and capabilities
Configuration options
Troubleshooting
Developer Guide
Learn how to contribute to and develop Qwen Code. This section covers:
Architecture...</p><div style="font-size:16px;line-height:1.8;color:#333">Qwen Code Documentation
Welcome to the Qwen Code documentation. Qwen Code is an agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Documentation Sections
User Guide
Learn how to use Qwen Code as an end user. This section covers:
Basic installation and setup
Common usage patterns
Features and capabilities
Configuration options
Troubleshooting
Developer Guide
Learn how to contribute to and develop Qwen Code. This section covers:
Architecture overview
Contributing guidelines
Core concepts and implementation details
Tools and development workflow
Extension and plugin development
Last updated on
May 18, 2026</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Qwen Code Documentation
Welcome to the Qwen Code documentation. Qwen Code is an agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Documentation Sections
User Guide
Learn how to use Qwen Code as an end user. This section covers:
Basic installation and setup
Common usage patterns
Features and capabilities
Configuration options
Troubleshooting
Developer Guide
Learn how to contribute to and develop Qwen Code. This section covers:
Architecture...</p><div style="font-size:16px;line-height:1.8;color:#333">Qwen Code Documentation
Welcome to the Qwen Code documentation. Qwen Code is an agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Documentation Sections
User Guide
Learn how to use Qwen Code as an end user. This section covers:
Basic installation and setup
Common usage patterns
Features and capabilities
Configuration options
Troubleshooting
Developer Guide
Learn how to contribute to and develop Qwen Code. This section covers:
Architecture overview
Contributing guidelines
Core concepts and implementation details
Tools and development workflow
Extension and plugin development
Last updated on
May 18, 2026</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Typescript SDK</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-typescript/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-typescript/</guid>
  <pubDate>Fri, 07 Jun 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Typescript SDK
Typescript SDK
@qwen-code/sdk
A minimum experimental TypeScript SDK for programmatic access to Qwen Code.
Feel free to submit a feature request/issue/PR.
Installation
npm
install
@qwen-code/sdk
Requirements
Node.js &gt;= 20.0.0
Qwen Code
&gt;= 0.4.0 (stable) installed and accessible in PATH
Note for nvm users
: If you use nvm to manage Node.js versions, the SDK may not be able to auto-detect the Qwen Code executable. You should explicitly set the
pathToQwenExecutable
opt...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Typescript SDK
Typescript SDK
@qwen-code/sdk
A minimum experimental TypeScript SDK for programmatic access to Qwen Code.
Feel free to submit a feature request/issue/PR.
Installation
npm
install
@qwen-code/sdk
Requirements
Node.js >= 20.0.0
Qwen Code
>= 0.4.0 (stable) installed and accessible in PATH
Note for nvm users
: If you use nvm to manage Node.js versions, the SDK may not be able to auto-detect the Qwen Code executable. You should explicitly set the
pathToQwenExecutable
option to the full path of the
qwen
binary.
Quick Start
import
{ query }
from
'@qwen-code/sdk'
;
// Single-turn query
const
result
=
query
({
prompt:
'What files are in the current directory?'
,
options: {
cwd:
'/path/to/project'
,
},
});
// Iterate over messages
for
await
(
const
message
of
result) {
if
(message.type
===
'assistant'
) {
console.
log
(
'Assistant:'
, message.message.content);
}
else
if
(message.type
===
'result'
) {
console.
log
(
'Result:'
, message.result);
}
}
API Reference
query(config)
Creates a new query session with the Qwen Code.
Parameters
prompt
:
string | AsyncIterable<SDKUserMessage>
- The prompt to send. Use a string for single-turn queries or an async iterable for multi-turn conversations.
options
:
QueryOptions
- Configuration options for the query session.
QueryOptions
Option
Type
Default
Description
cwd
string
process.cwd()
The working directory for the query session. Determines the context in which file operations and commands are executed.
model
string
-
The AI model to use (e.g.,
'qwen-max'
,
'qwen-plus'
,
'qwen-turbo'
). Takes precedence over
OPENAI_MODEL
and
QWEN_MODEL
environment variables.
pathToQwenExecutable
string
Auto-detected
Path to the Qwen Code executable. Supports multiple formats:
'qwen'
(native binary from PATH),
'/path/to/qwen'
(explicit path),
'/path/to/cli.js'
(Node.js bundle),
'node:/path/to/cli.js'
(force Node.js runtime),
'bun:/path/to/cli.js'
(force Bun runtime). If not provided, auto-detects from:
QWEN_CODE_CLI_PATH
env var,
~/.volta/bin/qwen
,
~/.npm-global/bin/qwen
,
/usr/local/bin/qwen
,
~/.local/bin/qwen
,
~/node_modules/.bin/qwen
,
~/.yarn/bin/qwen
.
permissionMode
'default' | 'plan' | 'auto-edit' | 'yolo'
'default'
Permission mode controlling tool execution approval. See
Permission Modes
for details.
canUseTool
CanUseTool
-
Custom permission handler for tool execution approval. Invoked when a tool requires confirmation. Must respond within 60 seconds or the request will be auto-denied. See
Custom Permission Handler
.
env
Record<string, string>
-
Environment variables to pass to the Qwen Code process. Merged with the current process environment.
systemPrompt
string | QuerySystemPromptPreset
-
System prompt configuration for the main session. Use a string to fully override the built-in Qwen Code system prompt, or a preset object to keep the built-in prompt and append extra instructions.
mcpServers
Record<string, McpServerConfig>
-
MCP (Model Context Protocol) servers to connect. Supports external servers (stdio/SSE/HTTP) and SDK-embedded servers. External servers are configured with transport options like
command
,
args
,
url
,
httpUrl
, etc. SDK servers use
{ type: 'sdk', name: string, instance: Server }
.
abortController
AbortController
-
Controller to cancel the query session. Call
abortController.abort()
to terminate the session and cleanup resources.
debug
boolean
false
Enable debug mode for verbose logging from the CLI process.
maxSessionTurns
number
-1
(unlimited)
Maximum number of conversation turns before the session automatically terminates. A turn consists of a user message and an assistant response.
coreTools
string[]
-
Equivalent to
tool.core
in settings.json. If specified, only these tools will be available to the AI. Example:
['read_file', 'write_file', 'run_terminal_cmd']
.
excludeTools
string[]
-
Equivalent to
tool.exclude
in settings.json. Excluded tools return a permission error immediately. Takes highest priority over all other permission settings. Supports pattern matching: tool name (
'write_file'
), tool class (
'ShellTool'
), or shell command prefix (
'ShellTool(rm )'
).
allowedTools
string[]
-
Equivalent to
tool.allowed
in settings.json. Matching tools bypass
canUseTool
callback and execute automatically. Only applies when tool requires confirmation. Supports same pattern matching as
excludeTools
.
authType
'openai' | 'qwen-oauth'
'openai'
Authentication type for the AI service. Using
'qwen-oauth'
in SDK is not recommended as credentials are stored in
~/.qwen
and may need periodic refresh.
agents
SubagentConfig[]
-
Configuration for subagents that can be invoked during the session. Subagents are specialized AI agents for specific tasks or domains.
includePartialMessages
boolean
false
When
true
, the SDK emits incomplete messages as they are being generated, allowing real-time streaming of the AI’s response.
Timeouts
The SDK enforces the following default timeouts:
Timeout
Default
Description
canUseTool
1 minute
Maximum time for
canUseTool
callback to respond. If exceeded, the tool request is auto-denied.
mcpRequest
1 minute
Maximum time for SDK MCP tool calls to complete.
controlRequest
1 minute
Maximum time for control operations like
initialize()
,
setModel()
,
setPermissionMode()
,
getContextUsage()
, and
interrupt()
to complete.
streamClose
1 minute
Maximum time to wait for initialization to complete before closing CLI stdin in multi-turn mode with SDK MCP servers.
You can customize these timeouts via the
timeout
option:
const
query
=
qwen.
query
(
'Your prompt'
, {
timeout: {
canUseTool:
60000
,
// 60 seconds for permission callback
mcpRequest:
600000
,
// 10 minutes for MCP tool calls
controlRequest:
60000
,
// 60 seconds for control requests
streamClose:
15000
,
// 15 seconds for stream close wait
},
});
Message Types
The SDK provides type guards to identify different message types:
import
{
isSDKUserMessage,
isSDKAssistantMessage,
isSDKSystemMessage,
isSDKResultMessage,
isSDKPartialAssistantMessage,
}
from
'@qwen-code/sdk'
;
for
await
(
const
message
of
result) {
if
(
isSDKAssistantMessage
(message)) {
// Handle assistant message
}
else
if
(
isSDKResultMessage
(message)) {
// Handle result message
}
}
Query Instance Methods
The
Query
instance returned by
query()
provides several methods:
const
q
=
query
({ prompt:
'Hello'
, options: {} });
// Get session ID
const
sessionId
=
q.
getSessionId
();
// Check if closed
const
closed
=
q.
isClosed
();
// Interrupt the current operation
await
q.
interrupt
();
// Change permission mode mid-session
await
q.
setPermissionMode
(
'yolo'
);
// Change model mid-session
await
q.
setModel
(
'qwen-max'
);
// Get context window usage breakdown (token counts per category)
const
usage
=
await
q.
getContextUsage
();
// Pass true to hint that per-item details should be displayed
const
detail
=
await
q.
getContextUsage
(
true
);
// Close the session
await
q.
close
();
Permission Modes
The SDK supports different permission modes for controlling tool execution:
default
: Write tools are denied unless approved via
canUseTool
callback or in
allowedTools
. Read-only tools execute without confirmation.
plan
: Blocks all write tools, instructing AI to present a plan first.
auto-edit
: Auto-approve edit tools (edit, write_file) while other tools require confirmation.
yolo
: All tools execute automatically without confirmation.
Permission Priority Chain
excludeTools
- Blocks tools completely
permissionMode: 'plan'
- Blocks non-read-only tools
permissionMode: 'yolo'
- Auto-approves all tools
allowedTools
- Auto-approves matching tools
canUseTool
callback - Custom approval logic
Default behavior - Auto-deny in SDK mode
Examples
Multi-turn Conversation
import
{ query,
type
SDKUserMessage }
from
'@qwen-code/sdk'
;
async
function*
generateMessages
()
:
AsyncIterable
<
SDKUserMessage
> {
yield
{
type:
'user'
,
session_id:
'my-session'
,
message: { role:
'user'
, content:
'Create a hello.txt file'
},
parent_tool_use_id:
null
,
};
// Wait for some condition or user input
yield
{
type:
'user'
,
session_id:
'my-session'
,
message: { role:
'user'
, content:
'Now read the file back'
},
parent_tool_use_id:
null
,
};
}
const
result
=
query
({
prompt:
generateMessages
(),
options: {
permissionMode:
'auto-edit'
,
},
});
for
await
(
const
message
of
result) {
console.
log
(message);
}
Custom Permission Handler
import
{ query,
type
CanUseTool }
from
'@qwen-code/sdk'
;
const
canUseTool
:
CanUseTool
=
async
(
toolName
,
input
, {
signal
})
=>
{
// Allow all read operations
if
(toolName.
startsWith
(
'read_'
)) {
return
{ behavior:
'allow'
, updatedInput: input };
}
// Prompt user for write operations (in a real app)
const
userApproved
=
await
promptUser
(
`Allow ${
toolName
}?`
);
if
(userApproved) {
return
{ behavior:
'allow'
, updatedInput: input };
}
return
{ behavior:
'deny'
, message:
'User denied the operation'
};
};
const
result
=
query
({
prompt:
'Create a new file'
,
options: {
canUseTool,
},
});
With External MCP Servers
import
{ query }
from
'@qwen-code/sdk'
;
const
result
=
query
({
prompt:
'Use the custom tool from my MCP server'
,
options: {
mcpServers: {
'my-server'
: {
command:
'node'
,
args: [
'path/to/mcp-server.js'
],
env: { PORT:
'3000'
},
},
},
},
});
Override the System Prompt
import
{ query }
from
'@qwen-code/sdk'
;
const
result
=
query
({
prompt:
'Say hello in one sentence.'
,
options: {
systemPrompt:
'You are a terse assistant. Answer in exactly one sentence.'
,
},
});
Append to the Built-in System Prompt
import
{ query }
from
'@qwen-code/sdk'
;
const
result
=
query
({
prompt:
'Review the current directory.'
,
options: {
systemPrompt: {
type:
'preset'
,
preset:
'qwen_code'
,
append:
'Be terse and focus on concrete findings.'
,
},
},
});
With SDK-Embedded MCP Servers
The SDK provides
tool
and
createSdkMcpServer
to create MCP servers that run in the same process as your SDK application. This is useful when you want to expose custom tools to the AI without running a separate server process.
tool(name, description, inputSchema, handler)
Creates a tool definition with Zod schema type inference.
Parameter
Type
Description
name
string
Tool name (1-64 chars, starts with letter, alphanumeric and underscores)
description
string
Human-readable description of what the tool does
inputSchema
ZodRawShape
Zod schema object defining the tool’s input parameters
handler
(args, extra) => Promise<Result>
Async function that executes the tool and returns MCP content blocks
The handler must return a
CallToolResult
object with the following structure:
{
content
: Array
<
|
{
type
:
'text'
;
text
: string }
|
{
type
:
'image'
;
data
: string;
mimeType
: string }
|
{
type
:
'resource'
;
uri
: string; mimeType
?:
string; text
?:
string }
>
;
isError
?:
boolean;
}
createSdkMcpServer(options)
Creates an SDK-embedded MCP server instance.
Option
Type
Default
Description
name
string
Required
Unique name for the MCP server
version
string
'1.0.0'
Server version
tools
SdkMcpToolDefinition[]
-
Array of tools created with
tool()
Returns a
McpSdkServerConfigWithInstance
object that can be passed directly to the
mcpServers
option.
Example
import
{ z }
from
'zod'
;
import
{ query, tool, createSdkMcpServer }
from
'@qwen-code/sdk'
;
// Define a tool with Zod schema
const
calculatorTool
=
tool
(
'calculate_sum'
,
'Add two numbers'
,
{ a: z.
number
(), b: z.
number
() },
async
(
args
)
=>
({
content: [{ type:
'text'
, text:
String
(args.a
+
args.b) }],
}),
);
// Create the MCP server
const
server
=
createSdkMcpServer
({
name:
'calculator'
,
tools: [calculatorTool],
});
// Use the server in a query
const
result
=
query
({
prompt:
'What is 42 + 17?'
,
options: {
permissionMode:
'yolo'
,
mcpServers: {
calculator: server,
},
},
});
for
await
(
const
message
of
result) {
console.
log
(message);
}
Abort a Query
import
{ query, isAbortError }
from
'@qwen-code/sdk'
;
const
abortController
=
new
AbortController
();
const
result
=
query
({
prompt:
'Long running task...'
,
options: {
abortController,
},
});
// Abort after 5 seconds
setTimeout
(()
=>
abortController.
abort
(),
5000
);
try
{
for
await
(
const
message
of
result) {
console.
log
(message);
}
}
catch
(error) {
if
(
isAbortError
(error)) {
console.
log
(
'Query was aborted'
);
}
else
{
throw
error;
}
}
Error Handling
The SDK provides an
AbortError
class for handling aborted queries:
import
{ AbortError, isAbortError }
from
'@qwen-code/sdk'
;
try
{
// ... query operations
}
catch
(error) {
if
(
isAbortError
(error)) {
// Handle abort
}
else
{
// Handle other errors
}
}
Last updated on
May 18, 2026
Contributing Guide
Python SDK (alpha)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-typescript/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Typescript SDK
Typescript SDK
@qwen-code/sdk
A minimum experimental TypeScript SDK for programmatic access to Qwen Code.
Feel free to submit a feature request/issue/PR.
Installation
npm
install
@qwen-code/sdk
Requirements
Node.js &gt;= 20.0.0
Qwen Code
&gt;= 0.4.0 (stable) installed and accessible in PATH
Note for nvm users
: If you use nvm to manage Node.js versions, the SDK may not be able to auto-detect the Qwen Code executable. You should explicitly set the
pathToQwenExecutable
opt...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Typescript SDK
Typescript SDK
@qwen-code/sdk
A minimum experimental TypeScript SDK for programmatic access to Qwen Code.
Feel free to submit a feature request/issue/PR.
Installation
npm
install
@qwen-code/sdk
Requirements
Node.js >= 20.0.0
Qwen Code
>= 0.4.0 (stable) installed and accessible in PATH
Note for nvm users
: If you use nvm to manage Node.js versions, the SDK may not be able to auto-detect the Qwen Code executable. You should explicitly set the
pathToQwenExecutable
option to the full path of the
qwen
binary.
Quick Start
import
{ query }
from
'@qwen-code/sdk'
;
// Single-turn query
const
result
=
query
({
prompt:
'What files are in the current directory?'
,
options: {
cwd:
'/path/to/project'
,
},
});
// Iterate over messages
for
await
(
const
message
of
result) {
if
(message.type
===
'assistant'
) {
console.
log
(
'Assistant:'
, message.message.content);
}
else
if
(message.type
===
'result'
) {
console.
log
(
'Result:'
, message.result);
}
}
API Reference
query(config)
Creates a new query session with the Qwen Code.
Parameters
prompt
:
string | AsyncIterable<SDKUserMessage>
- The prompt to send. Use a string for single-turn queries or an async iterable for multi-turn conversations.
options
:
QueryOptions
- Configuration options for the query session.
QueryOptions
Option
Type
Default
Description
cwd
string
process.cwd()
The working directory for the query session. Determines the context in which file operations and commands are executed.
model
string
-
The AI model to use (e.g.,
'qwen-max'
,
'qwen-plus'
,
'qwen-turbo'
). Takes precedence over
OPENAI_MODEL
and
QWEN_MODEL
environment variables.
pathToQwenExecutable
string
Auto-detected
Path to the Qwen Code executable. Supports multiple formats:
'qwen'
(native binary from PATH),
'/path/to/qwen'
(explicit path),
'/path/to/cli.js'
(Node.js bundle),
'node:/path/to/cli.js'
(force Node.js runtime),
'bun:/path/to/cli.js'
(force Bun runtime). If not provided, auto-detects from:
QWEN_CODE_CLI_PATH
env var,
~/.volta/bin/qwen
,
~/.npm-global/bin/qwen
,
/usr/local/bin/qwen
,
~/.local/bin/qwen
,
~/node_modules/.bin/qwen
,
~/.yarn/bin/qwen
.
permissionMode
'default' | 'plan' | 'auto-edit' | 'yolo'
'default'
Permission mode controlling tool execution approval. See
Permission Modes
for details.
canUseTool
CanUseTool
-
Custom permission handler for tool execution approval. Invoked when a tool requires confirmation. Must respond within 60 seconds or the request will be auto-denied. See
Custom Permission Handler
.
env
Record<string, string>
-
Environment variables to pass to the Qwen Code process. Merged with the current process environment.
systemPrompt
string | QuerySystemPromptPreset
-
System prompt configuration for the main session. Use a string to fully override the built-in Qwen Code system prompt, or a preset object to keep the built-in prompt and append extra instructions.
mcpServers
Record<string, McpServerConfig>
-
MCP (Model Context Protocol) servers to connect. Supports external servers (stdio/SSE/HTTP) and SDK-embedded servers. External servers are configured with transport options like
command
,
args
,
url
,
httpUrl
, etc. SDK servers use
{ type: 'sdk', name: string, instance: Server }
.
abortController
AbortController
-
Controller to cancel the query session. Call
abortController.abort()
to terminate the session and cleanup resources.
debug
boolean
false
Enable debug mode for verbose logging from the CLI process.
maxSessionTurns
number
-1
(unlimited)
Maximum number of conversation turns before the session automatically terminates. A turn consists of a user message and an assistant response.
coreTools
string[]
-
Equivalent to
tool.core
in settings.json. If specified, only these tools will be available to the AI. Example:
['read_file', 'write_file', 'run_terminal_cmd']
.
excludeTools
string[]
-
Equivalent to
tool.exclude
in settings.json. Excluded tools return a permission error immediately. Takes highest priority over all other permission settings. Supports pattern matching: tool name (
'write_file'
), tool class (
'ShellTool'
), or shell command prefix (
'ShellTool(rm )'
).
allowedTools
string[]
-
Equivalent to
tool.allowed
in settings.json. Matching tools bypass
canUseTool
callback and execute automatically. Only applies when tool requires confirmation. Supports same pattern matching as
excludeTools
.
authType
'openai' | 'qwen-oauth'
'openai'
Authentication type for the AI service. Using
'qwen-oauth'
in SDK is not recommended as credentials are stored in
~/.qwen
and may need periodic refresh.
agents
SubagentConfig[]
-
Configuration for subagents that can be invoked during the session. Subagents are specialized AI agents for specific tasks or domains.
includePartialMessages
boolean
false
When
true
, the SDK emits incomplete messages as they are being generated, allowing real-time streaming of the AI’s response.
Timeouts
The SDK enforces the following default timeouts:
Timeout
Default
Description
canUseTool
1 minute
Maximum time for
canUseTool
callback to respond. If exceeded, the tool request is auto-denied.
mcpRequest
1 minute
Maximum time for SDK MCP tool calls to complete.
controlRequest
1 minute
Maximum time for control operations like
initialize()
,
setModel()
,
setPermissionMode()
,
getContextUsage()
, and
interrupt()
to complete.
streamClose
1 minute
Maximum time to wait for initialization to complete before closing CLI stdin in multi-turn mode with SDK MCP servers.
You can customize these timeouts via the
timeout
option:
const
query
=
qwen.
query
(
'Your prompt'
, {
timeout: {
canUseTool:
60000
,
// 60 seconds for permission callback
mcpRequest:
600000
,
// 10 minutes for MCP tool calls
controlRequest:
60000
,
// 60 seconds for control requests
streamClose:
15000
,
// 15 seconds for stream close wait
},
});
Message Types
The SDK provides type guards to identify different message types:
import
{
isSDKUserMessage,
isSDKAssistantMessage,
isSDKSystemMessage,
isSDKResultMessage,
isSDKPartialAssistantMessage,
}
from
'@qwen-code/sdk'
;
for
await
(
const
message
of
result) {
if
(
isSDKAssistantMessage
(message)) {
// Handle assistant message
}
else
if
(
isSDKResultMessage
(message)) {
// Handle result message
}
}
Query Instance Methods
The
Query
instance returned by
query()
provides several methods:
const
q
=
query
({ prompt:
'Hello'
, options: {} });
// Get session ID
const
sessionId
=
q.
getSessionId
();
// Check if closed
const
closed
=
q.
isClosed
();
// Interrupt the current operation
await
q.
interrupt
();
// Change permission mode mid-session
await
q.
setPermissionMode
(
'yolo'
);
// Change model mid-session
await
q.
setModel
(
'qwen-max'
);
// Get context window usage breakdown (token counts per category)
const
usage
=
await
q.
getContextUsage
();
// Pass true to hint that per-item details should be displayed
const
detail
=
await
q.
getContextUsage
(
true
);
// Close the session
await
q.
close
();
Permission Modes
The SDK supports different permission modes for controlling tool execution:
default
: Write tools are denied unless approved via
canUseTool
callback or in
allowedTools
. Read-only tools execute without confirmation.
plan
: Blocks all write tools, instructing AI to present a plan first.
auto-edit
: Auto-approve edit tools (edit, write_file) while other tools require confirmation.
yolo
: All tools execute automatically without confirmation.
Permission Priority Chain
excludeTools
- Blocks tools completely
permissionMode: 'plan'
- Blocks non-read-only tools
permissionMode: 'yolo'
- Auto-approves all tools
allowedTools
- Auto-approves matching tools
canUseTool
callback - Custom approval logic
Default behavior - Auto-deny in SDK mode
Examples
Multi-turn Conversation
import
{ query,
type
SDKUserMessage }
from
'@qwen-code/sdk'
;
async
function*
generateMessages
()
:
AsyncIterable
<
SDKUserMessage
> {
yield
{
type:
'user'
,
session_id:
'my-session'
,
message: { role:
'user'
, content:
'Create a hello.txt file'
},
parent_tool_use_id:
null
,
};
// Wait for some condition or user input
yield
{
type:
'user'
,
session_id:
'my-session'
,
message: { role:
'user'
, content:
'Now read the file back'
},
parent_tool_use_id:
null
,
};
}
const
result
=
query
({
prompt:
generateMessages
(),
options: {
permissionMode:
'auto-edit'
,
},
});
for
await
(
const
message
of
result) {
console.
log
(message);
}
Custom Permission Handler
import
{ query,
type
CanUseTool }
from
'@qwen-code/sdk'
;
const
canUseTool
:
CanUseTool
=
async
(
toolName
,
input
, {
signal
})
=>
{
// Allow all read operations
if
(toolName.
startsWith
(
'read_'
)) {
return
{ behavior:
'allow'
, updatedInput: input };
}
// Prompt user for write operations (in a real app)
const
userApproved
=
await
promptUser
(
`Allow ${
toolName
}?`
);
if
(userApproved) {
return
{ behavior:
'allow'
, updatedInput: input };
}
return
{ behavior:
'deny'
, message:
'User denied the operation'
};
};
const
result
=
query
({
prompt:
'Create a new file'
,
options: {
canUseTool,
},
});
With External MCP Servers
import
{ query }
from
'@qwen-code/sdk'
;
const
result
=
query
({
prompt:
'Use the custom tool from my MCP server'
,
options: {
mcpServers: {
'my-server'
: {
command:
'node'
,
args: [
'path/to/mcp-server.js'
],
env: { PORT:
'3000'
},
},
},
},
});
Override the System Prompt
import
{ query }
from
'@qwen-code/sdk'
;
const
result
=
query
({
prompt:
'Say hello in one sentence.'
,
options: {
systemPrompt:
'You are a terse assistant. Answer in exactly one sentence.'
,
},
});
Append to the Built-in System Prompt
import
{ query }
from
'@qwen-code/sdk'
;
const
result
=
query
({
prompt:
'Review the current directory.'
,
options: {
systemPrompt: {
type:
'preset'
,
preset:
'qwen_code'
,
append:
'Be terse and focus on concrete findings.'
,
},
},
});
With SDK-Embedded MCP Servers
The SDK provides
tool
and
createSdkMcpServer
to create MCP servers that run in the same process as your SDK application. This is useful when you want to expose custom tools to the AI without running a separate server process.
tool(name, description, inputSchema, handler)
Creates a tool definition with Zod schema type inference.
Parameter
Type
Description
name
string
Tool name (1-64 chars, starts with letter, alphanumeric and underscores)
description
string
Human-readable description of what the tool does
inputSchema
ZodRawShape
Zod schema object defining the tool’s input parameters
handler
(args, extra) => Promise<Result>
Async function that executes the tool and returns MCP content blocks
The handler must return a
CallToolResult
object with the following structure:
{
content
: Array
<
|
{
type
:
'text'
;
text
: string }
|
{
type
:
'image'
;
data
: string;
mimeType
: string }
|
{
type
:
'resource'
;
uri
: string; mimeType
?:
string; text
?:
string }
>
;
isError
?:
boolean;
}
createSdkMcpServer(options)
Creates an SDK-embedded MCP server instance.
Option
Type
Default
Description
name
string
Required
Unique name for the MCP server
version
string
'1.0.0'
Server version
tools
SdkMcpToolDefinition[]
-
Array of tools created with
tool()
Returns a
McpSdkServerConfigWithInstance
object that can be passed directly to the
mcpServers
option.
Example
import
{ z }
from
'zod'
;
import
{ query, tool, createSdkMcpServer }
from
'@qwen-code/sdk'
;
// Define a tool with Zod schema
const
calculatorTool
=
tool
(
'calculate_sum'
,
'Add two numbers'
,
{ a: z.
number
(), b: z.
number
() },
async
(
args
)
=>
({
content: [{ type:
'text'
, text:
String
(args.a
+
args.b) }],
}),
);
// Create the MCP server
const
server
=
createSdkMcpServer
({
name:
'calculator'
,
tools: [calculatorTool],
});
// Use the server in a query
const
result
=
query
({
prompt:
'What is 42 + 17?'
,
options: {
permissionMode:
'yolo'
,
mcpServers: {
calculator: server,
},
},
});
for
await
(
const
message
of
result) {
console.
log
(message);
}
Abort a Query
import
{ query, isAbortError }
from
'@qwen-code/sdk'
;
const
abortController
=
new
AbortController
();
const
result
=
query
({
prompt:
'Long running task...'
,
options: {
abortController,
},
});
// Abort after 5 seconds
setTimeout
(()
=>
abortController.
abort
(),
5000
);
try
{
for
await
(
const
message
of
result) {
console.
log
(message);
}
}
catch
(error) {
if
(
isAbortError
(error)) {
console.
log
(
'Query was aborted'
);
}
else
{
throw
error;
}
}
Error Handling
The SDK provides an
AbortError
class for handling aborted queries:
import
{ AbortError, isAbortError }
from
'@qwen-code/sdk'
;
try
{
// ... query operations
}
catch
(error) {
if
(
isAbortError
(error)) {
// Handle abort
}
else
{
// Handle other errors
}
}
Last updated on
May 18, 2026
Contributing Guide
Python SDK (alpha)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-typescript/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>IDE Integration</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-integration/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-integration/</guid>
  <pubDate>Tue, 04 Jun 2024 00:00:00 +0000</pubDate>
  <category>Integrations</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
IDE Integration
Qwen Code can integrate with your IDE to provide a more seamless and context-aware experience. This integration allows the CLI to understand your workspace better and enables powerful features like native in-editor diffing.
Currently, the only supported IDE is
Visual Studio Code
and other editors that support VS Code extensions. To build support for other editors, see the
IDE Companion Extension Spec
.
Features
Workspace Context:
The CLI automatically gains awareness o...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
IDE Integration
Qwen Code can integrate with your IDE to provide a more seamless and context-aware experience. This integration allows the CLI to understand your workspace better and enables powerful features like native in-editor diffing.
Currently, the only supported IDE is
Visual Studio Code
and other editors that support VS Code extensions. To build support for other editors, see the
IDE Companion Extension Spec
.
Features
Workspace Context:
The CLI automatically gains awareness of your workspace to provide more relevant and accurate responses. This context includes:
The
10 most recently accessed files
in your workspace.
Your active cursor position.
Any text you have selected (up to a 16KB limit; longer selections will be truncated).
Native Diffing:
When Qwen suggests code modifications, you can view the changes directly within your IDE’s native diff viewer. This allows you to review, edit, and accept or reject the suggested changes seamlessly.
VS Code Commands:
You can access Qwen Code features directly from the VS Code Command Palette (
Cmd+Shift+P
or
Ctrl+Shift+P
):
Qwen Code: Run
: Starts a new Qwen Code session in the integrated terminal.
Qwen Code: Accept Diff
: Accepts the changes in the active diff editor.
Qwen Code: Close Diff Editor
: Rejects the changes and closes the active diff editor.
Qwen Code: View Third-Party Notices
: Displays the third-party notices for the extension.
Installation and Setup
There are three ways to set up the IDE integration:
1. Automatic Nudge (Recommended)
When you run Qwen Code inside a supported editor, it will automatically detect your environment and prompt you to connect. Answering “Yes” will automatically run the necessary setup, which includes installing the companion extension and enabling the connection.
2. Manual Installation from CLI
If you previously dismissed the prompt or want to install the extension manually, you can run the following command inside Qwen Code:
/ide install
This will find the correct extension for your IDE and install it.
3. Manual Installation from a Marketplace
You can also install the extension directly from a marketplace.
For Visual Studio Code:
Install from the
VS Code Marketplace
.
For VS Code Forks:
To support forks of VS Code, the extension is also published on the
Open VSX Registry
. Follow your editor’s instructions for installing extensions from this registry.
NOTE:
The “Qwen Code Companion” extension may appear towards the bottom of search results. If you don’t see it immediately, try scrolling down or sorting by “Newly Published”.
After manually installing the extension, you must run
/ide enable
in the CLI to activate the integration.
Usage
Enabling and Disabling
You can control the IDE integration from within the CLI:
To enable the connection to the IDE, run:
/ide enable
To disable the connection, run:
/ide disable
When enabled, Qwen Code will automatically attempt to connect to the IDE companion extension.
Checking the Status
To check the connection status and see the context the CLI has received from the IDE, run:
/ide status
If connected, this command will show the IDE it’s connected to and a list of recently opened files it is aware of.
(Note: The file list is limited to 10 recently accessed files within your workspace and only includes local files on disk.)
Working with Diffs
When you ask Qwen model to modify a file, it can open a diff view directly in your editor.
To accept a diff
, you can perform any of the following actions:
Click the
checkmark icon
in the diff editor’s title bar.
Save the file (e.g., with
Cmd+S
or
Ctrl+S
).
Open the Command Palette and run
Qwen Code: Accept Diff
.
Respond with
yes
in the CLI when prompted.
To reject a diff
, you can:
Click the
‘x’ icon
in the diff editor’s title bar.
Close the diff editor tab.
Open the Command Palette and run
Qwen Code: Close Diff Editor
.
Respond with
no
in the CLI when prompted.
You can also
modify the suggested changes
directly in the diff view before accepting them.
If you select ‘Yes, allow always’ in the CLI, changes will no longer show up in the IDE as they will be auto-accepted.
Using with Sandboxing
If you are using Qwen Code within a sandbox, please be aware of the following:
On macOS:
The IDE integration requires network access to communicate with the IDE companion extension. You must use a Seatbelt profile that allows network access.
In a Docker Container:
If you run Qwen Code inside a Docker (or Podman) container, the IDE integration can still connect to the VS Code extension running on your host machine. The CLI is configured to automatically find the IDE server on
host.docker.internal
. No special configuration is usually required, but you may need to ensure your Docker networking setup allows connections from the container to the host.
Troubleshooting
If you encounter issues with IDE integration, here are some common error messages and how to resolve them.
Connection Errors
Message:
🔴 Disconnected: Failed to connect to IDE companion extension for [IDE Name]. Please ensure the extension is running and try restarting your terminal. To install the extension, run /ide install.
Cause:
Qwen Code could not find the necessary environment variables (
QWEN_CODE_IDE_WORKSPACE_PATH
or
QWEN_CODE_IDE_SERVER_PORT
) to connect to the IDE. This usually means the IDE companion extension is not running or did not initialize correctly.
Solution:
Make sure you have installed the
Qwen Code Companion
extension in your IDE and that it is enabled.
Open a new terminal window in your IDE to ensure it picks up the correct environment.
Message:
🔴 Disconnected: IDE connection error. The connection was lost unexpectedly. Please try reconnecting by running /ide enable
Cause:
The connection to the IDE companion was lost.
Solution:
Run
/ide enable
to try and reconnect. If the issue continues, open a new terminal window or restart your IDE.
Configuration Errors
Message:
🔴 Disconnected: Directory mismatch. Qwen Code is running in a different location than the open workspace in [IDE Name]. Please run the CLI from the same directory as your project's root folder.
Cause:
The CLI’s current working directory is outside the folder or workspace you have open in your IDE.
Solution:
cd
into the same directory that is open in your IDE and restart the CLI.
Message:
🔴 Disconnected: To use this feature, please open a workspace folder in [IDE Name] and try again.
Cause:
You have no workspace open in your IDE.
Solution:
Open a workspace in your IDE and restart the CLI.
General Errors
Message:
IDE integration is not supported in your current environment. To use this feature, run Qwen Code in one of these supported IDEs: [List of IDEs]
Cause:
You are running Qwen Code in a terminal or environment that is not a supported IDE.
Solution:
Run Qwen Code from the integrated terminal of a supported IDE, like VS Code.
Message:
No installer is available for IDE. Please install the Qwen Code Companion extension manually from the marketplace.
Cause:
You ran
/ide install
, but the CLI does not have an automated installer for your specific IDE.
Solution:
Open your IDE’s extension marketplace, search for “Qwen Code Companion”, and install it manually.
Last updated on
May 18, 2026
QuickStart</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-integration/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
IDE Integration
Qwen Code can integrate with your IDE to provide a more seamless and context-aware experience. This integration allows the CLI to understand your workspace better and enables powerful features like native in-editor diffing.
Currently, the only supported IDE is
Visual Studio Code
and other editors that support VS Code extensions. To build support for other editors, see the
IDE Companion Extension Spec
.
Features
Workspace Context:
The CLI automatically gains awareness o...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
IDE Integration
Qwen Code can integrate with your IDE to provide a more seamless and context-aware experience. This integration allows the CLI to understand your workspace better and enables powerful features like native in-editor diffing.
Currently, the only supported IDE is
Visual Studio Code
and other editors that support VS Code extensions. To build support for other editors, see the
IDE Companion Extension Spec
.
Features
Workspace Context:
The CLI automatically gains awareness of your workspace to provide more relevant and accurate responses. This context includes:
The
10 most recently accessed files
in your workspace.
Your active cursor position.
Any text you have selected (up to a 16KB limit; longer selections will be truncated).
Native Diffing:
When Qwen suggests code modifications, you can view the changes directly within your IDE’s native diff viewer. This allows you to review, edit, and accept or reject the suggested changes seamlessly.
VS Code Commands:
You can access Qwen Code features directly from the VS Code Command Palette (
Cmd+Shift+P
or
Ctrl+Shift+P
):
Qwen Code: Run
: Starts a new Qwen Code session in the integrated terminal.
Qwen Code: Accept Diff
: Accepts the changes in the active diff editor.
Qwen Code: Close Diff Editor
: Rejects the changes and closes the active diff editor.
Qwen Code: View Third-Party Notices
: Displays the third-party notices for the extension.
Installation and Setup
There are three ways to set up the IDE integration:
1. Automatic Nudge (Recommended)
When you run Qwen Code inside a supported editor, it will automatically detect your environment and prompt you to connect. Answering “Yes” will automatically run the necessary setup, which includes installing the companion extension and enabling the connection.
2. Manual Installation from CLI
If you previously dismissed the prompt or want to install the extension manually, you can run the following command inside Qwen Code:
/ide install
This will find the correct extension for your IDE and install it.
3. Manual Installation from a Marketplace
You can also install the extension directly from a marketplace.
For Visual Studio Code:
Install from the
VS Code Marketplace
.
For VS Code Forks:
To support forks of VS Code, the extension is also published on the
Open VSX Registry
. Follow your editor’s instructions for installing extensions from this registry.
NOTE:
The “Qwen Code Companion” extension may appear towards the bottom of search results. If you don’t see it immediately, try scrolling down or sorting by “Newly Published”.
After manually installing the extension, you must run
/ide enable
in the CLI to activate the integration.
Usage
Enabling and Disabling
You can control the IDE integration from within the CLI:
To enable the connection to the IDE, run:
/ide enable
To disable the connection, run:
/ide disable
When enabled, Qwen Code will automatically attempt to connect to the IDE companion extension.
Checking the Status
To check the connection status and see the context the CLI has received from the IDE, run:
/ide status
If connected, this command will show the IDE it’s connected to and a list of recently opened files it is aware of.
(Note: The file list is limited to 10 recently accessed files within your workspace and only includes local files on disk.)
Working with Diffs
When you ask Qwen model to modify a file, it can open a diff view directly in your editor.
To accept a diff
, you can perform any of the following actions:
Click the
checkmark icon
in the diff editor’s title bar.
Save the file (e.g., with
Cmd+S
or
Ctrl+S
).
Open the Command Palette and run
Qwen Code: Accept Diff
.
Respond with
yes
in the CLI when prompted.
To reject a diff
, you can:
Click the
‘x’ icon
in the diff editor’s title bar.
Close the diff editor tab.
Open the Command Palette and run
Qwen Code: Close Diff Editor
.
Respond with
no
in the CLI when prompted.
You can also
modify the suggested changes
directly in the diff view before accepting them.
If you select ‘Yes, allow always’ in the CLI, changes will no longer show up in the IDE as they will be auto-accepted.
Using with Sandboxing
If you are using Qwen Code within a sandbox, please be aware of the following:
On macOS:
The IDE integration requires network access to communicate with the IDE companion extension. You must use a Seatbelt profile that allows network access.
In a Docker Container:
If you run Qwen Code inside a Docker (or Podman) container, the IDE integration can still connect to the VS Code extension running on your host machine. The CLI is configured to automatically find the IDE server on
host.docker.internal
. No special configuration is usually required, but you may need to ensure your Docker networking setup allows connections from the container to the host.
Troubleshooting
If you encounter issues with IDE integration, here are some common error messages and how to resolve them.
Connection Errors
Message:
🔴 Disconnected: Failed to connect to IDE companion extension for [IDE Name]. Please ensure the extension is running and try restarting your terminal. To install the extension, run /ide install.
Cause:
Qwen Code could not find the necessary environment variables (
QWEN_CODE_IDE_WORKSPACE_PATH
or
QWEN_CODE_IDE_SERVER_PORT
) to connect to the IDE. This usually means the IDE companion extension is not running or did not initialize correctly.
Solution:
Make sure you have installed the
Qwen Code Companion
extension in your IDE and that it is enabled.
Open a new terminal window in your IDE to ensure it picks up the correct environment.
Message:
🔴 Disconnected: IDE connection error. The connection was lost unexpectedly. Please try reconnecting by running /ide enable
Cause:
The connection to the IDE companion was lost.
Solution:
Run
/ide enable
to try and reconnect. If the issue continues, open a new terminal window or restart your IDE.
Configuration Errors
Message:
🔴 Disconnected: Directory mismatch. Qwen Code is running in a different location than the open workspace in [IDE Name]. Please run the CLI from the same directory as your project's root folder.
Cause:
The CLI’s current working directory is outside the folder or workspace you have open in your IDE.
Solution:
cd
into the same directory that is open in your IDE and restart the CLI.
Message:
🔴 Disconnected: To use this feature, please open a workspace folder in [IDE Name] and try again.
Cause:
You have no workspace open in your IDE.
Solution:
Open a workspace in your IDE and restart the CLI.
General Errors
Message:
IDE integration is not supported in your current environment. To use this feature, run Qwen Code in one of these supported IDEs: [List of IDEs]
Cause:
You are running Qwen Code in a terminal or environment that is not a supported IDE.
Solution:
Run Qwen Code from the integrated terminal of a supported IDE, like VS Code.
Message:
No installer is available for IDE. Please install the Qwen Code Companion extension manually from the marketplace.
Cause:
You ran
/ide install
, but the CLI does not have an automated installer for your specific IDE.
Solution:
Open your IDE’s extension marketplace, search for “Qwen Code Companion”, and install it manually.
Last updated on
May 18, 2026
QuickStart</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-integration/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Python SDK</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-python/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-python/</guid>
  <pubDate>Sun, 02 Jun 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Python SDK (alpha)
Python SDK
qwen-code-sdk
qwen-code-sdk
is an experimental Python SDK for Qwen Code. v1 targets the
existing
stream-json
CLI protocol and keeps the transport surface small and
testable.
Scope
Package name:
qwen-code-sdk
Import path:
qwen_code_sdk
Runtime requirement: Python
&gt;=3.10
CLI dependency: external
qwen
executable is required in v1
Transport scope: process transport only
Not included in v1: ACP transport, SDK-embedded MCP servers
Install
pip
install
qwen-...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Python SDK (alpha)
Python SDK
qwen-code-sdk
qwen-code-sdk
is an experimental Python SDK for Qwen Code. v1 targets the
existing
stream-json
CLI protocol and keeps the transport surface small and
testable.
Scope
Package name:
qwen-code-sdk
Import path:
qwen_code_sdk
Runtime requirement: Python
>=3.10
CLI dependency: external
qwen
executable is required in v1
Transport scope: process transport only
Not included in v1: ACP transport, SDK-embedded MCP servers
Install
pip
install
qwen-code-sdk
If
qwen
is not on
PATH
, pass
path_to_qwen_executable
explicitly.
Quick Start
import
asyncio
from
qwen_code_sdk
import
is_sdk_result_message, query
async
def
main
() ->
None
:
result
=
query(
"Explain the repository structure."
,
{
"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())
API Surface
Top-level entry points
query(prompt, options=None) -> Query
query_sync(prompt, options=None) -> SyncQuery
prompt
supports either:
str
for single-turn requests
AsyncIterable[SDKUserMessage]
for multi-turn streams
Query
Async iterable over SDK messages
close()
interrupt()
set_model(model)
set_permission_mode(mode)
supported_commands()
mcp_server_status()
get_session_id()
is_closed()
QueryOptions
Supported options in v1:
cwd
model
path_to_qwen_executable
permission_mode
can_use_tool
env
system_prompt
append_system_prompt
debug
max_session_turns
core_tools
exclude_tools
allowed_tools
auth_type
include_partial_messages
resume
continue_session
session_id
timeout
mcp_servers
stderr
Session argument priority is fixed as:
resume
continue_session
session_id
Permission Handling
When the CLI emits a
can_use_tool
control request, the SDK routes it through
can_use_tool(tool_name, tool_input, context)
.
Default behavior: deny
Default timeout: 60 seconds
Timeout fallback: deny
Callback exceptions: converted to deny with an error message
Callback context:
cancel_event
,
suggestions
, and
blocked_path
Callback contract:
can_use_tool
must be async with 3 positional arguments;
stderr
must accept 1 positional string argument
Error Model
ValidationError
: invalid options, invalid UUIDs, unsupported combinations
ControlRequestTimeoutError
: initialize, interrupt, or other control request
timed out
ProcessExitError
: CLI exited non-zero
AbortError
: control request or session was cancelled
Troubleshooting
If the SDK cannot start the CLI:
Verify
qwen --version
works in the target environment
Pass
path_to_qwen_executable
if your shell uses
nvm
,
pyenv
, or other
non-standard PATH setup
Use
debug=True
or
stderr=print
to surface CLI stderr while debugging
If session control calls time out:
Check that the target
qwen
version supports
--input-format stream-json
Increase
timeout.control_request
Verify that no wrapper script is swallowing stdout/stderr
Repository Integration
Repository-level helper commands:
npm run test:sdk:python
npm run lint:sdk:python
npm run typecheck:sdk:python
npm run smoke:sdk:python -- --qwen qwen
Real E2E Smoke
For a real runtime check (actual
qwen
process + real model call), run from
the repository root. The npm helper uses
python3
, so ensure it resolves to a
Python
>=3.10
interpreter:
npm
run
smoke:sdk:python
--
--qwen
qwen
This script runs:
async single-turn query
async control flow (
supported_commands
, permission mode updates)
sync
query_sync
query
It prints JSON and returns non-zero on failure.
Last updated on
May 18, 2026
Typescript SDK
Java SDK (alpha)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-python/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Python SDK (alpha)
Python SDK
qwen-code-sdk
qwen-code-sdk
is an experimental Python SDK for Qwen Code. v1 targets the
existing
stream-json
CLI protocol and keeps the transport surface small and
testable.
Scope
Package name:
qwen-code-sdk
Import path:
qwen_code_sdk
Runtime requirement: Python
&gt;=3.10
CLI dependency: external
qwen
executable is required in v1
Transport scope: process transport only
Not included in v1: ACP transport, SDK-embedded MCP servers
Install
pip
install
qwen-...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Python SDK (alpha)
Python SDK
qwen-code-sdk
qwen-code-sdk
is an experimental Python SDK for Qwen Code. v1 targets the
existing
stream-json
CLI protocol and keeps the transport surface small and
testable.
Scope
Package name:
qwen-code-sdk
Import path:
qwen_code_sdk
Runtime requirement: Python
>=3.10
CLI dependency: external
qwen
executable is required in v1
Transport scope: process transport only
Not included in v1: ACP transport, SDK-embedded MCP servers
Install
pip
install
qwen-code-sdk
If
qwen
is not on
PATH
, pass
path_to_qwen_executable
explicitly.
Quick Start
import
asyncio
from
qwen_code_sdk
import
is_sdk_result_message, query
async
def
main
() ->
None
:
result
=
query(
"Explain the repository structure."
,
{
"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())
API Surface
Top-level entry points
query(prompt, options=None) -> Query
query_sync(prompt, options=None) -> SyncQuery
prompt
supports either:
str
for single-turn requests
AsyncIterable[SDKUserMessage]
for multi-turn streams
Query
Async iterable over SDK messages
close()
interrupt()
set_model(model)
set_permission_mode(mode)
supported_commands()
mcp_server_status()
get_session_id()
is_closed()
QueryOptions
Supported options in v1:
cwd
model
path_to_qwen_executable
permission_mode
can_use_tool
env
system_prompt
append_system_prompt
debug
max_session_turns
core_tools
exclude_tools
allowed_tools
auth_type
include_partial_messages
resume
continue_session
session_id
timeout
mcp_servers
stderr
Session argument priority is fixed as:
resume
continue_session
session_id
Permission Handling
When the CLI emits a
can_use_tool
control request, the SDK routes it through
can_use_tool(tool_name, tool_input, context)
.
Default behavior: deny
Default timeout: 60 seconds
Timeout fallback: deny
Callback exceptions: converted to deny with an error message
Callback context:
cancel_event
,
suggestions
, and
blocked_path
Callback contract:
can_use_tool
must be async with 3 positional arguments;
stderr
must accept 1 positional string argument
Error Model
ValidationError
: invalid options, invalid UUIDs, unsupported combinations
ControlRequestTimeoutError
: initialize, interrupt, or other control request
timed out
ProcessExitError
: CLI exited non-zero
AbortError
: control request or session was cancelled
Troubleshooting
If the SDK cannot start the CLI:
Verify
qwen --version
works in the target environment
Pass
path_to_qwen_executable
if your shell uses
nvm
,
pyenv
, or other
non-standard PATH setup
Use
debug=True
or
stderr=print
to surface CLI stderr while debugging
If session control calls time out:
Check that the target
qwen
version supports
--input-format stream-json
Increase
timeout.control_request
Verify that no wrapper script is swallowing stdout/stderr
Repository Integration
Repository-level helper commands:
npm run test:sdk:python
npm run lint:sdk:python
npm run typecheck:sdk:python
npm run smoke:sdk:python -- --qwen qwen
Real E2E Smoke
For a real runtime check (actual
qwen
process + real model call), run from
the repository root. The npm helper uses
python3
, so ensure it resolves to a
Python
>=3.10
interpreter:
npm
run
smoke:sdk:python
--
--qwen
qwen
This script runs:
async single-turn query
async control flow (
supported_commands
, permission mode updates)
sync
query_sync
query
It prints JSON and returns non-zero on failure.
Last updated on
May 18, 2026
Typescript SDK
Java SDK (alpha)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/sdk-python/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>JetBrains IDEs</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/integration-jetbrains/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/integration-jetbrains/</guid>
  <pubDate>Fri, 31 May 2024 00:00:00 +0000</pubDate>
  <category>Integrations</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
JetBrains IDEs
JetBrains IDEs
JetBrains IDEs provide native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within your JetBrains IDE with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within your JetBrains IDE
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
Symbol management
: #-mention files to add them to the conversation con...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
JetBrains IDEs
JetBrains IDEs
JetBrains IDEs provide native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within your JetBrains IDE with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within your JetBrains IDE
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
Symbol management
: #-mention files to add them to the conversation context
Conversation history
: Access to past conversations within the IDE
Requirements
JetBrains IDE with ACP support (IntelliJ IDEA, WebStorm, PyCharm, etc.)
Qwen Code CLI installed
Installation
Install from ACP Registry (Recommend)
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Open your JetBrains IDE and navigate to AI Chat tool window.
Click
Add ACP Agent
, then click
Install
.
For users using JetBrains AI Assistant and/or other ACP agents, click
Install From ACP Registry
in Agents List, then install Qwen Code ACP.
The Qwen Code agent should now be available in the AI Assistant panel.
Manual Install (for older version of JetBrains IDEs)
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Open your JetBrains IDE and navigate to AI Chat tool window.
Click the 3-dot menu in the upper-right corner and select
Configure ACP Agent
and configure Qwen Code with the following settings:
{
"agent_servers"
: {
"qwen"
: {
"command"
:
"/path/to/qwen"
,
"args"
: [
"--acp"
],
"env"
: {}
}
}
}
The Qwen Code agent should now be available in the AI Assistant panel
Troubleshooting
Agent not appearing
Run
qwen --version
in terminal to verify installation
Ensure your JetBrains IDE version supports ACP
Restart your JetBrains IDE
Qwen Code not responding
Check your internet connection
Verify CLI works by running
qwen
in terminal
File an issue on GitHub
if the problem persists
Last updated on
May 18, 2026
Zed IDE
Github Actions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-jetbrains/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
JetBrains IDEs
JetBrains IDEs
JetBrains IDEs provide native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within your JetBrains IDE with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within your JetBrains IDE
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
Symbol management
: #-mention files to add them to the conversation con...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
JetBrains IDEs
JetBrains IDEs
JetBrains IDEs provide native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within your JetBrains IDE with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within your JetBrains IDE
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
Symbol management
: #-mention files to add them to the conversation context
Conversation history
: Access to past conversations within the IDE
Requirements
JetBrains IDE with ACP support (IntelliJ IDEA, WebStorm, PyCharm, etc.)
Qwen Code CLI installed
Installation
Install from ACP Registry (Recommend)
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Open your JetBrains IDE and navigate to AI Chat tool window.
Click
Add ACP Agent
, then click
Install
.
For users using JetBrains AI Assistant and/or other ACP agents, click
Install From ACP Registry
in Agents List, then install Qwen Code ACP.
The Qwen Code agent should now be available in the AI Assistant panel.
Manual Install (for older version of JetBrains IDEs)
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Open your JetBrains IDE and navigate to AI Chat tool window.
Click the 3-dot menu in the upper-right corner and select
Configure ACP Agent
and configure Qwen Code with the following settings:
{
"agent_servers"
: {
"qwen"
: {
"command"
:
"/path/to/qwen"
,
"args"
: [
"--acp"
],
"env"
: {}
}
}
}
The Qwen Code agent should now be available in the AI Assistant panel
Troubleshooting
Agent not appearing
Run
qwen --version
in terminal to verify installation
Ensure your JetBrains IDE version supports ACP
Restart your JetBrains IDE
Qwen Code not responding
Check your internet connection
Verify CLI works by running
qwen
in terminal
File an issue on GitHub
if the problem persists
Last updated on
May 18, 2026
Zed IDE
Github Actions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-jetbrains/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code: Terms of Service and Privacy Notice</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/support/tos-privacy/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/support/tos-privacy/</guid>
  <pubDate>Fri, 24 May 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Support
Terms of Service
Qwen Code: Terms of Service and Privacy Notice
Qwen Code is an open-source AI coding assistant tool maintained by the Qwen Code team. This document outlines the terms of service and privacy policies that apply when using Qwen Code’s authentication methods and AI model services.
How to determine your authentication method
Qwen Code supports three authentication methods to access AI models. Your authentication method determines which terms of service and privacy...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Support
Terms of Service
Qwen Code: Terms of Service and Privacy Notice
Qwen Code is an open-source AI coding assistant tool maintained by the Qwen Code team. This document outlines the terms of service and privacy policies that apply when using Qwen Code’s authentication methods and AI model services.
How to determine your authentication method
Qwen Code supports three authentication methods to access AI models. Your authentication method determines which terms of service and privacy policies apply to your usage:
Qwen OAuth
— Log in with your qwen.ai account (free tier discontinued 2026-04-15)
Alibaba Cloud Coding Plan
— Use an API key from Alibaba Cloud
API Key
— Bring your own API key
For each authentication method, different Terms of Service and Privacy Notices may apply depending on the underlying service provider.
Authentication Method
Provider
Terms of Service
Privacy Notice
Qwen OAuth
Qwen AI
Qwen Terms of Service
Qwen Privacy Policy
Alibaba Cloud Coding Plan
Alibaba Cloud
See
details below
See
details below
API Key
Various Providers
Depends on your chosen API provider (OpenAI, Anthropic, etc.)
Depends on your chosen API provider
1. If you are using Qwen OAuth Authentication
When you authenticate using your qwen.ai account, these Terms of Service and Privacy Notice documents apply:
Terms of Service:
Your use is governed by the
Qwen Terms of Service
.
Privacy Notice:
The collection and use of your data is described in the
Qwen Privacy Policy
.
For details about authentication setup, quotas, and supported features, see
Authentication Setup
.
2. If you are using Alibaba Cloud Coding Plan
When you authenticate using an API key from Alibaba Cloud, the applicable Terms of Service and Privacy Notice from Alibaba Cloud apply.
Alibaba Cloud Coding Plan is available in two regions:
阿里云百炼 (aliyun.com)
—
bailian.console.aliyun.com
Alibaba Cloud (alibabacloud.com)
—
bailian.console.alibabacloud.com
Important
When using Alibaba Cloud Coding Plan, you are subject to Alibaba Cloud’s terms and privacy policies. Please review their documentation for specific details about data usage, retention, and privacy practices.
3. If you are using your own API Key
When you authenticate using API keys from other providers, the applicable Terms of Service and Privacy Notice depend on your chosen provider.
Important
When using your own API key, you are subject to the terms and privacy policies of your chosen API provider, not Qwen Code’s terms. Please review your provider’s documentation for specific details about data usage, retention, and privacy practices.
Qwen Code supports various OpenAI-compatible providers. Please refer to your specific provider’s terms of service and privacy policy for detailed information.
Usage Statistics and Telemetry
Qwen Code may collect anonymous usage statistics and
telemetry
data to improve the user experience and product quality. This data collection is optional and can be controlled through configuration settings.
What Data is Collected
When enabled, Qwen Code may collect:
Anonymous usage statistics (commands run, performance metrics)
Error reports and crash data
Feature usage patterns
Data Collection by Authentication Method
Qwen OAuth:
Usage statistics are governed by Qwen’s privacy policy. You can opt-out through Qwen Code’s configuration settings.
Alibaba Cloud Coding Plan:
Usage statistics are governed by Alibaba Cloud’s privacy policy. You can opt-out through Qwen Code’s configuration settings.
API Key:
No additional data is collected by Qwen Code beyond what your chosen API provider collects.
Frequently Asked Questions (FAQ)
1. Is my code, including prompts and answers, used to train AI models?
Whether your code, including prompts and answers, is used to train AI models depends on your authentication method and the specific AI service provider you use:
Qwen OAuth
: Data usage is governed by
Qwen’s Privacy Policy
. Please refer to their policy for specific details about data collection and model training practices.
Alibaba Cloud Coding Plan
: Data usage is governed by Alibaba Cloud’s privacy policy. Please refer to their policy for specific details about data collection and model training practices.
API Key
: Data usage depends entirely on your chosen API provider. Each provider has their own data usage policies. Please review the privacy policy and terms of service of your specific provider.
Important
: Qwen Code itself does not use your prompts, code, or responses for model training. Any data usage for training purposes would be governed by the policies of the AI service provider you authenticate with.
2. What are Usage Statistics and what does the opt-out control?
The
Usage Statistics
setting controls optional data collection by Qwen Code for improving the user experience and product quality.
When enabled, Qwen Code may collect:
Anonymous telemetry (commands run, performance metrics, feature usage)
Error reports and crash data
General usage patterns
What is NOT collected by Qwen Code:
Your code content
Prompts sent to AI models
Responses from AI models
Personal information
The Usage Statistics setting only controls data collection by Qwen Code itself. It does not affect what data your chosen AI service provider (Qwen, OpenAI, etc.) may collect according to their own privacy policies.
3. How do I switch between authentication methods?
You can switch between Qwen OAuth, Alibaba Cloud Coding Plan, and your own API key at any time:
During startup
: Choose your preferred authentication method when prompted
Within the CLI
: Use the
/auth
command to reconfigure your authentication method
Environment variables
: Set up
.env
files for automatic API key authentication
For detailed instructions, see the
Authentication Setup
documentation.
Last updated on
May 18, 2026
Troubleshooting
Uninstall</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/support/tos-privacy/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Support
Terms of Service
Qwen Code: Terms of Service and Privacy Notice
Qwen Code is an open-source AI coding assistant tool maintained by the Qwen Code team. This document outlines the terms of service and privacy policies that apply when using Qwen Code’s authentication methods and AI model services.
How to determine your authentication method
Qwen Code supports three authentication methods to access AI models. Your authentication method determines which terms of service and privacy...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Support
Terms of Service
Qwen Code: Terms of Service and Privacy Notice
Qwen Code is an open-source AI coding assistant tool maintained by the Qwen Code team. This document outlines the terms of service and privacy policies that apply when using Qwen Code’s authentication methods and AI model services.
How to determine your authentication method
Qwen Code supports three authentication methods to access AI models. Your authentication method determines which terms of service and privacy policies apply to your usage:
Qwen OAuth
— Log in with your qwen.ai account (free tier discontinued 2026-04-15)
Alibaba Cloud Coding Plan
— Use an API key from Alibaba Cloud
API Key
— Bring your own API key
For each authentication method, different Terms of Service and Privacy Notices may apply depending on the underlying service provider.
Authentication Method
Provider
Terms of Service
Privacy Notice
Qwen OAuth
Qwen AI
Qwen Terms of Service
Qwen Privacy Policy
Alibaba Cloud Coding Plan
Alibaba Cloud
See
details below
See
details below
API Key
Various Providers
Depends on your chosen API provider (OpenAI, Anthropic, etc.)
Depends on your chosen API provider
1. If you are using Qwen OAuth Authentication
When you authenticate using your qwen.ai account, these Terms of Service and Privacy Notice documents apply:
Terms of Service:
Your use is governed by the
Qwen Terms of Service
.
Privacy Notice:
The collection and use of your data is described in the
Qwen Privacy Policy
.
For details about authentication setup, quotas, and supported features, see
Authentication Setup
.
2. If you are using Alibaba Cloud Coding Plan
When you authenticate using an API key from Alibaba Cloud, the applicable Terms of Service and Privacy Notice from Alibaba Cloud apply.
Alibaba Cloud Coding Plan is available in two regions:
阿里云百炼 (aliyun.com)
—
bailian.console.aliyun.com
Alibaba Cloud (alibabacloud.com)
—
bailian.console.alibabacloud.com
Important
When using Alibaba Cloud Coding Plan, you are subject to Alibaba Cloud’s terms and privacy policies. Please review their documentation for specific details about data usage, retention, and privacy practices.
3. If you are using your own API Key
When you authenticate using API keys from other providers, the applicable Terms of Service and Privacy Notice depend on your chosen provider.
Important
When using your own API key, you are subject to the terms and privacy policies of your chosen API provider, not Qwen Code’s terms. Please review your provider’s documentation for specific details about data usage, retention, and privacy practices.
Qwen Code supports various OpenAI-compatible providers. Please refer to your specific provider’s terms of service and privacy policy for detailed information.
Usage Statistics and Telemetry
Qwen Code may collect anonymous usage statistics and
telemetry
data to improve the user experience and product quality. This data collection is optional and can be controlled through configuration settings.
What Data is Collected
When enabled, Qwen Code may collect:
Anonymous usage statistics (commands run, performance metrics)
Error reports and crash data
Feature usage patterns
Data Collection by Authentication Method
Qwen OAuth:
Usage statistics are governed by Qwen’s privacy policy. You can opt-out through Qwen Code’s configuration settings.
Alibaba Cloud Coding Plan:
Usage statistics are governed by Alibaba Cloud’s privacy policy. You can opt-out through Qwen Code’s configuration settings.
API Key:
No additional data is collected by Qwen Code beyond what your chosen API provider collects.
Frequently Asked Questions (FAQ)
1. Is my code, including prompts and answers, used to train AI models?
Whether your code, including prompts and answers, is used to train AI models depends on your authentication method and the specific AI service provider you use:
Qwen OAuth
: Data usage is governed by
Qwen’s Privacy Policy
. Please refer to their policy for specific details about data collection and model training practices.
Alibaba Cloud Coding Plan
: Data usage is governed by Alibaba Cloud’s privacy policy. Please refer to their policy for specific details about data collection and model training practices.
API Key
: Data usage depends entirely on your chosen API provider. Each provider has their own data usage policies. Please review the privacy policy and terms of service of your specific provider.
Important
: Qwen Code itself does not use your prompts, code, or responses for model training. Any data usage for training purposes would be governed by the policies of the AI service provider you authenticate with.
2. What are Usage Statistics and what does the opt-out control?
The
Usage Statistics
setting controls optional data collection by Qwen Code for improving the user experience and product quality.
When enabled, Qwen Code may collect:
Anonymous telemetry (commands run, performance metrics, feature usage)
Error reports and crash data
General usage patterns
What is NOT collected by Qwen Code:
Your code content
Prompts sent to AI models
Responses from AI models
Personal information
The Usage Statistics setting only controls data collection by Qwen Code itself. It does not affect what data your chosen AI service provider (Qwen, OpenAI, etc.) may collect according to their own privacy policies.
3. How do I switch between authentication methods?
You can switch between Qwen OAuth, Alibaba Cloud Coding Plan, and your own API key at any time:
During startup
: Choose your preferred authentication method when prompted
Within the CLI
: Use the
/auth
command to reconfigure your authentication method
Environment variables
: Set up
.env
files for automatic API key authentication
For detailed instructions, see the
Authentication Setup
documentation.
Last updated on
May 18, 2026
Troubleshooting
Uninstall</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/support/tos-privacy/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Slash Command 重构路线图</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/roadmap/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/roadmap/</guid>
  <pubDate>Wed, 22 May 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Slash Command 重构路线图
Slash Command 重构路线图
总体目标
用 Qwen 内部架构风格，交付一个在外部体验上 95% 对齐 Claude Code 的 command 平台，同时修复三模式分裂、命令来源单一、prompt command 无法被模型调用三个核心问题。
核心设计原则
每个 Phase 可独立 ship
：完成后行为是自洽的，不依赖未来 Phase 才能运行
Phase 1 是纯基础设施
：除修复 MCP_PROMPT 被错误拦截外，不改变任何现有可用命令集
行为变化与架构变化分开
：Phase 1 做架构，Phase 2 做能力扩展
不照搬 Claude Code 内部架构
：但对齐用户可感知的能力面
Phase 1：基础设施重建（纯架构，零行为变化）
目标
建立统一的命令元数据模型和跨模式管理机制，为后续所有 Phase 提供底层支撑。
功能点
1.1 扩展
SlashCommand
元数据模型
在现有
SlashCommand
接口上新增以下字段：
来源字段
source: CommandSour...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Slash Command 重构路线图
Slash Command 重构路线图
总体目标
用 Qwen 内部架构风格，交付一个在外部体验上 95% 对齐 Claude Code 的 command 平台，同时修复三模式分裂、命令来源单一、prompt command 无法被模型调用三个核心问题。
核心设计原则
每个 Phase 可独立 ship
：完成后行为是自洽的，不依赖未来 Phase 才能运行
Phase 1 是纯基础设施
：除修复 MCP_PROMPT 被错误拦截外，不改变任何现有可用命令集
行为变化与架构变化分开
：Phase 1 做架构，Phase 2 做能力扩展
不照搬 Claude Code 内部架构
：但对齐用户可感知的能力面
Phase 1：基础设施重建（纯架构，零行为变化）
目标
建立统一的命令元数据模型和跨模式管理机制，为后续所有 Phase 提供底层支撑。
功能点
1.1 扩展
SlashCommand
元数据模型
在现有
SlashCommand
接口上新增以下字段：
来源字段
source: CommandSource
：命令来源枚举（
builtin-command
/
bundled-skill
/
skill-dir-command
/
plugin-command
/
mcp-prompt
等）
sourceLabel?: string
：展示用的来源标签（如
"Built-in"
/
"MCP: github-server"
）
模式能力字段
supportedModes: ExecutionMode[]
：声明在哪些运行模式下可用（
interactive
/
non_interactive
/
acp
）
执行类型字段
commandType: CommandType
：声明执行类型（
prompt
/
local
/
local-jsx
）
可见性字段
userInvocable: boolean
：用户是否可通过 slash command 调用（默认
true
）
modelInvocable: boolean
：模型是否可通过 tool call 调用（默认
false
）
辅助元数据字段
（为 Phase 3 预留，Phase 1 仅定义，不使用）
argumentHint?: string
：参数提示，如
"<model-id>"
/
"show|list|set"
whenToUse?: string
：何时调用该命令的说明（供模型使用）
examples?: string[]
：使用示例
1.2 Loader 填充 source/commandType 字段
每个 Loader 在构建
SlashCommand
时必须填充
source
和
commandType
：
Loader
source
commandType
BuiltinCommandLoader
builtin-command
由各命令声明（
local
/
local-jsx
）
BundledSkillLoader
bundled-skill
prompt
FileCommandLoader
（用户/项目）
skill-dir-command
prompt
FileCommandLoader
（插件）
plugin-command
prompt
McpPromptLoader
mcp-prompt
prompt
1.3 内置命令声明
supportedModes
和
commandType
为所有 built-in 命令显式声明：
commandType
：
local
（无 UI 依赖）或
local-jsx
（依赖 dialog/React）
supportedModes
：
local
类命令声明
['interactive', 'non_interactive', 'acp']
；
local-jsx
类命令声明
['interactive']
1.4 用 capability-based 过滤替换硬编码白名单
删除
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
常量
删除
filterCommandsForNonInteractive
函数
新增
filterCommandsForMode(commands, mode)
函数，基于
supportedModes
字段过滤
新增
getEffectiveSupportedModes(cmd)
工具函数（考虑 CommandKind 默认策略）
修改
handleSlashCommand
/
getAvailableCommands
函数签名，移除
allowedBuiltinCommandNames
参数
1.5 CommandService 升级为统一 Registry
新增
getCommandsForMode(mode: ExecutionMode)
方法
新增
getModelInvocableCommands()
方法（Phase 2/3 使用，Phase 1 提供接口）
现有
getCommands()
保持不变（interactive 使用）
验收标准
SlashCommand
接口包含所有新字段，TypeScript 编译通过
所有 Loader 填充
source
和
commandType
字段
所有 built-in 命令声明
commandType
和
supportedModes
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
已删除，被 capability filter 取代
non-interactive 下可用命令集与重构前完全一致
（现有测试不 break）
MCP prompt commands 在 non-interactive/acp 下可正常执行（修复原有错误限制）
CommandService.getCommandsForMode('non_interactive')
返回正确的命令集
所有现有测试通过
Phase 2：能力扩展（命令整理与 prompt command 模型调用）
目标
基于 Phase 1 的元数据基础，扩展三种模式下的命令可用范围，并打通 prompt command 的模型调用通路。
功能点
2.1 扩展 non-interactive / acp 可用命令集
ACP 语义设计原则
将命令扩展到 ACP/non-interactive 模式前，需遵循以下设计原则：
接收方不同
：ACP 模式下消息的接收方是 IDE（Zed/VS Code 插件），而非终端用户。消息内容以纯文本或 Markdown 格式为宜，不应包含 terminal 专用的 ANSI 样式。
实现策略是增加模式分支，而非替换
：正确做法是在命令的
action
内部新增模式判断——interactive 路径保持现有 UI 渲染逻辑不变，non_interactive/acp 路径返回适合机器消费的
message
或
submit_prompt
。两条路径共存于同一个
action
函数中。
有状态操作需说明语义
：在单次非交互调用中（如 CLI
-p
参数），
/model set
、
/language set
等有状态命令的变更仅在本次 session 内有效，应在命令响应文本中注明。
只读 vs 有副作用
：只读命令（如
/about
、
/stats
）直接返回当前状态文本；有副作用命令（如
/model set
、
/language set
）需在响应中确认操作结果。
避免环境相关副作用
：打开浏览器（
/docs
、
/insight
）、操作剪贴板（
/copy
）等依赖图形环境的操作，在 non_interactive/acp 路径下应跳过，改为在响应文本中返回相关 URL 或内容本身。
待扩展命令总览
注：
btw
、
bug
、
compress
、
context
、
init
、
summary
已在 Phase 1 中扩展到全模式，不在本阶段列表中。
以下 13 个命令将在 Phase 2 中扩展到
non_interactive
和
acp
模式：
A 类：action 已返回
message
或
submit_prompt
，只需扩展
supportedModes
并设计 ACP 消息内容
命令
返回类型
ACP/non-interactive 处理要点
/copy
message
ACP 下无剪贴板，改为在响应文本中返回内容本身或提示
/export
message
返回导出文件的完整路径
/plan
submit_prompt
无需改动，直接扩展模式
/restore
message
返回恢复操作的结果描述
/language
message
返回当前语言设置或变更确认文本
/statusline
submit_prompt
无需改动，直接扩展模式
A’ 类：有参数时正常执行，无参数时触发 dialog（需增加无参数路径的 non-interactive 处理）
命令
无参数 interactive 行为
无参数 non_interactive/acp 行为
/model
打开模型选择 dialog
返回当前模型名称及说明文本
/approval-mode
打开审批模式 dialog
返回当前审批模式及说明文本
B 类：action 内部使用
context.ui.addItem()
渲染 React 组件，需增加模式分支返回纯文本
命令
interactive 行为
non_interactive/acp 返回内容
/about
渲染版本/配置 React 组件
版本号、当前模型、关键配置的纯文本摘要
/stats
渲染 token/费用统计组件
session 统计数据的纯文本格式
/insight
渲染分析组件 + 打开浏览器
non_interactive
同步生成返回文件路径；
acp
通过
stream_messages
推送进度和结果
/docs
渲染文档入口 + 打开浏览器
返回文档 URL，不打开浏览器
C 类：特殊处理
命令
interactive 行为
non_interactive/acp 行为
/clear
调用
context.ui.clear()
清空终端显示
返回上下文边界标记 message，内容为
"Context cleared. Previous messages are no longer in context."
2.2 prompt command 模型调用打通
在
CommandService
（或
CommandRegistry
）中实现
getModelInvocableCommands()
，返回所有
modelInvocable: true
的命令
将
BundledSkillLoader
、
FileCommandLoader
（用户/项目命令）加载的命令标记为
modelInvocable: true
MCP prompt 不标记为
modelInvocable
：MCP prompt 通过独立的 MCP tool call 机制由模型调用，无需经过
SkillTool
中转
改造
SkillTool
：从只消费
SkillManager.listSkills()
改为同时消费
CommandService.getModelInvocableCommands()
构建统一的模型可调用命令描述，注入
SkillTool
的 description
2.3 mid-input slash command 检测（基础版）
在
InputPrompt
中检测光标附近的 slash token（不限于行首）
检测到 slash token 后通过 inline ghost text 提示最佳匹配命令名（Tab 接受）
不
包含 dropdown 补全菜单、argument hints、source badge 等（Phase 3 做）
ghost text 候选集仅限
modelInvocable: true
的命令（skill / file command）
验收标准
2.1 命令扩展
A 类：
/copy
、
/export
、
/plan
、
/restore
、
/language
、
/statusline
在 non-interactive 和 acp 模式下可正常执行并返回有意义的文本输出
A’ 类：
/model
、
/approval-mode
无参数时在 non-interactive/acp 下返回当前状态文本（不触发 dialog）；有参数时执行变更并返回确认文本
B 类：
/about
、
/stats
、
/docs
在 non-interactive/acp 下返回纯文本，
/docs
不打开浏览器；
/insight
在
non_interactive
下同步生成并返回文件路径 message，在
acp
下通过
stream_messages
推送进度
C 类：
/clear
在 non-interactive/acp 下返回上下文边界标记 message，不调用
context.ui.clear()
所有扩展命令在 interactive 模式下行为与重构前完全一致（无退化）
2.2 模型调用
模型在对话中可以通过
SkillTool
调用 bundled skill、file command（用户/项目）
MCP prompt 不经过
SkillTool
，通过 MCP tool call 机制由模型原生调用
模型不可以调用 built-in commands（
userInvocable: true
，
modelInvocable: false
）
SkillTool
的 description 包含所有
modelInvocable
命令的描述
2.3 mid-input slash
mid-input slash：在正文中输入
/
后通过 inline ghost text 提示最佳匹配命令（Tab 接受）
Phase 3：体验对齐（补全增强 + Claude Code 命令补齐）
目标
在 Phase 1/2 的元数据和命令能力基础上，补齐补全体验，并补充 Claude Code 中存在而 Qwen Code 缺失的命令。
功能点
3.1 补全体验增强
source badge
在补全菜单中展示命令来源标签（
[MCP]
已有，扩展为
[Skill]
、
[Custom]
等）
使用
source
/
sourceLabel
字段渲染
argument hint
补全菜单中命令名后展示
argumentHint
（如
set <model-id>
）
argumentHint
由 Phase 1 元数据字段提供
recently used 排序
记录用户最近使用的命令（session 级别，无需持久化）
在补全排序中给近期使用的命令加权
alias 命中高亮
当补全命中
altNames
而非主名时，在展示时注明（如
help (alias: ?)
）
冲突策略对齐
明确优先级：built-in > bundled/skill-dir > plugin > mcp
冲突时将低优先级命令重命名（如
pluginName.commandName
）
3.2 mid-input slash command 完整版
在 Phase 2 基础版上增加 argument hints 和 source badge 展示
ghost text 提示（输入
/he
时显示
/help
的淡色提示）
有效命令 token 高亮（已完成匹配的 slash command 显示不同颜色）
3.3 Help 目录重构
将
/help
从平铺列表改为分组目录：
Built-in Commands
（local + local-jsx，注明 mode）
Bundled Skills
Custom Commands
（用户/项目 file commands）
Plugin Commands
MCP Commands
每条命令展示：名称、argumentHint、description、source、supportedModes 标记
3.4 ACP available commands 元数据增强
在
sendAvailableCommandsUpdate()
中将更多元数据暴露给 ACP 客户端：
argumentHint
source
supportedModes
subcommands
（名称列表）
modelInvocable
3.5 Claude Code 缺失命令补齐
补充 Qwen Code 当前没有、Claude Code 有且常用的命令：
命令
类型
说明
/doctor
local
环境自检，输出配置/连接/工具状态诊断
/release-notes
local
展示当前版本的更新日志
/cost
local
展示当前 session 的 token 消耗和费用估算
注：
/review
、
/commit
等任务类命令以 bundled skill 形式提供，不在此列。
验收标准
补全菜单展示 source badge（
[MCP]
、
[Skill]
、
[Custom]
）
补全菜单展示 argumentHint（如
set <model-id>
）
近期使用的命令在补全列表中优先出现
alias 命中时在补全项中注明原名
mid-input slash：ghost text 提示正确渲染
/help
输出按来源分组，每条命令展示支持模式标记
ACP available commands 包含
argumentHint
、
source
、
subcommands
字段
/doctor
、
/release-notes
、
/cost
三个命令可用
/doctor
在 non-interactive 模式下可执行（返回
message
）
各 Phase 依赖关系
Phase 1（元数据 + 统一过滤）
│
├──► Phase 2（能力扩展）
│        │
│        ├──► slash command 子命令拆分
│        └──► prompt command 模型调用（需要 getModelInvocableCommands()）
│
└──► Phase 3（体验对齐）
│
├──► source badge（需要 Phase 1 source 字段）
├──► argument hint（需要 Phase 1 argumentHint 字段）
└──► Help 分组（需要 Phase 1 source 字段）
Phase 2 和 Phase 3 不互相依赖，可以并行推进（或根据优先级调换部分子项）。
Last updated on
May 18, 2026
Phase 2 技术设计文档：能力扩展
Tool-Use Summary Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/roadmap/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Slash Command 重构路线图
Slash Command 重构路线图
总体目标
用 Qwen 内部架构风格，交付一个在外部体验上 95% 对齐 Claude Code 的 command 平台，同时修复三模式分裂、命令来源单一、prompt command 无法被模型调用三个核心问题。
核心设计原则
每个 Phase 可独立 ship
：完成后行为是自洽的，不依赖未来 Phase 才能运行
Phase 1 是纯基础设施
：除修复 MCP_PROMPT 被错误拦截外，不改变任何现有可用命令集
行为变化与架构变化分开
：Phase 1 做架构，Phase 2 做能力扩展
不照搬 Claude Code 内部架构
：但对齐用户可感知的能力面
Phase 1：基础设施重建（纯架构，零行为变化）
目标
建立统一的命令元数据模型和跨模式管理机制，为后续所有 Phase 提供底层支撑。
功能点
1.1 扩展
SlashCommand
元数据模型
在现有
SlashCommand
接口上新增以下字段：
来源字段
source: CommandSour...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Slash Command 重构路线图
Slash Command 重构路线图
总体目标
用 Qwen 内部架构风格，交付一个在外部体验上 95% 对齐 Claude Code 的 command 平台，同时修复三模式分裂、命令来源单一、prompt command 无法被模型调用三个核心问题。
核心设计原则
每个 Phase 可独立 ship
：完成后行为是自洽的，不依赖未来 Phase 才能运行
Phase 1 是纯基础设施
：除修复 MCP_PROMPT 被错误拦截外，不改变任何现有可用命令集
行为变化与架构变化分开
：Phase 1 做架构，Phase 2 做能力扩展
不照搬 Claude Code 内部架构
：但对齐用户可感知的能力面
Phase 1：基础设施重建（纯架构，零行为变化）
目标
建立统一的命令元数据模型和跨模式管理机制，为后续所有 Phase 提供底层支撑。
功能点
1.1 扩展
SlashCommand
元数据模型
在现有
SlashCommand
接口上新增以下字段：
来源字段
source: CommandSource
：命令来源枚举（
builtin-command
/
bundled-skill
/
skill-dir-command
/
plugin-command
/
mcp-prompt
等）
sourceLabel?: string
：展示用的来源标签（如
"Built-in"
/
"MCP: github-server"
）
模式能力字段
supportedModes: ExecutionMode[]
：声明在哪些运行模式下可用（
interactive
/
non_interactive
/
acp
）
执行类型字段
commandType: CommandType
：声明执行类型（
prompt
/
local
/
local-jsx
）
可见性字段
userInvocable: boolean
：用户是否可通过 slash command 调用（默认
true
）
modelInvocable: boolean
：模型是否可通过 tool call 调用（默认
false
）
辅助元数据字段
（为 Phase 3 预留，Phase 1 仅定义，不使用）
argumentHint?: string
：参数提示，如
"<model-id>"
/
"show|list|set"
whenToUse?: string
：何时调用该命令的说明（供模型使用）
examples?: string[]
：使用示例
1.2 Loader 填充 source/commandType 字段
每个 Loader 在构建
SlashCommand
时必须填充
source
和
commandType
：
Loader
source
commandType
BuiltinCommandLoader
builtin-command
由各命令声明（
local
/
local-jsx
）
BundledSkillLoader
bundled-skill
prompt
FileCommandLoader
（用户/项目）
skill-dir-command
prompt
FileCommandLoader
（插件）
plugin-command
prompt
McpPromptLoader
mcp-prompt
prompt
1.3 内置命令声明
supportedModes
和
commandType
为所有 built-in 命令显式声明：
commandType
：
local
（无 UI 依赖）或
local-jsx
（依赖 dialog/React）
supportedModes
：
local
类命令声明
['interactive', 'non_interactive', 'acp']
；
local-jsx
类命令声明
['interactive']
1.4 用 capability-based 过滤替换硬编码白名单
删除
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
常量
删除
filterCommandsForNonInteractive
函数
新增
filterCommandsForMode(commands, mode)
函数，基于
supportedModes
字段过滤
新增
getEffectiveSupportedModes(cmd)
工具函数（考虑 CommandKind 默认策略）
修改
handleSlashCommand
/
getAvailableCommands
函数签名，移除
allowedBuiltinCommandNames
参数
1.5 CommandService 升级为统一 Registry
新增
getCommandsForMode(mode: ExecutionMode)
方法
新增
getModelInvocableCommands()
方法（Phase 2/3 使用，Phase 1 提供接口）
现有
getCommands()
保持不变（interactive 使用）
验收标准
SlashCommand
接口包含所有新字段，TypeScript 编译通过
所有 Loader 填充
source
和
commandType
字段
所有 built-in 命令声明
commandType
和
supportedModes
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
已删除，被 capability filter 取代
non-interactive 下可用命令集与重构前完全一致
（现有测试不 break）
MCP prompt commands 在 non-interactive/acp 下可正常执行（修复原有错误限制）
CommandService.getCommandsForMode('non_interactive')
返回正确的命令集
所有现有测试通过
Phase 2：能力扩展（命令整理与 prompt command 模型调用）
目标
基于 Phase 1 的元数据基础，扩展三种模式下的命令可用范围，并打通 prompt command 的模型调用通路。
功能点
2.1 扩展 non-interactive / acp 可用命令集
ACP 语义设计原则
将命令扩展到 ACP/non-interactive 模式前，需遵循以下设计原则：
接收方不同
：ACP 模式下消息的接收方是 IDE（Zed/VS Code 插件），而非终端用户。消息内容以纯文本或 Markdown 格式为宜，不应包含 terminal 专用的 ANSI 样式。
实现策略是增加模式分支，而非替换
：正确做法是在命令的
action
内部新增模式判断——interactive 路径保持现有 UI 渲染逻辑不变，non_interactive/acp 路径返回适合机器消费的
message
或
submit_prompt
。两条路径共存于同一个
action
函数中。
有状态操作需说明语义
：在单次非交互调用中（如 CLI
-p
参数），
/model set
、
/language set
等有状态命令的变更仅在本次 session 内有效，应在命令响应文本中注明。
只读 vs 有副作用
：只读命令（如
/about
、
/stats
）直接返回当前状态文本；有副作用命令（如
/model set
、
/language set
）需在响应中确认操作结果。
避免环境相关副作用
：打开浏览器（
/docs
、
/insight
）、操作剪贴板（
/copy
）等依赖图形环境的操作，在 non_interactive/acp 路径下应跳过，改为在响应文本中返回相关 URL 或内容本身。
待扩展命令总览
注：
btw
、
bug
、
compress
、
context
、
init
、
summary
已在 Phase 1 中扩展到全模式，不在本阶段列表中。
以下 13 个命令将在 Phase 2 中扩展到
non_interactive
和
acp
模式：
A 类：action 已返回
message
或
submit_prompt
，只需扩展
supportedModes
并设计 ACP 消息内容
命令
返回类型
ACP/non-interactive 处理要点
/copy
message
ACP 下无剪贴板，改为在响应文本中返回内容本身或提示
/export
message
返回导出文件的完整路径
/plan
submit_prompt
无需改动，直接扩展模式
/restore
message
返回恢复操作的结果描述
/language
message
返回当前语言设置或变更确认文本
/statusline
submit_prompt
无需改动，直接扩展模式
A’ 类：有参数时正常执行，无参数时触发 dialog（需增加无参数路径的 non-interactive 处理）
命令
无参数 interactive 行为
无参数 non_interactive/acp 行为
/model
打开模型选择 dialog
返回当前模型名称及说明文本
/approval-mode
打开审批模式 dialog
返回当前审批模式及说明文本
B 类：action 内部使用
context.ui.addItem()
渲染 React 组件，需增加模式分支返回纯文本
命令
interactive 行为
non_interactive/acp 返回内容
/about
渲染版本/配置 React 组件
版本号、当前模型、关键配置的纯文本摘要
/stats
渲染 token/费用统计组件
session 统计数据的纯文本格式
/insight
渲染分析组件 + 打开浏览器
non_interactive
同步生成返回文件路径；
acp
通过
stream_messages
推送进度和结果
/docs
渲染文档入口 + 打开浏览器
返回文档 URL，不打开浏览器
C 类：特殊处理
命令
interactive 行为
non_interactive/acp 行为
/clear
调用
context.ui.clear()
清空终端显示
返回上下文边界标记 message，内容为
"Context cleared. Previous messages are no longer in context."
2.2 prompt command 模型调用打通
在
CommandService
（或
CommandRegistry
）中实现
getModelInvocableCommands()
，返回所有
modelInvocable: true
的命令
将
BundledSkillLoader
、
FileCommandLoader
（用户/项目命令）加载的命令标记为
modelInvocable: true
MCP prompt 不标记为
modelInvocable
：MCP prompt 通过独立的 MCP tool call 机制由模型调用，无需经过
SkillTool
中转
改造
SkillTool
：从只消费
SkillManager.listSkills()
改为同时消费
CommandService.getModelInvocableCommands()
构建统一的模型可调用命令描述，注入
SkillTool
的 description
2.3 mid-input slash command 检测（基础版）
在
InputPrompt
中检测光标附近的 slash token（不限于行首）
检测到 slash token 后通过 inline ghost text 提示最佳匹配命令名（Tab 接受）
不
包含 dropdown 补全菜单、argument hints、source badge 等（Phase 3 做）
ghost text 候选集仅限
modelInvocable: true
的命令（skill / file command）
验收标准
2.1 命令扩展
A 类：
/copy
、
/export
、
/plan
、
/restore
、
/language
、
/statusline
在 non-interactive 和 acp 模式下可正常执行并返回有意义的文本输出
A’ 类：
/model
、
/approval-mode
无参数时在 non-interactive/acp 下返回当前状态文本（不触发 dialog）；有参数时执行变更并返回确认文本
B 类：
/about
、
/stats
、
/docs
在 non-interactive/acp 下返回纯文本，
/docs
不打开浏览器；
/insight
在
non_interactive
下同步生成并返回文件路径 message，在
acp
下通过
stream_messages
推送进度
C 类：
/clear
在 non-interactive/acp 下返回上下文边界标记 message，不调用
context.ui.clear()
所有扩展命令在 interactive 模式下行为与重构前完全一致（无退化）
2.2 模型调用
模型在对话中可以通过
SkillTool
调用 bundled skill、file command（用户/项目）
MCP prompt 不经过
SkillTool
，通过 MCP tool call 机制由模型原生调用
模型不可以调用 built-in commands（
userInvocable: true
，
modelInvocable: false
）
SkillTool
的 description 包含所有
modelInvocable
命令的描述
2.3 mid-input slash
mid-input slash：在正文中输入
/
后通过 inline ghost text 提示最佳匹配命令（Tab 接受）
Phase 3：体验对齐（补全增强 + Claude Code 命令补齐）
目标
在 Phase 1/2 的元数据和命令能力基础上，补齐补全体验，并补充 Claude Code 中存在而 Qwen Code 缺失的命令。
功能点
3.1 补全体验增强
source badge
在补全菜单中展示命令来源标签（
[MCP]
已有，扩展为
[Skill]
、
[Custom]
等）
使用
source
/
sourceLabel
字段渲染
argument hint
补全菜单中命令名后展示
argumentHint
（如
set <model-id>
）
argumentHint
由 Phase 1 元数据字段提供
recently used 排序
记录用户最近使用的命令（session 级别，无需持久化）
在补全排序中给近期使用的命令加权
alias 命中高亮
当补全命中
altNames
而非主名时，在展示时注明（如
help (alias: ?)
）
冲突策略对齐
明确优先级：built-in > bundled/skill-dir > plugin > mcp
冲突时将低优先级命令重命名（如
pluginName.commandName
）
3.2 mid-input slash command 完整版
在 Phase 2 基础版上增加 argument hints 和 source badge 展示
ghost text 提示（输入
/he
时显示
/help
的淡色提示）
有效命令 token 高亮（已完成匹配的 slash command 显示不同颜色）
3.3 Help 目录重构
将
/help
从平铺列表改为分组目录：
Built-in Commands
（local + local-jsx，注明 mode）
Bundled Skills
Custom Commands
（用户/项目 file commands）
Plugin Commands
MCP Commands
每条命令展示：名称、argumentHint、description、source、supportedModes 标记
3.4 ACP available commands 元数据增强
在
sendAvailableCommandsUpdate()
中将更多元数据暴露给 ACP 客户端：
argumentHint
source
supportedModes
subcommands
（名称列表）
modelInvocable
3.5 Claude Code 缺失命令补齐
补充 Qwen Code 当前没有、Claude Code 有且常用的命令：
命令
类型
说明
/doctor
local
环境自检，输出配置/连接/工具状态诊断
/release-notes
local
展示当前版本的更新日志
/cost
local
展示当前 session 的 token 消耗和费用估算
注：
/review
、
/commit
等任务类命令以 bundled skill 形式提供，不在此列。
验收标准
补全菜单展示 source badge（
[MCP]
、
[Skill]
、
[Custom]
）
补全菜单展示 argumentHint（如
set <model-id>
）
近期使用的命令在补全列表中优先出现
alias 命中时在补全项中注明原名
mid-input slash：ghost text 提示正确渲染
/help
输出按来源分组，每条命令展示支持模式标记
ACP available commands 包含
argumentHint
、
source
、
subcommands
字段
/doctor
、
/release-notes
、
/cost
三个命令可用
/doctor
在 non-interactive 模式下可执行（返回
message
）
各 Phase 依赖关系
Phase 1（元数据 + 统一过滤）
│
├──► Phase 2（能力扩展）
│        │
│        ├──► slash command 子命令拆分
│        └──► prompt command 模型调用（需要 getModelInvocableCommands()）
│
└──► Phase 3（体验对齐）
│
├──► source badge（需要 Phase 1 source 字段）
├──► argument hint（需要 Phase 1 argumentHint 字段）
└──► Help 分组（需要 Phase 1 source 字段）
Phase 2 和 Phase 3 不互相依赖，可以并行推进（或根据优先级调换部分子项）。
Last updated on
May 18, 2026
Phase 2 技术设计文档：能力扩展
Tool-Use Summary Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/roadmap/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Connect Qwen Code to tools via MCP</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/mcp/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/mcp/</guid>
  <pubDate>Fri, 17 May 2024 00:00:00 +0000</pubDate>
  <category>MCP</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
MCP
Connect Qwen Code to tools via MCP
Qwen Code can connect to external tools and data sources through the
Model Context Protocol (MCP)
. MCP servers give Qwen Code access to your tools, databases, and APIs.
What you can do with MCP
With MCP servers connected, you can ask Qwen Code to:
Work with files and repos (read/search/write, depending on the tools you enable)
Query databases (schema inspection, queries, reporting)
Integrate internal services (wrap your APIs as MCP tool...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
MCP
Connect Qwen Code to tools via MCP
Qwen Code can connect to external tools and data sources through the
Model Context Protocol (MCP)
. MCP servers give Qwen Code access to your tools, databases, and APIs.
What you can do with MCP
With MCP servers connected, you can ask Qwen Code to:
Work with files and repos (read/search/write, depending on the tools you enable)
Query databases (schema inspection, queries, reporting)
Integrate internal services (wrap your APIs as MCP tools)
Automate workflows (repeatable tasks exposed as tools/prompts)
Tip
If you’re looking for the “one command to get started”, jump to
Quick start
.
Quick start
Qwen Code loads MCP servers from
mcpServers
in your
settings.json
. You can configure servers either:
By editing
settings.json
directly
By using
qwen mcp
commands (see
CLI reference
)
Add your first server
Add a server (example: remote HTTP MCP server):
qwen
mcp
add
--transport
http
my-server
http://localhost:3000/mcp
Open MCP management dialog to view and manage servers:
qwen
mcp
Restart Qwen Code in the same project (or start it if it wasn’t running yet), then ask the model to use tools from that server.
Where configuration is stored (scopes)
Most users only need these two scopes:
Project scope (default)
:
.qwen/settings.json
in your project root
User scope
:
~/.qwen/settings.json
across all projects on your machine
Write to user scope:
qwen
mcp
add
--scope
user
--transport
http
my-server
http://localhost:3000/mcp
Tip
For advanced configuration layers (system defaults/system settings and precedence rules), see
Settings
.
Configure servers
Choose a transport
Transport
When to use
JSON field(s)
http
Recommended for remote services; works well for cloud MCP servers
httpUrl
(+ optional
headers
)
sse
Legacy/deprecated servers that only support Server-Sent Events
url
(+ optional
headers
)
stdio
Local process (scripts, CLIs, Docker) on your machine
command
,
args
(+ optional
cwd
,
env
)
Note
If a server supports both, prefer
HTTP
over
SSE
.
Configure via
settings.json
vs
qwen mcp add
Both approaches produce the same
mcpServers
entries in your
settings.json
—use whichever you prefer.
Stdio server (local process)
JSON (
.qwen/settings.json
):
{
"mcpServers"
: {
"pythonTools"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
,
"--port"
,
"8080"
],
"cwd"
:
"./mcp-servers/python"
,
"env"
: {
"DATABASE_URL"
:
"$DB_CONNECTION_STRING"
,
"API_KEY"
:
"${EXTERNAL_API_KEY}"
},
"timeout"
:
15000
}
}
}
CLI (writes to project scope by default):
qwen
mcp
add
pythonTools
-e
DATABASE_URL=
$DB_CONNECTION_STRING
-e
API_KEY=
$EXTERNAL_API_KEY
\
--timeout
15000
python
-m
my_mcp_server
--port
8080
HTTP server (remote streamable HTTP)
JSON:
{
"mcpServers"
: {
"httpServerWithAuth"
: {
"httpUrl"
:
"http://localhost:3000/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer your-api-token"
},
"timeout"
:
5000
}
}
}
CLI:
qwen
mcp
add
--transport
http
httpServerWithAuth
http://localhost:3000/mcp
\
--header
"Authorization: Bearer your-api-token"
--timeout
5000
SSE server (remote Server-Sent Events)
JSON:
{
"mcpServers"
: {
"sseServer"
: {
"url"
:
"http://localhost:8080/sse"
,
"timeout"
:
30000
}
}
}
CLI:
qwen
mcp
add
--transport
sse
sseServer
http://localhost:8080/sse
--timeout
30000
Safety and control
Trust (skip confirmations)
Server trust
(
trust: true
): bypasses confirmation prompts for that server (use sparingly).
OAuth authentication
Qwen Code supports OAuth 2.0 authentication for MCP servers. This is useful when accessing remote servers that require authentication.
Basic usage
When you add an MCP server with OAuth credentials, Qwen Code will automatically handle the authentication flow:
qwen
mcp
add
--transport
sse
oauth-server
https://api.example.com/sse/
\
--oauth-client-id
your-client-id
\
--oauth-redirect-uri
https://your-server.com/oauth/callback
\
--oauth-authorization-url
https://provider.example.com/authorize
\
--oauth-token-url
https://provider.example.com/token
Important: Redirect URI configuration
The OAuth flow requires a redirect URI where the authorization provider sends the authentication code.
Local development
: By default, Qwen Code uses
http://localhost:7777/oauth/callback
. This works when running Qwen Code on your local machine with a local browser.
Remote/cloud deployments
: When running Qwen Code on remote servers, cloud IDEs, or web terminals, the default
localhost
redirect will NOT work. You MUST configure
--oauth-redirect-uri
to point to a publicly accessible URL that can receive the OAuth callback.
Example for remote servers:
qwen
mcp
add
--transport
sse
remote-server
https://api.example.com/sse/
\
--oauth-redirect-uri
https://your-remote-server.example.com/oauth/callback
Manual configuration via settings.json
You can also configure OAuth by editing
settings.json
directly:
{
"mcpServers"
: {
"oauthServer"
: {
"url"
:
"https://api.example.com/sse/"
,
"oauth"
: {
"enabled"
:
true
,
"clientId"
:
"your-client-id"
,
"clientSecret"
:
"your-client-secret"
,
"authorizationUrl"
:
"https://provider.example.com/authorize"
,
"tokenUrl"
:
"https://provider.example.com/token"
,
"redirectUri"
:
"https://your-server.com/oauth/callback"
,
"scopes"
: [
"read"
,
"write"
]
}
}
}
}
OAuth configuration properties:
Property
Description
enabled
Enable OAuth for this server (boolean)
clientId
OAuth client identifier (string, optional with dynamic registration)
clientSecret
OAuth client secret (string, optional for public clients)
authorizationUrl
OAuth authorization endpoint (string, auto-discovered if omitted)
tokenUrl
OAuth token endpoint (string, auto-discovered if omitted)
scopes
Required OAuth scopes (array of strings)
redirectUri
Custom redirect URI (string).
Critical for remote deployments
. Defaults to
http://localhost:7777/oauth/callback
tokenParamName
Query parameter name for tokens in SSE URLs (string)
audiences
Audiences the token is valid for (array of strings)
Token management
OAuth tokens are automatically:
Stored securely
in
~/.qwen/mcp-oauth-tokens.json
Refreshed
when expired (if refresh tokens are available)
Validated
before each connection attempt
Use the
/mcp auth
command within Qwen Code to manage OAuth authentication interactively.
Tool filtering (allow/deny tools per server)
Use
includeTools
/
excludeTools
to restrict tools exposed by a server (from Qwen Code’s perspective).
Example: include only a few tools:
{
"mcpServers"
: {
"filteredServer"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
],
"includeTools"
: [
"safe_tool"
,
"file_reader"
,
"data_processor"
],
"timeout"
:
30000
}
}
}
Global allow/deny lists
The
mcp
object in your
settings.json
defines global rules for all MCP servers:
mcp.allowed
: allow-list of MCP server names (keys in
mcpServers
)
mcp.excluded
: deny-list of MCP server names
Example:
{
"mcp"
: {
"allowed"
: [
"my-trusted-server"
],
"excluded"
: [
"experimental-server"
]
}
}
Troubleshooting
Server shows “Disconnected” in
qwen mcp list
: verify the URL/command is correct, then increase
timeout
.
Stdio server fails to start
: use an absolute
command
path, and double-check
cwd
/
env
.
Environment variables in JSON don’t resolve
: ensure they exist in the environment where Qwen Code runs (shell vs GUI app environments can differ).
Reference
settings.json
structure
Server-specific configuration (
mcpServers
)
Add an
mcpServers
object to your
settings.json
file:
// ... file contains other config objects
{
"mcpServers"
: {
"serverName"
: {
"command"
:
"path/to/server"
,
"args"
: [
"--arg1"
,
"value1"
],
"env"
: {
"API_KEY"
:
"$MY_API_TOKEN"
},
"cwd"
:
"./server-directory"
,
"timeout"
:
30000
,
"trust"
:
false
}
}
}
Configuration properties:
Required (one of the following):
Property
Description
command
Path to the executable for Stdio transport
url
SSE endpoint URL (e.g.,
"http://localhost:8080/sse"
)
httpUrl
HTTP streaming endpoint URL
Optional:
Property
Type/Default
Description
args
array
Command-line arguments for Stdio transport
headers
object
Custom HTTP headers when using
url
or
httpUrl
env
object
Environment variables for the server process. Values can reference environment variables using
$VAR_NAME
or
${VAR_NAME}
syntax
cwd
string
Working directory for Stdio transport
timeout
number
(default: 600,000)
Request timeout in milliseconds (default: 600,000ms = 10 minutes)
trust
boolean
(default: false)
When
true
, bypasses all tool call confirmations for this server (default:
false
)
includeTools
array
List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.
excludeTools
array
List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server.
Note:
excludeTools
takes precedence over
includeTools
- if a tool is in both lists, it will be excluded.
targetAudience
string
The OAuth Client ID allowlisted on the IAP-protected application you are trying to access. Used with
authProviderType: 'service_account_impersonation'
.
targetServiceAccount
string
The email address of the Google Cloud Service Account to impersonate. Used with
authProviderType: 'service_account_impersonation'
.
Manage MCP servers with
qwen mcp
You can always configure MCP servers by manually editing
settings.json
, but the CLI is usually faster.
Adding a server (
qwen mcp add
)
qwen
mcp
add
[options]
<
name
>
<
commandOrUrl
>
[args...]
Argument/Option
Description
Default
Example
<name>
A unique name for the server.
—
example-server
<commandOrUrl>
The command to execute (for
stdio
) or the URL (for
http
/
sse
).
—
/usr/bin/python
or
http://localhost:8
[args...]
Optional arguments for a
stdio
command.
—
--port 5000
-s
,
--scope
Configuration scope (user or project).
project
-s user
-t
,
--transport
Transport type (
stdio
,
sse
,
http
).
stdio
-t sse
-e
,
--env
Set environment variables.
—
-e KEY=value
-H
,
--header
Set HTTP headers for SSE and HTTP transports.
—
-H "X-Api-Key: abc123"
--timeout
Set connection timeout in milliseconds.
—
--timeout 30000
--trust
Trust the server (bypass all tool call confirmation prompts).
— (
false
)
--trust
--description
Set the description for the server.
—
--description "Local tools"
--include-tools
A comma-separated list of tools to include.
all tools included
--include-tools mytool,othertool
--exclude-tools
A comma-separated list of tools to exclude.
none
--exclude-tools mytool
--oauth-client-id
OAuth client ID for MCP server authentication.
—
--oauth-client-id your-client-id
--oauth-client-secret
OAuth client secret for MCP server authentication.
—
--oauth-client-secret your-client-secret
--oauth-redirect-uri
OAuth redirect URI for authentication callback.
http://localhost:7777/oauth/callback
--oauth-redirect-uri https://your-server.com/oauth/callback
--oauth-authorization-url
OAuth authorization URL.
—
--oauth-authorization-url https://provider.example.com/authorize
--oauth-token-url
OAuth token URL.
—
--oauth-token-url https://provider.example.com/token
--oauth-scopes
OAuth scopes (comma-separated).
—
--oauth-scopes scope1,scope2
--oauth-*
flags apply only to
--transport sse
and
--transport http
. Combining them with
--transport stdio
is rejected.
Removing a server (
qwen mcp remove
)
qwen
mcp
remove
<
nam
e
>
Last updated on
May 18, 2026
Approval Mode
LSP (Language Server Protocol)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/mcp/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
MCP
Connect Qwen Code to tools via MCP
Qwen Code can connect to external tools and data sources through the
Model Context Protocol (MCP)
. MCP servers give Qwen Code access to your tools, databases, and APIs.
What you can do with MCP
With MCP servers connected, you can ask Qwen Code to:
Work with files and repos (read/search/write, depending on the tools you enable)
Query databases (schema inspection, queries, reporting)
Integrate internal services (wrap your APIs as MCP tool...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
MCP
Connect Qwen Code to tools via MCP
Qwen Code can connect to external tools and data sources through the
Model Context Protocol (MCP)
. MCP servers give Qwen Code access to your tools, databases, and APIs.
What you can do with MCP
With MCP servers connected, you can ask Qwen Code to:
Work with files and repos (read/search/write, depending on the tools you enable)
Query databases (schema inspection, queries, reporting)
Integrate internal services (wrap your APIs as MCP tools)
Automate workflows (repeatable tasks exposed as tools/prompts)
Tip
If you’re looking for the “one command to get started”, jump to
Quick start
.
Quick start
Qwen Code loads MCP servers from
mcpServers
in your
settings.json
. You can configure servers either:
By editing
settings.json
directly
By using
qwen mcp
commands (see
CLI reference
)
Add your first server
Add a server (example: remote HTTP MCP server):
qwen
mcp
add
--transport
http
my-server
http://localhost:3000/mcp
Open MCP management dialog to view and manage servers:
qwen
mcp
Restart Qwen Code in the same project (or start it if it wasn’t running yet), then ask the model to use tools from that server.
Where configuration is stored (scopes)
Most users only need these two scopes:
Project scope (default)
:
.qwen/settings.json
in your project root
User scope
:
~/.qwen/settings.json
across all projects on your machine
Write to user scope:
qwen
mcp
add
--scope
user
--transport
http
my-server
http://localhost:3000/mcp
Tip
For advanced configuration layers (system defaults/system settings and precedence rules), see
Settings
.
Configure servers
Choose a transport
Transport
When to use
JSON field(s)
http
Recommended for remote services; works well for cloud MCP servers
httpUrl
(+ optional
headers
)
sse
Legacy/deprecated servers that only support Server-Sent Events
url
(+ optional
headers
)
stdio
Local process (scripts, CLIs, Docker) on your machine
command
,
args
(+ optional
cwd
,
env
)
Note
If a server supports both, prefer
HTTP
over
SSE
.
Configure via
settings.json
vs
qwen mcp add
Both approaches produce the same
mcpServers
entries in your
settings.json
—use whichever you prefer.
Stdio server (local process)
JSON (
.qwen/settings.json
):
{
"mcpServers"
: {
"pythonTools"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
,
"--port"
,
"8080"
],
"cwd"
:
"./mcp-servers/python"
,
"env"
: {
"DATABASE_URL"
:
"$DB_CONNECTION_STRING"
,
"API_KEY"
:
"${EXTERNAL_API_KEY}"
},
"timeout"
:
15000
}
}
}
CLI (writes to project scope by default):
qwen
mcp
add
pythonTools
-e
DATABASE_URL=
$DB_CONNECTION_STRING
-e
API_KEY=
$EXTERNAL_API_KEY
\
--timeout
15000
python
-m
my_mcp_server
--port
8080
HTTP server (remote streamable HTTP)
JSON:
{
"mcpServers"
: {
"httpServerWithAuth"
: {
"httpUrl"
:
"http://localhost:3000/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer your-api-token"
},
"timeout"
:
5000
}
}
}
CLI:
qwen
mcp
add
--transport
http
httpServerWithAuth
http://localhost:3000/mcp
\
--header
"Authorization: Bearer your-api-token"
--timeout
5000
SSE server (remote Server-Sent Events)
JSON:
{
"mcpServers"
: {
"sseServer"
: {
"url"
:
"http://localhost:8080/sse"
,
"timeout"
:
30000
}
}
}
CLI:
qwen
mcp
add
--transport
sse
sseServer
http://localhost:8080/sse
--timeout
30000
Safety and control
Trust (skip confirmations)
Server trust
(
trust: true
): bypasses confirmation prompts for that server (use sparingly).
OAuth authentication
Qwen Code supports OAuth 2.0 authentication for MCP servers. This is useful when accessing remote servers that require authentication.
Basic usage
When you add an MCP server with OAuth credentials, Qwen Code will automatically handle the authentication flow:
qwen
mcp
add
--transport
sse
oauth-server
https://api.example.com/sse/
\
--oauth-client-id
your-client-id
\
--oauth-redirect-uri
https://your-server.com/oauth/callback
\
--oauth-authorization-url
https://provider.example.com/authorize
\
--oauth-token-url
https://provider.example.com/token
Important: Redirect URI configuration
The OAuth flow requires a redirect URI where the authorization provider sends the authentication code.
Local development
: By default, Qwen Code uses
http://localhost:7777/oauth/callback
. This works when running Qwen Code on your local machine with a local browser.
Remote/cloud deployments
: When running Qwen Code on remote servers, cloud IDEs, or web terminals, the default
localhost
redirect will NOT work. You MUST configure
--oauth-redirect-uri
to point to a publicly accessible URL that can receive the OAuth callback.
Example for remote servers:
qwen
mcp
add
--transport
sse
remote-server
https://api.example.com/sse/
\
--oauth-redirect-uri
https://your-remote-server.example.com/oauth/callback
Manual configuration via settings.json
You can also configure OAuth by editing
settings.json
directly:
{
"mcpServers"
: {
"oauthServer"
: {
"url"
:
"https://api.example.com/sse/"
,
"oauth"
: {
"enabled"
:
true
,
"clientId"
:
"your-client-id"
,
"clientSecret"
:
"your-client-secret"
,
"authorizationUrl"
:
"https://provider.example.com/authorize"
,
"tokenUrl"
:
"https://provider.example.com/token"
,
"redirectUri"
:
"https://your-server.com/oauth/callback"
,
"scopes"
: [
"read"
,
"write"
]
}
}
}
}
OAuth configuration properties:
Property
Description
enabled
Enable OAuth for this server (boolean)
clientId
OAuth client identifier (string, optional with dynamic registration)
clientSecret
OAuth client secret (string, optional for public clients)
authorizationUrl
OAuth authorization endpoint (string, auto-discovered if omitted)
tokenUrl
OAuth token endpoint (string, auto-discovered if omitted)
scopes
Required OAuth scopes (array of strings)
redirectUri
Custom redirect URI (string).
Critical for remote deployments
. Defaults to
http://localhost:7777/oauth/callback
tokenParamName
Query parameter name for tokens in SSE URLs (string)
audiences
Audiences the token is valid for (array of strings)
Token management
OAuth tokens are automatically:
Stored securely
in
~/.qwen/mcp-oauth-tokens.json
Refreshed
when expired (if refresh tokens are available)
Validated
before each connection attempt
Use the
/mcp auth
command within Qwen Code to manage OAuth authentication interactively.
Tool filtering (allow/deny tools per server)
Use
includeTools
/
excludeTools
to restrict tools exposed by a server (from Qwen Code’s perspective).
Example: include only a few tools:
{
"mcpServers"
: {
"filteredServer"
: {
"command"
:
"python"
,
"args"
: [
"-m"
,
"my_mcp_server"
],
"includeTools"
: [
"safe_tool"
,
"file_reader"
,
"data_processor"
],
"timeout"
:
30000
}
}
}
Global allow/deny lists
The
mcp
object in your
settings.json
defines global rules for all MCP servers:
mcp.allowed
: allow-list of MCP server names (keys in
mcpServers
)
mcp.excluded
: deny-list of MCP server names
Example:
{
"mcp"
: {
"allowed"
: [
"my-trusted-server"
],
"excluded"
: [
"experimental-server"
]
}
}
Troubleshooting
Server shows “Disconnected” in
qwen mcp list
: verify the URL/command is correct, then increase
timeout
.
Stdio server fails to start
: use an absolute
command
path, and double-check
cwd
/
env
.
Environment variables in JSON don’t resolve
: ensure they exist in the environment where Qwen Code runs (shell vs GUI app environments can differ).
Reference
settings.json
structure
Server-specific configuration (
mcpServers
)
Add an
mcpServers
object to your
settings.json
file:
// ... file contains other config objects
{
"mcpServers"
: {
"serverName"
: {
"command"
:
"path/to/server"
,
"args"
: [
"--arg1"
,
"value1"
],
"env"
: {
"API_KEY"
:
"$MY_API_TOKEN"
},
"cwd"
:
"./server-directory"
,
"timeout"
:
30000
,
"trust"
:
false
}
}
}
Configuration properties:
Required (one of the following):
Property
Description
command
Path to the executable for Stdio transport
url
SSE endpoint URL (e.g.,
"http://localhost:8080/sse"
)
httpUrl
HTTP streaming endpoint URL
Optional:
Property
Type/Default
Description
args
array
Command-line arguments for Stdio transport
headers
object
Custom HTTP headers when using
url
or
httpUrl
env
object
Environment variables for the server process. Values can reference environment variables using
$VAR_NAME
or
${VAR_NAME}
syntax
cwd
string
Working directory for Stdio transport
timeout
number
(default: 600,000)
Request timeout in milliseconds (default: 600,000ms = 10 minutes)
trust
boolean
(default: false)
When
true
, bypasses all tool call confirmations for this server (default:
false
)
includeTools
array
List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.
excludeTools
array
List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server.
Note:
excludeTools
takes precedence over
includeTools
- if a tool is in both lists, it will be excluded.
targetAudience
string
The OAuth Client ID allowlisted on the IAP-protected application you are trying to access. Used with
authProviderType: 'service_account_impersonation'
.
targetServiceAccount
string
The email address of the Google Cloud Service Account to impersonate. Used with
authProviderType: 'service_account_impersonation'
.
Manage MCP servers with
qwen mcp
You can always configure MCP servers by manually editing
settings.json
, but the CLI is usually faster.
Adding a server (
qwen mcp add
)
qwen
mcp
add
[options]
<
name
>
<
commandOrUrl
>
[args...]
Argument/Option
Description
Default
Example
<name>
A unique name for the server.
—
example-server
<commandOrUrl>
The command to execute (for
stdio
) or the URL (for
http
/
sse
).
—
/usr/bin/python
or
http://localhost:8
[args...]
Optional arguments for a
stdio
command.
—
--port 5000
-s
,
--scope
Configuration scope (user or project).
project
-s user
-t
,
--transport
Transport type (
stdio
,
sse
,
http
).
stdio
-t sse
-e
,
--env
Set environment variables.
—
-e KEY=value
-H
,
--header
Set HTTP headers for SSE and HTTP transports.
—
-H "X-Api-Key: abc123"
--timeout
Set connection timeout in milliseconds.
—
--timeout 30000
--trust
Trust the server (bypass all tool call confirmation prompts).
— (
false
)
--trust
--description
Set the description for the server.
—
--description "Local tools"
--include-tools
A comma-separated list of tools to include.
all tools included
--include-tools mytool,othertool
--exclude-tools
A comma-separated list of tools to exclude.
none
--exclude-tools mytool
--oauth-client-id
OAuth client ID for MCP server authentication.
—
--oauth-client-id your-client-id
--oauth-client-secret
OAuth client secret for MCP server authentication.
—
--oauth-client-secret your-client-secret
--oauth-redirect-uri
OAuth redirect URI for authentication callback.
http://localhost:7777/oauth/callback
--oauth-redirect-uri https://your-server.com/oauth/callback
--oauth-authorization-url
OAuth authorization URL.
—
--oauth-authorization-url https://provider.example.com/authorize
--oauth-token-url
OAuth token URL.
—
--oauth-token-url https://provider.example.com/token
--oauth-scopes
OAuth scopes (comma-separated).
—
--oauth-scopes scope1,scope2
--oauth-*
flags apply only to
--transport sse
and
--transport http
. Combining them with
--transport stdio
is rejected.
Removing a server (
qwen mcp remove
)
qwen
mcp
remove
<
nam
e
>
Last updated on
May 18, 2026
Approval Mode
LSP (Language Server Protocol)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/mcp/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Commands</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/commands/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/commands/</guid>
  <pubDate>Fri, 17 May 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Commands
Commands
This document details all commands supported by Qwen Code, helping you efficiently manage sessions, customize the interface, and control its behavior.
Qwen Code commands are triggered through specific prefixes and fall into three categories:
Prefix Type
Function Description
Typical Use Case
Slash Commands (
/
)
Meta-level control of Qwen Code itself
Managing sessions, modifying settings, getting help
At Commands (
@
)
Quickly inject local file content into c...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Commands
Commands
This document details all commands supported by Qwen Code, helping you efficiently manage sessions, customize the interface, and control its behavior.
Qwen Code commands are triggered through specific prefixes and fall into three categories:
Prefix Type
Function Description
Typical Use Case
Slash Commands (
/
)
Meta-level control of Qwen Code itself
Managing sessions, modifying settings, getting help
At Commands (
@
)
Quickly inject local file content into conversation
Allowing AI to analyze specified files or code under directories
Exclamation Commands (
!
)
Direct interaction with system Shell
Executing system commands like
git status
,
ls
, etc.
1. Slash Commands (
/
)
Slash commands are used to manage Qwen Code sessions, interface, and basic behavior.
1.1 Session and Project Management
These commands help you save, restore, and summarize work progress.
Command
Description
Usage Examples
/init
Analyze current directory and create initial context file
/init
/summary
Generate project summary based on conversation history
/summary
/compress
Replace chat history with summary to save Tokens
/compress
/resume
Resume a previous conversation session
/resume
/recap
Generate a one-line session recap now
/recap
/restore
Restore files to state before tool execution
/restore
(list) or
/restore <ID>
1.2 Interface and Workspace Control
Commands for adjusting interface appearance and work environment.
Command
Description
Usage Examples
/clear
Clear terminal screen content
/clear
(shortcut:
Ctrl+L
)
/context
Show context window usage breakdown
/context
→
detail
Show per-item context usage breakdown
/context detail
/theme
Change Qwen Code visual theme
/theme
/vim
Turn input area Vim editing mode on/off
/vim
/directory
Manage multi-directory support workspace
/dir add ./src,./tests
/editor
Open dialog to select supported editor
/editor
1.3 Language Settings
Commands specifically for controlling interface and output language.
Command
Description
Usage Examples
/language
View or change language settings
/language
→
ui [language]
Set UI interface language
/language ui zh-CN
→
output [language]
Set LLM output language
/language output Chinese
Available built-in UI languages:
zh-CN
(Simplified Chinese),
en-US
(English),
ru-RU
(Russian),
de-DE
(German)
Output language examples:
Chinese
,
English
,
Japanese
, etc.
1.4 Tool and Model Management
Commands for managing AI tools and models.
Command
Description
Usage Examples
/mcp
List configured MCP servers and tools
/mcp
,
/mcp desc
/tools
Display currently available tool list
/tools
,
/tools desc
/skills
List and run available skills
/skills
,
/skills <name>
/plan
Switch to plan mode or exit plan mode
/plan
,
/plan <task>
,
/plan exit
/approval-mode
Change approval mode for tool usage
/approval-mode <mode (auto-edit)> --project
→
plan
Analysis only, no execution
Secure review
→
default
Require approval for edits
Daily use
→
auto-edit
Automatically approve edits
Trusted environment
→
yolo
Automatically approve all
Quick prototyping
/model
Switch model used in current session
/model
/model --fast
Set a lighter model for prompt suggestions
/model --fast qwen3-coder-flash
/extensions
List all active extensions in current session
/extensions
/memory
Open the Memory Manager dialog
/memory
/remember
Save a durable memory
/remember Prefer terse responses
/forget
Remove matching entries from auto-memory
/forget <query>
/dream
Manually run auto-memory consolidation
/dream
1.5 Built-in Skills
These commands invoke bundled skills that provide specialized workflows.
Command
Description
Usage Examples
/review
Review code changes with 5 parallel agents + deterministic analysis
/review
,
/review 123
,
/review 123 --comment
/loop
Run a prompt on a recurring schedule
/loop 5m check the build
/qc-helper
Answer questions about Qwen Code usage and configuration
/qc-helper how do I configure MCP?
See
Code Review
for full
/review
documentation.
1.6 Side Question (
/btw
)
The
/btw
command allows you to ask quick side questions without interrupting or affecting the main conversation flow.
Command
Description
/btw <your question>
Ask a quick side question
?btw <your question>
Alternative syntax for side questions
How It Works:
The side question is sent as a separate API call with recent conversation context (up to the last 20 messages)
The response is displayed above the Composer — you can continue typing while waiting
The main conversation is
not blocked
— it continues independently
The side question response does
not
become part of the main conversation history
Answers are rendered with full Markdown support (code blocks, lists, tables, etc.)
Keyboard Shortcuts (Interactive Mode):
Shortcut
Action
Escape
Cancel (while loading) or dismiss (after completed)
Space
or
Enter
Dismiss the answer (when input is empty)
Ctrl+C
or
Ctrl+D
Cancel an in-flight side question
Example:
(While the main conversation is about refactoring code)
> /btw What's the difference between let and var in JavaScript?
╭──────────────────────────────────────────╮
│ /btw What's the difference between let   │
│     and var in JavaScript?               │
│                                          │
│ + Answering...                           │
│ Press Escape, Ctrl+C, or Ctrl+D to cancel│
╰──────────────────────────────────────────╯
> (Composer remains active — keep typing)
(After the answer arrives)
╭──────────────────────────────────────────╮
│ /btw What's the difference between let   │
│     and var in JavaScript?               │
│                                          │
│ `let` is block-scoped, while `var` is    │
│ function-scoped. `let` was introduced    │
│ in ES6 and doesn't hoist the same way.   │
│                                          │
│ Press Space, Enter, or Escape to dismiss │
╰──────────────────────────────────────────╯
> (Composer still active)
Supported Execution Modes:
Mode
Behavior
Interactive
Shows above Composer with Markdown rendering
Non-interactive
Returns text result:
btw> question\nanswer
ACP (Agent Protocol)
Returns stream_messages async generator
Tip
Use
/btw
when you need a quick answer without derailing your main task. It’s especially useful for clarifying concepts, checking facts, or getting quick explanations while staying focused on your primary workflow.
1.7 Session Recap (
/recap
)
The
/recap
command generates a short “where you left off” summary of the
current session, so you can resume an old conversation without scrolling
back through pages of history.
Command
Description
/recap
Generate and show a one-line session recap
How it works:
Uses the configured fast model (
fastModel
setting) when available, falling
back to the main session model. A small, cheap model is enough for a recap.
The recent conversation (up to 30 messages, text only — tool calls and tool
responses are filtered out) is sent to the model with a tight system prompt.
The recap is rendered in dim color with a
❯
prefix so it stands apart
from real assistant replies.
Refuses with an inline error if a model turn is in flight or another command
is processing. If there is no usable conversation, or the underlying
generation fails,
/recap
shows a short info message instead of a recap —
the manual command always responds with something.
Auto-trigger when returning from being away:
If the terminal is blurred for
5+ minutes
and gets focused again, a recap
is generated and shown automatically (only when no model response is in
progress; otherwise it waits for the current turn to finish and then fires).
Unlike the manual command, the auto-trigger is fully silent on failure: if
generation errors or there is nothing to summarize, no message is added to
the history. Controlled by the
general.showSessionRecap
setting
(default:
true
); the manual
/recap
command always works regardless of
this setting.
Example:
> /recap
❯ Refactoring loopDetectionService.ts to address long-session OOM caused by
unbounded streamContentHistory and contentStats. The next step is to
implement option B (LRU sliding window with FNV-1a) pending confirmation.
Tip
Configure a fast model via
/model --fast <model>
(e.g.
qwen3-coder-flash
) to make
/recap
fast and cheap. Set
general.showSessionRecap
to
false
to opt out of the auto-trigger
while keeping the manual command available.
1.8 Information, Settings, and Help
Commands for obtaining information and performing system settings.
Command
Description
Usage Examples
/help
Display help information for available commands
/help
or
/?
/about
Display version information
/about
/stats
Display detailed statistics for current session
/stats
/settings
Open settings editor
/settings
/auth
Change authentication method
/auth
/bug
Submit issue about Qwen Code
/bug Button click unresponsive
/copy
Copy last output content to clipboard
/copy
/quit
Exit Qwen Code immediately
/quit
or
/exit
1.9 Common Shortcuts
Shortcut
Function
Note
Ctrl/cmd+L
Clear screen
Equivalent to
/clear
Ctrl/cmd+T
Toggle tool description
MCP tool management
Ctrl/cmd+C
×2
Exit confirmation
Secure exit mechanism
Ctrl/cmd+Z
Undo input
Text editing
Ctrl/cmd+Shift+Z
Redo input
Text editing
1.10 CLI Auth Subcommands
In addition to the in-session
/auth
slash command, Qwen Code provides standalone CLI subcommands for managing authentication directly from the terminal:
Command
Description
qwen auth
Interactive authentication setup
qwen auth coding-plan
Authenticate with Alibaba Cloud Coding Plan
qwen auth coding-plan --region china --key sk-sp-…
Non-interactive Coding Plan setup (for scripting)
qwen auth api-key
Authenticate with an API key
qwen auth qwen-oauth
Authenticate with Qwen OAuth
(discontinued on 2026-04-15)
qwen auth status
Show current authentication status
Tip
These commands run outside of a Qwen Code session. Use them to configure authentication before starting a session, or in scripts and CI environments. See the
Authentication
page for full details.
2. @ Commands (Introducing Files)
@ commands are used to quickly add local file or directory content to the conversation.
Command Format
Description
Examples
@<file path>
Inject content of specified file
@src/main.py Please explain this code
@<directory path>
Recursively read all text files in directory
@docs/ Summarize content of this document
Standalone
@
Used when discussing
@
symbol itself
@ What is this symbol used for in programming?
Note: Spaces in paths need to be escaped with backslash (e.g.,
@My\ Documents/file.txt
)
3. Exclamation Commands (
!
) - Shell Command Execution
Exclamation commands allow you to execute system commands directly within Qwen Code.
Command Format
Description
Examples
!<shell command>
Execute command in sub-Shell
!ls -la
,
!git status
Standalone
!
Switch Shell mode, any input is executed directly as Shell command
!
(enter) → Input command →
!
(exit)
Environment Variables: Commands executed via
!
will set the
QWEN_CODE=1
environment variable.
4. Custom Commands
Save frequently used prompts as shortcut commands to improve work efficiency and ensure consistency.
Note
Custom commands now use Markdown format with optional YAML frontmatter. TOML format is deprecated but still supported for backwards compatibility. When TOML files are detected, an automatic migration prompt will be displayed.
Quick Overview
Function
Description
Advantages
Priority
Applicable Scenarios
Namespace
Subdirectory creates colon-named commands
Better command organization
Global Commands
~/.qwen/commands/
Available in all projects
Low
Personal frequently used commands, cross-project use
Project Commands
<project root directory>/.qwen/commands/
Project-specific, version-controllable
High
Team sharing, project-specific commands
Priority Rules: Project commands > User commands (project command used when names are same)
Command Naming Rules
File Path to Command Name Mapping Table
File Location
Generated Command
Example Call
~/.qwen/commands/test.md
/test
/test Parameter
<project>/.qwen/commands/git/commit.md
/git:commit
/git:commit Message
Naming Rules: Path separator (
/
or
\
) converted to colon (
:
)
Markdown File Format Specification (Recommended)
Custom commands use Markdown files with optional YAML frontmatter:
---
description
:
Optional description (displayed in /help)
---
Your prompt content here.
Use {{args}} for parameter injection.
Field
Required
Description
Example
description
Optional
Command description (displayed in /help)
description: Code analysis tool
Prompt body
Required
Prompt content sent to model
Any Markdown content after the frontmatter
TOML File Format (Deprecated)
Warning
Deprecated:
TOML format is still supported but will be removed in a future version. Please migrate to Markdown format.
Field
Required
Description
Example
prompt
Required
Prompt content sent to model
prompt = "Please analyze code: {{args}}"
description
Optional
Command description (displayed in /help)
description = "Code analysis tool"
Parameter Processing Mechanism
Processing Method
Syntax
Applicable Scenarios
Security Features
Context-aware Injection
{{args}}
Need precise parameter control
Automatic Shell escaping
Default Parameter Processing
No special marking
Simple commands, parameter appending
Append as-is
Shell Command Injection
!{command}
Need dynamic content
Execution confirmation required before
1. Context-aware Injection (
{{args}}
)
Scenario
TOML Configuration
Call Method
Actual Effect
Raw Injection
prompt = "Fix: {{args}}"
/fix "Button issue"
Fix: "Button issue"
In Shell Command
prompt = "Search: !{grep {{args}} .}"
/search "hello"
Execute
grep "hello" .
2. Default Parameter Processing
Input Situation
Processing Method
Example
Has parameters
Append to end of prompt (separated by two line breaks)
/cmd parameter
→ Original prompt + parameter
No parameters
Send prompt as is
/cmd
→ Original prompt
🚀 Dynamic Content Injection
Injection Type
Syntax
Processing Order
Purpose
File Content
@{file path}
Processed first
Inject static reference files
Shell Commands
!{command}
Processed in middle
Inject dynamic execution results
Parameter Replacement
{{args}}
Processed last
Inject user parameters
3. Shell Command Execution (
!{...}
)
Operation
User Interaction
1. Parse command and parameters
-
2. Automatic Shell escaping
-
3. Show confirmation dialog
✅ User confirmation
4. Execute command
-
5. Inject output to prompt
-
Example: Git Commit Message Generation
---
description
:
Generate Commit message based on staged changes
---
Please generate a Commit message based on the following diff:
```diff
!{git diff --staged}
```
4. File Content Injection (
@{...}
)
File Type
Support Status
Processing Method
Text Files
✅ Full Support
Directly inject content
Images/PDF
✅ Multi-modal Support
Encode and inject
Binary Files
⚠️ Limited Support
May be skipped or truncated
Directory
✅ Recursive Injection
Follow .gitignore rules
Example: Code Review Command
---
description
:
Code review based on best practices
---
Review {{args}}, reference standards:
@{docs/code-standards.md}
Practical Creation Example
”Pure Function Refactoring” Command Creation Steps Table
Operation
Command/Code
1. Create directory structure
mkdir -p ~/.qwen/commands/refactor
2. Create command file
touch ~/.qwen/commands/refactor/pure.md
3. Edit command content
Refer to the complete code below.
4. Test command
@file.js
→
/refactor:pure
---
description
:
Refactor code to pure function
---
Please analyze code in current context, refactor to pure function.
Requirements:
1.
Provide refactored code
2.
Explain key changes and pure function characteristic implementation
3.
Maintain function unchanged
Custom Command Best Practices Summary
Command Design Recommendations Table
Practice Points
Recommended Approach
Avoid
Command Naming
Use namespaces for organization
Avoid overly generic names
Parameter Processing
Clearly use
{{args}}
Rely on default appending (easy to confuse)
Error Handling
Utilize Shell error output
Ignore execution failure
File Organization
Organize by function in directories
All commands in root directory
Description Field
Always provide clear description
Rely on auto-generated description
Security Features Reminder Table
Security Mechanism
Protection Effect
User Operation
Shell Escaping
Prevent command injection
Automatic processing
Execution Confirmation
Avoid accidental execution
Dialog confirmation
Error Reporting
Help diagnose issues
View error information
Last updated on
May 18, 2026
Github Actions
Code Review</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/commands/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Commands
Commands
This document details all commands supported by Qwen Code, helping you efficiently manage sessions, customize the interface, and control its behavior.
Qwen Code commands are triggered through specific prefixes and fall into three categories:
Prefix Type
Function Description
Typical Use Case
Slash Commands (
/
)
Meta-level control of Qwen Code itself
Managing sessions, modifying settings, getting help
At Commands (
@
)
Quickly inject local file content into c...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Commands
Commands
This document details all commands supported by Qwen Code, helping you efficiently manage sessions, customize the interface, and control its behavior.
Qwen Code commands are triggered through specific prefixes and fall into three categories:
Prefix Type
Function Description
Typical Use Case
Slash Commands (
/
)
Meta-level control of Qwen Code itself
Managing sessions, modifying settings, getting help
At Commands (
@
)
Quickly inject local file content into conversation
Allowing AI to analyze specified files or code under directories
Exclamation Commands (
!
)
Direct interaction with system Shell
Executing system commands like
git status
,
ls
, etc.
1. Slash Commands (
/
)
Slash commands are used to manage Qwen Code sessions, interface, and basic behavior.
1.1 Session and Project Management
These commands help you save, restore, and summarize work progress.
Command
Description
Usage Examples
/init
Analyze current directory and create initial context file
/init
/summary
Generate project summary based on conversation history
/summary
/compress
Replace chat history with summary to save Tokens
/compress
/resume
Resume a previous conversation session
/resume
/recap
Generate a one-line session recap now
/recap
/restore
Restore files to state before tool execution
/restore
(list) or
/restore <ID>
1.2 Interface and Workspace Control
Commands for adjusting interface appearance and work environment.
Command
Description
Usage Examples
/clear
Clear terminal screen content
/clear
(shortcut:
Ctrl+L
)
/context
Show context window usage breakdown
/context
→
detail
Show per-item context usage breakdown
/context detail
/theme
Change Qwen Code visual theme
/theme
/vim
Turn input area Vim editing mode on/off
/vim
/directory
Manage multi-directory support workspace
/dir add ./src,./tests
/editor
Open dialog to select supported editor
/editor
1.3 Language Settings
Commands specifically for controlling interface and output language.
Command
Description
Usage Examples
/language
View or change language settings
/language
→
ui [language]
Set UI interface language
/language ui zh-CN
→
output [language]
Set LLM output language
/language output Chinese
Available built-in UI languages:
zh-CN
(Simplified Chinese),
en-US
(English),
ru-RU
(Russian),
de-DE
(German)
Output language examples:
Chinese
,
English
,
Japanese
, etc.
1.4 Tool and Model Management
Commands for managing AI tools and models.
Command
Description
Usage Examples
/mcp
List configured MCP servers and tools
/mcp
,
/mcp desc
/tools
Display currently available tool list
/tools
,
/tools desc
/skills
List and run available skills
/skills
,
/skills <name>
/plan
Switch to plan mode or exit plan mode
/plan
,
/plan <task>
,
/plan exit
/approval-mode
Change approval mode for tool usage
/approval-mode <mode (auto-edit)> --project
→
plan
Analysis only, no execution
Secure review
→
default
Require approval for edits
Daily use
→
auto-edit
Automatically approve edits
Trusted environment
→
yolo
Automatically approve all
Quick prototyping
/model
Switch model used in current session
/model
/model --fast
Set a lighter model for prompt suggestions
/model --fast qwen3-coder-flash
/extensions
List all active extensions in current session
/extensions
/memory
Open the Memory Manager dialog
/memory
/remember
Save a durable memory
/remember Prefer terse responses
/forget
Remove matching entries from auto-memory
/forget <query>
/dream
Manually run auto-memory consolidation
/dream
1.5 Built-in Skills
These commands invoke bundled skills that provide specialized workflows.
Command
Description
Usage Examples
/review
Review code changes with 5 parallel agents + deterministic analysis
/review
,
/review 123
,
/review 123 --comment
/loop
Run a prompt on a recurring schedule
/loop 5m check the build
/qc-helper
Answer questions about Qwen Code usage and configuration
/qc-helper how do I configure MCP?
See
Code Review
for full
/review
documentation.
1.6 Side Question (
/btw
)
The
/btw
command allows you to ask quick side questions without interrupting or affecting the main conversation flow.
Command
Description
/btw <your question>
Ask a quick side question
?btw <your question>
Alternative syntax for side questions
How It Works:
The side question is sent as a separate API call with recent conversation context (up to the last 20 messages)
The response is displayed above the Composer — you can continue typing while waiting
The main conversation is
not blocked
— it continues independently
The side question response does
not
become part of the main conversation history
Answers are rendered with full Markdown support (code blocks, lists, tables, etc.)
Keyboard Shortcuts (Interactive Mode):
Shortcut
Action
Escape
Cancel (while loading) or dismiss (after completed)
Space
or
Enter
Dismiss the answer (when input is empty)
Ctrl+C
or
Ctrl+D
Cancel an in-flight side question
Example:
(While the main conversation is about refactoring code)
> /btw What's the difference between let and var in JavaScript?
╭──────────────────────────────────────────╮
│ /btw What's the difference between let   │
│     and var in JavaScript?               │
│                                          │
│ + Answering...                           │
│ Press Escape, Ctrl+C, or Ctrl+D to cancel│
╰──────────────────────────────────────────╯
> (Composer remains active — keep typing)
(After the answer arrives)
╭──────────────────────────────────────────╮
│ /btw What's the difference between let   │
│     and var in JavaScript?               │
│                                          │
│ `let` is block-scoped, while `var` is    │
│ function-scoped. `let` was introduced    │
│ in ES6 and doesn't hoist the same way.   │
│                                          │
│ Press Space, Enter, or Escape to dismiss │
╰──────────────────────────────────────────╯
> (Composer still active)
Supported Execution Modes:
Mode
Behavior
Interactive
Shows above Composer with Markdown rendering
Non-interactive
Returns text result:
btw> question\nanswer
ACP (Agent Protocol)
Returns stream_messages async generator
Tip
Use
/btw
when you need a quick answer without derailing your main task. It’s especially useful for clarifying concepts, checking facts, or getting quick explanations while staying focused on your primary workflow.
1.7 Session Recap (
/recap
)
The
/recap
command generates a short “where you left off” summary of the
current session, so you can resume an old conversation without scrolling
back through pages of history.
Command
Description
/recap
Generate and show a one-line session recap
How it works:
Uses the configured fast model (
fastModel
setting) when available, falling
back to the main session model. A small, cheap model is enough for a recap.
The recent conversation (up to 30 messages, text only — tool calls and tool
responses are filtered out) is sent to the model with a tight system prompt.
The recap is rendered in dim color with a
❯
prefix so it stands apart
from real assistant replies.
Refuses with an inline error if a model turn is in flight or another command
is processing. If there is no usable conversation, or the underlying
generation fails,
/recap
shows a short info message instead of a recap —
the manual command always responds with something.
Auto-trigger when returning from being away:
If the terminal is blurred for
5+ minutes
and gets focused again, a recap
is generated and shown automatically (only when no model response is in
progress; otherwise it waits for the current turn to finish and then fires).
Unlike the manual command, the auto-trigger is fully silent on failure: if
generation errors or there is nothing to summarize, no message is added to
the history. Controlled by the
general.showSessionRecap
setting
(default:
true
); the manual
/recap
command always works regardless of
this setting.
Example:
> /recap
❯ Refactoring loopDetectionService.ts to address long-session OOM caused by
unbounded streamContentHistory and contentStats. The next step is to
implement option B (LRU sliding window with FNV-1a) pending confirmation.
Tip
Configure a fast model via
/model --fast <model>
(e.g.
qwen3-coder-flash
) to make
/recap
fast and cheap. Set
general.showSessionRecap
to
false
to opt out of the auto-trigger
while keeping the manual command available.
1.8 Information, Settings, and Help
Commands for obtaining information and performing system settings.
Command
Description
Usage Examples
/help
Display help information for available commands
/help
or
/?
/about
Display version information
/about
/stats
Display detailed statistics for current session
/stats
/settings
Open settings editor
/settings
/auth
Change authentication method
/auth
/bug
Submit issue about Qwen Code
/bug Button click unresponsive
/copy
Copy last output content to clipboard
/copy
/quit
Exit Qwen Code immediately
/quit
or
/exit
1.9 Common Shortcuts
Shortcut
Function
Note
Ctrl/cmd+L
Clear screen
Equivalent to
/clear
Ctrl/cmd+T
Toggle tool description
MCP tool management
Ctrl/cmd+C
×2
Exit confirmation
Secure exit mechanism
Ctrl/cmd+Z
Undo input
Text editing
Ctrl/cmd+Shift+Z
Redo input
Text editing
1.10 CLI Auth Subcommands
In addition to the in-session
/auth
slash command, Qwen Code provides standalone CLI subcommands for managing authentication directly from the terminal:
Command
Description
qwen auth
Interactive authentication setup
qwen auth coding-plan
Authenticate with Alibaba Cloud Coding Plan
qwen auth coding-plan --region china --key sk-sp-…
Non-interactive Coding Plan setup (for scripting)
qwen auth api-key
Authenticate with an API key
qwen auth qwen-oauth
Authenticate with Qwen OAuth
(discontinued on 2026-04-15)
qwen auth status
Show current authentication status
Tip
These commands run outside of a Qwen Code session. Use them to configure authentication before starting a session, or in scripts and CI environments. See the
Authentication
page for full details.
2. @ Commands (Introducing Files)
@ commands are used to quickly add local file or directory content to the conversation.
Command Format
Description
Examples
@<file path>
Inject content of specified file
@src/main.py Please explain this code
@<directory path>
Recursively read all text files in directory
@docs/ Summarize content of this document
Standalone
@
Used when discussing
@
symbol itself
@ What is this symbol used for in programming?
Note: Spaces in paths need to be escaped with backslash (e.g.,
@My\ Documents/file.txt
)
3. Exclamation Commands (
!
) - Shell Command Execution
Exclamation commands allow you to execute system commands directly within Qwen Code.
Command Format
Description
Examples
!<shell command>
Execute command in sub-Shell
!ls -la
,
!git status
Standalone
!
Switch Shell mode, any input is executed directly as Shell command
!
(enter) → Input command →
!
(exit)
Environment Variables: Commands executed via
!
will set the
QWEN_CODE=1
environment variable.
4. Custom Commands
Save frequently used prompts as shortcut commands to improve work efficiency and ensure consistency.
Note
Custom commands now use Markdown format with optional YAML frontmatter. TOML format is deprecated but still supported for backwards compatibility. When TOML files are detected, an automatic migration prompt will be displayed.
Quick Overview
Function
Description
Advantages
Priority
Applicable Scenarios
Namespace
Subdirectory creates colon-named commands
Better command organization
Global Commands
~/.qwen/commands/
Available in all projects
Low
Personal frequently used commands, cross-project use
Project Commands
<project root directory>/.qwen/commands/
Project-specific, version-controllable
High
Team sharing, project-specific commands
Priority Rules: Project commands > User commands (project command used when names are same)
Command Naming Rules
File Path to Command Name Mapping Table
File Location
Generated Command
Example Call
~/.qwen/commands/test.md
/test
/test Parameter
<project>/.qwen/commands/git/commit.md
/git:commit
/git:commit Message
Naming Rules: Path separator (
/
or
\
) converted to colon (
:
)
Markdown File Format Specification (Recommended)
Custom commands use Markdown files with optional YAML frontmatter:
---
description
:
Optional description (displayed in /help)
---
Your prompt content here.
Use {{args}} for parameter injection.
Field
Required
Description
Example
description
Optional
Command description (displayed in /help)
description: Code analysis tool
Prompt body
Required
Prompt content sent to model
Any Markdown content after the frontmatter
TOML File Format (Deprecated)
Warning
Deprecated:
TOML format is still supported but will be removed in a future version. Please migrate to Markdown format.
Field
Required
Description
Example
prompt
Required
Prompt content sent to model
prompt = "Please analyze code: {{args}}"
description
Optional
Command description (displayed in /help)
description = "Code analysis tool"
Parameter Processing Mechanism
Processing Method
Syntax
Applicable Scenarios
Security Features
Context-aware Injection
{{args}}
Need precise parameter control
Automatic Shell escaping
Default Parameter Processing
No special marking
Simple commands, parameter appending
Append as-is
Shell Command Injection
!{command}
Need dynamic content
Execution confirmation required before
1. Context-aware Injection (
{{args}}
)
Scenario
TOML Configuration
Call Method
Actual Effect
Raw Injection
prompt = "Fix: {{args}}"
/fix "Button issue"
Fix: "Button issue"
In Shell Command
prompt = "Search: !{grep {{args}} .}"
/search "hello"
Execute
grep "hello" .
2. Default Parameter Processing
Input Situation
Processing Method
Example
Has parameters
Append to end of prompt (separated by two line breaks)
/cmd parameter
→ Original prompt + parameter
No parameters
Send prompt as is
/cmd
→ Original prompt
🚀 Dynamic Content Injection
Injection Type
Syntax
Processing Order
Purpose
File Content
@{file path}
Processed first
Inject static reference files
Shell Commands
!{command}
Processed in middle
Inject dynamic execution results
Parameter Replacement
{{args}}
Processed last
Inject user parameters
3. Shell Command Execution (
!{...}
)
Operation
User Interaction
1. Parse command and parameters
-
2. Automatic Shell escaping
-
3. Show confirmation dialog
✅ User confirmation
4. Execute command
-
5. Inject output to prompt
-
Example: Git Commit Message Generation
---
description
:
Generate Commit message based on staged changes
---
Please generate a Commit message based on the following diff:
```diff
!{git diff --staged}
```
4. File Content Injection (
@{...}
)
File Type
Support Status
Processing Method
Text Files
✅ Full Support
Directly inject content
Images/PDF
✅ Multi-modal Support
Encode and inject
Binary Files
⚠️ Limited Support
May be skipped or truncated
Directory
✅ Recursive Injection
Follow .gitignore rules
Example: Code Review Command
---
description
:
Code review based on best practices
---
Review {{args}}, reference standards:
@{docs/code-standards.md}
Practical Creation Example
”Pure Function Refactoring” Command Creation Steps Table
Operation
Command/Code
1. Create directory structure
mkdir -p ~/.qwen/commands/refactor
2. Create command file
touch ~/.qwen/commands/refactor/pure.md
3. Edit command content
Refer to the complete code below.
4. Test command
@file.js
→
/refactor:pure
---
description
:
Refactor code to pure function
---
Please analyze code in current context, refactor to pure function.
Requirements:
1.
Provide refactored code
2.
Explain key changes and pure function characteristic implementation
3.
Maintain function unchanged
Custom Command Best Practices Summary
Command Design Recommendations Table
Practice Points
Recommended Approach
Avoid
Command Naming
Use namespaces for organization
Avoid overly generic names
Parameter Processing
Clearly use
{{args}}
Rely on default appending (easy to confuse)
Error Handling
Utilize Shell error output
Ignore execution failure
File Organization
Organize by function in directories
All commands in root directory
Description Field
Always provide clear description
Rely on auto-generated description
Security Features Reminder Table
Security Mechanism
Protection Effect
User Operation
Shell Escaping
Prevent command injection
Automatic processing
Execution Confirmation
Avoid accidental execution
Dialog confirmation
Error Reporting
Help diagnose issues
View error information
Last updated on
May 18, 2026
Github Actions
Code Review</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/commands/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Adaptive Output Token Escalation Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/adaptive-output-token-escalation/adaptive-output-token-escalation-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/adaptive-output-token-escalation/adaptive-output-token-escalation-design/</guid>
  <pubDate>Thu, 16 May 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Adaptive Output Token Escalation
Adaptive Output Token Escalation Design
Adaptive Output Token Escalation Design
Reduces GPU slot over-reservation by ~4x through a “low default + escalate on truncation” strategy for output tokens, with multi-turn recovery for responses that exceed even the escalated limit.
Problem
Every API request reserves a fixed GPU slot proportional to
max_tokens
. The previous default of 32K tokens means each request reserves a 32K output slot, but 99% of responses a...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Adaptive Output Token Escalation
Adaptive Output Token Escalation Design
Adaptive Output Token Escalation Design
Reduces GPU slot over-reservation by ~4x through a “low default + escalate on truncation” strategy for output tokens, with multi-turn recovery for responses that exceed even the escalated limit.
Problem
Every API request reserves a fixed GPU slot proportional to
max_tokens
. The previous default of 32K tokens means each request reserves a 32K output slot, but 99% of responses are under 5K tokens. This over-reserves GPU capacity by 4-6x, limiting server concurrency and increasing cost.
Solution
Use a capped default of
8K
output tokens. When a response is truncated (the model hits
max_tokens
):
Escalate
to the model’s full output limit (with 64K as a floor for unknown models)
If still truncated,
recover
by keeping the partial response in history and injecting a continuation message, up to 3 times
If recovery is exhausted, fall back to the tool scheduler’s truncation guidance
Since <1% of requests are actually truncated, this reduces average slot reservation significantly while preserving output quality for long responses.
Architecture
Request (max_tokens = 8K)
│
▼
┌─────────────────────────┐
│  Response truncated?     │──── No ──▶ Done ✓
│  (MAX_TOKENS)            │
└───────────┬──────────────┘
│ Yes
▼
┌──────────────────────────────────────────────────┐
│  Layer 1: Escalate to model output limit         │
│  ┌────────────────────────────────────────────┐  │
│  │ Pop partial response from history          │  │
│  │ RETRY (isContinuation: false → reset UI)   │  │
│  │ Re-send at max(64K, model output limit)    │  │
│  └────────────────────────────────────────────┘  │
└───────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────┐
│  Still truncated?        │──── No ──▶ Done ✓
│  (MAX_TOKENS)            │
└───────────┬──────────────┘
│ Yes
▼
┌──────────────────────────────────────────────────┐
│  Layer 2: Multi-turn recovery (up to 3×)         │
│  ┌────────────────────────────────────────────┐  │
│  │ Keep partial response in history           │  │
│  │ Push user message: "Resume directly..."    │  │
│  │ RETRY (isContinuation: true → keep UI buf) │  │
│  │ Re-send with updated history               │  │
│  │ Model continues from where it left off     │  │
│  └──────────────┬─────────────────────────────┘  │
│                 │                                 │
│          ┌──────┴──────┐                          │
│          │ Succeeded?  │── Yes ──▶ Done ✓         │
│          └──────┬──────┘                          │
│                 │ No (still truncated)            │
│                 ▼                                 │
│          attempt < 3? ── Yes ──▶ loop back ↑      │
└───────────┬──────────────────────────────────────┘
│ No (exhausted)
▼
┌──────────────────────────────────────────────────┐
│  Layer 3: Tool scheduler fallback                │
│  ┌────────────────────────────────────────────┐  │
│  │ Reject truncated Edit/Write tool calls     │  │
│  │ Return guidance: "You MUST split into      │  │
│  │ smaller parts — write skeleton first,      │  │
│  │ then edit incrementally."                  │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘
Token limit determination
The effective
max_tokens
is resolved in the following priority order:
Priority
Source
Value (known model)
Value (unknown model)
Escalation behavior
1 (highest)
User config (
samplingParams.max_tokens
)
min(userValue, modelLimit)
userValue
No escalation
2
Environment variable (
QWEN_CODE_MAX_OUTPUT_TOKENS
)
min(envValue, modelLimit)
envValue
No escalation
3 (lowest)
Capped default
min(modelLimit, 8K)
min(32K, 8K)
= 8K
Escalates to model limit (64K floor) + recovery
A “known model” is one that has an explicit entry in
OUTPUT_PATTERNS
(checked via
hasExplicitOutputLimit()
). For known models, the effective value is always capped at the model’s declared output limit to avoid API errors. Unknown models (custom deployments, self-hosted endpoints) pass the user’s value through directly, since the backend may support larger limits.
This logic is implemented in three content generators:
DefaultOpenAICompatibleProvider.applyOutputTokenLimit()
— OpenAI-compatible providers
DashScopeProvider
— inherits
applyOutputTokenLimit()
from the default provider
AnthropicContentGenerator.buildSamplingParameters()
— Anthropic provider
Escalation mechanism
The escalation logic lives in
geminiChat.ts
, placed
outside
the main retry loop. This is intentional:
The retry loop handles transient errors (rate limits, invalid streams, content validation)
Truncation is not an error — it’s a successful response that was cut short
Errors from the escalated stream should propagate directly to the caller, not be caught by retry logic
Escalation steps (geminiChat.ts)
1. Stream completes successfully (lastError === null)
2. Last chunk has finishReason === MAX_TOKENS
3. Guard checks pass:
- maxTokensEscalated === false (prevent infinite escalation)
- hasUserMaxTokensOverride === false (respect user intent)
4. Compute escalated limit: max(ESCALATED_MAX_TOKENS, tokenLimit(model, 'output'))
5. Pop the partial model response from chat history
6. Yield RETRY event (isContinuation: false) → UI discards partial output and resets buffers
7. Re-send the same request with maxOutputTokens: escalatedLimit
Recovery steps (geminiChat.ts)
If the escalated response is also truncated (finishReason === MAX_TOKENS), the recovery loop runs up to
MAX_OUTPUT_RECOVERY_ATTEMPTS
(3) times:
1. Partial model response is already in history (pushed by processStreamResponse)
2. Push a recovery user message: OUTPUT_RECOVERY_MESSAGE
3. Yield RETRY event (isContinuation: true) → UI keeps text buffer for continuation
4. Re-send with updated history (model sees its partial output + recovery instruction)
5. If still truncated and attempts remain, loop back to step 1
6. If recovery attempt throws (empty response, network error):
- Pop the dangling recovery message from history
- Break out of recovery loop
State cleanup on RETRY (turn.ts)
When the
Turn
class receives a RETRY event, it clears accumulated state to prevent inconsistencies:
pendingToolCalls
— cleared to avoid duplicate tool calls if the first truncated response contained completed tool calls that are repeated in the escalated response
pendingCitations
— cleared to avoid duplicate citations
debugResponses
— cleared to avoid stale debug data
finishReason
— reset to
undefined
so the new response’s finish reason is used
The
isContinuation
flag is passed through to the UI so it can decide whether to reset text buffers (escalation) or keep them (recovery).
Constants
Defined in
geminiChat.ts
and
tokenLimits.ts
:
Constant
Value
Purpose
CAPPED_DEFAULT_MAX_TOKENS
8,000
Default output token limit when no user override is set
ESCALATED_MAX_TOKENS
64,000
Floor for escalation (used when model limit is unknown)
MAX_OUTPUT_RECOVERY_ATTEMPTS
3
Max multi-turn recovery attempts after escalation
The effective escalated limit is
max(ESCALATED_MAX_TOKENS, tokenLimit(model, 'output'))
:
Model
Escalated limit
Claude Opus 4.6
131,072 (128K)
GPT-5 / o-series
131,072 (128K)
Qwen3.x
65,536 (64K)
Unknown models
64,000 (floor)
Design decisions
Why 8K default?
99% of responses are under 5K tokens
8K provides reasonable headroom for slightly longer responses without triggering unnecessary retries
Reduces average slot reservation from 32K to 8K (4x improvement)
Why escalate to model limit instead of fixed 64K?
Models with higher output limits (Claude Opus 128K, GPT-5 128K) were constrained to 64K unnecessarily
Using the model’s actual limit captures the vast majority of long outputs without a second retry
ESCALATED_MAX_TOKENS
(64K) serves as a floor for unknown models where
tokenLimit()
returns the default 32K
Why multi-turn recovery instead of progressive escalation?
Progressive escalation (8K → 16K → 32K → 64K) requires regenerating the full response each time
Multi-turn recovery keeps the partial response and lets the model continue, saving tokens and latency
Recovery messages are cheap (~40 tokens each) compared to regenerating large responses
The 3-attempt limit prevents infinite loops while covering most practical cases
Why is escalation outside the retry loop?
Truncation is a success case, not an error
Errors from the escalated stream (rate limits, network failures) should propagate directly rather than being silently retried with incorrect parameters
Keeps the retry loop focused on its original purpose (transient error recovery)
Recovery errors are caught separately to avoid aborting the entire conversation
Last updated on
May 18, 2026
Memory 记忆管理系统</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/adaptive-output-token-escalation/adaptive-output-token-escalation-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Adaptive Output Token Escalation
Adaptive Output Token Escalation Design
Adaptive Output Token Escalation Design
Reduces GPU slot over-reservation by ~4x through a “low default + escalate on truncation” strategy for output tokens, with multi-turn recovery for responses that exceed even the escalated limit.
Problem
Every API request reserves a fixed GPU slot proportional to
max_tokens
. The previous default of 32K tokens means each request reserves a 32K output slot, but 99% of responses a...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Adaptive Output Token Escalation
Adaptive Output Token Escalation Design
Adaptive Output Token Escalation Design
Reduces GPU slot over-reservation by ~4x through a “low default + escalate on truncation” strategy for output tokens, with multi-turn recovery for responses that exceed even the escalated limit.
Problem
Every API request reserves a fixed GPU slot proportional to
max_tokens
. The previous default of 32K tokens means each request reserves a 32K output slot, but 99% of responses are under 5K tokens. This over-reserves GPU capacity by 4-6x, limiting server concurrency and increasing cost.
Solution
Use a capped default of
8K
output tokens. When a response is truncated (the model hits
max_tokens
):
Escalate
to the model’s full output limit (with 64K as a floor for unknown models)
If still truncated,
recover
by keeping the partial response in history and injecting a continuation message, up to 3 times
If recovery is exhausted, fall back to the tool scheduler’s truncation guidance
Since <1% of requests are actually truncated, this reduces average slot reservation significantly while preserving output quality for long responses.
Architecture
Request (max_tokens = 8K)
│
▼
┌─────────────────────────┐
│  Response truncated?     │──── No ──▶ Done ✓
│  (MAX_TOKENS)            │
└───────────┬──────────────┘
│ Yes
▼
┌──────────────────────────────────────────────────┐
│  Layer 1: Escalate to model output limit         │
│  ┌────────────────────────────────────────────┐  │
│  │ Pop partial response from history          │  │
│  │ RETRY (isContinuation: false → reset UI)   │  │
│  │ Re-send at max(64K, model output limit)    │  │
│  └────────────────────────────────────────────┘  │
└───────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────┐
│  Still truncated?        │──── No ──▶ Done ✓
│  (MAX_TOKENS)            │
└───────────┬──────────────┘
│ Yes
▼
┌──────────────────────────────────────────────────┐
│  Layer 2: Multi-turn recovery (up to 3×)         │
│  ┌────────────────────────────────────────────┐  │
│  │ Keep partial response in history           │  │
│  │ Push user message: "Resume directly..."    │  │
│  │ RETRY (isContinuation: true → keep UI buf) │  │
│  │ Re-send with updated history               │  │
│  │ Model continues from where it left off     │  │
│  └──────────────┬─────────────────────────────┘  │
│                 │                                 │
│          ┌──────┴──────┐                          │
│          │ Succeeded?  │── Yes ──▶ Done ✓         │
│          └──────┬──────┘                          │
│                 │ No (still truncated)            │
│                 ▼                                 │
│          attempt < 3? ── Yes ──▶ loop back ↑      │
└───────────┬──────────────────────────────────────┘
│ No (exhausted)
▼
┌──────────────────────────────────────────────────┐
│  Layer 3: Tool scheduler fallback                │
│  ┌────────────────────────────────────────────┐  │
│  │ Reject truncated Edit/Write tool calls     │  │
│  │ Return guidance: "You MUST split into      │  │
│  │ smaller parts — write skeleton first,      │  │
│  │ then edit incrementally."                  │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘
Token limit determination
The effective
max_tokens
is resolved in the following priority order:
Priority
Source
Value (known model)
Value (unknown model)
Escalation behavior
1 (highest)
User config (
samplingParams.max_tokens
)
min(userValue, modelLimit)
userValue
No escalation
2
Environment variable (
QWEN_CODE_MAX_OUTPUT_TOKENS
)
min(envValue, modelLimit)
envValue
No escalation
3 (lowest)
Capped default
min(modelLimit, 8K)
min(32K, 8K)
= 8K
Escalates to model limit (64K floor) + recovery
A “known model” is one that has an explicit entry in
OUTPUT_PATTERNS
(checked via
hasExplicitOutputLimit()
). For known models, the effective value is always capped at the model’s declared output limit to avoid API errors. Unknown models (custom deployments, self-hosted endpoints) pass the user’s value through directly, since the backend may support larger limits.
This logic is implemented in three content generators:
DefaultOpenAICompatibleProvider.applyOutputTokenLimit()
— OpenAI-compatible providers
DashScopeProvider
— inherits
applyOutputTokenLimit()
from the default provider
AnthropicContentGenerator.buildSamplingParameters()
— Anthropic provider
Escalation mechanism
The escalation logic lives in
geminiChat.ts
, placed
outside
the main retry loop. This is intentional:
The retry loop handles transient errors (rate limits, invalid streams, content validation)
Truncation is not an error — it’s a successful response that was cut short
Errors from the escalated stream should propagate directly to the caller, not be caught by retry logic
Escalation steps (geminiChat.ts)
1. Stream completes successfully (lastError === null)
2. Last chunk has finishReason === MAX_TOKENS
3. Guard checks pass:
- maxTokensEscalated === false (prevent infinite escalation)
- hasUserMaxTokensOverride === false (respect user intent)
4. Compute escalated limit: max(ESCALATED_MAX_TOKENS, tokenLimit(model, 'output'))
5. Pop the partial model response from chat history
6. Yield RETRY event (isContinuation: false) → UI discards partial output and resets buffers
7. Re-send the same request with maxOutputTokens: escalatedLimit
Recovery steps (geminiChat.ts)
If the escalated response is also truncated (finishReason === MAX_TOKENS), the recovery loop runs up to
MAX_OUTPUT_RECOVERY_ATTEMPTS
(3) times:
1. Partial model response is already in history (pushed by processStreamResponse)
2. Push a recovery user message: OUTPUT_RECOVERY_MESSAGE
3. Yield RETRY event (isContinuation: true) → UI keeps text buffer for continuation
4. Re-send with updated history (model sees its partial output + recovery instruction)
5. If still truncated and attempts remain, loop back to step 1
6. If recovery attempt throws (empty response, network error):
- Pop the dangling recovery message from history
- Break out of recovery loop
State cleanup on RETRY (turn.ts)
When the
Turn
class receives a RETRY event, it clears accumulated state to prevent inconsistencies:
pendingToolCalls
— cleared to avoid duplicate tool calls if the first truncated response contained completed tool calls that are repeated in the escalated response
pendingCitations
— cleared to avoid duplicate citations
debugResponses
— cleared to avoid stale debug data
finishReason
— reset to
undefined
so the new response’s finish reason is used
The
isContinuation
flag is passed through to the UI so it can decide whether to reset text buffers (escalation) or keep them (recovery).
Constants
Defined in
geminiChat.ts
and
tokenLimits.ts
:
Constant
Value
Purpose
CAPPED_DEFAULT_MAX_TOKENS
8,000
Default output token limit when no user override is set
ESCALATED_MAX_TOKENS
64,000
Floor for escalation (used when model limit is unknown)
MAX_OUTPUT_RECOVERY_ATTEMPTS
3
Max multi-turn recovery attempts after escalation
The effective escalated limit is
max(ESCALATED_MAX_TOKENS, tokenLimit(model, 'output'))
:
Model
Escalated limit
Claude Opus 4.6
131,072 (128K)
GPT-5 / o-series
131,072 (128K)
Qwen3.x
65,536 (64K)
Unknown models
64,000 (floor)
Design decisions
Why 8K default?
99% of responses are under 5K tokens
8K provides reasonable headroom for slightly longer responses without triggering unnecessary retries
Reduces average slot reservation from 32K to 8K (4x improvement)
Why escalate to model limit instead of fixed 64K?
Models with higher output limits (Claude Opus 128K, GPT-5 128K) were constrained to 64K unnecessarily
Using the model’s actual limit captures the vast majority of long outputs without a second retry
ESCALATED_MAX_TOKENS
(64K) serves as a floor for unknown models where
tokenLimit()
returns the default 32K
Why multi-turn recovery instead of progressive escalation?
Progressive escalation (8K → 16K → 32K → 64K) requires regenerating the full response each time
Multi-turn recovery keeps the partial response and lets the model continue, saving tokens and latency
Recovery messages are cheap (~40 tokens each) compared to regenerating large responses
The 3-attempt limit prevents infinite loops while covering most practical cases
Why is escalation outside the retry loop?
Truncation is a success case, not an error
Errors from the escalated stream (rate limits, network failures) should propagate directly rather than being silently retried with incorrect parameters
Keeps the retry loop focused on its original purpose (transient error recovery)
Recovery errors are caught separately to avoid aborting the entire conversation
Last updated on
May 18, 2026
Memory 记忆管理系统</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/adaptive-output-token-escalation/adaptive-output-token-escalation-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Subagents</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/sub-agents/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/sub-agents/</guid>
  <pubDate>Sat, 04 May 2024 00:00:00 +0000</pubDate>
  <category>Agent</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
SubAgents
Subagents
Subagents are specialized AI assistants that handle specific types of tasks within Qwen Code. They allow you to delegate focused work to AI agents that are configured with task-specific prompts, tools, and behaviors.
What are Subagents?
Subagents are independent AI assistants that:
Specialize in specific tasks
- Each Subagent is configured with a focused system prompt for particular types of work
Have separate context
- They maintain their own conversation...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
SubAgents
Subagents
Subagents are specialized AI assistants that handle specific types of tasks within Qwen Code. They allow you to delegate focused work to AI agents that are configured with task-specific prompts, tools, and behaviors.
What are Subagents?
Subagents are independent AI assistants that:
Specialize in specific tasks
- Each Subagent is configured with a focused system prompt for particular types of work
Have separate context
- They maintain their own conversation history, separate from your main chat
Use controlled tools
- You can configure which tools each Subagent has access to
Work autonomously
- Once given a task, they work independently until completion or failure
Provide detailed feedback
- You can see their progress, tool usage, and execution statistics in real-time
Fork Subagent (Implicit Fork)
In addition to named subagents, Qwen Code supports
implicit forking
— when the AI omits the
subagent_type
parameter, it triggers a fork that inherits the parent’s full conversation context.
How Fork Differs from Named Subagents
Named Subagent
Fork Subagent
Context
Starts fresh, no parent history
Inherits parent’s full conversation history
System prompt
Uses its own configured prompt
Uses parent’s exact system prompt (for cache sharing)
Execution
Blocks the parent until done
Runs in background, parent continues immediately
Use case
Specialized tasks (testing, docs)
Parallel tasks that need the current context
When Fork is Used
The AI automatically uses fork when it needs to:
Run multiple research tasks in parallel (e.g., “investigate module A, B, and C”)
Perform background work while continuing the main conversation
Delegate tasks that require understanding of the current conversation context
Prompt Cache Sharing
All forks share the parent’s exact API request prefix (system prompt, tools, conversation history), enabling DashScope prompt cache hits. When 3 forks run in parallel, the shared prefix is cached once and reused — saving 80%+ token costs compared to independent subagents.
Recursive Fork Prevention
Fork children cannot create further forks. This is enforced at runtime — if a fork attempts to spawn another fork, it receives an error instructing it to execute tasks directly.
Current Limitations
No result feedback
: Fork results are reflected in the UI progress display but are not automatically fed back into the main conversation. The parent AI sees a placeholder message and cannot act on the fork’s output.
No worktree isolation
: Forks share the parent’s working directory. Concurrent file modifications from multiple forks may conflict.
Key Benefits
Task Specialization
: Create agents optimized for specific workflows (testing, documentation, refactoring, etc.)
Context Isolation
: Keep specialized work separate from your main conversation
Context Inheritance
: Fork subagents inherit the full conversation for context-heavy parallel tasks
Prompt Cache Sharing
: Fork subagents share the parent’s cache prefix, reducing token costs
Reusability
: Save and reuse agent configurations across projects and sessions
Controlled Access
: Limit which tools each agent can use for security and focus
Progress Visibility
: Monitor agent execution with real-time progress updates
How Subagents Work
Configuration
: You create Subagents configurations that define their behavior, tools, and system prompts
Delegation
: The main AI can automatically delegate tasks to appropriate Subagents — or implicitly fork when no specific subagent type is needed
Execution
: Subagents work independently, using their configured tools to complete tasks
Results
: They return results and execution summaries back to the main conversation
Getting Started
Quick Start
Create your first Subagent
:
/agents create
Follow the guided wizard to create a specialized agent.
Manage existing agents
:
/agents manage
View and manage your configured Subagents.
Use Subagents automatically
: Simply ask the main AI to perform tasks that match your Subagents’ specializations. The AI will automatically delegate appropriate work.
Example Usage
User: "Please write comprehensive tests for the authentication module"
AI: I'll delegate this to your testing specialist Subagents.
[Delegates to "testing-expert" Subagents]
[Shows real-time progress of test creation]
[Returns with completed test files and execution summary]`
Management
CLI Commands
Subagents are managed through the
/agents
slash command and its subcommands:
Usage:
：
/agents create
。Creates a new Subagent through a guided step wizard.
Usage:
：
/agents manage
。Opens an interactive management dialog for viewing and managing existing Subagents.
Storage Locations
Subagents are stored as Markdown files in multiple locations:
Project-level
:
.qwen/agents/
(highest precedence)
User-level
:
~/.qwen/agents/
(fallback)
Extension-level
: Provided by installed extensions
This allows you to have project-specific agents, personal agents that work across all projects, and extension-provided agents that add specialized capabilities.
Extension Subagents
Extensions can provide custom subagents that become available when the extension is enabled. These agents are stored in the extension’s
agents/
directory and follow the same format as personal and project agents.
Extension subagents:
Are automatically discovered when the extension is enabled
Appear in the
/agents manage
dialog under “Extension Agents” section
Cannot be edited directly (edit the extension source instead)
Follow the same configuration format as user-defined agents
To see which extensions provide subagents, check the extension’s
qwen-extension.json
file for an
agents
field.
File Format
Subagents are configured using Markdown files with YAML frontmatter. This format is human-readable and easy to edit with any text editor.
Basic Structure
---
name: agent-name
description: Brief description of when and how to use this agent
model: inherit # Optional: inherit or model-id
approvalMode: auto-edit # Optional: default, plan, auto-edit, yolo
tools:         # Optional: allowlist of tools
- tool1
- tool2
disallowedTools: # Optional: blocklist of tools
- tool3
---
System prompt content goes here.
Multiple paragraphs are supported.
Model Selection
Use the optional
model
frontmatter field to control which model a subagent uses:
inherit
: Use the same model as the main conversation
Omit the field: Same as
inherit
glm-5
: Use that model ID with the main conversation’s auth type
openai:gpt-4o
: Use a different provider (resolves credentials from env vars)
Permission Mode
Use the optional
approvalMode
frontmatter field to control how a subagent’s tool calls are approved. Valid values:
default
: Tools require interactive approval (same as the main session default)
plan
: Analyze-only mode — the agent plans but does not execute changes
auto-edit
: Tools are auto-approved without prompting (recommended for most agents)
yolo
: All tools auto-approved, including potentially destructive ones
If you omit this field, the subagent’s permission mode is determined automatically:
If the parent session is in
yolo
or
auto-edit
mode, the subagent inherits that mode. A permissive parent stays permissive.
If the parent session is in
plan
mode, the subagent stays in plan mode. An analyze-only session cannot mutate files through a delegated agent.
If the parent session is in
default
mode (in a trusted folder), the subagent gets
auto-edit
so it can work autonomously.
When you do set
approvalMode
, the parent’s permissive modes still take priority. For example, if the parent is in yolo mode, a subagent with
approvalMode: plan
will still run in yolo mode.
---
name: cautious-reviewer
description: Reviews code without making changes
approvalMode: plan
tools:
- read_file
- grep_search
- glob
---
You are a code reviewer. Analyze the code and report findings.
Do not modify any files.
Tool Configuration
Use
tools
and
disallowedTools
to control which tools a subagent can access.
tools
(allowlist):
When specified, the subagent can only use the listed tools. When omitted, the subagent inherits all available tools from the parent session.
---
name: reader
description: Read-only agent for code exploration
tools:
- read_file
- grep_search
- glob
- list_directory
---
disallowedTools
(blocklist):
When specified, the listed tools are removed from the subagent’s tool pool. This is useful when you want “everything except X” without listing every permitted tool.
---
name: safe-worker
description: Agent that cannot modify files
disallowedTools:
- write_file
- edit
- run_shell_command
---
If both
tools
and
disallowedTools
are set, the allowlist is applied first, then the blocklist removes from that set.
MCP tools
follow the same rules. If a subagent has no
tools
list, it inherits all MCP tools from the parent session. If a subagent has an explicit
tools
list, it only gets MCP tools that are explicitly named in that list.
The
disallowedTools
field supports MCP server-level patterns:
mcp__server__tool_name
— blocks a specific MCP tool
mcp__server
— blocks all tools from that MCP server
---
name: no-slack
description: Agent without Slack access
disallowedTools:
- mcp__slack
---
Example Usage
---
name: project-documenter
description: Creates project documentation and README files
---
You are a documentation specialist.
Focus on creating clear, comprehensive documentation that helps both
new contributors and end users understand the project.
Using Subagents Effectively
Automatic Delegation
Qwen Code proactively delegates tasks based on:
The task description in your request
The description field in Subagents configurations
Current context and available tools
To encourage more proactive Subagents use, include phrases like “use PROACTIVELY” or “MUST BE USED” in your description field.
Explicit Invocation
Request a specific Subagent by mentioning it in your command:
Let the testing-expert Subagents create unit tests for the payment module
Have the documentation-writer Subagents update the API reference
Get the react-specialist Subagents to optimize this component's performance
Examples
Development Workflow Agents
Testing Specialist
Perfect for comprehensive test creation and test-driven development.
---
name: testing-expert
description: Writes comprehensive unit tests, integration tests, and handles test automation with best practices
tools:
- read_file
- write_file
- read_many_files
- run_shell_command
---
You are a testing specialist focused on creating high-quality, maintainable tests.
Your expertise includes:
- Unit testing with appropriate mocking and isolation
- Integration testing for component interactions
- Test-driven development practices
- Edge case identification and comprehensive coverage
- Performance and load testing when appropriate
For each testing task:
1. Analyze the code structure and dependencies
2. Identify key functionality, edge cases, and error conditions
3. Create comprehensive test suites with descriptive names
4. Include proper setup/teardown and meaningful assertions
5. Add comments explaining complex test scenarios
6. Ensure tests are maintainable and follow DRY principles
Always follow testing best practices for the detected language and framework.
Focus on both positive and negative test cases.
Use Cases:
“Write unit tests for the authentication service”
“Create integration tests for the payment processing workflow”
“Add test coverage for edge cases in the data validation module”
Documentation Writer
Specialized in creating clear, comprehensive documentation.
---
name: documentation-writer
description: Creates comprehensive documentation, README files, API docs, and user guides
tools:
- read_file
- write_file
- read_many_files
---
You are a technical documentation specialist.
Your role is to create clear, comprehensive documentation that serves both
developers and end users. Focus on:
**For API Documentation:**
- Clear endpoint descriptions with examples
- Parameter details with types and constraints
- Response format documentation
- Error code explanations
- Authentication requirements
**For User Documentation:**
- Step-by-step instructions with screenshots when helpful
- Installation and setup guides
- Configuration options and examples
- Troubleshooting sections for common issues
- FAQ sections based on common user questions
**For Developer Documentation:**
- Architecture overviews and design decisions
- Code examples that actually work
- Contributing guidelines
- Development environment setup
Always verify code examples and ensure documentation stays current with
the actual implementation. Use clear headings, bullet points, and examples.
Use Cases:
“Create API documentation for the user management endpoints”
“Write a comprehensive README for this project”
“Document the deployment process with troubleshooting steps”
Code Reviewer
Focused on code quality, security, and best practices.
---
name: code-reviewer
description: Reviews code for best practices, security issues, performance, and maintainability
tools:
- read_file
- read_many_files
---
You are an experienced code reviewer focused on quality, security, and maintainability.
Review criteria:
- **Code Structure**: Organization, modularity, and separation of concerns
- **Performance**: Algorithmic efficiency and resource usage
- **Security**: Vulnerability assessment and secure coding practices
- **Best Practices**: Language/framework-specific conventions
- **Error Handling**: Proper exception handling and edge case coverage
- **Readability**: Clear naming, comments, and code organization
- **Testing**: Test coverage and testability considerations
Provide constructive feedback with:
1. **Critical Issues**: Security vulnerabilities, major bugs
2. **Important Improvements**: Performance issues, design problems
3. **Minor Suggestions**: Style improvements, refactoring opportunities
4. **Positive Feedback**: Well-implemented patterns and good practices
Focus on actionable feedback with specific examples and suggested solutions.
Prioritize issues by impact and provide rationale for recommendations.
Use Cases:
“Review this authentication implementation for security issues”
“Check the performance implications of this database query logic”
“Evaluate the code structure and suggest improvements”
Technology-Specific Agents
React Specialist
Optimized for React development, hooks, and component patterns.
---
name: react-specialist
description: Expert in React development, hooks, component patterns, and modern React best practices
tools:
- read_file
- write_file
- read_many_files
- run_shell_command
---
You are a React specialist with deep expertise in modern React development.
Your expertise covers:
- **Component Design**: Functional components, custom hooks, composition patterns
- **State Management**: useState, useReducer, Context API, and external libraries
- **Performance**: React.memo, useMemo, useCallback, code splitting
- **Testing**: React Testing Library, Jest, component testing strategies
- **TypeScript Integration**: Proper typing for props, hooks, and components
- **Modern Patterns**: Suspense, Error Boundaries, Concurrent Features
For React tasks:
1. Use functional components and hooks by default
2. Implement proper TypeScript typing
3. Follow React best practices and conventions
4. Consider performance implications
5. Include appropriate error handling
6. Write testable, maintainable code
Always stay current with React best practices and avoid deprecated patterns.
Focus on accessibility and user experience considerations.
Use Cases:
“Create a reusable data table component with sorting and filtering”
“Implement a custom hook for API data fetching with caching”
“Refactor this class component to use modern React patterns”
Python Expert
Specialized in Python development, frameworks, and best practices.
---
name: python-expert
description: Expert in Python development, frameworks, testing, and Python-specific best practices
tools:
- read_file
- write_file
- read_many_files
- run_shell_command
---
You are a Python expert with deep knowledge of the Python ecosystem.
Your expertise includes:
- **Core Python**: Pythonic patterns, data structures, algorithms
- **Frameworks**: Django, Flask, FastAPI, SQLAlchemy
- **Testing**: pytest, unittest, mocking, test-driven development
- **Data Science**: pandas, numpy, matplotlib, jupyter notebooks
- **Async Programming**: asyncio, async/await patterns
- **Package Management**: pip, poetry, virtual environments
- **Code Quality**: PEP 8, type hints, linting with pylint/flake8
For Python tasks:
1. Follow PEP 8 style guidelines
2. Use type hints for better code documentation
3. Implement proper error handling with specific exceptions
4. Write comprehensive docstrings
5. Consider performance and memory usage
6. Include appropriate logging
7. Write testable, modular code
Focus on writing clean, maintainable Python code that follows community standards.
Use Cases:
“Create a FastAPI service for user authentication with JWT tokens”
“Implement a data processing pipeline with pandas and error handling”
“Write a CLI tool using argparse with comprehensive help documentation”
Best Practices
Design Principles
Single Responsibility Principle
Each Subagent should have a clear, focused purpose.
✅ Good:
---
name: testing-expert
description: Writes comprehensive unit tests and integration tests
---
❌ Avoid:
---
name: general-helper
description: Helps with testing, documentation, code review, and deployment
---
Why:
Focused agents produce better results and are easier to maintain.
Clear Specialization
Define specific expertise areas rather than broad capabilities.
✅ Good:
---
name: react-performance-optimizer
description: Optimizes React applications for performance using profiling and best practices
---
❌ Avoid:
---
name: frontend-developer
description: Works on frontend development tasks
---
Why:
Specific expertise leads to more targeted and effective assistance.
Actionable Descriptions
Write descriptions that clearly indicate when to use the agent.
✅ Good:
description: Reviews code for security vulnerabilities, performance issues, and maintainability concerns
❌ Avoid:
description: A helpful code reviewer
Why:
Clear descriptions help the main AI choose the right agent for each task.
Configuration Best Practices
System Prompt Guidelines
Be Specific About Expertise:
You are a Python testing specialist with expertise in:
- pytest framework and fixtures
- Mock objects and dependency injection
- Test-driven development practices
- Performance testing with pytest-benchmark
Include Step-by-Step Approaches:
For each testing task:
1. Analyze the code structure and dependencies
2. Identify key functionality and edge cases
3. Create comprehensive test suites with clear naming
4. Include setup/teardown and proper assertions
5. Add comments explaining complex test scenarios
Specify Output Standards:
Always follow these standards:
- Use descriptive test names that explain the scenario
- Include both positive and negative test cases
- Add docstrings for complex test functions
- Ensure tests are independent and can run in any order
Security Considerations
Tool Restrictions
: Use
tools
to limit which tools a subagent can access, or
disallowedTools
to block specific tools while inheriting everything else
Permission Mode
: Subagents inherit their parent’s permission mode by default. Plan-mode sessions cannot escalate to auto-edit through delegated agents. Privileged modes (auto-edit, yolo) are blocked in untrusted folders.
Sandboxing
: All tool execution follows the same security model as direct tool use
Audit Trail
: All Subagents actions are logged and visible in real-time
Access Control
: Project and user-level separation provides appropriate boundaries
Sensitive Information
: Avoid including secrets or credentials in agent configurations
Production Environments
: Consider separate agents for production vs development environments
Limits
The following soft warnings apply to Subagent configurations (no hard limits are enforced):
Description Field
: A warning is shown for descriptions exceeding 1,000 characters
System Prompt
: A warning is shown for system prompts exceeding 10,000 characters
Last updated on
May 18, 2026
Tool-Use Summaries
Agent Arena</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/sub-agents/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
SubAgents
Subagents
Subagents are specialized AI assistants that handle specific types of tasks within Qwen Code. They allow you to delegate focused work to AI agents that are configured with task-specific prompts, tools, and behaviors.
What are Subagents?
Subagents are independent AI assistants that:
Specialize in specific tasks
- Each Subagent is configured with a focused system prompt for particular types of work
Have separate context
- They maintain their own conversation...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
SubAgents
Subagents
Subagents are specialized AI assistants that handle specific types of tasks within Qwen Code. They allow you to delegate focused work to AI agents that are configured with task-specific prompts, tools, and behaviors.
What are Subagents?
Subagents are independent AI assistants that:
Specialize in specific tasks
- Each Subagent is configured with a focused system prompt for particular types of work
Have separate context
- They maintain their own conversation history, separate from your main chat
Use controlled tools
- You can configure which tools each Subagent has access to
Work autonomously
- Once given a task, they work independently until completion or failure
Provide detailed feedback
- You can see their progress, tool usage, and execution statistics in real-time
Fork Subagent (Implicit Fork)
In addition to named subagents, Qwen Code supports
implicit forking
— when the AI omits the
subagent_type
parameter, it triggers a fork that inherits the parent’s full conversation context.
How Fork Differs from Named Subagents
Named Subagent
Fork Subagent
Context
Starts fresh, no parent history
Inherits parent’s full conversation history
System prompt
Uses its own configured prompt
Uses parent’s exact system prompt (for cache sharing)
Execution
Blocks the parent until done
Runs in background, parent continues immediately
Use case
Specialized tasks (testing, docs)
Parallel tasks that need the current context
When Fork is Used
The AI automatically uses fork when it needs to:
Run multiple research tasks in parallel (e.g., “investigate module A, B, and C”)
Perform background work while continuing the main conversation
Delegate tasks that require understanding of the current conversation context
Prompt Cache Sharing
All forks share the parent’s exact API request prefix (system prompt, tools, conversation history), enabling DashScope prompt cache hits. When 3 forks run in parallel, the shared prefix is cached once and reused — saving 80%+ token costs compared to independent subagents.
Recursive Fork Prevention
Fork children cannot create further forks. This is enforced at runtime — if a fork attempts to spawn another fork, it receives an error instructing it to execute tasks directly.
Current Limitations
No result feedback
: Fork results are reflected in the UI progress display but are not automatically fed back into the main conversation. The parent AI sees a placeholder message and cannot act on the fork’s output.
No worktree isolation
: Forks share the parent’s working directory. Concurrent file modifications from multiple forks may conflict.
Key Benefits
Task Specialization
: Create agents optimized for specific workflows (testing, documentation, refactoring, etc.)
Context Isolation
: Keep specialized work separate from your main conversation
Context Inheritance
: Fork subagents inherit the full conversation for context-heavy parallel tasks
Prompt Cache Sharing
: Fork subagents share the parent’s cache prefix, reducing token costs
Reusability
: Save and reuse agent configurations across projects and sessions
Controlled Access
: Limit which tools each agent can use for security and focus
Progress Visibility
: Monitor agent execution with real-time progress updates
How Subagents Work
Configuration
: You create Subagents configurations that define their behavior, tools, and system prompts
Delegation
: The main AI can automatically delegate tasks to appropriate Subagents — or implicitly fork when no specific subagent type is needed
Execution
: Subagents work independently, using their configured tools to complete tasks
Results
: They return results and execution summaries back to the main conversation
Getting Started
Quick Start
Create your first Subagent
:
/agents create
Follow the guided wizard to create a specialized agent.
Manage existing agents
:
/agents manage
View and manage your configured Subagents.
Use Subagents automatically
: Simply ask the main AI to perform tasks that match your Subagents’ specializations. The AI will automatically delegate appropriate work.
Example Usage
User: "Please write comprehensive tests for the authentication module"
AI: I'll delegate this to your testing specialist Subagents.
[Delegates to "testing-expert" Subagents]
[Shows real-time progress of test creation]
[Returns with completed test files and execution summary]`
Management
CLI Commands
Subagents are managed through the
/agents
slash command and its subcommands:
Usage:
：
/agents create
。Creates a new Subagent through a guided step wizard.
Usage:
：
/agents manage
。Opens an interactive management dialog for viewing and managing existing Subagents.
Storage Locations
Subagents are stored as Markdown files in multiple locations:
Project-level
:
.qwen/agents/
(highest precedence)
User-level
:
~/.qwen/agents/
(fallback)
Extension-level
: Provided by installed extensions
This allows you to have project-specific agents, personal agents that work across all projects, and extension-provided agents that add specialized capabilities.
Extension Subagents
Extensions can provide custom subagents that become available when the extension is enabled. These agents are stored in the extension’s
agents/
directory and follow the same format as personal and project agents.
Extension subagents:
Are automatically discovered when the extension is enabled
Appear in the
/agents manage
dialog under “Extension Agents” section
Cannot be edited directly (edit the extension source instead)
Follow the same configuration format as user-defined agents
To see which extensions provide subagents, check the extension’s
qwen-extension.json
file for an
agents
field.
File Format
Subagents are configured using Markdown files with YAML frontmatter. This format is human-readable and easy to edit with any text editor.
Basic Structure
---
name: agent-name
description: Brief description of when and how to use this agent
model: inherit # Optional: inherit or model-id
approvalMode: auto-edit # Optional: default, plan, auto-edit, yolo
tools:         # Optional: allowlist of tools
- tool1
- tool2
disallowedTools: # Optional: blocklist of tools
- tool3
---
System prompt content goes here.
Multiple paragraphs are supported.
Model Selection
Use the optional
model
frontmatter field to control which model a subagent uses:
inherit
: Use the same model as the main conversation
Omit the field: Same as
inherit
glm-5
: Use that model ID with the main conversation’s auth type
openai:gpt-4o
: Use a different provider (resolves credentials from env vars)
Permission Mode
Use the optional
approvalMode
frontmatter field to control how a subagent’s tool calls are approved. Valid values:
default
: Tools require interactive approval (same as the main session default)
plan
: Analyze-only mode — the agent plans but does not execute changes
auto-edit
: Tools are auto-approved without prompting (recommended for most agents)
yolo
: All tools auto-approved, including potentially destructive ones
If you omit this field, the subagent’s permission mode is determined automatically:
If the parent session is in
yolo
or
auto-edit
mode, the subagent inherits that mode. A permissive parent stays permissive.
If the parent session is in
plan
mode, the subagent stays in plan mode. An analyze-only session cannot mutate files through a delegated agent.
If the parent session is in
default
mode (in a trusted folder), the subagent gets
auto-edit
so it can work autonomously.
When you do set
approvalMode
, the parent’s permissive modes still take priority. For example, if the parent is in yolo mode, a subagent with
approvalMode: plan
will still run in yolo mode.
---
name: cautious-reviewer
description: Reviews code without making changes
approvalMode: plan
tools:
- read_file
- grep_search
- glob
---
You are a code reviewer. Analyze the code and report findings.
Do not modify any files.
Tool Configuration
Use
tools
and
disallowedTools
to control which tools a subagent can access.
tools
(allowlist):
When specified, the subagent can only use the listed tools. When omitted, the subagent inherits all available tools from the parent session.
---
name: reader
description: Read-only agent for code exploration
tools:
- read_file
- grep_search
- glob
- list_directory
---
disallowedTools
(blocklist):
When specified, the listed tools are removed from the subagent’s tool pool. This is useful when you want “everything except X” without listing every permitted tool.
---
name: safe-worker
description: Agent that cannot modify files
disallowedTools:
- write_file
- edit
- run_shell_command
---
If both
tools
and
disallowedTools
are set, the allowlist is applied first, then the blocklist removes from that set.
MCP tools
follow the same rules. If a subagent has no
tools
list, it inherits all MCP tools from the parent session. If a subagent has an explicit
tools
list, it only gets MCP tools that are explicitly named in that list.
The
disallowedTools
field supports MCP server-level patterns:
mcp__server__tool_name
— blocks a specific MCP tool
mcp__server
— blocks all tools from that MCP server
---
name: no-slack
description: Agent without Slack access
disallowedTools:
- mcp__slack
---
Example Usage
---
name: project-documenter
description: Creates project documentation and README files
---
You are a documentation specialist.
Focus on creating clear, comprehensive documentation that helps both
new contributors and end users understand the project.
Using Subagents Effectively
Automatic Delegation
Qwen Code proactively delegates tasks based on:
The task description in your request
The description field in Subagents configurations
Current context and available tools
To encourage more proactive Subagents use, include phrases like “use PROACTIVELY” or “MUST BE USED” in your description field.
Explicit Invocation
Request a specific Subagent by mentioning it in your command:
Let the testing-expert Subagents create unit tests for the payment module
Have the documentation-writer Subagents update the API reference
Get the react-specialist Subagents to optimize this component's performance
Examples
Development Workflow Agents
Testing Specialist
Perfect for comprehensive test creation and test-driven development.
---
name: testing-expert
description: Writes comprehensive unit tests, integration tests, and handles test automation with best practices
tools:
- read_file
- write_file
- read_many_files
- run_shell_command
---
You are a testing specialist focused on creating high-quality, maintainable tests.
Your expertise includes:
- Unit testing with appropriate mocking and isolation
- Integration testing for component interactions
- Test-driven development practices
- Edge case identification and comprehensive coverage
- Performance and load testing when appropriate
For each testing task:
1. Analyze the code structure and dependencies
2. Identify key functionality, edge cases, and error conditions
3. Create comprehensive test suites with descriptive names
4. Include proper setup/teardown and meaningful assertions
5. Add comments explaining complex test scenarios
6. Ensure tests are maintainable and follow DRY principles
Always follow testing best practices for the detected language and framework.
Focus on both positive and negative test cases.
Use Cases:
“Write unit tests for the authentication service”
“Create integration tests for the payment processing workflow”
“Add test coverage for edge cases in the data validation module”
Documentation Writer
Specialized in creating clear, comprehensive documentation.
---
name: documentation-writer
description: Creates comprehensive documentation, README files, API docs, and user guides
tools:
- read_file
- write_file
- read_many_files
---
You are a technical documentation specialist.
Your role is to create clear, comprehensive documentation that serves both
developers and end users. Focus on:
**For API Documentation:**
- Clear endpoint descriptions with examples
- Parameter details with types and constraints
- Response format documentation
- Error code explanations
- Authentication requirements
**For User Documentation:**
- Step-by-step instructions with screenshots when helpful
- Installation and setup guides
- Configuration options and examples
- Troubleshooting sections for common issues
- FAQ sections based on common user questions
**For Developer Documentation:**
- Architecture overviews and design decisions
- Code examples that actually work
- Contributing guidelines
- Development environment setup
Always verify code examples and ensure documentation stays current with
the actual implementation. Use clear headings, bullet points, and examples.
Use Cases:
“Create API documentation for the user management endpoints”
“Write a comprehensive README for this project”
“Document the deployment process with troubleshooting steps”
Code Reviewer
Focused on code quality, security, and best practices.
---
name: code-reviewer
description: Reviews code for best practices, security issues, performance, and maintainability
tools:
- read_file
- read_many_files
---
You are an experienced code reviewer focused on quality, security, and maintainability.
Review criteria:
- **Code Structure**: Organization, modularity, and separation of concerns
- **Performance**: Algorithmic efficiency and resource usage
- **Security**: Vulnerability assessment and secure coding practices
- **Best Practices**: Language/framework-specific conventions
- **Error Handling**: Proper exception handling and edge case coverage
- **Readability**: Clear naming, comments, and code organization
- **Testing**: Test coverage and testability considerations
Provide constructive feedback with:
1. **Critical Issues**: Security vulnerabilities, major bugs
2. **Important Improvements**: Performance issues, design problems
3. **Minor Suggestions**: Style improvements, refactoring opportunities
4. **Positive Feedback**: Well-implemented patterns and good practices
Focus on actionable feedback with specific examples and suggested solutions.
Prioritize issues by impact and provide rationale for recommendations.
Use Cases:
“Review this authentication implementation for security issues”
“Check the performance implications of this database query logic”
“Evaluate the code structure and suggest improvements”
Technology-Specific Agents
React Specialist
Optimized for React development, hooks, and component patterns.
---
name: react-specialist
description: Expert in React development, hooks, component patterns, and modern React best practices
tools:
- read_file
- write_file
- read_many_files
- run_shell_command
---
You are a React specialist with deep expertise in modern React development.
Your expertise covers:
- **Component Design**: Functional components, custom hooks, composition patterns
- **State Management**: useState, useReducer, Context API, and external libraries
- **Performance**: React.memo, useMemo, useCallback, code splitting
- **Testing**: React Testing Library, Jest, component testing strategies
- **TypeScript Integration**: Proper typing for props, hooks, and components
- **Modern Patterns**: Suspense, Error Boundaries, Concurrent Features
For React tasks:
1. Use functional components and hooks by default
2. Implement proper TypeScript typing
3. Follow React best practices and conventions
4. Consider performance implications
5. Include appropriate error handling
6. Write testable, maintainable code
Always stay current with React best practices and avoid deprecated patterns.
Focus on accessibility and user experience considerations.
Use Cases:
“Create a reusable data table component with sorting and filtering”
“Implement a custom hook for API data fetching with caching”
“Refactor this class component to use modern React patterns”
Python Expert
Specialized in Python development, frameworks, and best practices.
---
name: python-expert
description: Expert in Python development, frameworks, testing, and Python-specific best practices
tools:
- read_file
- write_file
- read_many_files
- run_shell_command
---
You are a Python expert with deep knowledge of the Python ecosystem.
Your expertise includes:
- **Core Python**: Pythonic patterns, data structures, algorithms
- **Frameworks**: Django, Flask, FastAPI, SQLAlchemy
- **Testing**: pytest, unittest, mocking, test-driven development
- **Data Science**: pandas, numpy, matplotlib, jupyter notebooks
- **Async Programming**: asyncio, async/await patterns
- **Package Management**: pip, poetry, virtual environments
- **Code Quality**: PEP 8, type hints, linting with pylint/flake8
For Python tasks:
1. Follow PEP 8 style guidelines
2. Use type hints for better code documentation
3. Implement proper error handling with specific exceptions
4. Write comprehensive docstrings
5. Consider performance and memory usage
6. Include appropriate logging
7. Write testable, modular code
Focus on writing clean, maintainable Python code that follows community standards.
Use Cases:
“Create a FastAPI service for user authentication with JWT tokens”
“Implement a data processing pipeline with pandas and error handling”
“Write a CLI tool using argparse with comprehensive help documentation”
Best Practices
Design Principles
Single Responsibility Principle
Each Subagent should have a clear, focused purpose.
✅ Good:
---
name: testing-expert
description: Writes comprehensive unit tests and integration tests
---
❌ Avoid:
---
name: general-helper
description: Helps with testing, documentation, code review, and deployment
---
Why:
Focused agents produce better results and are easier to maintain.
Clear Specialization
Define specific expertise areas rather than broad capabilities.
✅ Good:
---
name: react-performance-optimizer
description: Optimizes React applications for performance using profiling and best practices
---
❌ Avoid:
---
name: frontend-developer
description: Works on frontend development tasks
---
Why:
Specific expertise leads to more targeted and effective assistance.
Actionable Descriptions
Write descriptions that clearly indicate when to use the agent.
✅ Good:
description: Reviews code for security vulnerabilities, performance issues, and maintainability concerns
❌ Avoid:
description: A helpful code reviewer
Why:
Clear descriptions help the main AI choose the right agent for each task.
Configuration Best Practices
System Prompt Guidelines
Be Specific About Expertise:
You are a Python testing specialist with expertise in:
- pytest framework and fixtures
- Mock objects and dependency injection
- Test-driven development practices
- Performance testing with pytest-benchmark
Include Step-by-Step Approaches:
For each testing task:
1. Analyze the code structure and dependencies
2. Identify key functionality and edge cases
3. Create comprehensive test suites with clear naming
4. Include setup/teardown and proper assertions
5. Add comments explaining complex test scenarios
Specify Output Standards:
Always follow these standards:
- Use descriptive test names that explain the scenario
- Include both positive and negative test cases
- Add docstrings for complex test functions
- Ensure tests are independent and can run in any order
Security Considerations
Tool Restrictions
: Use
tools
to limit which tools a subagent can access, or
disallowedTools
to block specific tools while inheriting everything else
Permission Mode
: Subagents inherit their parent’s permission mode by default. Plan-mode sessions cannot escalate to auto-edit through delegated agents. Privileged modes (auto-edit, yolo) are blocked in untrusted folders.
Sandboxing
: All tool execution follows the same security model as direct tool use
Audit Trail
: All Subagents actions are logged and visible in real-time
Access Control
: Project and user-level separation provides appropriate boundaries
Sensitive Information
: Avoid including secrets or credentials in agent configurations
Production Environments
: Consider separate agents for production vs development environments
Limits
The following soft warnings apply to Subagent configurations (no hard limits are enforced):
Description Field
: A warning is shown for descriptions exceeding 1,000 characters
System Prompt
: A warning is shown for system prompts exceeding 10,000 characters
Last updated on
May 18, 2026
Tool-Use Summaries
Agent Arena</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/sub-agents/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Contextual Tips</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/tips/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/tips/</guid>
  <pubDate>Thu, 02 May 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Contextual Tips
Contextual Tips
Qwen Code includes a contextual tips system that helps you discover features and stay aware of session state.
Startup Tips
Each time you launch Qwen Code, a tip is shown in the header area. Tips are selected by priority first, then rotated across sessions using LRU (least-recently-used) scheduling among tips of the same priority, so you see a different tip each time.
New users see onboarding-focused tips during their first sessions:
Sessions
Ex...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Contextual Tips
Contextual Tips
Qwen Code includes a contextual tips system that helps you discover features and stay aware of session state.
Startup Tips
Each time you launch Qwen Code, a tip is shown in the header area. Tips are selected by priority first, then rotated across sessions using LRU (least-recently-used) scheduling among tips of the same priority, so you see a different tip each time.
New users see onboarding-focused tips during their first sessions:
Sessions
Example tips
< 5
Slash commands (
/
), Tab autocomplete
< 10
QWEN.md
project context,
--continue
/
--resume
< 15
Shell commands with
!
prefix
After that, tips rotate through general features like
/compress
,
/approval-mode
,
/insight
,
/btw
, and more.
Post-Response Tips
During a conversation, Qwen Code monitors your context window usage and shows tips when action may be needed:
Context usage
Condition
Tip
50-80%
After a few prompts in session
Suggests
/compress
to free up context
80-95%
—
Warns context is getting full
>= 95%
—
Urgent: run
/compress
now or
/new
to continue
Post-response tips have per-tip cooldowns to avoid being repetitive.
Tip History
Tip display history is persisted at
~/.qwen/tip_history.json
. This file tracks:
Session count (used for new-user tip selection)
Which tips have been shown and when (used for LRU rotation and cooldown)
You can safely delete this file to reset tip history.
Disabling Tips
To hide all tips (both startup and post-response), set
ui.hideTips
to
true
in
~/.qwen/settings.json
:
{
"ui"
: {
"hideTips"
:
true
}
}
You can also toggle this in the settings dialog via the
/settings
command.
Tips are also automatically hidden when screen reader mode is enabled.
Last updated on
May 18, 2026
Scheduled Tasks
Settings</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/tips/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Contextual Tips
Contextual Tips
Qwen Code includes a contextual tips system that helps you discover features and stay aware of session state.
Startup Tips
Each time you launch Qwen Code, a tip is shown in the header area. Tips are selected by priority first, then rotated across sessions using LRU (least-recently-used) scheduling among tips of the same priority, so you see a different tip each time.
New users see onboarding-focused tips during their first sessions:
Sessions
Ex...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Contextual Tips
Contextual Tips
Qwen Code includes a contextual tips system that helps you discover features and stay aware of session state.
Startup Tips
Each time you launch Qwen Code, a tip is shown in the header area. Tips are selected by priority first, then rotated across sessions using LRU (least-recently-used) scheduling among tips of the same priority, so you see a different tip each time.
New users see onboarding-focused tips during their first sessions:
Sessions
Example tips
< 5
Slash commands (
/
), Tab autocomplete
< 10
QWEN.md
project context,
--continue
/
--resume
< 15
Shell commands with
!
prefix
After that, tips rotate through general features like
/compress
,
/approval-mode
,
/insight
,
/btw
, and more.
Post-Response Tips
During a conversation, Qwen Code monitors your context window usage and shows tips when action may be needed:
Context usage
Condition
Tip
50-80%
After a few prompts in session
Suggests
/compress
to free up context
80-95%
—
Warns context is getting full
>= 95%
—
Urgent: run
/compress
now or
/new
to continue
Post-response tips have per-tip cooldowns to avoid being repetitive.
Tip History
Tip display history is persisted at
~/.qwen/tip_history.json
. This file tracks:
Session count (used for new-user tip selection)
Which tips have been shown and when (used for LRU rotation and cooldown)
You can safely delete this file to reset tip history.
Disabling Tips
To hide all tips (both startup and post-response), set
ui.hideTips
to
true
in
~/.qwen/settings.json
:
{
"ui"
: {
"hideTips"
:
true
}
}
You can also toggle this in the settings dialog via the
/settings
command.
Tips are also automatically hidden when screen reader mode is enabled.
Last updated on
May 18, 2026
Scheduled Tasks
Settings</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/tips/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Web Search</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-search/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-search/</guid>
  <pubDate>Wed, 01 May 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Web Search
Web Search
Qwen Code supports web search capabilities through
MCP (Model Context Protocol)
integrations. Rather than a built-in search tool, web search is provided by connecting to external MCP servers, giving you full flexibility to choose the search service that best fits your needs.
⚠️ Breaking Change: Built-in
web_search
Tool Removed
Affected versions:
V0.0.7+
through the last release with built-in web search support.
The built-in
web_search
tool and all its ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Web Search
Web Search
Qwen Code supports web search capabilities through
MCP (Model Context Protocol)
integrations. Rather than a built-in search tool, web search is provided by connecting to external MCP servers, giving you full flexibility to choose the search service that best fits your needs.
⚠️ Breaking Change: Built-in
web_search
Tool Removed
Affected versions:
V0.0.7+
through the last release with built-in web search support.
The built-in
web_search
tool and all its associated configuration have been
removed
. If you were using any of the following, you should migrate to the MCP-based approach described in this document:
Removed
What to do
webSearch
block in
settings.json
Configure an MCP server in
mcpServers
instead (see below)
advanced.tavilyApiKey
in
settings.json
Use the
Tavily MCP server
TAVILY_API_KEY
environment variable
Use the
Tavily MCP server
DASHSCOPE_API_KEY
for web search
Use the
Alibaba Cloud Bailian WebSearch MCP
GLM_API_KEY
for web search
Use the
GLM WebSearch Prime MCP
--tavily-api-key
/
--glm-api-key
/
--dashscope-api-key
CLI flags
Configure via
mcpServers
in
settings.json
Migration Examples
Before (Tavily via built-in tool):
{
"webSearch"
: {
"provider"
: [{
"type"
:
"tavily"
,
"apiKey"
:
"tvly-xxx"
}],
"default"
:
"tavily"
}
}
After (Tavily via MCP):
{
"mcpServers"
: {
"tavily"
: {
"httpUrl"
:
"https://mcp.tavily.com/mcp/?tavilyApiKey=tvly-xxx"
}
}
}
Before (DashScope via built-in tool):
{
"webSearch"
: {
"provider"
: [{
"type"
:
"dashscope"
,
"apiKey"
:
"sk-xxx"
}],
"default"
:
"dashscope"
}
}
After (Alibaba Cloud Bailian WebSearch via MCP):
{
"mcpServers"
: {
"WebSearch"
: {
"httpUrl"
:
"https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer sk-xxx"
}
}
}
}
Supported MCP Web Search Services
Alibaba Cloud Bailian WebSearch (Recommended)
The official web search MCP service provided by Alibaba Cloud Bailian platform, powered by DashScope.
MCP Marketplace:
https://bailian.console.aliyun.com/cn-beijing?tab=mcp#/mcp-market/detail/WebSearch
Cost:
Paid (billed via Alibaba Cloud DashScope)
Get API Key:
https://help.aliyun.com/zh/model-studio/get-api-key
Best for:
Chinese-language queries, access to Chinese web content, integration with the Alibaba Cloud ecosystem
Setup
Method 1: CLI command
qwen
mcp
add
WebSearch
\
-t
http
\
"https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp"
\
-H
"Authorization: Bearer ${
DASHSCOPE_API_KEY
}"
Method 2:
settings.json
{
"mcpServers"
: {
"WebSearch"
: {
"httpUrl"
:
"https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer ${DASHSCOPE_API_KEY}"
}
}
}
}
Replace
${DASHSCOPE_API_KEY}
with your actual API key, or set it as an environment variable so Qwen Code picks it up automatically.
Tavily WebSearch
A production-ready MCP server providing real-time web search, extract, map, and crawl capabilities.
Repository:
https://github.com/tavily-ai/tavily-mcp
Cost:
Paid (free tier available)
Get API Key:
https://app.tavily.com/home
Best for:
General-purpose web search with high-quality AI-generated answers
Available Tools
tavily_search
— Real-time web search
tavily_extract
— Intelligent data extraction from web pages
tavily_map
— Create a structured map of a website
tavily_crawl
— Systematically explore websites
Setup
Method 1: CLI command (Remote MCP)
qwen
mcp
add
tavily
\
-t
http
\
"https://mcp.tavily.com/mcp/?tavilyApiKey=${
TAVILY_API_KEY
}"
Method 2:
settings.json
(Remote MCP)
{
"mcpServers"
: {
"tavily"
: {
"httpUrl"
:
"https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"
}
}
}
Replace
${TAVILY_API_KEY}
with your actual API key, or set it as an environment variable.
Method 3:
settings.json
(Local NPX)
{
"mcpServers"
: {
"tavily-mcp"
: {
"command"
:
"npx"
,
"args"
: [
"-y"
,
"tavily-mcp@latest"
],
"env"
: {
"TAVILY_API_KEY"
:
"your-api-key-here"
}
}
}
}
GLM WebSearch Prime (ZhipuAI)
The official web search Remote MCP service provided by ZhipuAI (智谱AI), designed for GLM Coding Plan users. Provides real-time web search including news, stock prices, weather, and more.
Documentation:
https://docs.bigmodel.cn/cn/coding-plan/mcp/search-mcp-server
Cost:
Included in GLM Coding Plan subscription (Lite: 100 calls/month, Pro: 1,000/month, Max: 4,000/month)
Get API Key:
https://open.bigmodel.cn/apikey/platform
Best for:
Chinese-language queries, real-time information retrieval
Available Tools
webSearchPrime
— Web search returning page title, URL, summary, site name, and favicon
Setup
Method 1: CLI command
qwen
mcp
add
web-search-prime
\
-t
http
\
"https://open.bigmodel.cn/api/mcp/web_search_prime/mcp"
\
-H
"Authorization: Bearer ${
GLM_API_KEY
}"
Method 2:
settings.json
{
"mcpServers"
: {
"web-search-prime"
: {
"httpUrl"
:
"https://open.bigmodel.cn/api/mcp/web_search_prime/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer ${GLM_API_KEY}"
}
}
}
}
Replace
${GLM_API_KEY}
with your actual ZhipuAI API key, or set it as an environment variable.
Last updated on
May 18, 2026
Web Fetch
MCP Servers</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-search/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Web Search
Web Search
Qwen Code supports web search capabilities through
MCP (Model Context Protocol)
integrations. Rather than a built-in search tool, web search is provided by connecting to external MCP servers, giving you full flexibility to choose the search service that best fits your needs.
⚠️ Breaking Change: Built-in
web_search
Tool Removed
Affected versions:
V0.0.7+
through the last release with built-in web search support.
The built-in
web_search
tool and all its ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Web Search
Web Search
Qwen Code supports web search capabilities through
MCP (Model Context Protocol)
integrations. Rather than a built-in search tool, web search is provided by connecting to external MCP servers, giving you full flexibility to choose the search service that best fits your needs.
⚠️ Breaking Change: Built-in
web_search
Tool Removed
Affected versions:
V0.0.7+
through the last release with built-in web search support.
The built-in
web_search
tool and all its associated configuration have been
removed
. If you were using any of the following, you should migrate to the MCP-based approach described in this document:
Removed
What to do
webSearch
block in
settings.json
Configure an MCP server in
mcpServers
instead (see below)
advanced.tavilyApiKey
in
settings.json
Use the
Tavily MCP server
TAVILY_API_KEY
environment variable
Use the
Tavily MCP server
DASHSCOPE_API_KEY
for web search
Use the
Alibaba Cloud Bailian WebSearch MCP
GLM_API_KEY
for web search
Use the
GLM WebSearch Prime MCP
--tavily-api-key
/
--glm-api-key
/
--dashscope-api-key
CLI flags
Configure via
mcpServers
in
settings.json
Migration Examples
Before (Tavily via built-in tool):
{
"webSearch"
: {
"provider"
: [{
"type"
:
"tavily"
,
"apiKey"
:
"tvly-xxx"
}],
"default"
:
"tavily"
}
}
After (Tavily via MCP):
{
"mcpServers"
: {
"tavily"
: {
"httpUrl"
:
"https://mcp.tavily.com/mcp/?tavilyApiKey=tvly-xxx"
}
}
}
Before (DashScope via built-in tool):
{
"webSearch"
: {
"provider"
: [{
"type"
:
"dashscope"
,
"apiKey"
:
"sk-xxx"
}],
"default"
:
"dashscope"
}
}
After (Alibaba Cloud Bailian WebSearch via MCP):
{
"mcpServers"
: {
"WebSearch"
: {
"httpUrl"
:
"https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer sk-xxx"
}
}
}
}
Supported MCP Web Search Services
Alibaba Cloud Bailian WebSearch (Recommended)
The official web search MCP service provided by Alibaba Cloud Bailian platform, powered by DashScope.
MCP Marketplace:
https://bailian.console.aliyun.com/cn-beijing?tab=mcp#/mcp-market/detail/WebSearch
Cost:
Paid (billed via Alibaba Cloud DashScope)
Get API Key:
https://help.aliyun.com/zh/model-studio/get-api-key
Best for:
Chinese-language queries, access to Chinese web content, integration with the Alibaba Cloud ecosystem
Setup
Method 1: CLI command
qwen
mcp
add
WebSearch
\
-t
http
\
"https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp"
\
-H
"Authorization: Bearer ${
DASHSCOPE_API_KEY
}"
Method 2:
settings.json
{
"mcpServers"
: {
"WebSearch"
: {
"httpUrl"
:
"https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer ${DASHSCOPE_API_KEY}"
}
}
}
}
Replace
${DASHSCOPE_API_KEY}
with your actual API key, or set it as an environment variable so Qwen Code picks it up automatically.
Tavily WebSearch
A production-ready MCP server providing real-time web search, extract, map, and crawl capabilities.
Repository:
https://github.com/tavily-ai/tavily-mcp
Cost:
Paid (free tier available)
Get API Key:
https://app.tavily.com/home
Best for:
General-purpose web search with high-quality AI-generated answers
Available Tools
tavily_search
— Real-time web search
tavily_extract
— Intelligent data extraction from web pages
tavily_map
— Create a structured map of a website
tavily_crawl
— Systematically explore websites
Setup
Method 1: CLI command (Remote MCP)
qwen
mcp
add
tavily
\
-t
http
\
"https://mcp.tavily.com/mcp/?tavilyApiKey=${
TAVILY_API_KEY
}"
Method 2:
settings.json
(Remote MCP)
{
"mcpServers"
: {
"tavily"
: {
"httpUrl"
:
"https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"
}
}
}
Replace
${TAVILY_API_KEY}
with your actual API key, or set it as an environment variable.
Method 3:
settings.json
(Local NPX)
{
"mcpServers"
: {
"tavily-mcp"
: {
"command"
:
"npx"
,
"args"
: [
"-y"
,
"tavily-mcp@latest"
],
"env"
: {
"TAVILY_API_KEY"
:
"your-api-key-here"
}
}
}
}
GLM WebSearch Prime (ZhipuAI)
The official web search Remote MCP service provided by ZhipuAI (智谱AI), designed for GLM Coding Plan users. Provides real-time web search including news, stock prices, weather, and more.
Documentation:
https://docs.bigmodel.cn/cn/coding-plan/mcp/search-mcp-server
Cost:
Included in GLM Coding Plan subscription (Lite: 100 calls/month, Pro: 1,000/month, Max: 4,000/month)
Get API Key:
https://open.bigmodel.cn/apikey/platform
Best for:
Chinese-language queries, real-time information retrieval
Available Tools
webSearchPrime
— Web search returning page title, URL, summary, site name, and favicon
Setup
Method 1: CLI command
qwen
mcp
add
web-search-prime
\
-t
http
\
"https://open.bigmodel.cn/api/mcp/web_search_prime/mcp"
\
-H
"Authorization: Bearer ${
GLM_API_KEY
}"
Method 2:
settings.json
{
"mcpServers"
: {
"web-search-prime"
: {
"httpUrl"
:
"https://open.bigmodel.cn/api/mcp/web_search_prime/mcp"
,
"headers"
: {
"Authorization"
:
"Bearer ${GLM_API_KEY}"
}
}
}
}
Replace
${GLM_API_KEY}
with your actual ZhipuAI API key, or set it as an environment variable.
Last updated on
May 18, 2026
Web Fetch
MCP Servers</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-search/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Headless Mode</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/headless/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/headless/</guid>
  <pubDate>Thu, 25 Apr 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Headless Mode
Headless Mode
Headless mode allows you to run Qwen Code programmatically from command line
scripts and automation tools without any interactive UI. This is ideal for
scripting, automation, CI/CD pipelines, and building AI-powered tools.
Overview
The headless mode provides a headless interface to Qwen Code that:
Accepts prompts via command line arguments or stdin
Returns structured output (text or JSON)
Supports file redirection and piping
Enables automation and ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Headless Mode
Headless Mode
Headless mode allows you to run Qwen Code programmatically from command line
scripts and automation tools without any interactive UI. This is ideal for
scripting, automation, CI/CD pipelines, and building AI-powered tools.
Overview
The headless mode provides a headless interface to Qwen Code that:
Accepts prompts via command line arguments or stdin
Returns structured output (text or JSON)
Supports file redirection and piping
Enables automation and scripting workflows
Provides consistent exit codes for error handling
Can resume previous sessions scoped to the current project for multi-step automation
Basic Usage
Direct Prompts
Use the
--prompt
(or
-p
) flag to run in headless mode:
qwen
--prompt
"What is machine learning?"
Stdin Input
Pipe input to Qwen Code from your terminal:
echo
"Explain this code"
|
qwen
Combining with File Input
Read from files and process with Qwen Code:
cat
README.md
|
qwen
--prompt
"Summarize this documentation"
Resume Previous Sessions (Headless)
Reuse conversation context from the current project in headless scripts:
# Continue the most recent session for this project and run a new prompt
qwen
--continue
-p
"Run the tests again and summarize failures"
# Resume a specific session ID directly (no UI)
qwen
--resume
123e4567-e89b-12d3-a456-426614174000
-p
"Apply the follow-up refactor"
Note
Session data is project-scoped JSONL under
~/.qwen/projects/<sanitized-cwd>/chats
.
Restores conversation history, tool outputs, and chat-compression checkpoints before sending the new prompt.
Customize the Main Session Prompt
You can change the main session system prompt for a single CLI run without editing shared memory files.
Override the Built-in System Prompt
Use
--system-prompt
to replace Qwen Code’s built-in main-session prompt for the current run:
qwen
-p
"Review this patch"
--system-prompt
"You are a terse release reviewer. Report only blocking issues."
Append Extra Instructions
Use
--append-system-prompt
to keep the built-in prompt and add extra instructions for this run:
qwen
-p
"Review this patch"
--append-system-prompt
"Be terse and focus on concrete findings."
You can combine both flags when you want a custom base prompt plus an extra run-specific instruction:
qwen
-p
"Summarize this repository"
\
--system-prompt
"You are a migration planner."
\
--append-system-prompt
"Return exactly three bullets."
Note
--system-prompt
applies only to the current run’s main session.
Loaded memory and context files such as
QWEN.md
are still appended after
--system-prompt
.
--append-system-prompt
is applied after the built-in prompt and loaded memory, and can be used together with
--system-prompt
.
Output Formats
Qwen Code supports multiple output formats for different use cases:
Text Output (Default)
Standard human-readable output:
qwen
-p
"What is the capital of France?"
Response format:
The capital of France is Paris.
JSON Output
Returns structured data as a JSON array. All messages are buffered and output together when the session completes. This format is ideal for programmatic processing and automation scripts.
The JSON output is an array of message objects. The output includes multiple message types: system messages (session initialization), assistant messages (AI responses), and result messages (execution summary).
Example Usage
qwen
-p
"What is the capital of France?"
--output-format
json
Output (at end of execution):
[
{
"type"
:
"system"
,
"subtype"
:
"session_start"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"model"
:
"qwen3-coder-plus"
,
...
},
{
"type"
:
"assistant"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"message"
: {
"id"
:
"..."
,
"type"
:
"message"
,
"role"
:
"assistant"
,
"model"
:
"qwen3-coder-plus"
,
"content"
: [
{
"type"
:
"text"
,
"text"
:
"The capital of France is Paris."
}
],
"usage"
: {
...
}
},
"parent_tool_use_id"
:
null
},
{
"type"
:
"result"
,
"subtype"
:
"success"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"is_error"
:
false
,
"duration_ms"
:
1234
,
"result"
:
"The capital of France is Paris."
,
"usage"
: {
...
}
}
]
Stream-JSON Output
Stream-JSON format emits JSON messages immediately as they occur during execution, enabling real-time monitoring. This format uses line-delimited JSON where each message is a complete JSON object on a single line.
qwen
-p
"Explain TypeScript"
--output-format
stream-json
Output (streaming as events occur):
{
"type"
:
"system"
,
"subtype"
:
"session_start"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
}
{
"type"
:
"assistant"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"message"
:{
...
}}
{
"type"
:
"result"
,
"subtype"
:
"success"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
}
When combined with
--include-partial-messages
, additional stream events are emitted in real-time (message_start, content_block_delta, etc.) for real-time UI updates.
qwen
-p
"Write a Python script"
--output-format
stream-json
--include-partial-messages
Input Format
The
--input-format
parameter controls how Qwen Code consumes input from standard input:
text
(default): Standard text input from stdin or command-line arguments
stream-json
: JSON message protocol via stdin for bidirectional communication
Note:
Stream-json input mode is currently under construction and is intended for SDK integration. It requires
--output-format stream-json
to be set.
File Redirection
Save output to files or pipe to other commands:
# Save to file
qwen
-p
"Explain Docker"
>
docker-explanation.txt
qwen
-p
"Explain Docker"
--output-format
json
>
docker-explanation.json
# Append to file
qwen
-p
"Add more details"
>>
docker-explanation.txt
# Pipe to other tools
qwen
-p
"What is Kubernetes?"
--output-format
json
|
jq
'.response'
qwen
-p
"Explain microservices"
|
wc
-w
qwen
-p
"List programming languages"
|
grep
-i
"python"
# Stream-JSON output for real-time processing
qwen
-p
"Explain Docker"
--output-format
stream-json
|
jq
'.type'
qwen
-p
"Write code"
--output-format
stream-json
--include-partial-messages
|
jq
'.event.type'
Configuration Options
Key command-line options for headless usage:
Option
Description
Example
--prompt
,
-p
Run in headless mode
qwen -p "query"
--output-format
,
-o
Specify output format (text, json, stream-json)
qwen -p "query" --output-format json
--input-format
Specify input format (text, stream-json)
qwen --input-format text --output-format stream-json
--include-partial-messages
Include partial messages in stream-json output
qwen -p "query" --output-format stream-json --include-partial-messages
--system-prompt
Override the main session system prompt for this run
qwen -p "query" --system-prompt "You are a terse reviewer."
--append-system-prompt
Append extra instructions to the main session system prompt for this run
qwen -p "query" --append-system-prompt "Focus on concrete findings."
--debug
,
-d
Enable debug mode
qwen -p "query" --debug
--all-files
,
-a
Include all files in context
qwen -p "query" --all-files
--include-directories
Include additional directories
qwen -p "query" --include-directories src,docs
--yolo
,
-y
Auto-approve all actions
qwen -p "query" --yolo
--approval-mode
Set approval mode
qwen -p "query" --approval-mode auto_edit
--continue
Resume the most recent session for this project
qwen --continue -p "Pick up where we left off"
--resume [sessionId]
Resume a specific session (or choose interactively)
qwen --resume 123e... -p "Finish the refactor"
For complete details on all available configuration options, settings files, and environment variables, see the
Configuration Guide
.
Examples
Code review
cat
src/auth.py
|
qwen
-p
"Review this authentication code for security issues"
>
security-review.txt
Generate commit messages
result
=
$(
git
diff
--cached
|
qwen
-p
"Write a concise commit message for these changes"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
API documentation
result
=
$(
cat
api/routes.js
|
qwen
-p
"Generate OpenAPI spec for these routes"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
>
openapi.json
Batch code analysis
for
file
in
src/*.py
;
do
echo
"Analyzing
$file
..."
result
=
$(
cat
"
$file
"
|
qwen
-p
"Find potential bugs and suggest improvements"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
>
"reports/$(
basename
"
$file
").analysis"
echo
"Completed analysis for $(
basename
"
$file
")"
>>
reports/progress.log
done
PR code review
result
=
$(
git
diff
origin/main...HEAD
|
qwen
-p
"Review these changes for bugs, security issues, and code quality"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
>
pr-review.json
Log analysis
grep
"ERROR"
/var/log/app.log
|
tail
-20
|
qwen
-p
"Analyze these errors and suggest root cause and fixes"
>
error-analysis.txt
Release notes generation
result
=
$(
git
log
--oneline
v1.0.0..HEAD
|
qwen
-p
"Generate release notes from these commits"
--output-format
json
)
response
=
$(
echo
"
$result
"
|
jq
-r
'.response'
)
echo
"
$response
"
echo
"
$response
"
>>
CHANGELOG.md
Model and tool usage tracking
result
=
$(
qwen
-p
"Explain this database schema"
--include-directories
db
--output-format
json
)
total_tokens
=
$(
echo
"
$result
"
|
jq
-r
'.stats.models // {} | to_entries | map(.value.tokens.total) | add // 0'
)
models_used
=
$(
echo
"
$result
"
|
jq
-r
'.stats.models // {} | keys | join(", ") | if . == "" then "none" else . end'
)
tool_calls
=
$(
echo
"
$result
"
|
jq
-r
'.stats.tools.totalCalls // 0'
)
tools_used
=
$(
echo
"
$result
"
|
jq
-r
'.stats.tools.byName // {} | keys | join(", ") | if . == "" then "none" else . end'
)
echo
"$(
date
):
$total_tokens
tokens,
$tool_calls
tool calls (
$tools_used
) used with models:
$models_used
"
>>
usage.log
echo
"
$result
"
|
jq
-r
'.response'
>
schema-docs.md
echo
"Recent usage trends:"
tail
-5
usage.log
Persistent Retry Mode
When Qwen Code runs in CI/CD pipelines or as a background daemon, a brief API outage (rate limiting or overload) should not kill a multi-hour task.
Persistent retry mode
makes Qwen Code retry transient API errors indefinitely until the service recovers.
How it works
Transient errors only
: HTTP 429 (Rate Limit) and 529 (Overloaded) are retried indefinitely. Other errors (400, 500, etc.) still fail normally.
Exponential backoff with cap
: Retry delays grow exponentially but are capped at
5 minutes
per retry.
Heartbeat keepalive
: During long waits, a status line is printed to stderr every
30 seconds
to prevent CI runners from killing the process due to inactivity.
Graceful degradation
: Non-transient errors and interactive mode are completely unaffected.
Activation
Set the
QWEN_CODE_UNATTENDED_RETRY
environment variable to
true
or
1
(strict match, case-sensitive):
export
QWEN_CODE_UNATTENDED_RETRY
=
1
[!important]
Persistent retry requires an
explicit opt-in
.
CI=true
alone does
not
activate it — silently turning a fast-fail CI job into an infinite-wait job would be dangerous. Always set
QWEN_CODE_UNATTENDED_RETRY
explicitly in your pipeline configuration.
Examples
GitHub Actions
-
name
:
Automated code review
env
:
QWEN_CODE_UNATTENDED_RETRY
:
'1'
run
:
|
qwen -p "Review all files in src/ for security issues" \
--output-format json \
--yolo > review.json
Overnight batch processing
export
QWEN_CODE_UNATTENDED_RETRY
=
1
qwen
-p
"Migrate all callback-style functions to async/await in src/"
--yolo
Background daemon
QWEN_CODE_UNATTENDED_RETRY
=
1
nohup
qwen
-p
"Audit all dependencies for known CVEs"
\
--output-format
json
>
audit.json
2>
audit.log
&
Monitoring
During persistent retry, heartbeat messages are printed to
stderr
:
[qwen-code] Waiting for API capacity... attempt 3, retry in 45s
[qwen-code] Waiting for API capacity... attempt 3, retry in 15s
These messages keep CI runners alive and let you monitor progress. They do not appear in stdout, so JSON output piped to other tools remains clean.
Resources
CLI Configuration
- Complete configuration guide
Authentication
- Setup authentication
Commands
- Interactive commands reference
Tutorials
- Step-by-step automation guides
Last updated on
May 18, 2026
Memory
Dual Output</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/headless/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Headless Mode
Headless Mode
Headless mode allows you to run Qwen Code programmatically from command line
scripts and automation tools without any interactive UI. This is ideal for
scripting, automation, CI/CD pipelines, and building AI-powered tools.
Overview
The headless mode provides a headless interface to Qwen Code that:
Accepts prompts via command line arguments or stdin
Returns structured output (text or JSON)
Supports file redirection and piping
Enables automation and ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Headless Mode
Headless Mode
Headless mode allows you to run Qwen Code programmatically from command line
scripts and automation tools without any interactive UI. This is ideal for
scripting, automation, CI/CD pipelines, and building AI-powered tools.
Overview
The headless mode provides a headless interface to Qwen Code that:
Accepts prompts via command line arguments or stdin
Returns structured output (text or JSON)
Supports file redirection and piping
Enables automation and scripting workflows
Provides consistent exit codes for error handling
Can resume previous sessions scoped to the current project for multi-step automation
Basic Usage
Direct Prompts
Use the
--prompt
(or
-p
) flag to run in headless mode:
qwen
--prompt
"What is machine learning?"
Stdin Input
Pipe input to Qwen Code from your terminal:
echo
"Explain this code"
|
qwen
Combining with File Input
Read from files and process with Qwen Code:
cat
README.md
|
qwen
--prompt
"Summarize this documentation"
Resume Previous Sessions (Headless)
Reuse conversation context from the current project in headless scripts:
# Continue the most recent session for this project and run a new prompt
qwen
--continue
-p
"Run the tests again and summarize failures"
# Resume a specific session ID directly (no UI)
qwen
--resume
123e4567-e89b-12d3-a456-426614174000
-p
"Apply the follow-up refactor"
Note
Session data is project-scoped JSONL under
~/.qwen/projects/<sanitized-cwd>/chats
.
Restores conversation history, tool outputs, and chat-compression checkpoints before sending the new prompt.
Customize the Main Session Prompt
You can change the main session system prompt for a single CLI run without editing shared memory files.
Override the Built-in System Prompt
Use
--system-prompt
to replace Qwen Code’s built-in main-session prompt for the current run:
qwen
-p
"Review this patch"
--system-prompt
"You are a terse release reviewer. Report only blocking issues."
Append Extra Instructions
Use
--append-system-prompt
to keep the built-in prompt and add extra instructions for this run:
qwen
-p
"Review this patch"
--append-system-prompt
"Be terse and focus on concrete findings."
You can combine both flags when you want a custom base prompt plus an extra run-specific instruction:
qwen
-p
"Summarize this repository"
\
--system-prompt
"You are a migration planner."
\
--append-system-prompt
"Return exactly three bullets."
Note
--system-prompt
applies only to the current run’s main session.
Loaded memory and context files such as
QWEN.md
are still appended after
--system-prompt
.
--append-system-prompt
is applied after the built-in prompt and loaded memory, and can be used together with
--system-prompt
.
Output Formats
Qwen Code supports multiple output formats for different use cases:
Text Output (Default)
Standard human-readable output:
qwen
-p
"What is the capital of France?"
Response format:
The capital of France is Paris.
JSON Output
Returns structured data as a JSON array. All messages are buffered and output together when the session completes. This format is ideal for programmatic processing and automation scripts.
The JSON output is an array of message objects. The output includes multiple message types: system messages (session initialization), assistant messages (AI responses), and result messages (execution summary).
Example Usage
qwen
-p
"What is the capital of France?"
--output-format
json
Output (at end of execution):
[
{
"type"
:
"system"
,
"subtype"
:
"session_start"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"model"
:
"qwen3-coder-plus"
,
...
},
{
"type"
:
"assistant"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"message"
: {
"id"
:
"..."
,
"type"
:
"message"
,
"role"
:
"assistant"
,
"model"
:
"qwen3-coder-plus"
,
"content"
: [
{
"type"
:
"text"
,
"text"
:
"The capital of France is Paris."
}
],
"usage"
: {
...
}
},
"parent_tool_use_id"
:
null
},
{
"type"
:
"result"
,
"subtype"
:
"success"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"is_error"
:
false
,
"duration_ms"
:
1234
,
"result"
:
"The capital of France is Paris."
,
"usage"
: {
...
}
}
]
Stream-JSON Output
Stream-JSON format emits JSON messages immediately as they occur during execution, enabling real-time monitoring. This format uses line-delimited JSON where each message is a complete JSON object on a single line.
qwen
-p
"Explain TypeScript"
--output-format
stream-json
Output (streaming as events occur):
{
"type"
:
"system"
,
"subtype"
:
"session_start"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
}
{
"type"
:
"assistant"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"message"
:{
...
}}
{
"type"
:
"result"
,
"subtype"
:
"success"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
}
When combined with
--include-partial-messages
, additional stream events are emitted in real-time (message_start, content_block_delta, etc.) for real-time UI updates.
qwen
-p
"Write a Python script"
--output-format
stream-json
--include-partial-messages
Input Format
The
--input-format
parameter controls how Qwen Code consumes input from standard input:
text
(default): Standard text input from stdin or command-line arguments
stream-json
: JSON message protocol via stdin for bidirectional communication
Note:
Stream-json input mode is currently under construction and is intended for SDK integration. It requires
--output-format stream-json
to be set.
File Redirection
Save output to files or pipe to other commands:
# Save to file
qwen
-p
"Explain Docker"
>
docker-explanation.txt
qwen
-p
"Explain Docker"
--output-format
json
>
docker-explanation.json
# Append to file
qwen
-p
"Add more details"
>>
docker-explanation.txt
# Pipe to other tools
qwen
-p
"What is Kubernetes?"
--output-format
json
|
jq
'.response'
qwen
-p
"Explain microservices"
|
wc
-w
qwen
-p
"List programming languages"
|
grep
-i
"python"
# Stream-JSON output for real-time processing
qwen
-p
"Explain Docker"
--output-format
stream-json
|
jq
'.type'
qwen
-p
"Write code"
--output-format
stream-json
--include-partial-messages
|
jq
'.event.type'
Configuration Options
Key command-line options for headless usage:
Option
Description
Example
--prompt
,
-p
Run in headless mode
qwen -p "query"
--output-format
,
-o
Specify output format (text, json, stream-json)
qwen -p "query" --output-format json
--input-format
Specify input format (text, stream-json)
qwen --input-format text --output-format stream-json
--include-partial-messages
Include partial messages in stream-json output
qwen -p "query" --output-format stream-json --include-partial-messages
--system-prompt
Override the main session system prompt for this run
qwen -p "query" --system-prompt "You are a terse reviewer."
--append-system-prompt
Append extra instructions to the main session system prompt for this run
qwen -p "query" --append-system-prompt "Focus on concrete findings."
--debug
,
-d
Enable debug mode
qwen -p "query" --debug
--all-files
,
-a
Include all files in context
qwen -p "query" --all-files
--include-directories
Include additional directories
qwen -p "query" --include-directories src,docs
--yolo
,
-y
Auto-approve all actions
qwen -p "query" --yolo
--approval-mode
Set approval mode
qwen -p "query" --approval-mode auto_edit
--continue
Resume the most recent session for this project
qwen --continue -p "Pick up where we left off"
--resume [sessionId]
Resume a specific session (or choose interactively)
qwen --resume 123e... -p "Finish the refactor"
For complete details on all available configuration options, settings files, and environment variables, see the
Configuration Guide
.
Examples
Code review
cat
src/auth.py
|
qwen
-p
"Review this authentication code for security issues"
>
security-review.txt
Generate commit messages
result
=
$(
git
diff
--cached
|
qwen
-p
"Write a concise commit message for these changes"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
API documentation
result
=
$(
cat
api/routes.js
|
qwen
-p
"Generate OpenAPI spec for these routes"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
>
openapi.json
Batch code analysis
for
file
in
src/*.py
;
do
echo
"Analyzing
$file
..."
result
=
$(
cat
"
$file
"
|
qwen
-p
"Find potential bugs and suggest improvements"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
>
"reports/$(
basename
"
$file
").analysis"
echo
"Completed analysis for $(
basename
"
$file
")"
>>
reports/progress.log
done
PR code review
result
=
$(
git
diff
origin/main...HEAD
|
qwen
-p
"Review these changes for bugs, security issues, and code quality"
--output-format
json
)
echo
"
$result
"
|
jq
-r
'.response'
>
pr-review.json
Log analysis
grep
"ERROR"
/var/log/app.log
|
tail
-20
|
qwen
-p
"Analyze these errors and suggest root cause and fixes"
>
error-analysis.txt
Release notes generation
result
=
$(
git
log
--oneline
v1.0.0..HEAD
|
qwen
-p
"Generate release notes from these commits"
--output-format
json
)
response
=
$(
echo
"
$result
"
|
jq
-r
'.response'
)
echo
"
$response
"
echo
"
$response
"
>>
CHANGELOG.md
Model and tool usage tracking
result
=
$(
qwen
-p
"Explain this database schema"
--include-directories
db
--output-format
json
)
total_tokens
=
$(
echo
"
$result
"
|
jq
-r
'.stats.models // {} | to_entries | map(.value.tokens.total) | add // 0'
)
models_used
=
$(
echo
"
$result
"
|
jq
-r
'.stats.models // {} | keys | join(", ") | if . == "" then "none" else . end'
)
tool_calls
=
$(
echo
"
$result
"
|
jq
-r
'.stats.tools.totalCalls // 0'
)
tools_used
=
$(
echo
"
$result
"
|
jq
-r
'.stats.tools.byName // {} | keys | join(", ") | if . == "" then "none" else . end'
)
echo
"$(
date
):
$total_tokens
tokens,
$tool_calls
tool calls (
$tools_used
) used with models:
$models_used
"
>>
usage.log
echo
"
$result
"
|
jq
-r
'.response'
>
schema-docs.md
echo
"Recent usage trends:"
tail
-5
usage.log
Persistent Retry Mode
When Qwen Code runs in CI/CD pipelines or as a background daemon, a brief API outage (rate limiting or overload) should not kill a multi-hour task.
Persistent retry mode
makes Qwen Code retry transient API errors indefinitely until the service recovers.
How it works
Transient errors only
: HTTP 429 (Rate Limit) and 529 (Overloaded) are retried indefinitely. Other errors (400, 500, etc.) still fail normally.
Exponential backoff with cap
: Retry delays grow exponentially but are capped at
5 minutes
per retry.
Heartbeat keepalive
: During long waits, a status line is printed to stderr every
30 seconds
to prevent CI runners from killing the process due to inactivity.
Graceful degradation
: Non-transient errors and interactive mode are completely unaffected.
Activation
Set the
QWEN_CODE_UNATTENDED_RETRY
environment variable to
true
or
1
(strict match, case-sensitive):
export
QWEN_CODE_UNATTENDED_RETRY
=
1
[!important]
Persistent retry requires an
explicit opt-in
.
CI=true
alone does
not
activate it — silently turning a fast-fail CI job into an infinite-wait job would be dangerous. Always set
QWEN_CODE_UNATTENDED_RETRY
explicitly in your pipeline configuration.
Examples
GitHub Actions
-
name
:
Automated code review
env
:
QWEN_CODE_UNATTENDED_RETRY
:
'1'
run
:
|
qwen -p "Review all files in src/ for security issues" \
--output-format json \
--yolo > review.json
Overnight batch processing
export
QWEN_CODE_UNATTENDED_RETRY
=
1
qwen
-p
"Migrate all callback-style functions to async/await in src/"
--yolo
Background daemon
QWEN_CODE_UNATTENDED_RETRY
=
1
nohup
qwen
-p
"Audit all dependencies for known CVEs"
\
--output-format
json
>
audit.json
2>
audit.log
&
Monitoring
During persistent retry, heartbeat messages are printed to
stderr
:
[qwen-code] Waiting for API capacity... attempt 3, retry in 45s
[qwen-code] Waiting for API capacity... attempt 3, retry in 15s
These messages keep CI runners alive and let you monitor progress. They do not appear in stdout, so JSON output piped to other tools remains clean.
Resources
CLI Configuration
- Complete configuration guide
Authentication
- Setup authentication
Commands
- Interactive commands reference
Tutorials
- Step-by-step automation guides
Last updated on
May 18, 2026
Memory
Dual Output</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/headless/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Session Recap Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/session-recap/session-recap-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/session-recap/session-recap-design/</guid>
  <pubDate>Wed, 24 Apr 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Session Recap
Session Recap Design
Session Recap Design
A brief (1-2 sentence) “where did I leave off” summary surfaced when the user
returns to an idle session, either on demand (
/recap
) or after the
terminal has been blurred for 5+ minutes.
Overview
When a user
/resume
s an old session days later, scrolling back through
pages of history to remember
what they were doing and what came next
is a real friction point. Just reloading messages does not solve this
UX problem.
The goal is to p...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Session Recap
Session Recap Design
Session Recap Design
A brief (1-2 sentence) “where did I leave off” summary surfaced when the user
returns to an idle session, either on demand (
/recap
) or after the
terminal has been blurred for 5+ minutes.
Overview
When a user
/resume
s an old session days later, scrolling back through
pages of history to remember
what they were doing and what came next
is a real friction point. Just reloading messages does not solve this
UX problem.
The goal is to proactively surface a brief 1-2 sentence recap when the user
returns:
High-level task
(what they are doing) →
next step
(what to do next).
Visually distinct from real assistant replies, so it is never mistaken
for new model output.
Best-effort
: failures must be silent and never break the main flow.
Triggers
Trigger
Conditions
Implementation
Manual
User runs
/recap
recapCommand.ts
calls the same underlying service
Auto
Terminal blurred (DECSET 1004 focus protocol) for ≥ 5 min + focus returns + stream is
Idle
useAwaySummary.ts
— 5min blur timer +
useFocus
event listener
Both paths funnel into a single function —
generateSessionRecap()
— to
guarantee identical behavior. The auto-trigger is gated by
general.showSessionRecap
(default: off — explicit opt-in, so ambient
LLM calls are never silently added to a user’s bill); the manual
command ignores that setting.
Architecture
┌────────────────────────────────────────────────────────────────────────┐
│                          AppContainer.tsx                              │
│   isFocused = useFocus()                                               │
│   isIdle = streamingState === Idle                                     │
│       │                                                                │
│       ├─→ useAwaySummary({enabled, config, isFocused, isIdle,          │
│       │       │             addItem})                                  │
│       │       └─→ 5 min blur timer + idle/dedupe gates                 │
│       │              │                                                 │
│       │              ↓                                                 │
│       └─→ recapCommand (slash) ─→ generateSessionRecap(config, signal) │
│                                          │                             │
│                                          ↓                             │
│                              ┌─────────────────────────┐               │
│                              │ packages/core/services/ │               │
│                              │   sessionRecap.ts       │               │
│                              └─────────────────────────┘               │
│                                          │                             │
│                                          ↓                             │
│                              GeminiClient.generateContent              │
│                              (fastModel + tools:[])                    │
│                                                                        │
│   addItem({type: 'away_recap', text}) ─→ HistoryItemDisplay            │
│       └─ AwayRecapMessage rendered inline like any other history       │
│         item (※ + bold "recap: " + italic content, all dim);           │
│         scrolls naturally with the conversation. Mirrors Claude        │
│         Code's away_summary system message.                            │
└────────────────────────────────────────────────────────────────────────┘
Files
File
Responsibility
packages/core/src/services/sessionRecap.ts
One-shot LLM call + history filter + tag extraction
packages/cli/src/ui/hooks/useAwaySummary.ts
Auto-trigger React hook
packages/cli/src/ui/commands/recapCommand.ts
/recap
manual entry point
packages/cli/src/ui/components/messages/StatusMessages.tsx
AwayRecapMessage
renderer (
※
+ bold
recap:
+ italic content, all dim)
packages/cli/src/ui/types.ts
HistoryItemAwayRecap
type
packages/cli/src/ui/components/HistoryItemDisplay.tsx
Dispatches
away_recap
history items to the renderer
packages/cli/src/config/settingsSchema.ts
general.showSessionRecap
+
general.sessionRecapAwayThresholdMinutes
settings
Prompt Design
System Prompt
generationConfig.systemInstruction
replaces the main agent’s system
prompt for this single call, so the model behaves only as a recap
generator and not as a coding assistant.
Note that
GeminiClient.generateContent()
internally runs the prompt
through
getCustomSystemPrompt()
, which appends the user’s memory
(QWEN.md / managed auto-memory) as a suffix. The final system prompt is
therefore
recap prompt + user memory
— useful project context for the
recap, not a leak.
Bullets below correspond 1:1 with
RECAP_SYSTEM_PROMPT
:
Under 40 words, 1-2 plain sentences (no markdown / lists / headings). For Chinese, treat the budget as roughly 80 characters total.
First sentence: the high-level task. Then: the concrete next step.
Explicitly forbid: listing what was done, reciting tool calls, status reports.
Match the dominant language of the conversation (English or Chinese).
Wrap output in
<recap>...</recap>
; nothing outside the tags.
Structured Output + Extraction
The model is instructed to wrap its answer in
<recap>...</recap>
:
<recap>Refactoring loopDetectionService.ts to address long-session OOM. Next step is to implement option B.</recap>
Why: some models (GLM family, reasoning models) write a “thinking”
paragraph before the final answer. Returning the raw text would leak
that reasoning into the UI.
extractRecap()
has three fallback tiers:
Both tags present: take what is between
<recap>...</recap>
(preferred).
Only the open tag (e.g.
maxOutputTokens
truncated the close tag):
take everything after the open tag.
Tag missing entirely: return empty string → service returns
null
→ UI renders nothing.
The third tier is “skip rather than show the wrong thing” — surfacing
the model’s reasoning preamble is worse than showing no recap at all.
Call Parameters
Parameter
Value
Reason
model
getFastModel() ?? getModel()
Recap doesn’t need a frontier model
tools
[]
One-shot query, no tool use
maxOutputTokens
300
Headroom for 1-2 short sentences + tags
temperature
0.3
Mostly deterministic, with a bit of natural variation
systemInstruction
The recap-only prompt above
Replaces the main agent’s role definition
History Filtering
geminiClient.getChat().getHistory()
returns a
Content[]
that
includes:
user
/
model
text messages
model
functionCall
parts
user
functionResponse
parts (which can hold full file contents)
model
thought parts (
part.thought
/
part.thoughtSignature
,
the model’s hidden reasoning)
filterToDialog()
keeps only
user
/
model
parts that have
non-empty
text and are not thoughts
. Two reasons:
Tool calls / responses
: a single
functionResponse
can be 10K+
tokens. 30 such messages would drown the recap LLM in irrelevant
detail, both wasting tokens and biasing the recap toward
implementation noise like “called X tool to read Y file”.
Thought parts
: carry the model’s internal reasoning. Including
them risks treating hidden chain-of-thought as dialogue and
surfacing it in the recap text.
After dropping empty messages,
takeRecentDialog
slices to the last 30
messages and refuses to start the slice on a dangling model/tool
response.
Concurrency and Edge Cases
Auto-trigger hook state machine
useAwaySummary
keeps three refs:
Ref
Meaning
blurredAtRef
Blur start time (not cleared until focus returns)
recapPendingRef
Whether an LLM call is in flight
inFlightRef
The current in-flight
AbortController
useEffect
deps:
[enabled, config, isFocused, isIdle, addItem, thresholdMs]
.
Event
Action
!enabled || !config
Abort in-flight call + clear
inFlightRef
+ clear
blurredAtRef
!isFocused
and
blurredAtRef === null
Set
blurredAtRef = Date.now()
isFocused
and
blurredAtRef === null
Return early (no blur cycle to handle — first render or right after a brief-blur reset)
isFocused
and blur duration < 5 min
Clear
blurredAtRef
, wait for next blur cycle
isFocused
and blur ≥ 5 min and
recapPendingRef
Return (dedupe)
isFocused
and blur ≥ 5 min and
!isIdle
Preserve
blurredAtRef
and wait for the turn to finish (
isIdle
is in the deps, so the effect re-fires when streaming completes)
isFocused
and blur ≥ 5 min and
shouldFireRecap
returns false
Clear
blurredAtRef
and return — conversation hasn’t moved enough since the last recap (≥ 2 user turns required, mirrors Claude Code)
isFocused
and all conditions met
Clear
blurredAtRef
, set
recapPendingRef = true
, create
AbortController
, send the LLM request
The
.then
callback
re-checks
isIdleRef.current
: if the user has
started a new turn while the LLM was running, the late-arriving recap
is dropped to avoid inserting it mid-turn.
The
.finally
clears
recapPendingRef
, and clears
inFlightRef
only
if
inFlightRef.current === controller
(so it doesn’t overwrite a
newer controller).
A second
useEffect
aborts the in-flight controller on unmount.
/recap
gating
CommandContext.ui.isIdleRef
exposes the current stream state
(mirroring the existing
btwAbortControllerRef
pattern). In
interactive mode,
recapCommand
refuses when
!isIdleRef.current
or
pendingItem !== null
.
pendingItem
alone is insufficient
because a normal model reply runs with
streamingState === Responding
and a null
pendingItem
.
Configuration and Model Selection
User-facing knobs
Setting
Default
Notes
general.showSessionRecap
false
Auto-trigger only. Manual
/recap
ignores this.
general.sessionRecapAwayThresholdMinutes
5
Minutes blurred before auto-recap fires on focus-in. Matches Claude Code’s default.
fastModel
unset
Recommended (e.g.
qwen3-coder-flash
) for fast and cheap recaps.
Model fallback
config.getFastModel() ?? config.getModel()
:
User has a
fastModel
set and it is valid for the current auth type
→ use
fastModel
.
Otherwise → fall back to the main session model (works, just costlier
and slower).
Observability
createDebugLogger('SESSION_RECAP')
emits:
caught exceptions from the recap path (
debugLogger.warn
).
All failures are
fully transparent
to the user — recap is an
auxiliary feature and never throws into the UI. Developers can grep for
the
[SESSION_RECAP]
tag in the debug log file: written by default to
~/.qwen/debug/<sessionId>.txt
(
latest.txt
symlinks to the current
session); disable via
QWEN_DEBUG_LOG_FILE=0
.
Out of Scope
Item
Why not
Progress UI for
/recap
(spinner / pendingItem)
3-5 second wait is tolerable; adds complexity.
Automated tests
Service is small (~150 lines), end-to-end tested manually first; unit tests can land in a separate PR.
Localized prompts
The system prompt is for the model; English is the most reliable substrate. The model selects the output language from the conversation.
QWEN_CODE_ENABLE_AWAY_SUMMARY
env var
Claude Code uses it to keep the feature on when telemetry is disabled; Qwen Code’s current telemetry model doesn’t need this.
Auto-recap on
/resume
completion
A natural follow-up but needs a hook point in
useResumeCommand
; out of scope for this PR.
Last updated on
May 18, 2026
Speculation Engine Design
Session Title Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/session-recap/session-recap-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Session Recap
Session Recap Design
Session Recap Design
A brief (1-2 sentence) “where did I leave off” summary surfaced when the user
returns to an idle session, either on demand (
/recap
) or after the
terminal has been blurred for 5+ minutes.
Overview
When a user
/resume
s an old session days later, scrolling back through
pages of history to remember
what they were doing and what came next
is a real friction point. Just reloading messages does not solve this
UX problem.
The goal is to p...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Session Recap
Session Recap Design
Session Recap Design
A brief (1-2 sentence) “where did I leave off” summary surfaced when the user
returns to an idle session, either on demand (
/recap
) or after the
terminal has been blurred for 5+ minutes.
Overview
When a user
/resume
s an old session days later, scrolling back through
pages of history to remember
what they were doing and what came next
is a real friction point. Just reloading messages does not solve this
UX problem.
The goal is to proactively surface a brief 1-2 sentence recap when the user
returns:
High-level task
(what they are doing) →
next step
(what to do next).
Visually distinct from real assistant replies, so it is never mistaken
for new model output.
Best-effort
: failures must be silent and never break the main flow.
Triggers
Trigger
Conditions
Implementation
Manual
User runs
/recap
recapCommand.ts
calls the same underlying service
Auto
Terminal blurred (DECSET 1004 focus protocol) for ≥ 5 min + focus returns + stream is
Idle
useAwaySummary.ts
— 5min blur timer +
useFocus
event listener
Both paths funnel into a single function —
generateSessionRecap()
— to
guarantee identical behavior. The auto-trigger is gated by
general.showSessionRecap
(default: off — explicit opt-in, so ambient
LLM calls are never silently added to a user’s bill); the manual
command ignores that setting.
Architecture
┌────────────────────────────────────────────────────────────────────────┐
│                          AppContainer.tsx                              │
│   isFocused = useFocus()                                               │
│   isIdle = streamingState === Idle                                     │
│       │                                                                │
│       ├─→ useAwaySummary({enabled, config, isFocused, isIdle,          │
│       │       │             addItem})                                  │
│       │       └─→ 5 min blur timer + idle/dedupe gates                 │
│       │              │                                                 │
│       │              ↓                                                 │
│       └─→ recapCommand (slash) ─→ generateSessionRecap(config, signal) │
│                                          │                             │
│                                          ↓                             │
│                              ┌─────────────────────────┐               │
│                              │ packages/core/services/ │               │
│                              │   sessionRecap.ts       │               │
│                              └─────────────────────────┘               │
│                                          │                             │
│                                          ↓                             │
│                              GeminiClient.generateContent              │
│                              (fastModel + tools:[])                    │
│                                                                        │
│   addItem({type: 'away_recap', text}) ─→ HistoryItemDisplay            │
│       └─ AwayRecapMessage rendered inline like any other history       │
│         item (※ + bold "recap: " + italic content, all dim);           │
│         scrolls naturally with the conversation. Mirrors Claude        │
│         Code's away_summary system message.                            │
└────────────────────────────────────────────────────────────────────────┘
Files
File
Responsibility
packages/core/src/services/sessionRecap.ts
One-shot LLM call + history filter + tag extraction
packages/cli/src/ui/hooks/useAwaySummary.ts
Auto-trigger React hook
packages/cli/src/ui/commands/recapCommand.ts
/recap
manual entry point
packages/cli/src/ui/components/messages/StatusMessages.tsx
AwayRecapMessage
renderer (
※
+ bold
recap:
+ italic content, all dim)
packages/cli/src/ui/types.ts
HistoryItemAwayRecap
type
packages/cli/src/ui/components/HistoryItemDisplay.tsx
Dispatches
away_recap
history items to the renderer
packages/cli/src/config/settingsSchema.ts
general.showSessionRecap
+
general.sessionRecapAwayThresholdMinutes
settings
Prompt Design
System Prompt
generationConfig.systemInstruction
replaces the main agent’s system
prompt for this single call, so the model behaves only as a recap
generator and not as a coding assistant.
Note that
GeminiClient.generateContent()
internally runs the prompt
through
getCustomSystemPrompt()
, which appends the user’s memory
(QWEN.md / managed auto-memory) as a suffix. The final system prompt is
therefore
recap prompt + user memory
— useful project context for the
recap, not a leak.
Bullets below correspond 1:1 with
RECAP_SYSTEM_PROMPT
:
Under 40 words, 1-2 plain sentences (no markdown / lists / headings). For Chinese, treat the budget as roughly 80 characters total.
First sentence: the high-level task. Then: the concrete next step.
Explicitly forbid: listing what was done, reciting tool calls, status reports.
Match the dominant language of the conversation (English or Chinese).
Wrap output in
<recap>...</recap>
; nothing outside the tags.
Structured Output + Extraction
The model is instructed to wrap its answer in
<recap>...</recap>
:
<recap>Refactoring loopDetectionService.ts to address long-session OOM. Next step is to implement option B.</recap>
Why: some models (GLM family, reasoning models) write a “thinking”
paragraph before the final answer. Returning the raw text would leak
that reasoning into the UI.
extractRecap()
has three fallback tiers:
Both tags present: take what is between
<recap>...</recap>
(preferred).
Only the open tag (e.g.
maxOutputTokens
truncated the close tag):
take everything after the open tag.
Tag missing entirely: return empty string → service returns
null
→ UI renders nothing.
The third tier is “skip rather than show the wrong thing” — surfacing
the model’s reasoning preamble is worse than showing no recap at all.
Call Parameters
Parameter
Value
Reason
model
getFastModel() ?? getModel()
Recap doesn’t need a frontier model
tools
[]
One-shot query, no tool use
maxOutputTokens
300
Headroom for 1-2 short sentences + tags
temperature
0.3
Mostly deterministic, with a bit of natural variation
systemInstruction
The recap-only prompt above
Replaces the main agent’s role definition
History Filtering
geminiClient.getChat().getHistory()
returns a
Content[]
that
includes:
user
/
model
text messages
model
functionCall
parts
user
functionResponse
parts (which can hold full file contents)
model
thought parts (
part.thought
/
part.thoughtSignature
,
the model’s hidden reasoning)
filterToDialog()
keeps only
user
/
model
parts that have
non-empty
text and are not thoughts
. Two reasons:
Tool calls / responses
: a single
functionResponse
can be 10K+
tokens. 30 such messages would drown the recap LLM in irrelevant
detail, both wasting tokens and biasing the recap toward
implementation noise like “called X tool to read Y file”.
Thought parts
: carry the model’s internal reasoning. Including
them risks treating hidden chain-of-thought as dialogue and
surfacing it in the recap text.
After dropping empty messages,
takeRecentDialog
slices to the last 30
messages and refuses to start the slice on a dangling model/tool
response.
Concurrency and Edge Cases
Auto-trigger hook state machine
useAwaySummary
keeps three refs:
Ref
Meaning
blurredAtRef
Blur start time (not cleared until focus returns)
recapPendingRef
Whether an LLM call is in flight
inFlightRef
The current in-flight
AbortController
useEffect
deps:
[enabled, config, isFocused, isIdle, addItem, thresholdMs]
.
Event
Action
!enabled || !config
Abort in-flight call + clear
inFlightRef
+ clear
blurredAtRef
!isFocused
and
blurredAtRef === null
Set
blurredAtRef = Date.now()
isFocused
and
blurredAtRef === null
Return early (no blur cycle to handle — first render or right after a brief-blur reset)
isFocused
and blur duration < 5 min
Clear
blurredAtRef
, wait for next blur cycle
isFocused
and blur ≥ 5 min and
recapPendingRef
Return (dedupe)
isFocused
and blur ≥ 5 min and
!isIdle
Preserve
blurredAtRef
and wait for the turn to finish (
isIdle
is in the deps, so the effect re-fires when streaming completes)
isFocused
and blur ≥ 5 min and
shouldFireRecap
returns false
Clear
blurredAtRef
and return — conversation hasn’t moved enough since the last recap (≥ 2 user turns required, mirrors Claude Code)
isFocused
and all conditions met
Clear
blurredAtRef
, set
recapPendingRef = true
, create
AbortController
, send the LLM request
The
.then
callback
re-checks
isIdleRef.current
: if the user has
started a new turn while the LLM was running, the late-arriving recap
is dropped to avoid inserting it mid-turn.
The
.finally
clears
recapPendingRef
, and clears
inFlightRef
only
if
inFlightRef.current === controller
(so it doesn’t overwrite a
newer controller).
A second
useEffect
aborts the in-flight controller on unmount.
/recap
gating
CommandContext.ui.isIdleRef
exposes the current stream state
(mirroring the existing
btwAbortControllerRef
pattern). In
interactive mode,
recapCommand
refuses when
!isIdleRef.current
or
pendingItem !== null
.
pendingItem
alone is insufficient
because a normal model reply runs with
streamingState === Responding
and a null
pendingItem
.
Configuration and Model Selection
User-facing knobs
Setting
Default
Notes
general.showSessionRecap
false
Auto-trigger only. Manual
/recap
ignores this.
general.sessionRecapAwayThresholdMinutes
5
Minutes blurred before auto-recap fires on focus-in. Matches Claude Code’s default.
fastModel
unset
Recommended (e.g.
qwen3-coder-flash
) for fast and cheap recaps.
Model fallback
config.getFastModel() ?? config.getModel()
:
User has a
fastModel
set and it is valid for the current auth type
→ use
fastModel
.
Otherwise → fall back to the main session model (works, just costlier
and slower).
Observability
createDebugLogger('SESSION_RECAP')
emits:
caught exceptions from the recap path (
debugLogger.warn
).
All failures are
fully transparent
to the user — recap is an
auxiliary feature and never throws into the UI. Developers can grep for
the
[SESSION_RECAP]
tag in the debug log file: written by default to
~/.qwen/debug/<sessionId>.txt
(
latest.txt
symlinks to the current
session); disable via
QWEN_DEBUG_LOG_FILE=0
.
Out of Scope
Item
Why not
Progress UI for
/recap
(spinner / pendingItem)
3-5 second wait is tolerable; adds complexity.
Automated tests
Service is small (~150 lines), end-to-end tested manually first; unit tests can land in a separate PR.
Localized prompts
The system prompt is for the model; English is the most reliable substrate. The model selects the output language from the conversation.
QWEN_CODE_ENABLE_AWAY_SUMMARY
env var
Claude Code uses it to keep the feature on when telemetry is disabled; Qwen Code’s current telemetry model doesn’t need this.
Auto-recap on
/resume
completion
A natural follow-up but needs a hook point in
useResumeCommand
; out of scope for this PR.
Last updated on
May 18, 2026
Speculation Engine Design
Session Title Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/session-recap/session-recap-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Dual Output</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/dual-output/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/dual-output/</guid>
  <pubDate>Fri, 19 Apr 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Dual Output
Dual Output
Dual Output is a sidecar mode for the interactive TUI: while Qwen Code keeps
rendering normally on
stdout
, it concurrently emits a structured JSON event
stream to a separate channel so an external program — an IDE extension, a web
frontend, a CI pipeline, an automation script — can observe and steer the
session.
It also provides a reverse channel: an external program can write JSONL
commands into a file that the TUI watches, allowing it to submit prom...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Dual Output
Dual Output
Dual Output is a sidecar mode for the interactive TUI: while Qwen Code keeps
rendering normally on
stdout
, it concurrently emits a structured JSON event
stream to a separate channel so an external program — an IDE extension, a web
frontend, a CI pipeline, an automation script — can observe and steer the
session.
It also provides a reverse channel: an external program can write JSONL
commands into a file that the TUI watches, allowing it to submit prompts and
respond to tool-permission requests as if a human were at the keyboard.
Dual Output is fully optional. When the flags below are absent the TUI behaves
exactly as before with no extra I/O and no behavioral changes.
Use cases
Dual Output is a low-level plumbing primitive. These are concrete integrations
it unlocks:
Terminal + Chat dual-mode real-time sync
The flagship use case. A web or desktop ChatUI hosts the TUI inside a PTY
and renders a parallel conversation view driven by the structured event
stream:
User can type in either surface — the TUI (for terminal-native power-users)
or the web UI (for richer UX, shareable links, mobile). Both views stay
in sync because every message flows through the same JSON events.
Tool-approval prompts appear in both places; whoever approves first wins.
Session history is captured verbatim from
--json-file
, so the server
side has a canonical machine-readable transcript without parsing ANSI.
IDE extensions (VS Code / JetBrains / Cursor / Neovim)
Embed Qwen Code inside the IDE. The TUI runs in the editor’s integrated
terminal panel for users who want it, while the extension consumes
--json-fd
/
--json-file
events to drive:
Inline diff overlays when the agent touches files.
A webview side panel with formatted markdown, syntax-highlighted tool
calls, and clickable citations.
Status bar indicators (thinking / responding / awaiting approval).
Programmatic
confirmation_response
writes when the user clicks a
native IDE approval button.
Browser-based Chat frontends
A Node/Bun server spawns the TUI in a PTY for its rendering semantics but
exposes a WebSocket channel to the browser. Events on
--json-file
are
forwarded to the client; user messages typed in the browser are injected
via
--input-file
. No ANSI parsing on either side.
CI / automation observers
A CI job runs Qwen Code with a task prompt. The human sees the TUI in the
job log; the CI system tails
--json-file
to:
Fail the job if a
result
event reports an error.
Push
token usage
/
duration_ms
/
tool_use
counts to metrics.
Archive the full transcript as a build artifact.
Multi-agent orchestration
A supervisor agent spawns multiple TUI workers, each with its own pair of
event/input files. It watches progress, injects follow-up prompts, and
enforces global budget / safety policies by approving or denying tool
calls across all workers.
Session recording, audit, and replay
Tee every TUI session to a regular file with
--json-file
. Later:
Compliance audits can reconstruct exactly what was executed.
Automated regression tests can compare runs across model versions.
A replay tool can re-emit events through the same protocol to feed
visualization dashboards.
Observability dashboards
Stream
--json-file
into Loki / OTEL / any pipeline that accepts JSONL.
Extract
usage.input_tokens
,
tool_use.name
,
result.duration_api_ms
as first-class metrics in Grafana. No need for log-parsing regex.
Testing and QA
Integration tests spawn Qwen Code headlessly, drive it with
--input-file
scripts, and assert on
--json-file
events. Unlike parsing stdout ANSI,
assertions are stable across UI refactors.
Flags
Flag
Type
Purpose
--json-fd <n>
number,
n >= 3
Write structured JSON events to file descriptor
n
. The caller must provide this fd via spawn
stdio
configuration or shell redirection.
--json-file <path>
path
Write structured JSON events to a file. The path can be a regular file, a FIFO (named pipe), or
/dev/fd/N
.
--input-file <path>
path
Watch this file for JSONL commands written by an external program.
--json-fd
and
--json-file
are mutually exclusive. fds 0, 1, and 2 are
rejected to prevent corrupting the TUI’s own output.
Why two output flags? (
--json-fd
vs
--json-file
)
At first glance
--json-fd
looks sufficient — the caller spawns Qwen Code
with an extra file descriptor, the TUI writes events to it, done. In
practice, fd passing breaks down under the most important embedding
scenario: running the TUI inside a pseudo-terminal (PTY). That is why
this feature also exposes a path-based alternative.
When
--json-fd
works
Pure
child_process.spawn
with a
stdio
array:
const
child
=
spawn
(
'qwen'
, [
'--json-fd'
,
'3'
], {
stdio: [
'inherit'
,
'inherit'
,
'inherit'
, eventsFd],
});
Node’s spawn supports arbitrary
stdio
entries; fd 3 is inherited by the
child, which can write to it directly. Zero-copy, zero-buffer, zero
filesystem — the fastest path.
Why
--json-fd
does
not
work under PTY
PTY wrappers like
node-pty
and
bun-pty
are how any serious embedder
(IDE extensions, web terminals, tmux-like multiplexers) hosts an
interactive TUI. They cannot forward extra fds to the child, for three
reinforcing reasons:
API surface.
node-pty.spawn(file, args, options)
accepts
cwd
,
env
,
cols
,
rows
,
encoding
, etc. — but
no
stdio
array
. There
is simply no place in the API to say “also attach this fd as fd 3 in
the child”.
bun-pty
exposes the same shape.
forkpty(3)
semantics.
Under the hood, PTY wrappers call
forkpty(3)
(or the equivalent
posix_openpt
+
login_tty
dance).
That syscall allocates a master/slave pseudo-terminal pair and
redirects the child’s fds 0/1/2 to the slave side so the child thinks
it is attached to a real terminal. Any fds above 2 in the parent are
closed by
login_tty
, which calls
close(fd)
for
fd >= 3
before
exec
. Extra fds are actively wiped, not inherited.
Controlling-terminal side effect.
Even if you hacked an extra fd
through, it would not be a terminal, so the child’s TUI renderer
(which writes escape sequences assuming a TTY on fd 1) would still
need the slave for its output. You would end up with two independent
transports anyway.
In short: the moment an embedder needs a real TTY for TUI rendering —
which is every IDE extension, every web terminal, every desktop chat
app — fd inheritance is off the table.
--json-file
fills the gap
A file path is passed as an ordinary CLI argument, so it survives every
spawn model:
import
{ spawn }
from
'node-pty'
;
const
pty
=
spawn
(
'qwen'
,
[
'--json-file'
,
'/tmp/qwen-events.jsonl'
,
'--input-file'
,
'/tmp/qwen-input.jsonl'
,
],
{ cols:
120
, rows:
40
},
);
The child opens the file itself and writes events there; the embedder
tails the same path with
fs.watch
+ incremental reads. Three things to
note:
Regular file
, FIFO (named pipe), or
/dev/fd/N
all work. FIFO is
the lowest-latency option when both sides are on the same host.
The bridge opens FIFOs with
O_NONBLOCK
and falls back to blocking
mode on
ENXIO
(no reader yet), so PTY startup is never deadlocked
waiting for a consumer.
For multi-session isolation, use per-session paths under
$XDG_RUNTIME_DIR
or a
mkdtemp
’d directory with mode
0700
.
Which flag should I use?
Embedding style
Use
child_process.spawn
with plain stdio
--json-fd
node-pty
/
bun-pty
/ any PTY host
--json-file
Shell redirection / manual pipeline testing
either
CI log collection (regular file, read after exit)
--json-file
Lowest possible latency on same host
--json-file
+ FIFO
The general rule:
if you need the TUI to render correctly, you need a
PTY, which means you need
--json-file
.
--json-fd
is for simpler
embedders that do not care about TUI fidelity — typically programmatic
wrappers that throw away stdout anyway.
Quick start
Run Qwen Code with all three channels enabled:
mkfifo
/tmp/qwen-events.jsonl
/tmp/qwen-input.jsonl
qwen
\
--json-file
/tmp/qwen-events.jsonl
\
--input-file
/tmp/qwen-input.jsonl
In a second terminal, tail the event stream:
cat
/tmp/qwen-events.jsonl
In a third terminal, push a prompt into the running TUI:
echo
'{"type":"submit","text":"Explain this repo"}'
>>
/tmp/qwen-input.jsonl
The prompt appears in the TUI exactly as if the user typed it, and the
streaming response is mirrored on
/tmp/qwen-events.jsonl
.
Output event schema
Events are emitted as JSON Lines (one object per line). The schema is the same
one used by the non-interactive
--output-format=stream-json
mode, with
includePartialMessages
always enabled.
The first event on the channel is always
system
/
session_start
, emitted
when the bridge is constructed. Use it to correlate the channel with a
session id before any other event arrives.
// Session lifecycle
{
"type"
:
"system"
,
"subtype"
:
"session_start"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"data"
: {
"session_id"
:
"..."
,
"cwd"
:
"/path/to/cwd"
}
}
// Streaming events for an in-progress assistant turn
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"message_start"
,
"message"
: {
...
} },
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"content_block_start"
,
"index"
:
0
,
"content_block"
: {
"type"
:
"text"
} },
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"content_block_delta"
,
"index"
:
0
,
"delta"
: {
"type"
:
"text_delta"
,
"text"
:
"Hello"
} },
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"content_block_stop"
,
"index"
:
0
},
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"message_stop"
},
...
}
// Completed messages
{
"type"
:
"user"
,
"message"
: {
"role"
:
"user"
,
"content"
: [
...
] },
...
}
{
"type"
:
"assistant"
,
"message"
: {
"role"
:
"assistant"
,
"content"
: [
...
],
"usage"
: {
...
} },
...
}
{
"type"
:
"user"
,
"message"
: {
"role"
:
"user"
,
"content"
: [{
"type"
:
"tool_result"
,
...
}] } }
// Permission control plane (only when a tool needs approval)
{
"type"
:
"control_request"
,
"request_id"
:
"..."
,
"request"
: {
"subtype"
:
"can_use_tool"
,
"tool_name"
:
"run_shell_command"
,
"tool_use_id"
:
"..."
,
"input"
: {
"command"
:
"rm -rf /tmp/x"
},
"permission_suggestions"
:
null
,
"blocked_path"
:
null
}
}
{
"type"
:
"control_response"
,
"response"
: {
"subtype"
:
"success"
,
"request_id"
:
"..."
,
"response"
: {
"allowed"
:
true
}
}
}
control_response
is emitted whether the decision was made in the TUI
(native approval UI) or by an external
confirmation_response
(see below).
Either way, all observers see the final outcome.
Input command schema
Two command shapes are accepted on
--input-file
:
// Submit a user message into the prompt queue
{
"type"
:
"submit"
,
"text"
:
"What does this function do?"
}
// Reply to a pending control_request
{
"type"
:
"confirmation_response"
,
"request_id"
:
"..."
,
"allowed"
:
true
}
Behavior:
submit
commands are queued. If the TUI is busy responding, they are
retried automatically the next time the TUI returns to the idle state.
confirmation_response
commands are dispatched immediately and never
queued, because a tool call is blocking and the response must reach the
underlying
onConfirm
handler without waiting for any earlier
submit
.
Whichever side approves a tool first wins; the other side’s late response
is harmlessly dropped.
Lines that fail to parse as JSON are logged and skipped — they do not
stop the watcher.
Latency notes
The input file is observed with
fs.watchFile
at a 500 ms polling interval,
so worst-case round-trip latency for a remote
submit
is about half a
second. This is intentional: polling is portable across platforms and
filesystems (including macOS / network mounts), and matches the typical
human-in-the-loop pacing the feature targets. The output channel has no
polling — events are written synchronously as the TUI emits them.
Failure modes
Bad fd.
If the fd passed to
--json-fd
is not open or is one of
0/1/2, the TUI prints a warning to
stderr
and continues without dual
output enabled.
Bad path.
If the file passed to
--json-file
cannot be opened, the
TUI prints a warning and continues without dual output.
Consumer disconnect.
If the reader on the other side of the channel
goes away (
EPIPE
), the bridge silently disables itself and the TUI
keeps running. No retry.
Adapter exception.
Any exception thrown while emitting an event is
caught, logged, and disables the bridge. The TUI is never crashed by a
dual-output failure.
Spawn example
A typical embedding parent process spawns Qwen Code with both channels:
import
{ spawn }
from
'node:child_process'
;
import
{ openSync }
from
'node:fs'
;
const
eventsFd
=
openSync
(
'/tmp/qwen-events.jsonl'
,
'w'
);
const
child
=
spawn
(
'qwen'
,
[
'--json-fd'
,
'3'
,
'--input-file'
,
'/tmp/qwen-input.jsonl'
],
{ stdio: [
'inherit'
,
'inherit'
,
'inherit'
, eventsFd] },
);
The TUI still owns the user’s terminal on stdio 0/1/2, while the embedder
reads structured events on the file backing fd 3 and pushes commands by
appending JSONL lines to
/tmp/qwen-input.jsonl
.
Settings-based configuration
For long-lived embedders it is often inconvenient to thread CLI flags
through every launch. The same channels can be configured in
settings.json
under the top-level
dualOutput
key:
// ~/.qwen/settings.json  (user-level)
// or <workspace>/.qwen/settings.json  (workspace-level)
{
"dualOutput"
: {
"jsonFile"
:
"/tmp/qwen-events.jsonl"
,
"inputFile"
:
"/tmp/qwen-input.jsonl"
,
},
}
Precedence rules:
CLI flag
wins
over settings. Passing
--json-file /foo
on the
command line overrides
dualOutput.jsonFile
in settings.
--json-fd
has no settings equivalent — fd passing is a spawn-time
concern that cannot be statically declared.
If neither flag nor setting is present, dual output stays disabled
(identical to today’s default).
The
requiresRestart: true
flag means changes only take effect on the
next Qwen Code launch, since the bridge is constructed once during
startup.
Runnable demos
Every script below is copy-paste ready. Start with POC 1 to verify
the build has dual output; POC 4 is the closest analogue to a real
IDE-extension integration.
POC 1 — observe the event stream
Watch every structured event the TUI emits while a human uses it
normally:
# Terminal A
mkfifo
/tmp/qwen-events.jsonl
cat
/tmp/qwen-events.jsonl
|
jq
-c
'select(.type != "stream_event") | {type, subtype}'
# Terminal B
qwen
--json-file
/tmp/qwen-events.jsonl
# ...then chat normally; terminal A shows session_start,
# user/assistant/result/control_request lifecycle in real time.
Expected first line in terminal A:
{
"type"
:
"system"
,
"subtype"
:
"session_start"
}
POC 2 — inject prompts from outside
Drive the TUI from a second terminal without touching the keyboard of
the first:
# Terminal A
touch
/tmp/qwen-in.jsonl
qwen
--input-file
/tmp/qwen-in.jsonl
# Terminal B — the TUI responds as if you typed it
echo
'{"type":"submit","text":"list files in the current directory"}'
\
>>
/tmp/qwen-in.jsonl
POC 3 — remote tool-permission bridge
Approve or deny tool calls from a separate process:
# Terminal A — observe control_requests
mkfifo
/tmp/qwen-out.jsonl
touch
/tmp/qwen-in.jsonl
(
cat
/tmp/qwen-out.jsonl
\
|
jq
-c
'select(.type == "control_request")'
) &
# Terminal B
qwen
--json-file
/tmp/qwen-out.jsonl
--input-file
/tmp/qwen-in.jsonl
# Ask Qwen to do something that needs approval, e.g.
# "run `ls -la /tmp`". A control_request will appear in terminal A.
# Copy the request_id, then in a third terminal:
echo
'{"type":"confirmation_response","request_id":"<paste-id>","allowed":true}'
\
>>
/tmp/qwen-in.jsonl
# The TUI confirmation prompt dismisses and the tool executes.
If you reply with an unknown
request_id
, the bridge emits a
control_response
with
subtype: "error"
on the output channel so your
consumer can log it or retry:
{
"type"
:
"control_response"
,
"response"
: {
"subtype"
:
"error"
,
"request_id"
:
"..."
,
"error"
:
"unknown request_id (already resolved, cancelled, or never issued)"
}
}
POC 4 — Node embedder (IDE-like)
The most realistic shape: a parent process spawns Qwen Code, tails
events, and injects prompts on its own schedule.
// demo-embedder.ts
import
{ spawn }
from
'node:child_process'
;
import
{ appendFileSync, createReadStream, writeFileSync }
from
'node:fs'
;
import
{ createInterface }
from
'node:readline'
;
import
{ tmpdir }
from
'node:os'
;
import
{ join }
from
'node:path'
;
const
events
=
join
(
tmpdir
(),
`qwen-events-${
process
.
pid
}.jsonl`
);
const
input
=
join
(
tmpdir
(),
`qwen-input-${
process
.
pid
}.jsonl`
);
writeFileSync
(events,
''
);
writeFileSync
(input,
''
);
const
child
=
spawn
(
'qwen'
, [
'--json-file'
, events,
'--input-file'
, input], {
stdio:
'inherit'
,
});
// Tail the output channel. In production you'd use a proper
// byte-offset tail; this one re-streams from 0 for brevity.
const
rl
=
createInterface
({
input:
createReadStream
(events, { encoding:
'utf8'
}),
});
rl.
on
(
'line'
, (
line
)
=>
{
if
(
!
line.
trim
())
return
;
const
ev
=
JSON
.
parse
(line);
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_start'
) {
console.
log
(
'[embedder] handshake:'
, {
protocol_version: ev.data.protocol_version,
version: ev.data.version,
supported_events: ev.data.supported_events,
});
// Feature-detect before using a capability
if
(ev.data.supported_events.
includes
(
'control_request'
)) {
console.
log
(
'[embedder] permission control-plane available'
);
}
}
if
(ev.type
===
'assistant'
) {
console.
log
(
'[embedder] assistant turn ended, tokens ='
,
ev.message.usage?.output_tokens,
);
}
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_end'
) {
console.
log
(
'[embedder] session ended cleanly'
);
}
});
// After 2s, inject a prompt as if the user typed it
setTimeout
(()
=>
{
appendFileSync
(
input,
JSON
.
stringify
({ type:
'submit'
, text:
'hello from embedder'
})
+
'
\n
'
,
);
},
2000
);
child.
on
(
'exit'
, ()
=>
process.
exit
(
0
));
Run with:
npx
tsx
demo-embedder.ts
# Qwen Code TUI opens in the current terminal; the embedder logs
# handshake + turn-end + session_end events to the parent's stdout.
POC 5 — capability handshake feature detection
Older Qwen Code versions won’t emit
protocol_version
. Treat the field
as optional and feature-detect:
rl.
on
(
'line'
, (
line
)
=>
{
const
ev
=
JSON
.
parse
(line);
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_start'
) {
const
v
=
ev.data?.protocol_version
??
0
;
if
(v
<
1
) {
console.
error
(
'qwen-code dual output is present but protocol < 1; '
+
'falling back to best-effort behavior'
,
);
}
else
{
console.
log
(
'qwen-code dual output protocol v'
+
v);
}
}
});
POC 6 — session_end as a clean termination signal
rl.
on
(
'line'
, (
line
)
=>
{
const
ev
=
JSON
.
parse
(line);
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_end'
) {
console.
log
(
'[embedder] clean shutdown, session'
, ev.data.session_id);
// Flush metrics, close WebSockets, etc.
}
});
If the TUI crashes before
session_end
, the output stream closes
(
EPIPE
on next write); embedders should handle both paths.
POC 7 — failure drills (prove the flags never break the TUI)
qwen
--json-fd
1
# stderr: "Warning: dual output disabled — ..."
# TUI still launches normally.
qwen
--json-fd
9999
# stderr: "Warning: dual output disabled — fd 9999 not open"
# TUI still launches normally.
qwen
--json-fd
3
--json-file
/tmp/x.jsonl
# yargs rejects: "--json-fd and --json-file are mutually exclusive."
# Process exits before TUI starts.
qwen
--json-file
/nonexistent/dir/x.jsonl
# stderr warning; TUI still launches.
Relation to Claude Code
Claude Code exposes a similar stream-json event format under
--print --output-format stream-json
, but only in non-interactive mode
— it has no equivalent of running the TUI and a structured sidecar
channel at the same time. Dual Output fills that gap.
Last updated on
May 18, 2026
Headless Mode
Approval Mode</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/dual-output/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Dual Output
Dual Output
Dual Output is a sidecar mode for the interactive TUI: while Qwen Code keeps
rendering normally on
stdout
, it concurrently emits a structured JSON event
stream to a separate channel so an external program — an IDE extension, a web
frontend, a CI pipeline, an automation script — can observe and steer the
session.
It also provides a reverse channel: an external program can write JSONL
commands into a file that the TUI watches, allowing it to submit prom...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Dual Output
Dual Output
Dual Output is a sidecar mode for the interactive TUI: while Qwen Code keeps
rendering normally on
stdout
, it concurrently emits a structured JSON event
stream to a separate channel so an external program — an IDE extension, a web
frontend, a CI pipeline, an automation script — can observe and steer the
session.
It also provides a reverse channel: an external program can write JSONL
commands into a file that the TUI watches, allowing it to submit prompts and
respond to tool-permission requests as if a human were at the keyboard.
Dual Output is fully optional. When the flags below are absent the TUI behaves
exactly as before with no extra I/O and no behavioral changes.
Use cases
Dual Output is a low-level plumbing primitive. These are concrete integrations
it unlocks:
Terminal + Chat dual-mode real-time sync
The flagship use case. A web or desktop ChatUI hosts the TUI inside a PTY
and renders a parallel conversation view driven by the structured event
stream:
User can type in either surface — the TUI (for terminal-native power-users)
or the web UI (for richer UX, shareable links, mobile). Both views stay
in sync because every message flows through the same JSON events.
Tool-approval prompts appear in both places; whoever approves first wins.
Session history is captured verbatim from
--json-file
, so the server
side has a canonical machine-readable transcript without parsing ANSI.
IDE extensions (VS Code / JetBrains / Cursor / Neovim)
Embed Qwen Code inside the IDE. The TUI runs in the editor’s integrated
terminal panel for users who want it, while the extension consumes
--json-fd
/
--json-file
events to drive:
Inline diff overlays when the agent touches files.
A webview side panel with formatted markdown, syntax-highlighted tool
calls, and clickable citations.
Status bar indicators (thinking / responding / awaiting approval).
Programmatic
confirmation_response
writes when the user clicks a
native IDE approval button.
Browser-based Chat frontends
A Node/Bun server spawns the TUI in a PTY for its rendering semantics but
exposes a WebSocket channel to the browser. Events on
--json-file
are
forwarded to the client; user messages typed in the browser are injected
via
--input-file
. No ANSI parsing on either side.
CI / automation observers
A CI job runs Qwen Code with a task prompt. The human sees the TUI in the
job log; the CI system tails
--json-file
to:
Fail the job if a
result
event reports an error.
Push
token usage
/
duration_ms
/
tool_use
counts to metrics.
Archive the full transcript as a build artifact.
Multi-agent orchestration
A supervisor agent spawns multiple TUI workers, each with its own pair of
event/input files. It watches progress, injects follow-up prompts, and
enforces global budget / safety policies by approving or denying tool
calls across all workers.
Session recording, audit, and replay
Tee every TUI session to a regular file with
--json-file
. Later:
Compliance audits can reconstruct exactly what was executed.
Automated regression tests can compare runs across model versions.
A replay tool can re-emit events through the same protocol to feed
visualization dashboards.
Observability dashboards
Stream
--json-file
into Loki / OTEL / any pipeline that accepts JSONL.
Extract
usage.input_tokens
,
tool_use.name
,
result.duration_api_ms
as first-class metrics in Grafana. No need for log-parsing regex.
Testing and QA
Integration tests spawn Qwen Code headlessly, drive it with
--input-file
scripts, and assert on
--json-file
events. Unlike parsing stdout ANSI,
assertions are stable across UI refactors.
Flags
Flag
Type
Purpose
--json-fd <n>
number,
n >= 3
Write structured JSON events to file descriptor
n
. The caller must provide this fd via spawn
stdio
configuration or shell redirection.
--json-file <path>
path
Write structured JSON events to a file. The path can be a regular file, a FIFO (named pipe), or
/dev/fd/N
.
--input-file <path>
path
Watch this file for JSONL commands written by an external program.
--json-fd
and
--json-file
are mutually exclusive. fds 0, 1, and 2 are
rejected to prevent corrupting the TUI’s own output.
Why two output flags? (
--json-fd
vs
--json-file
)
At first glance
--json-fd
looks sufficient — the caller spawns Qwen Code
with an extra file descriptor, the TUI writes events to it, done. In
practice, fd passing breaks down under the most important embedding
scenario: running the TUI inside a pseudo-terminal (PTY). That is why
this feature also exposes a path-based alternative.
When
--json-fd
works
Pure
child_process.spawn
with a
stdio
array:
const
child
=
spawn
(
'qwen'
, [
'--json-fd'
,
'3'
], {
stdio: [
'inherit'
,
'inherit'
,
'inherit'
, eventsFd],
});
Node’s spawn supports arbitrary
stdio
entries; fd 3 is inherited by the
child, which can write to it directly. Zero-copy, zero-buffer, zero
filesystem — the fastest path.
Why
--json-fd
does
not
work under PTY
PTY wrappers like
node-pty
and
bun-pty
are how any serious embedder
(IDE extensions, web terminals, tmux-like multiplexers) hosts an
interactive TUI. They cannot forward extra fds to the child, for three
reinforcing reasons:
API surface.
node-pty.spawn(file, args, options)
accepts
cwd
,
env
,
cols
,
rows
,
encoding
, etc. — but
no
stdio
array
. There
is simply no place in the API to say “also attach this fd as fd 3 in
the child”.
bun-pty
exposes the same shape.
forkpty(3)
semantics.
Under the hood, PTY wrappers call
forkpty(3)
(or the equivalent
posix_openpt
+
login_tty
dance).
That syscall allocates a master/slave pseudo-terminal pair and
redirects the child’s fds 0/1/2 to the slave side so the child thinks
it is attached to a real terminal. Any fds above 2 in the parent are
closed by
login_tty
, which calls
close(fd)
for
fd >= 3
before
exec
. Extra fds are actively wiped, not inherited.
Controlling-terminal side effect.
Even if you hacked an extra fd
through, it would not be a terminal, so the child’s TUI renderer
(which writes escape sequences assuming a TTY on fd 1) would still
need the slave for its output. You would end up with two independent
transports anyway.
In short: the moment an embedder needs a real TTY for TUI rendering —
which is every IDE extension, every web terminal, every desktop chat
app — fd inheritance is off the table.
--json-file
fills the gap
A file path is passed as an ordinary CLI argument, so it survives every
spawn model:
import
{ spawn }
from
'node-pty'
;
const
pty
=
spawn
(
'qwen'
,
[
'--json-file'
,
'/tmp/qwen-events.jsonl'
,
'--input-file'
,
'/tmp/qwen-input.jsonl'
,
],
{ cols:
120
, rows:
40
},
);
The child opens the file itself and writes events there; the embedder
tails the same path with
fs.watch
+ incremental reads. Three things to
note:
Regular file
, FIFO (named pipe), or
/dev/fd/N
all work. FIFO is
the lowest-latency option when both sides are on the same host.
The bridge opens FIFOs with
O_NONBLOCK
and falls back to blocking
mode on
ENXIO
(no reader yet), so PTY startup is never deadlocked
waiting for a consumer.
For multi-session isolation, use per-session paths under
$XDG_RUNTIME_DIR
or a
mkdtemp
’d directory with mode
0700
.
Which flag should I use?
Embedding style
Use
child_process.spawn
with plain stdio
--json-fd
node-pty
/
bun-pty
/ any PTY host
--json-file
Shell redirection / manual pipeline testing
either
CI log collection (regular file, read after exit)
--json-file
Lowest possible latency on same host
--json-file
+ FIFO
The general rule:
if you need the TUI to render correctly, you need a
PTY, which means you need
--json-file
.
--json-fd
is for simpler
embedders that do not care about TUI fidelity — typically programmatic
wrappers that throw away stdout anyway.
Quick start
Run Qwen Code with all three channels enabled:
mkfifo
/tmp/qwen-events.jsonl
/tmp/qwen-input.jsonl
qwen
\
--json-file
/tmp/qwen-events.jsonl
\
--input-file
/tmp/qwen-input.jsonl
In a second terminal, tail the event stream:
cat
/tmp/qwen-events.jsonl
In a third terminal, push a prompt into the running TUI:
echo
'{"type":"submit","text":"Explain this repo"}'
>>
/tmp/qwen-input.jsonl
The prompt appears in the TUI exactly as if the user typed it, and the
streaming response is mirrored on
/tmp/qwen-events.jsonl
.
Output event schema
Events are emitted as JSON Lines (one object per line). The schema is the same
one used by the non-interactive
--output-format=stream-json
mode, with
includePartialMessages
always enabled.
The first event on the channel is always
system
/
session_start
, emitted
when the bridge is constructed. Use it to correlate the channel with a
session id before any other event arrives.
// Session lifecycle
{
"type"
:
"system"
,
"subtype"
:
"session_start"
,
"uuid"
:
"..."
,
"session_id"
:
"..."
,
"data"
: {
"session_id"
:
"..."
,
"cwd"
:
"/path/to/cwd"
}
}
// Streaming events for an in-progress assistant turn
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"message_start"
,
"message"
: {
...
} },
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"content_block_start"
,
"index"
:
0
,
"content_block"
: {
"type"
:
"text"
} },
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"content_block_delta"
,
"index"
:
0
,
"delta"
: {
"type"
:
"text_delta"
,
"text"
:
"Hello"
} },
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"content_block_stop"
,
"index"
:
0
},
...
}
{
"type"
:
"stream_event"
,
"event"
: {
"type"
:
"message_stop"
},
...
}
// Completed messages
{
"type"
:
"user"
,
"message"
: {
"role"
:
"user"
,
"content"
: [
...
] },
...
}
{
"type"
:
"assistant"
,
"message"
: {
"role"
:
"assistant"
,
"content"
: [
...
],
"usage"
: {
...
} },
...
}
{
"type"
:
"user"
,
"message"
: {
"role"
:
"user"
,
"content"
: [{
"type"
:
"tool_result"
,
...
}] } }
// Permission control plane (only when a tool needs approval)
{
"type"
:
"control_request"
,
"request_id"
:
"..."
,
"request"
: {
"subtype"
:
"can_use_tool"
,
"tool_name"
:
"run_shell_command"
,
"tool_use_id"
:
"..."
,
"input"
: {
"command"
:
"rm -rf /tmp/x"
},
"permission_suggestions"
:
null
,
"blocked_path"
:
null
}
}
{
"type"
:
"control_response"
,
"response"
: {
"subtype"
:
"success"
,
"request_id"
:
"..."
,
"response"
: {
"allowed"
:
true
}
}
}
control_response
is emitted whether the decision was made in the TUI
(native approval UI) or by an external
confirmation_response
(see below).
Either way, all observers see the final outcome.
Input command schema
Two command shapes are accepted on
--input-file
:
// Submit a user message into the prompt queue
{
"type"
:
"submit"
,
"text"
:
"What does this function do?"
}
// Reply to a pending control_request
{
"type"
:
"confirmation_response"
,
"request_id"
:
"..."
,
"allowed"
:
true
}
Behavior:
submit
commands are queued. If the TUI is busy responding, they are
retried automatically the next time the TUI returns to the idle state.
confirmation_response
commands are dispatched immediately and never
queued, because a tool call is blocking and the response must reach the
underlying
onConfirm
handler without waiting for any earlier
submit
.
Whichever side approves a tool first wins; the other side’s late response
is harmlessly dropped.
Lines that fail to parse as JSON are logged and skipped — they do not
stop the watcher.
Latency notes
The input file is observed with
fs.watchFile
at a 500 ms polling interval,
so worst-case round-trip latency for a remote
submit
is about half a
second. This is intentional: polling is portable across platforms and
filesystems (including macOS / network mounts), and matches the typical
human-in-the-loop pacing the feature targets. The output channel has no
polling — events are written synchronously as the TUI emits them.
Failure modes
Bad fd.
If the fd passed to
--json-fd
is not open or is one of
0/1/2, the TUI prints a warning to
stderr
and continues without dual
output enabled.
Bad path.
If the file passed to
--json-file
cannot be opened, the
TUI prints a warning and continues without dual output.
Consumer disconnect.
If the reader on the other side of the channel
goes away (
EPIPE
), the bridge silently disables itself and the TUI
keeps running. No retry.
Adapter exception.
Any exception thrown while emitting an event is
caught, logged, and disables the bridge. The TUI is never crashed by a
dual-output failure.
Spawn example
A typical embedding parent process spawns Qwen Code with both channels:
import
{ spawn }
from
'node:child_process'
;
import
{ openSync }
from
'node:fs'
;
const
eventsFd
=
openSync
(
'/tmp/qwen-events.jsonl'
,
'w'
);
const
child
=
spawn
(
'qwen'
,
[
'--json-fd'
,
'3'
,
'--input-file'
,
'/tmp/qwen-input.jsonl'
],
{ stdio: [
'inherit'
,
'inherit'
,
'inherit'
, eventsFd] },
);
The TUI still owns the user’s terminal on stdio 0/1/2, while the embedder
reads structured events on the file backing fd 3 and pushes commands by
appending JSONL lines to
/tmp/qwen-input.jsonl
.
Settings-based configuration
For long-lived embedders it is often inconvenient to thread CLI flags
through every launch. The same channels can be configured in
settings.json
under the top-level
dualOutput
key:
// ~/.qwen/settings.json  (user-level)
// or <workspace>/.qwen/settings.json  (workspace-level)
{
"dualOutput"
: {
"jsonFile"
:
"/tmp/qwen-events.jsonl"
,
"inputFile"
:
"/tmp/qwen-input.jsonl"
,
},
}
Precedence rules:
CLI flag
wins
over settings. Passing
--json-file /foo
on the
command line overrides
dualOutput.jsonFile
in settings.
--json-fd
has no settings equivalent — fd passing is a spawn-time
concern that cannot be statically declared.
If neither flag nor setting is present, dual output stays disabled
(identical to today’s default).
The
requiresRestart: true
flag means changes only take effect on the
next Qwen Code launch, since the bridge is constructed once during
startup.
Runnable demos
Every script below is copy-paste ready. Start with POC 1 to verify
the build has dual output; POC 4 is the closest analogue to a real
IDE-extension integration.
POC 1 — observe the event stream
Watch every structured event the TUI emits while a human uses it
normally:
# Terminal A
mkfifo
/tmp/qwen-events.jsonl
cat
/tmp/qwen-events.jsonl
|
jq
-c
'select(.type != "stream_event") | {type, subtype}'
# Terminal B
qwen
--json-file
/tmp/qwen-events.jsonl
# ...then chat normally; terminal A shows session_start,
# user/assistant/result/control_request lifecycle in real time.
Expected first line in terminal A:
{
"type"
:
"system"
,
"subtype"
:
"session_start"
}
POC 2 — inject prompts from outside
Drive the TUI from a second terminal without touching the keyboard of
the first:
# Terminal A
touch
/tmp/qwen-in.jsonl
qwen
--input-file
/tmp/qwen-in.jsonl
# Terminal B — the TUI responds as if you typed it
echo
'{"type":"submit","text":"list files in the current directory"}'
\
>>
/tmp/qwen-in.jsonl
POC 3 — remote tool-permission bridge
Approve or deny tool calls from a separate process:
# Terminal A — observe control_requests
mkfifo
/tmp/qwen-out.jsonl
touch
/tmp/qwen-in.jsonl
(
cat
/tmp/qwen-out.jsonl
\
|
jq
-c
'select(.type == "control_request")'
) &
# Terminal B
qwen
--json-file
/tmp/qwen-out.jsonl
--input-file
/tmp/qwen-in.jsonl
# Ask Qwen to do something that needs approval, e.g.
# "run `ls -la /tmp`". A control_request will appear in terminal A.
# Copy the request_id, then in a third terminal:
echo
'{"type":"confirmation_response","request_id":"<paste-id>","allowed":true}'
\
>>
/tmp/qwen-in.jsonl
# The TUI confirmation prompt dismisses and the tool executes.
If you reply with an unknown
request_id
, the bridge emits a
control_response
with
subtype: "error"
on the output channel so your
consumer can log it or retry:
{
"type"
:
"control_response"
,
"response"
: {
"subtype"
:
"error"
,
"request_id"
:
"..."
,
"error"
:
"unknown request_id (already resolved, cancelled, or never issued)"
}
}
POC 4 — Node embedder (IDE-like)
The most realistic shape: a parent process spawns Qwen Code, tails
events, and injects prompts on its own schedule.
// demo-embedder.ts
import
{ spawn }
from
'node:child_process'
;
import
{ appendFileSync, createReadStream, writeFileSync }
from
'node:fs'
;
import
{ createInterface }
from
'node:readline'
;
import
{ tmpdir }
from
'node:os'
;
import
{ join }
from
'node:path'
;
const
events
=
join
(
tmpdir
(),
`qwen-events-${
process
.
pid
}.jsonl`
);
const
input
=
join
(
tmpdir
(),
`qwen-input-${
process
.
pid
}.jsonl`
);
writeFileSync
(events,
''
);
writeFileSync
(input,
''
);
const
child
=
spawn
(
'qwen'
, [
'--json-file'
, events,
'--input-file'
, input], {
stdio:
'inherit'
,
});
// Tail the output channel. In production you'd use a proper
// byte-offset tail; this one re-streams from 0 for brevity.
const
rl
=
createInterface
({
input:
createReadStream
(events, { encoding:
'utf8'
}),
});
rl.
on
(
'line'
, (
line
)
=>
{
if
(
!
line.
trim
())
return
;
const
ev
=
JSON
.
parse
(line);
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_start'
) {
console.
log
(
'[embedder] handshake:'
, {
protocol_version: ev.data.protocol_version,
version: ev.data.version,
supported_events: ev.data.supported_events,
});
// Feature-detect before using a capability
if
(ev.data.supported_events.
includes
(
'control_request'
)) {
console.
log
(
'[embedder] permission control-plane available'
);
}
}
if
(ev.type
===
'assistant'
) {
console.
log
(
'[embedder] assistant turn ended, tokens ='
,
ev.message.usage?.output_tokens,
);
}
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_end'
) {
console.
log
(
'[embedder] session ended cleanly'
);
}
});
// After 2s, inject a prompt as if the user typed it
setTimeout
(()
=>
{
appendFileSync
(
input,
JSON
.
stringify
({ type:
'submit'
, text:
'hello from embedder'
})
+
'
\n
'
,
);
},
2000
);
child.
on
(
'exit'
, ()
=>
process.
exit
(
0
));
Run with:
npx
tsx
demo-embedder.ts
# Qwen Code TUI opens in the current terminal; the embedder logs
# handshake + turn-end + session_end events to the parent's stdout.
POC 5 — capability handshake feature detection
Older Qwen Code versions won’t emit
protocol_version
. Treat the field
as optional and feature-detect:
rl.
on
(
'line'
, (
line
)
=>
{
const
ev
=
JSON
.
parse
(line);
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_start'
) {
const
v
=
ev.data?.protocol_version
??
0
;
if
(v
<
1
) {
console.
error
(
'qwen-code dual output is present but protocol < 1; '
+
'falling back to best-effort behavior'
,
);
}
else
{
console.
log
(
'qwen-code dual output protocol v'
+
v);
}
}
});
POC 6 — session_end as a clean termination signal
rl.
on
(
'line'
, (
line
)
=>
{
const
ev
=
JSON
.
parse
(line);
if
(ev.type
===
'system'
&&
ev.subtype
===
'session_end'
) {
console.
log
(
'[embedder] clean shutdown, session'
, ev.data.session_id);
// Flush metrics, close WebSockets, etc.
}
});
If the TUI crashes before
session_end
, the output stream closes
(
EPIPE
on next write); embedders should handle both paths.
POC 7 — failure drills (prove the flags never break the TUI)
qwen
--json-fd
1
# stderr: "Warning: dual output disabled — ..."
# TUI still launches normally.
qwen
--json-fd
9999
# stderr: "Warning: dual output disabled — fd 9999 not open"
# TUI still launches normally.
qwen
--json-fd
3
--json-file
/tmp/x.jsonl
# yargs rejects: "--json-fd and --json-file are mutually exclusive."
# Process exits before TUI starts.
qwen
--json-file
/nonexistent/dir/x.jsonl
# stderr warning; TUI still launches.
Relation to Claude Code
Claude Code exposes a similar stream-json event format under
--print --output-format stream-json
, but only in non-interactive mode
— it has no equivalent of running the TUI and a structured sidecar
channel at the same time. Dual Output fills that gap.
Last updated on
May 18, 2026
Headless Mode
Approval Mode</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/dual-output/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Getting Started with Qwen Code Extensions</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/extension/getting-started-extensions/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/extension/getting-started-extensions/</guid>
  <pubDate>Fri, 05 Apr 2024 00:00:00 +0000</pubDate>
  <category>Getting Started</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Extension
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extension
Th...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Extension
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extension
The easiest way to start is by using one of the built-in templates. We’ll use the
mcp-server
example as our foundation.
Run the following command to create a new directory called
my-first-extension
with the template files:
qwen
extensions
new
my-first-extension
mcp-server
This will create a new directory with the following structure:
my-first-extension/
├── example.ts
├── qwen-extension.json
├── package.json
└── tsconfig.json
Step 2: Understand the Extension Files
Let’s look at the key files in your new extension.
qwen-extension.json
This is the manifest file for your extension. It tells Qwen Code how to load and use your extension.
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
name
: The unique name for your extension.
version
: The version of your extension.
mcpServers
: This section defines one or more Model Context Protocol (MCP) servers. MCP servers are how you can add new tools for the model to use.
command
,
args
,
cwd
: These fields specify how to start your server. Notice the use of the
${extensionPath}
variable, which Qwen Code replaces with the absolute path to your extension’s installation directory. This allows your extension to work regardless of where it’s installed.
example.ts
This file contains the source code for your MCP server. It’s a simple Node.js server that uses the
@modelcontextprotocol/sdk
.
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import
{ McpServer }
from
'@modelcontextprotocol/sdk/server/mcp.js'
;
import
{ StdioServerTransport }
from
'@modelcontextprotocol/sdk/server/stdio.js'
;
import
{ z }
from
'zod'
;
const
server
=
new
McpServer
({
name:
'prompt-server'
,
version:
'1.0.0'
,
});
// Registers a new tool named 'fetch_posts'
server.
registerTool
(
'fetch_posts'
,
{
description:
'Fetches a list of posts from a public API.'
,
inputSchema: z.
object
({}).shape,
},
async
()
=>
{
const
apiResponse
=
await
fetch
(
'https://jsonplaceholder.typicode.com/posts'
,
);
const
posts
=
await
apiResponse.
json
();
const
response
=
{ posts: posts.
slice
(
0
,
5
) };
return
{
content: [
{
type:
'text'
,
text:
JSON
.
stringify
(response),
},
],
};
},
);
// ... (prompt registration omitted for brevity)
const
transport
=
new
StdioServerTransport
();
await
server.
connect
(transport);
This server defines a single tool called
fetch_posts
that fetches data from a public API.
package.json
and
tsconfig.json
These are standard configuration files for a TypeScript project. The
package.json
file defines dependencies and a
build
script, and
tsconfig.json
configures the TypeScript compiler.
Step 3: Build and Link Your Extension
Before you can use the extension, you need to compile the TypeScript code and link the extension to your Qwen Code installation for local development.
Install dependencies:
cd
my-first-extension
npm
install
Build the server:
npm
run
build
This will compile
example.ts
into
dist/example.js
, which is the file referenced in your
qwen-extension.json
.
Link the extension:
The
link
command creates a symbolic link from the Qwen Code extensions directory to your development directory. This means any changes you make will be reflected immediately without needing to reinstall.
qwen
extensions
link
.
Now, restart your Qwen Code session. The new
fetch_posts
tool will be available. You can test it by asking: “fetch posts”.
Step 4: Add a Custom Command
Custom commands provide a way to create shortcuts for complex prompts. Let’s add a command that searches for a pattern in your code.
Create a
commands
directory and a subdirectory for your command group:
mkdir
-p
commands/fs
Create a file named
commands/fs/grep-code.md
:
---
description
:
Search for a pattern in code and summarize findings
---
Please summarize the findings for the pattern
`{{args}}`
.
Search Results:
!{grep -r {{args}} .}
This command,
/fs:grep-code
, will take an argument, run the
grep
shell command with it, and pipe the results into a prompt for summarization.
Note:
Commands use Markdown format with optional YAML frontmatter. TOML format is deprecated but still supported for backwards compatibility.
After saving the file, restart the Qwen Code. You can now run
/fs:grep-code "some pattern"
to use your new command.
Step 5: Add Custom Skills and Subagents (Optional)
Extensions can also provide custom skills and subagents to extend Qwen Code’s capabilities.
Adding a Custom Skill
Skills are model-invoked capabilities that the AI can automatically use when relevant.
Create a
skills
directory with a skill subdirectory:
mkdir
-p
skills/code-analyzer
Create a
skills/code-analyzer/SKILL.md
file:
---
name
:
code-analyzer
description
:
Analyzes code structure and provides insights about complexity, dependencies, and potential improvements
---
# Code Analyzer
## Instructions
When analyzing code, focus on:
-
Code complexity and maintainability
-
Dependencies and coupling
-
Potential performance issues
-
Suggestions for improvements
## Examples
-
"Analyze the complexity of this function"
-
"What are the dependencies of this module?"
Adding a Custom Subagent
Subagents are specialized AI assistants for specific tasks.
Create an
agents
directory:
mkdir
-p
agents
Create an
agents/refactoring-expert.md
file:
---
name
:
refactoring-expert
description
:
Specialized in code refactoring, improving code structure and maintainability
tools
:
-
read_file
-
write_file
-
read_many_files
---
You are a refactoring specialist focused on improving code quality.
Your expertise includes:
-
Identifying code smells and anti-patterns
-
Applying SOLID principles
-
Improving code readability and maintainability
-
Safe refactoring with minimal risk
For each refactoring task:
1.
Analyze the current code structure
2.
Identify areas for improvement
3.
Propose refactoring steps
4.
Implement changes incrementally
5.
Verify functionality is preserved
After restarting Qwen Code, your custom skills will be available via
/skills
and subagents via
/agents manage
.
Step 6: Add a Custom
QWEN.md
You can provide persistent context to the model by adding a
QWEN.md
file to your extension. This is useful for giving the model instructions on how to behave or information about your extension’s tools. Note that you may not always need this for extensions built to expose commands and prompts.
Create a file named
QWEN.md
in the root of your extension directory:
# My First Extension Instructions
You are an expert developer assistant. When the user asks you to fetch posts, use the
`fetch_posts`
tool. Be concise in your responses.
Update your
qwen-extension.json
to tell the CLI to load this file:
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"contextFileName"
:
"QWEN.md"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
Restart the CLI again. The model will now have the context from your
QWEN.md
file in every session where the extension is active.
Step 7: Releasing Your Extension
Once you are happy with your extension, you can share it with others. The two primary ways of releasing extensions are via a Git repository or through GitHub Releases. Using a public Git repository is the simplest method.
For detailed instructions on both methods, please refer to the
Extension Releasing Guide
.
Conclusion
You’ve successfully created a Qwen Code extension! You learned how to:
Bootstrap a new extension from a template.
Add custom tools with an MCP server.
Create convenient custom commands.
Add custom skills and subagents.
Provide persistent context to the model.
Link your extension for local development.
From here, you can explore more advanced features and build powerful new capabilities into the Qwen Code.
Last updated on
May 18, 2026
Introduction
Troubleshooting</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/extension/getting-started-extensions/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Extension
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extension
Th...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Extension
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extension
The easiest way to start is by using one of the built-in templates. We’ll use the
mcp-server
example as our foundation.
Run the following command to create a new directory called
my-first-extension
with the template files:
qwen
extensions
new
my-first-extension
mcp-server
This will create a new directory with the following structure:
my-first-extension/
├── example.ts
├── qwen-extension.json
├── package.json
└── tsconfig.json
Step 2: Understand the Extension Files
Let’s look at the key files in your new extension.
qwen-extension.json
This is the manifest file for your extension. It tells Qwen Code how to load and use your extension.
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
name
: The unique name for your extension.
version
: The version of your extension.
mcpServers
: This section defines one or more Model Context Protocol (MCP) servers. MCP servers are how you can add new tools for the model to use.
command
,
args
,
cwd
: These fields specify how to start your server. Notice the use of the
${extensionPath}
variable, which Qwen Code replaces with the absolute path to your extension’s installation directory. This allows your extension to work regardless of where it’s installed.
example.ts
This file contains the source code for your MCP server. It’s a simple Node.js server that uses the
@modelcontextprotocol/sdk
.
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import
{ McpServer }
from
'@modelcontextprotocol/sdk/server/mcp.js'
;
import
{ StdioServerTransport }
from
'@modelcontextprotocol/sdk/server/stdio.js'
;
import
{ z }
from
'zod'
;
const
server
=
new
McpServer
({
name:
'prompt-server'
,
version:
'1.0.0'
,
});
// Registers a new tool named 'fetch_posts'
server.
registerTool
(
'fetch_posts'
,
{
description:
'Fetches a list of posts from a public API.'
,
inputSchema: z.
object
({}).shape,
},
async
()
=>
{
const
apiResponse
=
await
fetch
(
'https://jsonplaceholder.typicode.com/posts'
,
);
const
posts
=
await
apiResponse.
json
();
const
response
=
{ posts: posts.
slice
(
0
,
5
) };
return
{
content: [
{
type:
'text'
,
text:
JSON
.
stringify
(response),
},
],
};
},
);
// ... (prompt registration omitted for brevity)
const
transport
=
new
StdioServerTransport
();
await
server.
connect
(transport);
This server defines a single tool called
fetch_posts
that fetches data from a public API.
package.json
and
tsconfig.json
These are standard configuration files for a TypeScript project. The
package.json
file defines dependencies and a
build
script, and
tsconfig.json
configures the TypeScript compiler.
Step 3: Build and Link Your Extension
Before you can use the extension, you need to compile the TypeScript code and link the extension to your Qwen Code installation for local development.
Install dependencies:
cd
my-first-extension
npm
install
Build the server:
npm
run
build
This will compile
example.ts
into
dist/example.js
, which is the file referenced in your
qwen-extension.json
.
Link the extension:
The
link
command creates a symbolic link from the Qwen Code extensions directory to your development directory. This means any changes you make will be reflected immediately without needing to reinstall.
qwen
extensions
link
.
Now, restart your Qwen Code session. The new
fetch_posts
tool will be available. You can test it by asking: “fetch posts”.
Step 4: Add a Custom Command
Custom commands provide a way to create shortcuts for complex prompts. Let’s add a command that searches for a pattern in your code.
Create a
commands
directory and a subdirectory for your command group:
mkdir
-p
commands/fs
Create a file named
commands/fs/grep-code.md
:
---
description
:
Search for a pattern in code and summarize findings
---
Please summarize the findings for the pattern
`{{args}}`
.
Search Results:
!{grep -r {{args}} .}
This command,
/fs:grep-code
, will take an argument, run the
grep
shell command with it, and pipe the results into a prompt for summarization.
Note:
Commands use Markdown format with optional YAML frontmatter. TOML format is deprecated but still supported for backwards compatibility.
After saving the file, restart the Qwen Code. You can now run
/fs:grep-code "some pattern"
to use your new command.
Step 5: Add Custom Skills and Subagents (Optional)
Extensions can also provide custom skills and subagents to extend Qwen Code’s capabilities.
Adding a Custom Skill
Skills are model-invoked capabilities that the AI can automatically use when relevant.
Create a
skills
directory with a skill subdirectory:
mkdir
-p
skills/code-analyzer
Create a
skills/code-analyzer/SKILL.md
file:
---
name
:
code-analyzer
description
:
Analyzes code structure and provides insights about complexity, dependencies, and potential improvements
---
# Code Analyzer
## Instructions
When analyzing code, focus on:
-
Code complexity and maintainability
-
Dependencies and coupling
-
Potential performance issues
-
Suggestions for improvements
## Examples
-
"Analyze the complexity of this function"
-
"What are the dependencies of this module?"
Adding a Custom Subagent
Subagents are specialized AI assistants for specific tasks.
Create an
agents
directory:
mkdir
-p
agents
Create an
agents/refactoring-expert.md
file:
---
name
:
refactoring-expert
description
:
Specialized in code refactoring, improving code structure and maintainability
tools
:
-
read_file
-
write_file
-
read_many_files
---
You are a refactoring specialist focused on improving code quality.
Your expertise includes:
-
Identifying code smells and anti-patterns
-
Applying SOLID principles
-
Improving code readability and maintainability
-
Safe refactoring with minimal risk
For each refactoring task:
1.
Analyze the current code structure
2.
Identify areas for improvement
3.
Propose refactoring steps
4.
Implement changes incrementally
5.
Verify functionality is preserved
After restarting Qwen Code, your custom skills will be available via
/skills
and subagents via
/agents manage
.
Step 6: Add a Custom
QWEN.md
You can provide persistent context to the model by adding a
QWEN.md
file to your extension. This is useful for giving the model instructions on how to behave or information about your extension’s tools. Note that you may not always need this for extensions built to expose commands and prompts.
Create a file named
QWEN.md
in the root of your extension directory:
# My First Extension Instructions
You are an expert developer assistant. When the user asks you to fetch posts, use the
`fetch_posts`
tool. Be concise in your responses.
Update your
qwen-extension.json
to tell the CLI to load this file:
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"contextFileName"
:
"QWEN.md"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
Restart the CLI again. The model will now have the context from your
QWEN.md
file in every session where the extension is active.
Step 7: Releasing Your Extension
Once you are happy with your extension, you can share it with others. The two primary ways of releasing extensions are via a Git repository or through GitHub Releases. Using a public Git repository is the simplest method.
For detailed instructions on both methods, please refer to the
Extension Releasing Guide
.
Conclusion
You’ve successfully created a Qwen Code extension! You learned how to:
Bootstrap a new extension from a template.
Add custom tools with an MCP server.
Create convenient custom commands.
Add custom skills and subagents.
Provide persistent context to the model.
Link your extension for local development.
From here, you can explore more advanced features and build powerful new capabilities into the Qwen Code.
Last updated on
May 18, 2026
Introduction
Troubleshooting</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/extension/getting-started-extensions/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Zed Editor</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/integration-zed/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/integration-zed/</guid>
  <pubDate>Fri, 05 Apr 2024 00:00:00 +0000</pubDate>
  <category>Integrations</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Zed IDE
Zed Editor
Zed Editor provides native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within Zed’s interface with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within Zed’s interface
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
File management
: @-mention files to add them to the conversation context
Conversation hist...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Zed IDE
Zed Editor
Zed Editor provides native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within Zed’s interface with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within Zed’s interface
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
File management
: @-mention files to add them to the conversation context
Conversation history
: Access to past conversations within Zed
Requirements
Zed Editor (latest version recommended)
Qwen Code CLI installed
Installation
Install from ACP Registry (Recommend)
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Download and install
Zed Editor
In Zed, click the
settings button
in the top right corner, select
“Add agent”
, choose
“Install from Registry”
, find
Qwen Code
, then click
Install
.
Manual Install
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Download and install
Zed Editor
In Zed, click the
settings button
in the top right corner, select
“Add agent”
, choose
“Create a custom agent”
, and add the following configuration:
"Qwen Code"
: {
"type"
:
"custom"
,
"command"
:
"qwen"
,
"args"
: [
"--acp"
],
"env"
: {}
}
Troubleshooting
Agent not appearing
Run
qwen --version
in terminal to verify installation
Check that the JSON configuration is valid
Restart Zed Editor
Qwen Code not responding
Check your internet connection
Verify CLI works by running
qwen
in terminal
File an issue on GitHub
if the problem persists
Last updated on
May 18, 2026
Visual Studio Code
JetBrains IDEs</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-zed/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Zed IDE
Zed Editor
Zed Editor provides native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within Zed’s interface with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within Zed’s interface
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
File management
: @-mention files to add them to the conversation context
Conversation hist...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Zed IDE
Zed Editor
Zed Editor provides native support for AI coding assistants through the Agent Client Protocol (ACP). This integration allows you to use Qwen Code directly within Zed’s interface with real-time code suggestions.
Features
Native agent experience
: Integrated AI assistant panel within Zed’s interface
Agent Client Protocol
: Full support for ACP enabling advanced IDE interactions
File management
: @-mention files to add them to the conversation context
Conversation history
: Access to past conversations within Zed
Requirements
Zed Editor (latest version recommended)
Qwen Code CLI installed
Installation
Install from ACP Registry (Recommend)
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Download and install
Zed Editor
In Zed, click the
settings button
in the top right corner, select
“Add agent”
, choose
“Install from Registry”
, find
Qwen Code
, then click
Install
.
Manual Install
Install Qwen Code CLI:
npm
install
-g
@qwen-code/qwen-code
Download and install
Zed Editor
In Zed, click the
settings button
in the top right corner, select
“Add agent”
, choose
“Create a custom agent”
, and add the following configuration:
"Qwen Code"
: {
"type"
:
"custom"
,
"command"
:
"qwen"
,
"args"
: [
"--acp"
],
"env"
: {}
}
Troubleshooting
Agent not appearing
Run
qwen --version
in terminal to verify installation
Check that the JSON configuration is valid
Restart Zed Editor
Qwen Code not responding
Check your internet connection
Verify CLI works by running
qwen
in terminal
File an issue on GitHub
if the problem persists
Last updated on
May 18, 2026
Visual Studio Code
JetBrains IDEs</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/integration-zed/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Custom Channel Plugins</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/plugins/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/plugins/</guid>
  <pubDate>Wed, 03 Apr 2024 00:00:00 +0000</pubDate>
  <category>Plugins</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
Plugins
Custom Channel Plugins
You can extend the channel system with custom platform adapters packaged as
extensions
. This lets you connect Qwen Code to any messaging platform, webhook, or custom transport.
How It Works
Channel plugins are loaded at startup from active extensions. When
qwen channel start
runs, it:
Scans all enabled extensions for
channels
entries in their
qwen-extension.json
Dynamically imports each channel’s entry point
Registers the channel type ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
Plugins
Custom Channel Plugins
You can extend the channel system with custom platform adapters packaged as
extensions
. This lets you connect Qwen Code to any messaging platform, webhook, or custom transport.
How It Works
Channel plugins are loaded at startup from active extensions. When
qwen channel start
runs, it:
Scans all enabled extensions for
channels
entries in their
qwen-extension.json
Dynamically imports each channel’s entry point
Registers the channel type so it can be referenced in
settings.json
Creates channel instances using the plugin’s factory function
Your custom channel gets the full shared pipeline for free: sender gating, group policies, session routing, slash commands, crash recovery, and the ACP bridge to the agent.
Installing a Custom Channel
Install an extension that provides a channel plugin:
# From a local path (for development or private plugins)
qwen
extensions
install
/path/to/my-channel-extension
# Or link it for development (changes are reflected immediately)
qwen
extensions
link
/path/to/my-channel-extension
Configuring a Custom Channel
Add a channel entry to
~/.qwen/settings.json
using the custom type provided by the extension:
{
"channels"
: {
"my-bot"
: {
"type"
:
"my-platform"
,
"apiKey"
:
"$MY_PLATFORM_API_KEY"
,
"senderPolicy"
:
"open"
,
"cwd"
:
"/path/to/project"
}
}
}
The
type
must match a channel type registered by an installed extension. Check the extension’s documentation for which plugin-specific fields are required (e.g.,
apiKey
,
webhookUrl
).
All standard channel options work with custom channels:
Option
Description
senderPolicy
allowlist
,
pairing
, or
open
allowedUsers
Static allowlist of sender IDs
sessionScope
user
,
thread
, or
single
cwd
Working directory for the agent
instructions
Prepended to the first message of each session
model
Model override for the channel
groupPolicy
disabled
,
allowlist
, or
open
groups
Per-group settings
See
Overview
for details on each option.
Starting the Channel
# Start all channels including custom ones
qwen
channel
start
# Start just your custom channel
qwen
channel
start
my-bot
What You Get for Free
Custom channels automatically support everything built-in channels do:
Sender policies
—
allowlist
,
pairing
, and
open
access control
Group policies
— Per-group settings with optional @mention gating
Session routing
— Per-user, per-thread, or single shared sessions
DM pairing
— Full pairing code flow for unknown users
Slash commands
—
/help
,
/clear
,
/status
work out of the box
Custom instructions
— Prepended to the first message in each session
Crash recovery
— Automatic restart with session preservation
Per-session serialization
— Messages are queued to prevent race conditions
Building Your Own Channel Plugin
Want to build a channel plugin for a new platform? See the
Channel Plugin Developer Guide
for the
ChannelPlugin
interface, the
Envelope
format, and extension points.
Last updated on
May 18, 2026
DingTalk
Hooks</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/plugins/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
Plugins
Custom Channel Plugins
You can extend the channel system with custom platform adapters packaged as
extensions
. This lets you connect Qwen Code to any messaging platform, webhook, or custom transport.
How It Works
Channel plugins are loaded at startup from active extensions. When
qwen channel start
runs, it:
Scans all enabled extensions for
channels
entries in their
qwen-extension.json
Dynamically imports each channel’s entry point
Registers the channel type ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
Plugins
Custom Channel Plugins
You can extend the channel system with custom platform adapters packaged as
extensions
. This lets you connect Qwen Code to any messaging platform, webhook, or custom transport.
How It Works
Channel plugins are loaded at startup from active extensions. When
qwen channel start
runs, it:
Scans all enabled extensions for
channels
entries in their
qwen-extension.json
Dynamically imports each channel’s entry point
Registers the channel type so it can be referenced in
settings.json
Creates channel instances using the plugin’s factory function
Your custom channel gets the full shared pipeline for free: sender gating, group policies, session routing, slash commands, crash recovery, and the ACP bridge to the agent.
Installing a Custom Channel
Install an extension that provides a channel plugin:
# From a local path (for development or private plugins)
qwen
extensions
install
/path/to/my-channel-extension
# Or link it for development (changes are reflected immediately)
qwen
extensions
link
/path/to/my-channel-extension
Configuring a Custom Channel
Add a channel entry to
~/.qwen/settings.json
using the custom type provided by the extension:
{
"channels"
: {
"my-bot"
: {
"type"
:
"my-platform"
,
"apiKey"
:
"$MY_PLATFORM_API_KEY"
,
"senderPolicy"
:
"open"
,
"cwd"
:
"/path/to/project"
}
}
}
The
type
must match a channel type registered by an installed extension. Check the extension’s documentation for which plugin-specific fields are required (e.g.,
apiKey
,
webhookUrl
).
All standard channel options work with custom channels:
Option
Description
senderPolicy
allowlist
,
pairing
, or
open
allowedUsers
Static allowlist of sender IDs
sessionScope
user
,
thread
, or
single
cwd
Working directory for the agent
instructions
Prepended to the first message of each session
model
Model override for the channel
groupPolicy
disabled
,
allowlist
, or
open
groups
Per-group settings
See
Overview
for details on each option.
Starting the Channel
# Start all channels including custom ones
qwen
channel
start
# Start just your custom channel
qwen
channel
start
my-bot
What You Get for Free
Custom channels automatically support everything built-in channels do:
Sender policies
—
allowlist
,
pairing
, and
open
access control
Group policies
— Per-group settings with optional @mention gating
Session routing
— Per-user, per-thread, or single shared sessions
DM pairing
— Full pairing code flow for unknown users
Slash commands
—
/help
,
/clear
,
/status
work out of the box
Custom instructions
— Prepended to the first message in each session
Crash recovery
— Automatic restart with session preservation
Per-session serialization
— Messages are queued to prevent race conditions
Building Your Own Channel Plugin
Want to build a channel plugin for a new platform? See the
Channel Plugin Developer Guide
for the
ChannelPlugin
interface, the
Envelope
format, and extension points.
Last updated on
May 18, 2026
DingTalk
Hooks</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/plugins/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Telegram</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/telegram/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/telegram/</guid>
  <pubDate>Sat, 23 Mar 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
Telegram
Telegram
This guide covers setting up a Qwen Code channel on Telegram.
Prerequisites
A Telegram account
A Telegram bot token (see below)
Creating a Bot
Open Telegram and search for
@BotFather
Send
/newbot
and follow the prompts to choose a name and username
BotFather will give you a bot token — save it securely
Finding Your User ID
To use
senderPolicy: &quot;allowlist&quot;
or
&quot;pairing&quot;
, you need your Telegram user ID (a numeric ID, not your username).
The easiest wa...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
Telegram
Telegram
This guide covers setting up a Qwen Code channel on Telegram.
Prerequisites
A Telegram account
A Telegram bot token (see below)
Creating a Bot
Open Telegram and search for
@BotFather
Send
/newbot
and follow the prompts to choose a name and username
BotFather will give you a bot token — save it securely
Finding Your User ID
To use
senderPolicy: "allowlist"
or
"pairing"
, you need your Telegram user ID (a numeric ID, not your username).
The easiest way to find it:
Search for
@userinfobot
on Telegram
Send it any message — it will reply with your user ID
Configuration
Add the channel to
~/.qwen/settings.json
:
{
"channels"
: {
"my-telegram"
: {
"type"
:
"telegram"
,
"token"
:
"$TELEGRAM_BOT_TOKEN"
,
"senderPolicy"
:
"allowlist"
,
"allowedUsers"
: [
"YOUR_USER_ID"
],
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/your/project"
,
"instructions"
:
"You are a concise coding assistant responding via Telegram. Keep responses short."
,
"groupPolicy"
:
"disabled"
,
"groups"
: {
"*"
: {
"requireMention"
:
true
}
}
}
}
}
Set the bot token as an environment variable:
export
TELEGRAM_BOT_TOKEN
=<
your-token-from-botfather
>
Or add it to a
.env
file that gets sourced before running.
Running
# Start only the Telegram channel
qwen
channel
start
my-telegram
# Or start all configured channels together
qwen
channel
start
Then open your bot in Telegram and send a message. You should see “Working…” appear immediately, followed by the agent’s response.
Group Chats
To use the bot in Telegram groups:
Set
groupPolicy
to
"allowlist"
or
"open"
in your channel config
Disable privacy mode
in BotFather:
/mybots
→ select your bot → Bot Settings → Group Privacy → Turn Off
Add the bot to a group. If it was already in the group,
remove and re-add it
(Telegram caches privacy settings from when the bot joined)
If using
groupPolicy: "allowlist"
, add the group’s chat ID to
groups
in your config
By default, the bot requires an @mention or a reply to respond in groups. Set
"requireMention": false
for a specific group to make it respond to all messages (useful for dedicated task groups). See
Group Chats
for full details.
Images and Files
You can send photos and documents to the bot, not just text.
Photos:
Send a photo and the agent will analyze it using its vision capabilities. This requires a multimodal model — add
"model": "qwen3.5-plus"
(or another vision-capable model) to your channel config. Photo captions are passed as the message text.
Documents:
Send a PDF, code file, or any document. The bot downloads it and saves it locally so the agent can read it with its file tools. This works with any model. Telegram’s file size limit is 20MB.
Tips
Keep instructions concise-focused
— Telegram has a 4096-character message limit. Adding instructions like “keep responses short” helps the agent stay within bounds.
Use
sessionScope: "user"
— This gives each user their own conversation. Use
/clear
to start fresh.
Restrict access
— Use
senderPolicy: "allowlist"
for a fixed set of users, or
"pairing"
to let new users request access with a code you approve via CLI. See
DM Pairing
for details.
Message Formatting
The agent’s markdown responses are automatically converted to Telegram-compatible HTML. Code blocks, bold, italic, links, and lists are all supported.
Troubleshooting
Bot doesn’t respond
Check that the bot token is correct and the environment variable is set
Verify your user ID is in
allowedUsers
if using
senderPolicy: "allowlist"
, or that you’ve been approved if using
"pairing"
Check the terminal output for errors
Bot doesn’t respond in groups
Check that
groupPolicy
is set to
"allowlist"
or
"open"
(default is
"disabled"
)
If using
"allowlist"
, verify the group’s chat ID is in the
groups
config
Make sure
Group Privacy is turned off
in BotFather — without this, the bot can’t see non-command messages in groups
If you changed privacy mode after adding the bot to a group,
remove and re-add the bot
to the group
By default, the bot requires an @mention or a reply. Send
@yourbotname hello
to test
”Sorry, something went wrong processing your message”
This usually means the agent encountered an error. Check the terminal output for details.
Bot takes a long time to respond
The agent may be running multiple tool calls (reading files, searching, etc.). The “Working…” indicator shows while the agent is processing. Complex tasks can take a minute or more.
Last updated on
May 18, 2026
Overview
WeChat</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/telegram/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Channels
Telegram
Telegram
This guide covers setting up a Qwen Code channel on Telegram.
Prerequisites
A Telegram account
A Telegram bot token (see below)
Creating a Bot
Open Telegram and search for
@BotFather
Send
/newbot
and follow the prompts to choose a name and username
BotFather will give you a bot token — save it securely
Finding Your User ID
To use
senderPolicy: &quot;allowlist&quot;
or
&quot;pairing&quot;
, you need your Telegram user ID (a numeric ID, not your username).
The easiest wa...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Channels
Telegram
Telegram
This guide covers setting up a Qwen Code channel on Telegram.
Prerequisites
A Telegram account
A Telegram bot token (see below)
Creating a Bot
Open Telegram and search for
@BotFather
Send
/newbot
and follow the prompts to choose a name and username
BotFather will give you a bot token — save it securely
Finding Your User ID
To use
senderPolicy: "allowlist"
or
"pairing"
, you need your Telegram user ID (a numeric ID, not your username).
The easiest way to find it:
Search for
@userinfobot
on Telegram
Send it any message — it will reply with your user ID
Configuration
Add the channel to
~/.qwen/settings.json
:
{
"channels"
: {
"my-telegram"
: {
"type"
:
"telegram"
,
"token"
:
"$TELEGRAM_BOT_TOKEN"
,
"senderPolicy"
:
"allowlist"
,
"allowedUsers"
: [
"YOUR_USER_ID"
],
"sessionScope"
:
"user"
,
"cwd"
:
"/path/to/your/project"
,
"instructions"
:
"You are a concise coding assistant responding via Telegram. Keep responses short."
,
"groupPolicy"
:
"disabled"
,
"groups"
: {
"*"
: {
"requireMention"
:
true
}
}
}
}
}
Set the bot token as an environment variable:
export
TELEGRAM_BOT_TOKEN
=<
your-token-from-botfather
>
Or add it to a
.env
file that gets sourced before running.
Running
# Start only the Telegram channel
qwen
channel
start
my-telegram
# Or start all configured channels together
qwen
channel
start
Then open your bot in Telegram and send a message. You should see “Working…” appear immediately, followed by the agent’s response.
Group Chats
To use the bot in Telegram groups:
Set
groupPolicy
to
"allowlist"
or
"open"
in your channel config
Disable privacy mode
in BotFather:
/mybots
→ select your bot → Bot Settings → Group Privacy → Turn Off
Add the bot to a group. If it was already in the group,
remove and re-add it
(Telegram caches privacy settings from when the bot joined)
If using
groupPolicy: "allowlist"
, add the group’s chat ID to
groups
in your config
By default, the bot requires an @mention or a reply to respond in groups. Set
"requireMention": false
for a specific group to make it respond to all messages (useful for dedicated task groups). See
Group Chats
for full details.
Images and Files
You can send photos and documents to the bot, not just text.
Photos:
Send a photo and the agent will analyze it using its vision capabilities. This requires a multimodal model — add
"model": "qwen3.5-plus"
(or another vision-capable model) to your channel config. Photo captions are passed as the message text.
Documents:
Send a PDF, code file, or any document. The bot downloads it and saves it locally so the agent can read it with its file tools. This works with any model. Telegram’s file size limit is 20MB.
Tips
Keep instructions concise-focused
— Telegram has a 4096-character message limit. Adding instructions like “keep responses short” helps the agent stay within bounds.
Use
sessionScope: "user"
— This gives each user their own conversation. Use
/clear
to start fresh.
Restrict access
— Use
senderPolicy: "allowlist"
for a fixed set of users, or
"pairing"
to let new users request access with a code you approve via CLI. See
DM Pairing
for details.
Message Formatting
The agent’s markdown responses are automatically converted to Telegram-compatible HTML. Code blocks, bold, italic, links, and lists are all supported.
Troubleshooting
Bot doesn’t respond
Check that the bot token is correct and the environment variable is set
Verify your user ID is in
allowedUsers
if using
senderPolicy: "allowlist"
, or that you’ve been approved if using
"pairing"
Check the terminal output for errors
Bot doesn’t respond in groups
Check that
groupPolicy
is set to
"allowlist"
or
"open"
(default is
"disabled"
)
If using
"allowlist"
, verify the group’s chat ID is in the
groups
config
Make sure
Group Privacy is turned off
in BotFather — without this, the bot can’t see non-command messages in groups
If you changed privacy mode after adding the bot to a group,
remove and re-add the bot
to the group
By default, the bot requires an @mention or a reply. Send
@yourbotname hello
to test
”Sorry, something went wrong processing your message”
This usually means the agent encountered an error. Check the terminal output for details.
Bot takes a long time to respond
The agent may be running multiple tool calls (reading files, searching, etc.). The “Working…” indicator shows while the agent is processing. Complex tasks can take a minute or more.
Last updated on
May 18, 2026
Overview
WeChat</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/channels/telegram/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Command 模块重构方案</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/compare/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/compare/</guid>
  <pubDate>Thu, 21 Mar 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Qwen Code Command 模块重构方案
Qwen Code Command 模块重构方案
1. 目标定义
本方案以以下原则为唯一前提：
代码架构可以不照搬 Claude Code
但命令系统的核心功能、使用体验、交互体验必须 95% 对齐 Claude Code
这里的“对齐”指用户可直接感知的能力，包括：
命令来源覆盖
命令帮助与发现性
命令补全与 mid-input slash command 体验
ACP / non-interactive 可用性
prompt command / skill 的模型调用能力
本次重构不是补几个字段，也不是把现有
SlashCommand
小修小补，而是把 command 模块从“interactive UI 附属能力”升级为“跨 interactive / ACP / non-interactive / model 的统一命令平台”。
2. 重写后的结论
Qwen 现有 command 系统的问题，不是完全没有能力，而是：
只在 interactive 主路径上较完整
类型模型太薄，无法承载 ...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Qwen Code Command 模块重构方案
Qwen Code Command 模块重构方案
1. 目标定义
本方案以以下原则为唯一前提：
代码架构可以不照搬 Claude Code
但命令系统的核心功能、使用体验、交互体验必须 95% 对齐 Claude Code
这里的“对齐”指用户可直接感知的能力，包括：
命令来源覆盖
命令帮助与发现性
命令补全与 mid-input slash command 体验
ACP / non-interactive 可用性
prompt command / skill 的模型调用能力
本次重构不是补几个字段，也不是把现有
SlashCommand
小修小补，而是把 command 模块从“interactive UI 附属能力”升级为“跨 interactive / ACP / non-interactive / model 的统一命令平台”。
2. 重写后的结论
Qwen 现有 command 系统的问题，不是完全没有能力，而是：
只在 interactive 主路径上较完整
类型模型太薄，无法承载 Claude 级别的产品面
ACP / non-interactive 依赖白名单，扩展性极差
command 来源虽然存在，但没有形成对用户可见的统一心智
prompt command 与模型 skill 暴露体系割裂
因此新的方案必须同时解决四件事：
补齐 Claude Code 的能力面
保留 Qwen 统一 outcome 模型的工程优势
建立统一 registry / resolver / executor / adapter 架构
让帮助、补全、ACP available commands、文档共用同一套元数据
3. 重构原则
3.1 功能对齐优先于实现对齐
允许不同：
内部类名
模块拆分方式
执行器实现
effect / outcome 结构
不允许不同：
命令来源覆盖明显缩水
命令帮助和补全体验明显缩水
ACP / non-interactive 可用性明显缩水
prompt command 与模型能力融合明显缩水
如果出现取舍，优先级应为：
用户体验对齐
命令能力覆盖对齐
模式一致性对齐
内部实现简洁
3.2 保留 Qwen 的统一 outcome 模型
不建议机械复制 Claude 的执行实现。
Qwen 当前统一结果模型仍然值得保留，因为它天然适合：
UI 接管
审批/确认
tool 调度
prompt 提交
跨模式适配
但它必须被升级为能够承载 Claude 级别的 command 能力，而不是继续作为简化版 UI 命令框架存在。
3.3 类型、来源、模式、可见性必须彻底解耦
新的 command 模型至少要把以下维度拆开：
类型
：命令怎么执行
来源
：命令从哪里来
模式能力
：在哪些运行环境可用
可见性
：对用户可见还是对模型可见
4. 需要对齐的 Claude Code 能力面
4.1 命令类型
Qwen 需要显式支持三类命令：
prompt
local
local-jsx
4.2 命令来源
Qwen 的 command schema 从第一阶段开始就必须覆盖以下来源：
built-in commands
bundled skills
skill dir commands
workflow commands
plugin commands
plugin skills
dynamic skills
mcp prompts
mcp skills
这里不能再退回到“先只支持当前已有那几类”。
4.3 命令元数据
至少补齐以下字段：
argumentHint
whenToUse
examples
sourceLabel
userFacingName
alias
immediate
isSensitive
userInvocable
modelInvocable
supportedModes
requiresUi
4.4 体验能力
至少补齐以下体验：
alias 命中补全
source badge
参数提示
recently used 排序
mid-input slash command 检测与补全
命令目录式 Help
ACP available commands 的完整表达
5. 新 command 模型
5.1 核心结构
建议引入统一
CommandDescriptor
，作为所有命令的注册格式。
它至少包含四部分：
identity
metadata
capabilities
handler
identity
id
name
altNames
canonicalPath
metadata
description
argumentHint
whenToUse
examples
group
source
sourceLabel
userFacingName
hidden
capabilities
type
:
prompt | local | local-jsx
supportedModes
:
interactive | acp | non_interactive
requiresUi
supportsDialog
supportsStreaming
supportsToolInvocation
supportsConfirmation
remoteSafe
readOnly
immediate
isSensitive
userInvocable
modelInvocable
handler
resolveArgs()
execute()
completion()
fallback()
5.2 三种命令类型的职责
prompt
用于：
skills
file commands
workflow prompt commands
plugin skills
mcp prompt / skill
特点：
产生 prompt / skill 资产
默认支持 interactive / ACP / non-interactive
可以被用户调用，也可以被模型调用
local
用于：
查询类命令
配置类命令
headless 可执行的状态类命令
大多数 built-in commands 的核心执行入口
特点：
不依赖 UI
应成为 ACP / non-interactive 的主承载类型
local-jsx
用于：
picker
面板
wizard
interactive UI shell
特点：
只处理 interactive UI
不能再作为唯一执行入口
必须提供 fallback 或对应 local 子命令
6. 命令来源模型
6.1 外部来源模型
这是给用户看的来源模型，必须和 Claude Code 的心智尽量一致：
builtin-command
bundled-skill
skill-dir-command
workflow-command
plugin-command
plugin-skill
dynamic-skill
builtin-plugin-skill
mcp-prompt
mcp-skill
这组字段将直接用于：
Help 分组
Completion source badge
ACP available commands
文档导出
6.2 内部归一化模型
为了不被外部命名绑死，内部再补一层实现字段：
providerType
artifactType
activationMode
builtinProvided
originPath
namespace
这样可以做到：
外部体验按 Claude 对齐
内部实现仍保持 Qwen 可维护性
6.3 冲突策略
统一按稳定
id
管理，展示名和输入名分离：
id
：稳定唯一标识
name
：输入主名
userFacingName
：帮助/补全展示名
冲突优先级建议：
built-in
bundled / skill-dir / workflow
plugin / builtin-plugin
dynamic
mcp 独立 namespace
7. 统一执行架构
7.1
CommandRegistry
职责：
聚合所有 loader/provider
建立多维索引
输出帮助、补全、ACP、文档视图
提供用户可见命令和模型可见命令的独立视图
必须支持的 provider：
BuiltinCommandLoader
BundledSkillLoader
FileCommandLoader
McpPromptLoader
WorkflowCommandLoader
PluginCommandLoader
PluginSkillLoader
DynamicSkillProvider
BuiltinPluginSkillLoader
即便部分 provider 首期未完全落地，schema 和 API 也必须先支持。
7.2
CommandResolver
职责：
解析 slash command
解析 alias
解析 subcommand path
识别 mid-input slash token
输出 canonical resolved command
7.3
CommandExecutor
职责：
做 capability 检查
执行
prompt | local | local-jsx
统一产出 outcome
处理 fallback / unsupported
7.4
ModeAdapter
必须拆出三种 adapter：
InteractiveModeAdapter
AcpModeAdapter
NonInteractiveModeAdapter
这样三种模式才能共用同一套 command registry 和 executor，而不是各自硬编码。
8. UI 命令重构原则：核心命令与交互壳分离
这是 ACP 和 non-interactive 真正可用的关键。
凡是当前本质为“打开 dialog”的命令，都必须改造成：
一个 interactive shell
一组 local 子命令
第一批必须拆分的命令
/model
/permissions
/mcp
/resume
/hooks
/extensions
/agents
/approval-mode
目标形态示例
/model
/model
/model show
/model list
/model set <id>
/permissions
/permissions
/permissions show
/permissions set <mode>
/permissions allow <tool>
/permissions deny <tool>
/mcp
/mcp
/mcp list
/mcp show <server>
/mcp enable <server>
/mcp disable <server>
9. Prompt Command / Skill 统一设计
这是重构里的 P0，不是后补能力。
9.1 目标
建立统一的
Model-Invocable Prompt Command Registry
，把以下资产合并为一个模型可调用视图：
bundled skills
file commands
workflow prompt commands
plugin skills
mcp prompts / mcp skills
9.2 关键字段
必须新增：
userInvocable
modelInvocable
allowedTools
whenToUse
argSchema
或最小参数描述
contextMode: inline | fork
agent
effort
9.3 与
SkillTool
的关系
重构后不应再由
SkillTool
只消费狭义 skills。
应改成：
CommandRegistry.getModelInvocablePromptCommands()
产出统一视图
SkillTool
或未来统一 command tool 消费该视图
用户 slash command 与模型 skill invocation 共用同一套 prompt-command 资产池
这样 Qwen 才能在体验上接近 Claude 对
/review
、
/commit
、
/openspec-apply
这类能力的处理方式。
10. Help / Completion / Discoverability 重做
10.1 Completion
补全项至少要展示：
label
description
argumentHint
sourceBadge
modeBadges
aliasHit
recentlyUsedScore
排序至少考虑：
精确命中
alias 命中
最近使用
prefix 命中
fuzzy 命中
10.2 Mid-input slash command
必须补齐：
光标附近 slash token 检测
ghost text 提示
Tab 完成
有效命令 token 高亮
第一阶段先对齐输入体验；是否引入更强的“内嵌命令执行语义”可在后续迭代。
10.3 Help
Help 不再是平铺列表，而是完整命令目录。
至少分组为：
Built-in Commands
Bundled Skills
Skill Dir Commands
Workflow Commands
Plugin Commands
Plugin Skills
Dynamic Skills
Builtin Plugin Skills
MCP Commands / MCP Skills
每条命令至少展示：
名称
参数提示
描述
来源
支持模式
是否模型可调用
子命令摘要
11. ACP / Non-Interactive 重构
11.1 彻底废弃白名单思路
旧方案：
built-in allowlist
FILE / SKILL 特判
其它结果类型 unsupported
新方案：
每个命令自己声明 capability
registry 负责过滤
adapter 负责执行和 fallback
11.2 outcome 支持目标
interactive
submit_prompt
message
stream_messages
tool
dialog
load_history
confirm_action
confirm_shell_commands
acp
submit_prompt
message
stream_messages
tool
confirm_action
confirm_shell_commands
dialog fallback
non_interactive
submit_prompt
message
stream_messages
tool
confirm_action
confirm_shell_commands
dialog fallback / structured failure
11.3 ACP available commands 输出
必须至少包含：
name
description
argumentHint
source
examples
supportedModes
interactiveOnly
subcommands
modelInvocable
12. 文档、帮助、补全共用同一份元数据
重构后以下内容必须由同一个 registry 视图导出：
Help
Completion
ACP available commands
文档导出
这是为了解决当前“实现、帮助、文档三套命令面不一致”的问题。
13. 实施分期
Phase 1：底座重建
交付：
新
CommandDescriptor
完整来源 schema
capability 模型
userInvocable / modelInvocable
CommandRegistry
CommandResolver
CommandExecutor
三种
ModeAdapter
getModelInvocablePromptCommands()
Phase 2：核心命令迁移
交付：
/model
/permissions
/mcp
/resume
/hooks
/extensions
/agents
/approval-mode
这些命令都必须完成“interactive shell + local 子命令”重构。
Phase 3：模型能力打通
交付：
SkillTool
接入统一 registry 视图
file command / bundled skill / mcp prompt / plugin skill 进入统一 model-invocable 集合
prompt command 与 skill 资产彻底统一
Phase 4：体验层对齐 Claude
交付：
recently used 排序
source badge
argument hint
mode badge
完整 help 目录
mid-input slash command 体验
文档自动导出或校验
14. 验收标准
完成后至少满足：
帮助、补全、ACP、文档都能表达完整来源模型
除纯 UI 壳命令外，大多数 built-in command 可在 ACP / non-interactive 使用
prompt command 与模型 skill 调用使用同一套资产池
命令体验在帮助、补全、来源表达、参数提示、mid-input 体验上达到 Claude Code 95% 水平
不再依赖 built-in allowlist 维持 ACP / non-interactive 命令能力
15. 最终判断
这次重构的本质不是“给现有 SlashCommand 多加几个字段”，而是：
用 Qwen 的内部架构风格，交付一个在外部体验上 95% 对齐 Claude Code 的 command 平台
如果必须二选一：
内部实现更像 Claude
外部体验更像 Claude
本方案明确选择后者。
Last updated on
May 18, 2026
Session Title Design
Phase 1 技术设计文档：基础设施重建</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/compare/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Qwen Code Command 模块重构方案
Qwen Code Command 模块重构方案
1. 目标定义
本方案以以下原则为唯一前提：
代码架构可以不照搬 Claude Code
但命令系统的核心功能、使用体验、交互体验必须 95% 对齐 Claude Code
这里的“对齐”指用户可直接感知的能力，包括：
命令来源覆盖
命令帮助与发现性
命令补全与 mid-input slash command 体验
ACP / non-interactive 可用性
prompt command / skill 的模型调用能力
本次重构不是补几个字段，也不是把现有
SlashCommand
小修小补，而是把 command 模块从“interactive UI 附属能力”升级为“跨 interactive / ACP / non-interactive / model 的统一命令平台”。
2. 重写后的结论
Qwen 现有 command 系统的问题，不是完全没有能力，而是：
只在 interactive 主路径上较完整
类型模型太薄，无法承载 ...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Qwen Code Command 模块重构方案
Qwen Code Command 模块重构方案
1. 目标定义
本方案以以下原则为唯一前提：
代码架构可以不照搬 Claude Code
但命令系统的核心功能、使用体验、交互体验必须 95% 对齐 Claude Code
这里的“对齐”指用户可直接感知的能力，包括：
命令来源覆盖
命令帮助与发现性
命令补全与 mid-input slash command 体验
ACP / non-interactive 可用性
prompt command / skill 的模型调用能力
本次重构不是补几个字段，也不是把现有
SlashCommand
小修小补，而是把 command 模块从“interactive UI 附属能力”升级为“跨 interactive / ACP / non-interactive / model 的统一命令平台”。
2. 重写后的结论
Qwen 现有 command 系统的问题，不是完全没有能力，而是：
只在 interactive 主路径上较完整
类型模型太薄，无法承载 Claude 级别的产品面
ACP / non-interactive 依赖白名单，扩展性极差
command 来源虽然存在，但没有形成对用户可见的统一心智
prompt command 与模型 skill 暴露体系割裂
因此新的方案必须同时解决四件事：
补齐 Claude Code 的能力面
保留 Qwen 统一 outcome 模型的工程优势
建立统一 registry / resolver / executor / adapter 架构
让帮助、补全、ACP available commands、文档共用同一套元数据
3. 重构原则
3.1 功能对齐优先于实现对齐
允许不同：
内部类名
模块拆分方式
执行器实现
effect / outcome 结构
不允许不同：
命令来源覆盖明显缩水
命令帮助和补全体验明显缩水
ACP / non-interactive 可用性明显缩水
prompt command 与模型能力融合明显缩水
如果出现取舍，优先级应为：
用户体验对齐
命令能力覆盖对齐
模式一致性对齐
内部实现简洁
3.2 保留 Qwen 的统一 outcome 模型
不建议机械复制 Claude 的执行实现。
Qwen 当前统一结果模型仍然值得保留，因为它天然适合：
UI 接管
审批/确认
tool 调度
prompt 提交
跨模式适配
但它必须被升级为能够承载 Claude 级别的 command 能力，而不是继续作为简化版 UI 命令框架存在。
3.3 类型、来源、模式、可见性必须彻底解耦
新的 command 模型至少要把以下维度拆开：
类型
：命令怎么执行
来源
：命令从哪里来
模式能力
：在哪些运行环境可用
可见性
：对用户可见还是对模型可见
4. 需要对齐的 Claude Code 能力面
4.1 命令类型
Qwen 需要显式支持三类命令：
prompt
local
local-jsx
4.2 命令来源
Qwen 的 command schema 从第一阶段开始就必须覆盖以下来源：
built-in commands
bundled skills
skill dir commands
workflow commands
plugin commands
plugin skills
dynamic skills
mcp prompts
mcp skills
这里不能再退回到“先只支持当前已有那几类”。
4.3 命令元数据
至少补齐以下字段：
argumentHint
whenToUse
examples
sourceLabel
userFacingName
alias
immediate
isSensitive
userInvocable
modelInvocable
supportedModes
requiresUi
4.4 体验能力
至少补齐以下体验：
alias 命中补全
source badge
参数提示
recently used 排序
mid-input slash command 检测与补全
命令目录式 Help
ACP available commands 的完整表达
5. 新 command 模型
5.1 核心结构
建议引入统一
CommandDescriptor
，作为所有命令的注册格式。
它至少包含四部分：
identity
metadata
capabilities
handler
identity
id
name
altNames
canonicalPath
metadata
description
argumentHint
whenToUse
examples
group
source
sourceLabel
userFacingName
hidden
capabilities
type
:
prompt | local | local-jsx
supportedModes
:
interactive | acp | non_interactive
requiresUi
supportsDialog
supportsStreaming
supportsToolInvocation
supportsConfirmation
remoteSafe
readOnly
immediate
isSensitive
userInvocable
modelInvocable
handler
resolveArgs()
execute()
completion()
fallback()
5.2 三种命令类型的职责
prompt
用于：
skills
file commands
workflow prompt commands
plugin skills
mcp prompt / skill
特点：
产生 prompt / skill 资产
默认支持 interactive / ACP / non-interactive
可以被用户调用，也可以被模型调用
local
用于：
查询类命令
配置类命令
headless 可执行的状态类命令
大多数 built-in commands 的核心执行入口
特点：
不依赖 UI
应成为 ACP / non-interactive 的主承载类型
local-jsx
用于：
picker
面板
wizard
interactive UI shell
特点：
只处理 interactive UI
不能再作为唯一执行入口
必须提供 fallback 或对应 local 子命令
6. 命令来源模型
6.1 外部来源模型
这是给用户看的来源模型，必须和 Claude Code 的心智尽量一致：
builtin-command
bundled-skill
skill-dir-command
workflow-command
plugin-command
plugin-skill
dynamic-skill
builtin-plugin-skill
mcp-prompt
mcp-skill
这组字段将直接用于：
Help 分组
Completion source badge
ACP available commands
文档导出
6.2 内部归一化模型
为了不被外部命名绑死，内部再补一层实现字段：
providerType
artifactType
activationMode
builtinProvided
originPath
namespace
这样可以做到：
外部体验按 Claude 对齐
内部实现仍保持 Qwen 可维护性
6.3 冲突策略
统一按稳定
id
管理，展示名和输入名分离：
id
：稳定唯一标识
name
：输入主名
userFacingName
：帮助/补全展示名
冲突优先级建议：
built-in
bundled / skill-dir / workflow
plugin / builtin-plugin
dynamic
mcp 独立 namespace
7. 统一执行架构
7.1
CommandRegistry
职责：
聚合所有 loader/provider
建立多维索引
输出帮助、补全、ACP、文档视图
提供用户可见命令和模型可见命令的独立视图
必须支持的 provider：
BuiltinCommandLoader
BundledSkillLoader
FileCommandLoader
McpPromptLoader
WorkflowCommandLoader
PluginCommandLoader
PluginSkillLoader
DynamicSkillProvider
BuiltinPluginSkillLoader
即便部分 provider 首期未完全落地，schema 和 API 也必须先支持。
7.2
CommandResolver
职责：
解析 slash command
解析 alias
解析 subcommand path
识别 mid-input slash token
输出 canonical resolved command
7.3
CommandExecutor
职责：
做 capability 检查
执行
prompt | local | local-jsx
统一产出 outcome
处理 fallback / unsupported
7.4
ModeAdapter
必须拆出三种 adapter：
InteractiveModeAdapter
AcpModeAdapter
NonInteractiveModeAdapter
这样三种模式才能共用同一套 command registry 和 executor，而不是各自硬编码。
8. UI 命令重构原则：核心命令与交互壳分离
这是 ACP 和 non-interactive 真正可用的关键。
凡是当前本质为“打开 dialog”的命令，都必须改造成：
一个 interactive shell
一组 local 子命令
第一批必须拆分的命令
/model
/permissions
/mcp
/resume
/hooks
/extensions
/agents
/approval-mode
目标形态示例
/model
/model
/model show
/model list
/model set <id>
/permissions
/permissions
/permissions show
/permissions set <mode>
/permissions allow <tool>
/permissions deny <tool>
/mcp
/mcp
/mcp list
/mcp show <server>
/mcp enable <server>
/mcp disable <server>
9. Prompt Command / Skill 统一设计
这是重构里的 P0，不是后补能力。
9.1 目标
建立统一的
Model-Invocable Prompt Command Registry
，把以下资产合并为一个模型可调用视图：
bundled skills
file commands
workflow prompt commands
plugin skills
mcp prompts / mcp skills
9.2 关键字段
必须新增：
userInvocable
modelInvocable
allowedTools
whenToUse
argSchema
或最小参数描述
contextMode: inline | fork
agent
effort
9.3 与
SkillTool
的关系
重构后不应再由
SkillTool
只消费狭义 skills。
应改成：
CommandRegistry.getModelInvocablePromptCommands()
产出统一视图
SkillTool
或未来统一 command tool 消费该视图
用户 slash command 与模型 skill invocation 共用同一套 prompt-command 资产池
这样 Qwen 才能在体验上接近 Claude 对
/review
、
/commit
、
/openspec-apply
这类能力的处理方式。
10. Help / Completion / Discoverability 重做
10.1 Completion
补全项至少要展示：
label
description
argumentHint
sourceBadge
modeBadges
aliasHit
recentlyUsedScore
排序至少考虑：
精确命中
alias 命中
最近使用
prefix 命中
fuzzy 命中
10.2 Mid-input slash command
必须补齐：
光标附近 slash token 检测
ghost text 提示
Tab 完成
有效命令 token 高亮
第一阶段先对齐输入体验；是否引入更强的“内嵌命令执行语义”可在后续迭代。
10.3 Help
Help 不再是平铺列表，而是完整命令目录。
至少分组为：
Built-in Commands
Bundled Skills
Skill Dir Commands
Workflow Commands
Plugin Commands
Plugin Skills
Dynamic Skills
Builtin Plugin Skills
MCP Commands / MCP Skills
每条命令至少展示：
名称
参数提示
描述
来源
支持模式
是否模型可调用
子命令摘要
11. ACP / Non-Interactive 重构
11.1 彻底废弃白名单思路
旧方案：
built-in allowlist
FILE / SKILL 特判
其它结果类型 unsupported
新方案：
每个命令自己声明 capability
registry 负责过滤
adapter 负责执行和 fallback
11.2 outcome 支持目标
interactive
submit_prompt
message
stream_messages
tool
dialog
load_history
confirm_action
confirm_shell_commands
acp
submit_prompt
message
stream_messages
tool
confirm_action
confirm_shell_commands
dialog fallback
non_interactive
submit_prompt
message
stream_messages
tool
confirm_action
confirm_shell_commands
dialog fallback / structured failure
11.3 ACP available commands 输出
必须至少包含：
name
description
argumentHint
source
examples
supportedModes
interactiveOnly
subcommands
modelInvocable
12. 文档、帮助、补全共用同一份元数据
重构后以下内容必须由同一个 registry 视图导出：
Help
Completion
ACP available commands
文档导出
这是为了解决当前“实现、帮助、文档三套命令面不一致”的问题。
13. 实施分期
Phase 1：底座重建
交付：
新
CommandDescriptor
完整来源 schema
capability 模型
userInvocable / modelInvocable
CommandRegistry
CommandResolver
CommandExecutor
三种
ModeAdapter
getModelInvocablePromptCommands()
Phase 2：核心命令迁移
交付：
/model
/permissions
/mcp
/resume
/hooks
/extensions
/agents
/approval-mode
这些命令都必须完成“interactive shell + local 子命令”重构。
Phase 3：模型能力打通
交付：
SkillTool
接入统一 registry 视图
file command / bundled skill / mcp prompt / plugin skill 进入统一 model-invocable 集合
prompt command 与 skill 资产彻底统一
Phase 4：体验层对齐 Claude
交付：
recently used 排序
source badge
argument hint
mode badge
完整 help 目录
mid-input slash command 体验
文档自动导出或校验
14. 验收标准
完成后至少满足：
帮助、补全、ACP、文档都能表达完整来源模型
除纯 UI 壳命令外，大多数 built-in command 可在 ACP / non-interactive 使用
prompt command 与模型 skill 调用使用同一套资产池
命令体验在帮助、补全、来源表达、参数提示、mid-input 体验上达到 Claude Code 95% 水平
不再依赖 built-in allowlist 维持 ACP / non-interactive 命令能力
15. 最终判断
这次重构的本质不是“给现有 SlashCommand 多加几个字段”，而是：
用 Qwen 的内部架构风格，交付一个在外部体验上 95% 对齐 Claude Code 的 command 平台
如果必须二选一：
内部实现更像 Claude
外部体验更像 Claude
本方案明确选择后者。
Last updated on
May 18, 2026
Session Title Design
Phase 1 技术设计文档：基础设施重建</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/compare/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Web Fetch Tool ( web_fetch )</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-fetch/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-fetch/</guid>
  <pubDate>Thu, 21 Mar 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Web Fetch
Web Fetch Tool (
web_fetch
)
This document describes the
web_fetch
tool for Qwen Code.
Description
Use
web_fetch
to fetch content from a specified URL and process it using an AI model. The tool takes a URL and a prompt as input, fetches the URL content, and processes the content with the prompt using a small, fast model.
Arguments
web_fetch
takes three arguments:
url
(string, required): The URL to fetch content from. Must be a fully-formed valid URL starting with
...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Web Fetch
Web Fetch Tool (
web_fetch
)
This document describes the
web_fetch
tool for Qwen Code.
Description
Use
web_fetch
to fetch content from a specified URL and process it using an AI model. The tool takes a URL and a prompt as input, fetches the URL content, and processes the content with the prompt using a small, fast model.
Arguments
web_fetch
takes three arguments:
url
(string, required): The URL to fetch content from. Must be a fully-formed valid URL starting with
http://
or
https://
.
prompt
(string, required): The prompt describing what information you want to extract from the page content.
format
(string, optional): Controls only the
Accept
header sent to the server, indicating your content preference.
All fetched content is normalized to plain text for LLM processing
, regardless of the format specified. Defaults to
"auto"
if not specified.
"auto"
(default): Prefers markdown via content negotiation (
Accept: text/markdown, text/html
), accepts HTML as fallback.
Recommended for most use cases
as it can reduce token usage by up to 80% for servers that support markdown.
"markdown"
: Sends
Accept: text/markdown
. Use when you explicitly need markdown content.
"html"
: Sends
Accept: text/html
. Use when the server requires HTML in the Accept header. Content is still converted to plain text for LLM processing.
"text"
: Sends
Accept: text/plain
. Use when you specifically need plain text content.
How to use
web_fetch
with Qwen Code
To use
web_fetch
with Qwen Code, provide a URL and a prompt describing what you want to extract from that URL. The tool will ask for confirmation before fetching the URL. Once confirmed, the tool will fetch the content directly and process it using an AI model.
The tool automatically:
Converts HTML to text when necessary
Handles GitHub blob URLs (converting them to raw URLs)
Upgrades HTTP URLs to HTTPS for security
Supports content negotiation for markdown (reduces token usage significantly)
Usage:
web_fetch(url="https://example.com", prompt="Summarize the main points of this article")
With format specification:
web_fetch(url="https://example.com", prompt="Get the raw content", format="markdown")
web_fetch
examples
Summarize a single article:
web_fetch(url="https://example.com/news/latest", prompt="Can you summarize the main points of this article?")
Extract specific information:
web_fetch(url="https://arxiv.org/abs/2401.0001", prompt="What are the key findings and methodology described in this paper?")
Analyze GitHub documentation:
web_fetch(url="https://github.com/QwenLM/Qwen/blob/main/README.md", prompt="What are the installation steps and main features?")
Get markdown content (for servers supporting Markdown for Agents):
web_fetch(url="https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/", prompt="Extract the key information", format="markdown")
Important notes
Single URL processing:
web_fetch
processes one URL at a time. To analyze multiple URLs, make separate calls to the tool.
URL format:
The tool automatically upgrades HTTP URLs to HTTPS and converts GitHub blob URLs to raw format for better content access.
Content negotiation:
The tool supports “Markdown for Agents” content negotiation. When using
format="auto"
(default), it sends
Accept: text/markdown, text/html
headers, allowing servers that support markdown to return it directly instead of HTML. This can reduce token usage by up to 80%.
Content processing:
The tool fetches content directly and processes it using an AI model. When the server returns HTML, it converts it to readable text format. When the server returns markdown or plain text, it uses the content as-is.
Output quality:
The quality of the output will depend on the clarity of the instructions in the prompt.
MCP tools:
If an MCP-provided web fetch tool is available (starting with “mcp__”), prefer using that tool as it may have fewer restrictions.
Markdown for Agents Support
Qwen Code’s
web_fetch
tool implements support for
Cloudflare’s Markdown for Agents
specification. This feature allows websites to serve markdown content directly to AI agents, significantly reducing token usage compared to parsing HTML.
How it works
The
format
parameter controls
only
the
Accept
header sent to the server (it does not affect the output format):
format="auto"
: sends
Accept: text/markdown, text/html
format="markdown"
: sends
Accept: text/markdown
format="html"
: sends
Accept: text/html
format="text"
: sends
Accept: text/plain
If the server supports markdown, it returns
Content-Type: text/markdown
The tool uses markdown or plain text content directly without conversion
If the server returns HTML, it converts to readable text format for LLM processing
All content is normalized to text before being processed by the AI model
Benefits
Token efficiency:
Markdown content typically uses 80% fewer tokens than equivalent HTML
Better structure:
Markdown preserves semantic structure (headings, lists, etc.)
Backward compatible:
Works with all websites, enhanced experience for supporting servers
Example servers supporting markdown
Cloudflare Developer Documentation
Cloudflare Blog
Any website using Cloudflare’s “Markdown for Agents” feature
Last updated on
May 18, 2026
Exit Plan Mode
Web Search</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-fetch/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Web Fetch
Web Fetch Tool (
web_fetch
)
This document describes the
web_fetch
tool for Qwen Code.
Description
Use
web_fetch
to fetch content from a specified URL and process it using an AI model. The tool takes a URL and a prompt as input, fetches the URL content, and processes the content with the prompt using a small, fast model.
Arguments
web_fetch
takes three arguments:
url
(string, required): The URL to fetch content from. Must be a fully-formed valid URL starting with
...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Web Fetch
Web Fetch Tool (
web_fetch
)
This document describes the
web_fetch
tool for Qwen Code.
Description
Use
web_fetch
to fetch content from a specified URL and process it using an AI model. The tool takes a URL and a prompt as input, fetches the URL content, and processes the content with the prompt using a small, fast model.
Arguments
web_fetch
takes three arguments:
url
(string, required): The URL to fetch content from. Must be a fully-formed valid URL starting with
http://
or
https://
.
prompt
(string, required): The prompt describing what information you want to extract from the page content.
format
(string, optional): Controls only the
Accept
header sent to the server, indicating your content preference.
All fetched content is normalized to plain text for LLM processing
, regardless of the format specified. Defaults to
"auto"
if not specified.
"auto"
(default): Prefers markdown via content negotiation (
Accept: text/markdown, text/html
), accepts HTML as fallback.
Recommended for most use cases
as it can reduce token usage by up to 80% for servers that support markdown.
"markdown"
: Sends
Accept: text/markdown
. Use when you explicitly need markdown content.
"html"
: Sends
Accept: text/html
. Use when the server requires HTML in the Accept header. Content is still converted to plain text for LLM processing.
"text"
: Sends
Accept: text/plain
. Use when you specifically need plain text content.
How to use
web_fetch
with Qwen Code
To use
web_fetch
with Qwen Code, provide a URL and a prompt describing what you want to extract from that URL. The tool will ask for confirmation before fetching the URL. Once confirmed, the tool will fetch the content directly and process it using an AI model.
The tool automatically:
Converts HTML to text when necessary
Handles GitHub blob URLs (converting them to raw URLs)
Upgrades HTTP URLs to HTTPS for security
Supports content negotiation for markdown (reduces token usage significantly)
Usage:
web_fetch(url="https://example.com", prompt="Summarize the main points of this article")
With format specification:
web_fetch(url="https://example.com", prompt="Get the raw content", format="markdown")
web_fetch
examples
Summarize a single article:
web_fetch(url="https://example.com/news/latest", prompt="Can you summarize the main points of this article?")
Extract specific information:
web_fetch(url="https://arxiv.org/abs/2401.0001", prompt="What are the key findings and methodology described in this paper?")
Analyze GitHub documentation:
web_fetch(url="https://github.com/QwenLM/Qwen/blob/main/README.md", prompt="What are the installation steps and main features?")
Get markdown content (for servers supporting Markdown for Agents):
web_fetch(url="https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/", prompt="Extract the key information", format="markdown")
Important notes
Single URL processing:
web_fetch
processes one URL at a time. To analyze multiple URLs, make separate calls to the tool.
URL format:
The tool automatically upgrades HTTP URLs to HTTPS and converts GitHub blob URLs to raw format for better content access.
Content negotiation:
The tool supports “Markdown for Agents” content negotiation. When using
format="auto"
(default), it sends
Accept: text/markdown, text/html
headers, allowing servers that support markdown to return it directly instead of HTML. This can reduce token usage by up to 80%.
Content processing:
The tool fetches content directly and processes it using an AI model. When the server returns HTML, it converts it to readable text format. When the server returns markdown or plain text, it uses the content as-is.
Output quality:
The quality of the output will depend on the clarity of the instructions in the prompt.
MCP tools:
If an MCP-provided web fetch tool is available (starting with “mcp__”), prefer using that tool as it may have fewer restrictions.
Markdown for Agents Support
Qwen Code’s
web_fetch
tool implements support for
Cloudflare’s Markdown for Agents
specification. This feature allows websites to serve markdown content directly to AI agents, significantly reducing token usage compared to parsing HTML.
How it works
The
format
parameter controls
only
the
Accept
header sent to the server (it does not affect the output format):
format="auto"
: sends
Accept: text/markdown, text/html
format="markdown"
: sends
Accept: text/markdown
format="html"
: sends
Accept: text/html
format="text"
: sends
Accept: text/plain
If the server supports markdown, it returns
Content-Type: text/markdown
The tool uses markdown or plain text content directly without conversion
If the server returns HTML, it converts to readable text format for LLM processing
All content is normalized to text before being processed by the AI model
Benefits
Token efficiency:
Markdown content typically uses 80% fewer tokens than equivalent HTML
Better structure:
Markdown preserves semantic structure (headings, lists, etc.)
Backward compatible:
Works with all websites, enhanced experience for supporting servers
Example servers supporting markdown
Cloudflare Developer Documentation
Cloudflare Blog
Any website using Cloudflare’s “Markdown for Agents” feature
Last updated on
May 18, 2026
Exit Plan Mode
Web Search</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/web-fetch/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code overview</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/overview/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/overview/</guid>
  <pubDate>Sat, 16 Mar 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Overview
Qwen Code overview
Learn about Qwen Code, Qwen’s agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Get started in 30 seconds
Install Qwen Code:
Linux / macOS
curl
-fsSL
https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh
|
bash
Windows (Run as Administrator)
powershell
-
Command
&quot;Invoke-WebRequest &apos;https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.bat&apos; -OutFi...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Overview
Qwen Code overview
Learn about Qwen Code, Qwen’s agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Get started in 30 seconds
Install Qwen Code:
Linux / macOS
curl
-fsSL
https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh
|
bash
Windows (Run as Administrator)
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. If the installation fails, please refer to
Manual Installation
in the Quickstart guide.
Start using Qwen Code:
cd
your-project
qwen
Choose your authentication method —
API Key
or
Alibaba Cloud Coding Plan
(
intl
) — and follow the prompts to configure. See the API setup guide (
Beijing
/
intl
) for step-by-step instructions. Then let’s start with understanding your codebase. Try one of these commands:
what does this project do?
You’ll be prompted to log in on first use. That’s it!
Continue with Quickstart (5 mins) →
Tip
See
troubleshooting
if you hit issues.
Note
New VS Code Extension (Beta)
: Prefer a graphical interface? Our new
VS Code extension
provides an easy-to-use native IDE experience without requiring terminal familiarity. Simply install from the marketplace and start coding with Qwen Code directly in your sidebar. Download and install the
Qwen Code Companion
now.
What Qwen Code does for you
Build features from descriptions
: Tell Qwen Code what you want to build in plain language. It will make a plan, write the code, and ensure it works.
Debug and fix issues
: Describe a bug or paste an error message. Qwen Code will analyze your codebase, identify the problem, and implement a fix.
Navigate any codebase
: Ask anything about your team’s codebase, and get a thoughtful answer back. Qwen Code maintains awareness of your entire project structure, can find up-to-date information from the web, and with
MCP
can pull from external datasources like Google Drive, Figma, and Slack.
Automate tedious tasks
: Fix fiddly lint issues, resolve merge conflicts, and write release notes. Do all this in a single command from your developer machines, or automatically in CI.
Followup suggestions
: Qwen Code predicts what you want to type next and shows it as ghost text. Press Tab to accept, or just keep typing to dismiss.
Why developers love Qwen Code
Works in your terminal
: Not another chat window. Not another IDE. Qwen Code meets you where you already work, with the tools you already love.
Takes action
: Qwen Code can directly edit files, run commands, and create commits. Need more?
MCP
lets Qwen Code read your design docs in Google Drive, update your tickets in Jira, or use
your
custom developer tooling.
Unix philosophy
: Qwen Code is composable and scriptable.
tail -f app.log | qwen -p "Slack me if you see any anomalies appear in this log stream"
works
. Your CI can run
qwen -p "If there are new text strings, translate them into French and raise a PR for @lang-fr-team to review"
.
Last updated on
May 18, 2026
QuickStart</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/overview/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Overview
Qwen Code overview
Learn about Qwen Code, Qwen’s agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Get started in 30 seconds
Install Qwen Code:
Linux / macOS
curl
-fsSL
https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh
|
bash
Windows (Run as Administrator)
powershell
-
Command
&quot;Invoke-WebRequest &apos;https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.bat&apos; -OutFi...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Overview
Qwen Code overview
Learn about Qwen Code, Qwen’s agentic coding tool that lives in your terminal and helps you turn ideas into code faster than ever before.
Get started in 30 seconds
Install Qwen Code:
Linux / macOS
curl
-fsSL
https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh
|
bash
Windows (Run as Administrator)
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. If the installation fails, please refer to
Manual Installation
in the Quickstart guide.
Start using Qwen Code:
cd
your-project
qwen
Choose your authentication method —
API Key
or
Alibaba Cloud Coding Plan
(
intl
) — and follow the prompts to configure. See the API setup guide (
Beijing
/
intl
) for step-by-step instructions. Then let’s start with understanding your codebase. Try one of these commands:
what does this project do?
You’ll be prompted to log in on first use. That’s it!
Continue with Quickstart (5 mins) →
Tip
See
troubleshooting
if you hit issues.
Note
New VS Code Extension (Beta)
: Prefer a graphical interface? Our new
VS Code extension
provides an easy-to-use native IDE experience without requiring terminal familiarity. Simply install from the marketplace and start coding with Qwen Code directly in your sidebar. Download and install the
Qwen Code Companion
now.
What Qwen Code does for you
Build features from descriptions
: Tell Qwen Code what you want to build in plain language. It will make a plan, write the code, and ensure it works.
Debug and fix issues
: Describe a bug or paste an error message. Qwen Code will analyze your codebase, identify the problem, and implement a fix.
Navigate any codebase
: Ask anything about your team’s codebase, and get a thoughtful answer back. Qwen Code maintains awareness of your entire project structure, can find up-to-date information from the web, and with
MCP
can pull from external datasources like Google Drive, Figma, and Slack.
Automate tedious tasks
: Fix fiddly lint issues, resolve merge conflicts, and write release notes. Do all this in a single command from your developer machines, or automatically in CI.
Followup suggestions
: Qwen Code predicts what you want to type next and shows it as ghost text. Press Tab to accept, or just keep typing to dismiss.
Why developers love Qwen Code
Works in your terminal
: Not another chat window. Not another IDE. Qwen Code meets you where you already work, with the tools you already love.
Takes action
: Qwen Code can directly edit files, run commands, and create commits. Need more?
MCP
lets Qwen Code read your design docs in Google Drive, update your tickets in Jira, or use
your
custom developer tooling.
Unix philosophy
: Qwen Code is composable and scriptable.
tail -f app.log | qwen -p "Slack me if you see any anomalies appear in this log stream"
works
. Your CI can run
qwen -p "If there are new text strings, translate them into French and raise a PR for @lang-fr-team to review"
.
Last updated on
May 18, 2026
QuickStart</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/overview/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Trusted Folders</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/configuration/trusted-folders/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/configuration/trusted-folders/</guid>
  <pubDate>Thu, 14 Mar 2024 00:00:00 +0000</pubDate>
  <category>Configuration</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Trusted Folders
Trusted Folders
The Trusted Folders feature is a security setting that gives you control over which projects can use the full capabilities of the Qwen Code. It prevents potentially malicious code from running by asking you to approve a folder before the CLI loads any project-specific configurations from it.
Enabling the Feature
The Trusted Folders feature is
disabled by default
. To use it, you must first enable it in your settings.
Add the following to y...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Trusted Folders
Trusted Folders
The Trusted Folders feature is a security setting that gives you control over which projects can use the full capabilities of the Qwen Code. It prevents potentially malicious code from running by asking you to approve a folder before the CLI loads any project-specific configurations from it.
Enabling the Feature
The Trusted Folders feature is
disabled by default
. To use it, you must first enable it in your settings.
Add the following to your user
settings.json
file:
{
"security"
: {
"folderTrust"
: {
"enabled"
:
true
}
}
}
How It Works: The Trust Dialog
Once the feature is enabled, the first time you run the Qwen Code from a folder, a dialog will automatically appear, prompting you to make a choice:
Trust folder
: Grants full trust to the current folder (e.g.
my-project
).
Trust parent folder
: Grants trust to the parent directory (e.g.
safe-projects
), which automatically trusts all of its subdirectories as well. This is useful if you keep all your safe projects in one place.
Don’t trust
: Marks the folder as untrusted. The CLI will operate in a restricted “safe mode.”
Your choice is saved in a central file (
~/.qwen/trustedFolders.json
), so you will only be asked once per folder.
Why Trust Matters: The Impact of an Untrusted Workspace
When a folder is
untrusted
, the Qwen Code runs in a restricted “safe mode” to protect you. In this mode, the following features are disabled:
Workspace Settings are Ignored
: The CLI will
not
load the
.qwen/settings.json
file from the project. This prevents the loading of custom tools and other potentially dangerous configurations.
Environment Variables are Ignored
: The CLI will
not
load any
.env
files from the project.
Extension Management is Restricted
: You
cannot install, update, or uninstall
extensions.
Tool Auto-Acceptance is Disabled
: You will always be prompted before any tool is run, even if you have auto-acceptance enabled globally.
Automatic Memory Loading is Disabled
: The CLI will not automatically load files into context from directories specified in local settings.
Granting trust to a folder unlocks the full functionality of the Qwen Code for that workspace.
Managing Your Trust Settings
If you need to change a decision or see all your settings, you have a couple of options:
Change the Current Folder’s Trust
: Run the
/permissions
command from within the CLI. This will bring up the same interactive dialog, allowing you to change the trust level for the current folder.
View All Trust Rules
: To see a complete list of all your trusted and untrusted folder rules, you can inspect the contents of the
~/.qwen/trustedFolders.json
file in your home directory.
The Trust Check Process (Advanced)
For advanced users, it’s helpful to know the exact order of operations for how trust is determined:
IDE Trust Signal
: If you are using the
IDE Integration
, the CLI first asks the IDE if the workspace is trusted. The IDE’s response takes highest priority.
Local Trust File
: If the IDE is not connected, the CLI checks the central
~/.qwen/trustedFolders.json
file.
Last updated on
May 18, 2026
Ignoring Files
Themes</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/trusted-folders/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Trusted Folders
Trusted Folders
The Trusted Folders feature is a security setting that gives you control over which projects can use the full capabilities of the Qwen Code. It prevents potentially malicious code from running by asking you to approve a folder before the CLI loads any project-specific configurations from it.
Enabling the Feature
The Trusted Folders feature is
disabled by default
. To use it, you must first enable it in your settings.
Add the following to y...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Trusted Folders
Trusted Folders
The Trusted Folders feature is a security setting that gives you control over which projects can use the full capabilities of the Qwen Code. It prevents potentially malicious code from running by asking you to approve a folder before the CLI loads any project-specific configurations from it.
Enabling the Feature
The Trusted Folders feature is
disabled by default
. To use it, you must first enable it in your settings.
Add the following to your user
settings.json
file:
{
"security"
: {
"folderTrust"
: {
"enabled"
:
true
}
}
}
How It Works: The Trust Dialog
Once the feature is enabled, the first time you run the Qwen Code from a folder, a dialog will automatically appear, prompting you to make a choice:
Trust folder
: Grants full trust to the current folder (e.g.
my-project
).
Trust parent folder
: Grants trust to the parent directory (e.g.
safe-projects
), which automatically trusts all of its subdirectories as well. This is useful if you keep all your safe projects in one place.
Don’t trust
: Marks the folder as untrusted. The CLI will operate in a restricted “safe mode.”
Your choice is saved in a central file (
~/.qwen/trustedFolders.json
), so you will only be asked once per folder.
Why Trust Matters: The Impact of an Untrusted Workspace
When a folder is
untrusted
, the Qwen Code runs in a restricted “safe mode” to protect you. In this mode, the following features are disabled:
Workspace Settings are Ignored
: The CLI will
not
load the
.qwen/settings.json
file from the project. This prevents the loading of custom tools and other potentially dangerous configurations.
Environment Variables are Ignored
: The CLI will
not
load any
.env
files from the project.
Extension Management is Restricted
: You
cannot install, update, or uninstall
extensions.
Tool Auto-Acceptance is Disabled
: You will always be prompted before any tool is run, even if you have auto-acceptance enabled globally.
Automatic Memory Loading is Disabled
: The CLI will not automatically load files into context from directories specified in local settings.
Granting trust to a folder unlocks the full functionality of the Qwen Code for that workspace.
Managing Your Trust Settings
If you need to change a decision or see all your settings, you have a couple of options:
Change the Current Folder’s Trust
: Run the
/permissions
command from within the CLI. This will bring up the same interactive dialog, allowing you to change the trust level for the current folder.
View All Trust Rules
: To see a complete list of all your trusted and untrusted folder rules, you can inspect the contents of the
~/.qwen/trustedFolders.json
file in your home directory.
The Trust Check Process (Advanced)
For advanced users, it’s helpful to know the exact order of operations for how trust is determined:
IDE Trust Signal
: If you are using the
IDE Integration
, the CLI first asks the IDE if the workspace is trusted. The IDE’s response takes highest priority.
Local Trust File
: If the IDE is not connected, the CLI checks the central
~/.qwen/trustedFolders.json
file.
Last updated on
May 18, 2026
Ignoring Files
Themes</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/trusted-folders/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Architecture Overview</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/architecture/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/architecture/</guid>
  <pubDate>Thu, 14 Mar 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Architecture
Qwen Code Architecture Overview
This document provides a high-level overview of Qwen Code’s architecture.
Core Components
Qwen Code is primarily composed of two main packages, along with a suite of tools that can be used by the system in the course of handling command-line input:
1. CLI Package (
packages/cli
)
Purpose:
This contains the user-facing portion of Qwen Code, such as handling the initial user input, presenting the final output, and managing the overall us...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Architecture
Qwen Code Architecture Overview
This document provides a high-level overview of Qwen Code’s architecture.
Core Components
Qwen Code is primarily composed of two main packages, along with a suite of tools that can be used by the system in the course of handling command-line input:
1. CLI Package (
packages/cli
)
Purpose:
This contains the user-facing portion of Qwen Code, such as handling the initial user input, presenting the final output, and managing the overall user experience.
Key Functions:
Input Processing:
Handles user input through various methods including direct text entry, slash commands (e.g.,
/help
,
/clear
,
/model
), at commands (
@file
for including file content), and exclamation mark commands (
!command
for shell execution).
History Management:
Maintains conversation history and enables features like session resumption.
Display Rendering:
Formats and presents responses to the user in the terminal with syntax highlighting and proper formatting.
Theme and UI Customization:
Supports customizable themes and UI elements for a personalized experience.
Configuration Settings:
Manages various configuration options through JSON settings files, environment variables, and command-line arguments.
2. Core Package (
packages/core
)
Purpose:
This acts as the backend for Qwen Code. It receives requests sent from
packages/cli
, orchestrates interactions with the configured model API, and manages the execution of available tools.
Key Functions:
API Client:
Communicates with the Qwen model API to send prompts and receive responses.
Prompt Construction:
Builds appropriate prompts for the model, incorporating conversation history and available tool definitions.
Tool Registration and Execution:
Manages the registration of available tools and executes them based on model requests.
State Management:
Maintains conversation and session state information.
Server-side Configuration:
Handles server-side configuration and settings.
3. Tools (
packages/core/src/tools/
)
Purpose:
These are individual modules that extend the capabilities of the Qwen model, allowing it to interact with the local environment (e.g., file system, shell commands, web fetching).
Interaction:
packages/core
invokes these tools based on requests from the Qwen model.
Common Tools Include:
File Operations:
Reading, writing, and editing files
Shell Commands:
Executing system commands with user approval for potentially dangerous operations
Search Tools:
Finding files and searching content within the project
Web Tools:
Fetching content from the web
MCP Integration:
Connecting to Model Context Protocol servers for extended capabilities
Interaction Flow
A typical interaction with Qwen Code follows this flow:
User Input:
The user types a prompt or command into the terminal, which is managed by
packages/cli
.
Request to Core:
packages/cli
sends the user’s input to
packages/core
.
Request Processing:
The core package:
Constructs an appropriate prompt for the configured model API, possibly including conversation history and available tool definitions.
Sends the prompt to the model API.
Model API Response:
The model API processes the prompt and returns a response. This response might be a direct answer or a request to use one of the available tools.
Tool Execution (if applicable):
When the model API requests a tool, the core package prepares to execute it.
If the requested tool can modify the file system or execute shell commands, the user is first given details of the tool and its arguments, and the user must approve the execution.
Read-only operations, such as reading files, might not require explicit user confirmation to proceed.
Once confirmed, or if confirmation is not required, the core package executes the relevant action within the relevant tool, and the result is sent back to the model API by the core package.
The model API processes the tool result and generates a final response.
Response to CLI:
The core package sends the final response back to the CLI package.
Display to User:
The CLI package formats and displays the response to the user in the terminal.
Configuration Options
Qwen Code offers multiple ways to configure its behavior:
Configuration Layers (in order of precedence)
Command-line arguments
Environment variables
Project settings file (
.qwen/settings.json
)
User settings file (
~/.qwen/settings.json
)
System settings files
Default values
Key Configuration Categories
General Settings:
vim mode, preferred editor, auto-update preferences
UI Settings:
Theme customization, banner visibility, footer display
Model Settings:
Model selection, session turn limits, compression settings
Context Settings:
Context file names, directory inclusion, file filtering
Tool Settings:
Approval modes, sandboxing, tool restrictions
Privacy Settings:
Usage statistics collection
Advanced Settings:
Debug options, custom bug reporting commands
Key Design Principles
Modularity:
Separating the CLI (frontend) from the Core (backend) allows for independent development and potential future extensions (e.g., different frontends for the same backend).
Extensibility:
The tool system is designed to be extensible, allowing new capabilities to be added through custom tools or MCP server integration.
User Experience:
The CLI focuses on providing a rich and interactive terminal experience with features like syntax highlighting, customizable themes, and intuitive command structures.
Security:
Implements approval mechanisms for potentially dangerous operations and sandboxing options to protect the user’s system.
Flexibility:
Supports multiple configuration methods and can adapt to different workflows and environments.
Last updated on
May 18, 2026
Roadmap</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/architecture/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Architecture
Qwen Code Architecture Overview
This document provides a high-level overview of Qwen Code’s architecture.
Core Components
Qwen Code is primarily composed of two main packages, along with a suite of tools that can be used by the system in the course of handling command-line input:
1. CLI Package (
packages/cli
)
Purpose:
This contains the user-facing portion of Qwen Code, such as handling the initial user input, presenting the final output, and managing the overall us...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Architecture
Qwen Code Architecture Overview
This document provides a high-level overview of Qwen Code’s architecture.
Core Components
Qwen Code is primarily composed of two main packages, along with a suite of tools that can be used by the system in the course of handling command-line input:
1. CLI Package (
packages/cli
)
Purpose:
This contains the user-facing portion of Qwen Code, such as handling the initial user input, presenting the final output, and managing the overall user experience.
Key Functions:
Input Processing:
Handles user input through various methods including direct text entry, slash commands (e.g.,
/help
,
/clear
,
/model
), at commands (
@file
for including file content), and exclamation mark commands (
!command
for shell execution).
History Management:
Maintains conversation history and enables features like session resumption.
Display Rendering:
Formats and presents responses to the user in the terminal with syntax highlighting and proper formatting.
Theme and UI Customization:
Supports customizable themes and UI elements for a personalized experience.
Configuration Settings:
Manages various configuration options through JSON settings files, environment variables, and command-line arguments.
2. Core Package (
packages/core
)
Purpose:
This acts as the backend for Qwen Code. It receives requests sent from
packages/cli
, orchestrates interactions with the configured model API, and manages the execution of available tools.
Key Functions:
API Client:
Communicates with the Qwen model API to send prompts and receive responses.
Prompt Construction:
Builds appropriate prompts for the model, incorporating conversation history and available tool definitions.
Tool Registration and Execution:
Manages the registration of available tools and executes them based on model requests.
State Management:
Maintains conversation and session state information.
Server-side Configuration:
Handles server-side configuration and settings.
3. Tools (
packages/core/src/tools/
)
Purpose:
These are individual modules that extend the capabilities of the Qwen model, allowing it to interact with the local environment (e.g., file system, shell commands, web fetching).
Interaction:
packages/core
invokes these tools based on requests from the Qwen model.
Common Tools Include:
File Operations:
Reading, writing, and editing files
Shell Commands:
Executing system commands with user approval for potentially dangerous operations
Search Tools:
Finding files and searching content within the project
Web Tools:
Fetching content from the web
MCP Integration:
Connecting to Model Context Protocol servers for extended capabilities
Interaction Flow
A typical interaction with Qwen Code follows this flow:
User Input:
The user types a prompt or command into the terminal, which is managed by
packages/cli
.
Request to Core:
packages/cli
sends the user’s input to
packages/core
.
Request Processing:
The core package:
Constructs an appropriate prompt for the configured model API, possibly including conversation history and available tool definitions.
Sends the prompt to the model API.
Model API Response:
The model API processes the prompt and returns a response. This response might be a direct answer or a request to use one of the available tools.
Tool Execution (if applicable):
When the model API requests a tool, the core package prepares to execute it.
If the requested tool can modify the file system or execute shell commands, the user is first given details of the tool and its arguments, and the user must approve the execution.
Read-only operations, such as reading files, might not require explicit user confirmation to proceed.
Once confirmed, or if confirmation is not required, the core package executes the relevant action within the relevant tool, and the result is sent back to the model API by the core package.
The model API processes the tool result and generates a final response.
Response to CLI:
The core package sends the final response back to the CLI package.
Display to User:
The CLI package formats and displays the response to the user in the terminal.
Configuration Options
Qwen Code offers multiple ways to configure its behavior:
Configuration Layers (in order of precedence)
Command-line arguments
Environment variables
Project settings file (
.qwen/settings.json
)
User settings file (
~/.qwen/settings.json
)
System settings files
Default values
Key Configuration Categories
General Settings:
vim mode, preferred editor, auto-update preferences
UI Settings:
Theme customization, banner visibility, footer display
Model Settings:
Model selection, session turn limits, compression settings
Context Settings:
Context file names, directory inclusion, file filtering
Tool Settings:
Approval modes, sandboxing, tool restrictions
Privacy Settings:
Usage statistics collection
Advanced Settings:
Debug options, custom bug reporting commands
Key Design Principles
Modularity:
Separating the CLI (frontend) from the Core (backend) allows for independent development and potential future extensions (e.g., different frontends for the same backend).
Extensibility:
The tool system is designed to be extensible, allowing new capabilities to be added through custom tools or MCP server integration.
User Experience:
The CLI focuses on providing a rich and interactive terminal experience with features like syntax highlighting, customizable themes, and intuitive command structures.
Security:
Implements approval mechanisms for potentially dangerous operations and sandboxing options to protect the user’s system.
Flexibility:
Supports multiple configuration methods and can adapt to different workflows and environments.
Last updated on
May 18, 2026
Roadmap</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/architecture/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Sandbox | Qwen Code Docs</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/sandbox/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/sandbox/</guid>
  <pubDate>Wed, 13 Mar 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Sandboxing
Customizing the sandbox environment (Docker/Podman)
Currently, the project does not support the use of the BUILD_SANDBOX function after installation through the npm package
To build a custom sandbox, you need to access the build scripts (scripts/build_sandbox.js) in the source code repository.
These build scripts are not included in the packages released by npm.
The code contains hard-coded path checks that explicitly reject build requests from non-source code en...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Sandboxing
Customizing the sandbox environment (Docker/Podman)
Currently, the project does not support the use of the BUILD_SANDBOX function after installation through the npm package
To build a custom sandbox, you need to access the build scripts (scripts/build_sandbox.js) in the source code repository.
These build scripts are not included in the packages released by npm.
The code contains hard-coded path checks that explicitly reject build requests from non-source code environments.
If you need extra tools inside the container (e.g.,
git
,
python
,
rg
), create a custom Dockerfile, The specific operation is as follows
1、Clone qwen code project first,
https://github.com/QwenLM/qwen-code.git
2、Make sure you perform the following operation in the source code repository directory
# 1. First, install the dependencies of the project
npm
install
# 2. Build the Qwen Code project
npm
run
build
# 3. Verify that the dist directory has been generated
ls
-la
packages/cli/dist/
# 4. Create a global link in the CLI package directory
cd
packages/cli
npm
link
# 5. Verification link (it should now point to the source code)
which
qwen
# Expected output: /xxx/xxx/.nvm/versions/node/v24.11.1/bin/qwen
# Or similar paths, but it should be a symbolic link
# 6. For details of the symbolic link, you can see the specific source code path
ls
-la
$(
dirname
$(
which
qwen
))
/../lib/node_modules/@qwen-code/qwen-code
# It should show that this is a symbolic link pointing to your source code directory
# 7.Test the version of qwen
qwen
-v
# npm link will overwrite the global qwen. To avoid being unable to distinguish the same version number, you can uninstall the global CLI first
3、Create your sandbox Dockerfile under the root directory of your own project
Path:
.qwen/sandbox.Dockerfile
Official mirror image address:
https://github.com/QwenLM/qwen-code/pkgs/container/qwen-code
# Based on the official Qwen sandbox image (It is recommended to explicitly specify the version)
FROM
ghcr.io/qwenlm/qwen-code:sha-570ec43
# Add your extra tools here
RUN
apt-get
update
&&
apt-get
install
-y
\
git
\
python3
\
ripgrep
4、Create the first sandbox image under the root directory of your project
QWEN_SANDBOX
=
docker
BUILD_SANDBOX
=
1
qwen
-s
# Observe whether the sandbox version of the tool you launched is consistent with the version of your custom image. If they are consistent, the startup will be successful
This builds a project-specific image based on the default sandbox image.
Remove npm link
If you want to restore the official CLI of qwen, please remove the npm link
# Method 1: Unlink globally
npm
unlink
-g
@qwen-code/qwen-code
# Method 2: Remove it in the packages/cli directory
cd
packages/cli
npm
unlink
# Verification has been lifted
which
qwen
# It should display "qwen not found"
# Reinstall the global version if necessary
npm
install
-g
@qwen-code/qwen-code
# Verification Recovery
which
qwen
qwen
--version
Last updated on
May 18, 2026
MCP Servers
Memory Tool (save_memory)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/sandbox/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Sandboxing
Customizing the sandbox environment (Docker/Podman)
Currently, the project does not support the use of the BUILD_SANDBOX function after installation through the npm package
To build a custom sandbox, you need to access the build scripts (scripts/build_sandbox.js) in the source code repository.
These build scripts are not included in the packages released by npm.
The code contains hard-coded path checks that explicitly reject build requests from non-source code en...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Sandboxing
Customizing the sandbox environment (Docker/Podman)
Currently, the project does not support the use of the BUILD_SANDBOX function after installation through the npm package
To build a custom sandbox, you need to access the build scripts (scripts/build_sandbox.js) in the source code repository.
These build scripts are not included in the packages released by npm.
The code contains hard-coded path checks that explicitly reject build requests from non-source code environments.
If you need extra tools inside the container (e.g.,
git
,
python
,
rg
), create a custom Dockerfile, The specific operation is as follows
1、Clone qwen code project first,
https://github.com/QwenLM/qwen-code.git
2、Make sure you perform the following operation in the source code repository directory
# 1. First, install the dependencies of the project
npm
install
# 2. Build the Qwen Code project
npm
run
build
# 3. Verify that the dist directory has been generated
ls
-la
packages/cli/dist/
# 4. Create a global link in the CLI package directory
cd
packages/cli
npm
link
# 5. Verification link (it should now point to the source code)
which
qwen
# Expected output: /xxx/xxx/.nvm/versions/node/v24.11.1/bin/qwen
# Or similar paths, but it should be a symbolic link
# 6. For details of the symbolic link, you can see the specific source code path
ls
-la
$(
dirname
$(
which
qwen
))
/../lib/node_modules/@qwen-code/qwen-code
# It should show that this is a symbolic link pointing to your source code directory
# 7.Test the version of qwen
qwen
-v
# npm link will overwrite the global qwen. To avoid being unable to distinguish the same version number, you can uninstall the global CLI first
3、Create your sandbox Dockerfile under the root directory of your own project
Path:
.qwen/sandbox.Dockerfile
Official mirror image address:
https://github.com/QwenLM/qwen-code/pkgs/container/qwen-code
# Based on the official Qwen sandbox image (It is recommended to explicitly specify the version)
FROM
ghcr.io/qwenlm/qwen-code:sha-570ec43
# Add your extra tools here
RUN
apt-get
update
&&
apt-get
install
-y
\
git
\
python3
\
ripgrep
4、Create the first sandbox image under the root directory of your project
QWEN_SANDBOX
=
docker
BUILD_SANDBOX
=
1
qwen
-s
# Observe whether the sandbox version of the tool you launched is consistent with the version of your custom image. If they are consistent, the startup will be successful
This builds a project-specific image based on the default sandbox image.
Remove npm link
If you want to restore the official CLI of qwen, please remove the npm link
# Method 1: Unlink globally
npm
unlink
-g
@qwen-code/qwen-code
# Method 2: Remove it in the packages/cli directory
cd
packages/cli
npm
unlink
# Verification has been lifted
which
qwen
# It should display "qwen not found"
# Reinstall the global version if necessary
npm
install
-g
@qwen-code/qwen-code
# Verification Recovery
which
qwen
qwen
--version
Last updated on
May 18, 2026
MCP Servers
Memory Tool (save_memory)</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/sandbox/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Troubleshooting</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/support/troubleshooting/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/support/troubleshooting/</guid>
  <pubDate>Tue, 12 Mar 2024 00:00:00 +0000</pubDate>
  <category>Troubleshooting</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Support
Troubleshooting
Troubleshooting
This guide provides solutions to common issues and debugging tips, including topics on:
Authentication or login errors
Frequently asked questions (FAQs)
Debugging tips
Existing GitHub Issues similar to yours or creating new Issues
Authentication or login errors
Error:
Qwen OAuth free tier was discontinued on 2026-04-15
Cause:
Qwen OAuth is no longer available as of April 15, 2026.
Solution:
Switch to a different authentication method. Run
qwen
→...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Support
Troubleshooting
Troubleshooting
This guide provides solutions to common issues and debugging tips, including topics on:
Authentication or login errors
Frequently asked questions (FAQs)
Debugging tips
Existing GitHub Issues similar to yours or creating new Issues
Authentication or login errors
Error:
Qwen OAuth free tier was discontinued on 2026-04-15
Cause:
Qwen OAuth is no longer available as of April 15, 2026.
Solution:
Switch to a different authentication method. Run
qwen
→
/auth
and choose one of:
API Key
: Use an API key from Alibaba Cloud Model Studio (
Beijing
/
intl
). See the API setup guide (
Beijing
/
intl
).
Alibaba Cloud Coding Plan
: Subscribe for a fixed monthly fee with higher quotas. See the Coding Plan guide (
Beijing
/
intl
).
Error:
UNABLE_TO_GET_ISSUER_CERT_LOCALLY
,
UNABLE_TO_VERIFY_LEAF_SIGNATURE
, or
unable to get local issuer certificate
Cause:
You may be on a corporate network with a firewall that intercepts and inspects SSL/TLS traffic. This often requires a custom root CA certificate to be trusted by Node.js.
Solution:
Set the
NODE_EXTRA_CA_CERTS
environment variable to the absolute path of your corporate root CA certificate file.
Example:
export NODE_EXTRA_CA_CERTS=/path/to/your/corporate-ca.crt
Error:
Device authorization flow failed: fetch failed
Cause:
Node.js could not reach Qwen OAuth endpoints (often a proxy or SSL/TLS trust issue). When available, Qwen Code will also print the underlying error cause (for example:
UNABLE_TO_VERIFY_LEAF_SIGNATURE
). Note: this error is specific to the legacy Qwen OAuth flow.
Solution:
If you are still using Qwen OAuth, switch to API Key or Coding Plan via
/auth
.
If you are behind a proxy, set it via
qwen --proxy <url>
(or the
proxy
setting in
settings.json
).
If your network uses a corporate TLS inspection CA, set
NODE_EXTRA_CA_CERTS
as described above.
Issue: Unable to display UI after authentication failure
Cause:
If authentication fails after selecting an authentication type, the
security.auth.selectedType
setting may be persisted in
settings.json
. On restart, the CLI may get stuck trying to authenticate with the failed auth type and fail to display the UI.
Solution:
Clear the
security.auth.selectedType
configuration item in your
settings.json
file:
Open
~/.qwen/settings.json
(or
./.qwen/settings.json
for project-specific settings)
Remove the
security.auth.selectedType
field
Restart the CLI to allow it to prompt for authentication again
Frequently asked questions (FAQs)
Q: How do I update Qwen Code to the latest version?
A: If you installed it globally via
npm
, update it using the command
npm install -g @qwen-code/qwen-code@latest
. If you compiled it from source, pull the latest changes from the repository, and then rebuild using the command
npm run build
.
Q: Where are the Qwen Code configuration or settings files stored?
A: The Qwen Code configuration is stored in two
settings.json
files:
In your home directory:
~/.qwen/settings.json
.
In your project’s root directory:
./.qwen/settings.json
.
Refer to
Qwen Code Configuration
for more details.
Q: Why don’t I see cached token counts in my stats output?
A: Cached token information is only displayed when cached tokens are being used. This feature is available for API key users (e.g., Alibaba Cloud Model Studio API key or Google Cloud Vertex AI). You can still view your total token usage using the
/stats
command.
Common error messages and solutions
Error:
EADDRINUSE
(Address already in use) when starting an MCP server.
Cause:
Another process is already using the port that the MCP server is trying to bind to.
Solution:
Either stop the other process that is using the port or configure the MCP server to use a different port.
Error: Command not found (when attempting to run Qwen Code with
qwen
).
Cause:
The CLI is not correctly installed or it is not in your system’s
PATH
.
Solution:
The update depends on how you installed Qwen Code:
If you installed
qwen
globally, check that your
npm
global binary directory is in your
PATH
. You can update using the command
npm install -g @qwen-code/qwen-code@latest
.
If you are running
qwen
from source, ensure you are using the correct command to invoke it (e.g.
node packages/cli/dist/index.js ...
). To update, pull the latest changes from the repository, and then rebuild using the command
npm run build
.
Error:
MODULE_NOT_FOUND
or import errors.
Cause:
Dependencies are not installed correctly, or the project hasn’t been built.
Solution:
Run
npm install
to ensure all dependencies are present.
Run
npm run build
to compile the project.
Verify that the build completed successfully with
npm run start
.
Error: “Operation not permitted”, “Permission denied”, or similar.
Cause:
When sandboxing is enabled, Qwen Code may attempt operations that are restricted by your sandbox configuration, such as writing outside the project directory or system temp directory.
Solution:
Refer to the
Configuration: Sandboxing
documentation for more information, including how to customize your sandbox configuration.
Qwen Code is not running in interactive mode in “CI” environments
Issue:
Qwen Code does not enter interactive mode (no prompt appears) if an environment variable starting with
CI_
(e.g.
CI_TOKEN
) is set. This is because the
is-in-ci
package, used by the underlying UI framework, detects these variables and assumes a non-interactive CI environment.
Cause:
The
is-in-ci
package checks for the presence of
CI
,
CONTINUOUS_INTEGRATION
, or any environment variable with a
CI_
prefix. When any of these are found, it signals that the environment is non-interactive, which prevents the CLI from starting in its interactive mode.
Solution:
If the
CI_
prefixed variable is not needed for the CLI to function, you can temporarily unset it for the command. e.g.
env -u CI_TOKEN qwen
DEBUG mode not working from project .env file
Issue:
Setting
DEBUG=true
in a project’s
.env
file doesn’t enable debug mode for the CLI.
Cause:
The
DEBUG
and
DEBUG_MODE
variables are automatically excluded from project
.env
files to prevent interference with the CLI behavior.
Solution:
Use a
.qwen/.env
file instead, or configure the
advanced.excludedEnvVars
setting in your
settings.json
to exclude fewer variables.
IDE Companion not connecting
Ensure VS Code has a single workspace folder open.
Restart the integrated terminal after installing the extension so it inherits:
QWEN_CODE_IDE_WORKSPACE_PATH
QWEN_CODE_IDE_SERVER_PORT
If running in a container, verify
host.docker.internal
resolves. Otherwise, map the host appropriately.
Reinstall the companion with
/ide install
and use “Qwen Code: Run” in the Command Palette to verify it launches.
Exit Codes
The Qwen Code uses specific exit codes to indicate the reason for termination. This is especially useful for scripting and automation.
Exit Code
Error Type
Description
41
FatalAuthenticationError
An error occurred during the authentication process.
42
FatalInputError
Invalid or missing input was provided to the CLI. (non-interactive mode only)
44
FatalSandboxError
An error occurred with the sandboxing environment (e.g. Docker, Podman, or Seatbelt).
52
FatalConfigError
A configuration file (
settings.json
) is invalid or contains errors.
53
FatalTurnLimitedError
The maximum number of conversational turns for the session was reached. (non-interactive mode only)
Debugging Tips
CLI debugging:
Use the
--verbose
flag (if available) with CLI commands for more detailed output.
Check the CLI logs, often found in a user-specific configuration or cache directory.
Core debugging:
Check the server console output for error messages or stack traces.
Increase log verbosity if configurable.
Use Node.js debugging tools (e.g.
node --inspect
) if you need to step through server-side code.
Tool issues:
If a specific tool is failing, try to isolate the issue by running the simplest possible version of the command or operation the tool performs.
For
run_shell_command
, check that the command works directly in your shell first.
For
file system tools
, verify that paths are correct and check the permissions.
Pre-flight checks:
Always run
npm run preflight
before committing code. This can catch many common issues related to formatting, linting, and type errors.
Existing GitHub Issues similar to yours or creating new Issues
If you encounter an issue that was not covered here in this
Troubleshooting guide
, consider searching the Qwen Code
Issue tracker on GitHub
. If you can’t find an issue similar to yours, consider creating a new GitHub Issue with a detailed description. Pull requests are also welcome!
Last updated on
May 18, 2026
Keyboard Shortcuts
Terms of Service</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/support/troubleshooting/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Support
Troubleshooting
Troubleshooting
This guide provides solutions to common issues and debugging tips, including topics on:
Authentication or login errors
Frequently asked questions (FAQs)
Debugging tips
Existing GitHub Issues similar to yours or creating new Issues
Authentication or login errors
Error:
Qwen OAuth free tier was discontinued on 2026-04-15
Cause:
Qwen OAuth is no longer available as of April 15, 2026.
Solution:
Switch to a different authentication method. Run
qwen
→...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Support
Troubleshooting
Troubleshooting
This guide provides solutions to common issues and debugging tips, including topics on:
Authentication or login errors
Frequently asked questions (FAQs)
Debugging tips
Existing GitHub Issues similar to yours or creating new Issues
Authentication or login errors
Error:
Qwen OAuth free tier was discontinued on 2026-04-15
Cause:
Qwen OAuth is no longer available as of April 15, 2026.
Solution:
Switch to a different authentication method. Run
qwen
→
/auth
and choose one of:
API Key
: Use an API key from Alibaba Cloud Model Studio (
Beijing
/
intl
). See the API setup guide (
Beijing
/
intl
).
Alibaba Cloud Coding Plan
: Subscribe for a fixed monthly fee with higher quotas. See the Coding Plan guide (
Beijing
/
intl
).
Error:
UNABLE_TO_GET_ISSUER_CERT_LOCALLY
,
UNABLE_TO_VERIFY_LEAF_SIGNATURE
, or
unable to get local issuer certificate
Cause:
You may be on a corporate network with a firewall that intercepts and inspects SSL/TLS traffic. This often requires a custom root CA certificate to be trusted by Node.js.
Solution:
Set the
NODE_EXTRA_CA_CERTS
environment variable to the absolute path of your corporate root CA certificate file.
Example:
export NODE_EXTRA_CA_CERTS=/path/to/your/corporate-ca.crt
Error:
Device authorization flow failed: fetch failed
Cause:
Node.js could not reach Qwen OAuth endpoints (often a proxy or SSL/TLS trust issue). When available, Qwen Code will also print the underlying error cause (for example:
UNABLE_TO_VERIFY_LEAF_SIGNATURE
). Note: this error is specific to the legacy Qwen OAuth flow.
Solution:
If you are still using Qwen OAuth, switch to API Key or Coding Plan via
/auth
.
If you are behind a proxy, set it via
qwen --proxy <url>
(or the
proxy
setting in
settings.json
).
If your network uses a corporate TLS inspection CA, set
NODE_EXTRA_CA_CERTS
as described above.
Issue: Unable to display UI after authentication failure
Cause:
If authentication fails after selecting an authentication type, the
security.auth.selectedType
setting may be persisted in
settings.json
. On restart, the CLI may get stuck trying to authenticate with the failed auth type and fail to display the UI.
Solution:
Clear the
security.auth.selectedType
configuration item in your
settings.json
file:
Open
~/.qwen/settings.json
(or
./.qwen/settings.json
for project-specific settings)
Remove the
security.auth.selectedType
field
Restart the CLI to allow it to prompt for authentication again
Frequently asked questions (FAQs)
Q: How do I update Qwen Code to the latest version?
A: If you installed it globally via
npm
, update it using the command
npm install -g @qwen-code/qwen-code@latest
. If you compiled it from source, pull the latest changes from the repository, and then rebuild using the command
npm run build
.
Q: Where are the Qwen Code configuration or settings files stored?
A: The Qwen Code configuration is stored in two
settings.json
files:
In your home directory:
~/.qwen/settings.json
.
In your project’s root directory:
./.qwen/settings.json
.
Refer to
Qwen Code Configuration
for more details.
Q: Why don’t I see cached token counts in my stats output?
A: Cached token information is only displayed when cached tokens are being used. This feature is available for API key users (e.g., Alibaba Cloud Model Studio API key or Google Cloud Vertex AI). You can still view your total token usage using the
/stats
command.
Common error messages and solutions
Error:
EADDRINUSE
(Address already in use) when starting an MCP server.
Cause:
Another process is already using the port that the MCP server is trying to bind to.
Solution:
Either stop the other process that is using the port or configure the MCP server to use a different port.
Error: Command not found (when attempting to run Qwen Code with
qwen
).
Cause:
The CLI is not correctly installed or it is not in your system’s
PATH
.
Solution:
The update depends on how you installed Qwen Code:
If you installed
qwen
globally, check that your
npm
global binary directory is in your
PATH
. You can update using the command
npm install -g @qwen-code/qwen-code@latest
.
If you are running
qwen
from source, ensure you are using the correct command to invoke it (e.g.
node packages/cli/dist/index.js ...
). To update, pull the latest changes from the repository, and then rebuild using the command
npm run build
.
Error:
MODULE_NOT_FOUND
or import errors.
Cause:
Dependencies are not installed correctly, or the project hasn’t been built.
Solution:
Run
npm install
to ensure all dependencies are present.
Run
npm run build
to compile the project.
Verify that the build completed successfully with
npm run start
.
Error: “Operation not permitted”, “Permission denied”, or similar.
Cause:
When sandboxing is enabled, Qwen Code may attempt operations that are restricted by your sandbox configuration, such as writing outside the project directory or system temp directory.
Solution:
Refer to the
Configuration: Sandboxing
documentation for more information, including how to customize your sandbox configuration.
Qwen Code is not running in interactive mode in “CI” environments
Issue:
Qwen Code does not enter interactive mode (no prompt appears) if an environment variable starting with
CI_
(e.g.
CI_TOKEN
) is set. This is because the
is-in-ci
package, used by the underlying UI framework, detects these variables and assumes a non-interactive CI environment.
Cause:
The
is-in-ci
package checks for the presence of
CI
,
CONTINUOUS_INTEGRATION
, or any environment variable with a
CI_
prefix. When any of these are found, it signals that the environment is non-interactive, which prevents the CLI from starting in its interactive mode.
Solution:
If the
CI_
prefixed variable is not needed for the CLI to function, you can temporarily unset it for the command. e.g.
env -u CI_TOKEN qwen
DEBUG mode not working from project .env file
Issue:
Setting
DEBUG=true
in a project’s
.env
file doesn’t enable debug mode for the CLI.
Cause:
The
DEBUG
and
DEBUG_MODE
variables are automatically excluded from project
.env
files to prevent interference with the CLI behavior.
Solution:
Use a
.qwen/.env
file instead, or configure the
advanced.excludedEnvVars
setting in your
settings.json
to exclude fewer variables.
IDE Companion not connecting
Ensure VS Code has a single workspace folder open.
Restart the integrated terminal after installing the extension so it inherits:
QWEN_CODE_IDE_WORKSPACE_PATH
QWEN_CODE_IDE_SERVER_PORT
If running in a container, verify
host.docker.internal
resolves. Otherwise, map the host appropriately.
Reinstall the companion with
/ide install
and use “Qwen Code: Run” in the Command Palette to verify it launches.
Exit Codes
The Qwen Code uses specific exit codes to indicate the reason for termination. This is especially useful for scripting and automation.
Exit Code
Error Type
Description
41
FatalAuthenticationError
An error occurred during the authentication process.
42
FatalInputError
Invalid or missing input was provided to the CLI. (non-interactive mode only)
44
FatalSandboxError
An error occurred with the sandboxing environment (e.g. Docker, Podman, or Seatbelt).
52
FatalConfigError
A configuration file (
settings.json
) is invalid or contains errors.
53
FatalTurnLimitedError
The maximum number of conversational turns for the session was reached. (non-interactive mode only)
Debugging Tips
CLI debugging:
Use the
--verbose
flag (if available) with CLI commands for more detailed output.
Check the CLI logs, often found in a user-specific configuration or cache directory.
Core debugging:
Check the server console output for error messages or stack traces.
Increase log verbosity if configurable.
Use Node.js debugging tools (e.g.
node --inspect
) if you need to step through server-side code.
Tool issues:
If a specific tool is failing, try to isolate the issue by running the simplest possible version of the command or operation the tool performs.
For
run_shell_command
, check that the command works directly in your shell first.
For
file system tools
, verify that paths are correct and check the permissions.
Pre-flight checks:
Always run
npm run preflight
before committing code. This can catch many common issues related to formatting, linting, and type errors.
Existing GitHub Issues similar to yours or creating new Issues
If you encounter an issue that was not covered here in this
Troubleshooting guide
, consider searching the Qwen Code
Issue tracker on GitHub
. If you can’t find an issue similar to yours, consider creating a new GitHub Issue with a detailed description. Pull requests are also welcome!
Last updated on
May 18, 2026
Keyboard Shortcuts
Terms of Service</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/support/troubleshooting/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Model Providers</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/configuration/model-providers/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/configuration/model-providers/</guid>
  <pubDate>Fri, 08 Mar 2024 00:00:00 +0000</pubDate>
  <category>Models</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Model Providers
Model Providers
Qwen Code allows you to configure multiple model providers through the
modelProviders
setting in your
settings.json
. This enables you to switch between different AI models and providers using the
/model
command.
Overview
Use
modelProviders
to declare curated model lists per auth type that the
/model
picker can switch between. Keys must be valid auth types (
openai
,
anthropic
,
gemini
, etc.). Each entry requires an
id
and
must include
en...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Model Providers
Model Providers
Qwen Code allows you to configure multiple model providers through the
modelProviders
setting in your
settings.json
. This enables you to switch between different AI models and providers using the
/model
command.
Overview
Use
modelProviders
to declare curated model lists per auth type that the
/model
picker can switch between. Keys must be valid auth types (
openai
,
anthropic
,
gemini
, etc.). Each entry requires an
id
and
must include
envKey
, with optional
name
,
description
,
baseUrl
, and
generationConfig
. Credentials are never persisted in settings; the runtime reads them from
process.env[envKey]
. Qwen OAuth models remain hard-coded and cannot be overridden.
Note
Only the
/model
command exposes non-default auth types. Anthropic, Gemini, etc., must be defined via
modelProviders
. The
/auth
command lists Qwen OAuth, Alibaba Cloud Coding Plan, and API Key as the built-in authentication options.
Warning
Duplicate model IDs within the same authType:
Defining multiple models with the same
id
under a single
authType
(e.g., two entries with
"id": "gpt-4o"
in
openai
) is currently not supported. If duplicates exist,
the first occurrence wins
and subsequent duplicates are skipped with a warning. Note that the
id
field is used both as the configuration identifier and as the actual model name sent to the API, so using unique IDs (e.g.,
gpt-4o-creative
,
gpt-4o-balanced
) is not a viable workaround. This is a known limitation that we plan to address in a future release.
Configuration Examples by Auth Type
Below are comprehensive configuration examples for different authentication types, showing the available parameters and their combinations.
Supported Auth Types
The
modelProviders
object keys must be valid
authType
values. Currently supported auth types are:
Auth Type
Description
openai
OpenAI-compatible APIs (OpenAI, Azure OpenAI, local inference servers like vLLM/Ollama)
anthropic
Anthropic Claude API
gemini
Google Gemini API
qwen-oauth
Qwen OAuth (hard-coded, cannot be overridden in
modelProviders
)
[!warning]
If an invalid auth type key is used (e.g., a typo like
"openai-custom"
), the configuration will be
silently skipped
and the models will not appear in the
/model
picker. Always use one of the supported auth type values listed above.
SDKs Used for API Requests
Qwen Code uses the following official SDKs to send requests to each provider:
Auth Type
SDK Package
openai
openai
- Official OpenAI Node.js SDK
anthropic
@anthropic-ai/sdk
- Official Anthropic SDK
gemini
@google/genai
- Official Google GenAI SDK
qwen-oauth
openai
with custom provider (DashScope-compatible)
This means the
baseUrl
you configure should be compatible with the corresponding SDK’s expected API format. For example, when using
openai
auth type, the endpoint must accept OpenAI API format requests.
OpenAI-compatible providers (
openai
)
This auth type supports not only OpenAI’s official API but also any OpenAI-compatible endpoint, including aggregated model providers like OpenRouter.
{
"env"
: {
"OPENAI_API_KEY"
:
"sk-your-actual-openai-key-here"
,
"OPENROUTER_API_KEY"
:
"sk-or-your-actual-openrouter-key-here"
},
"modelProviders"
: {
"openai"
: [
{
"id"
:
"gpt-4o"
,
"name"
:
"GPT-4o"
,
"envKey"
:
"OPENAI_API_KEY"
,
"baseUrl"
:
"https://api.openai.com/v1"
,
"generationConfig"
: {
"timeout"
:
60000
,
"maxRetries"
:
3
,
"enableCacheControl"
:
true
,
"contextWindowSize"
:
128000
,
"modalities"
: {
"image"
:
true
},
"customHeaders"
: {
"X-Client-Request-ID"
:
"req-123"
},
"extra_body"
: {
"enable_thinking"
:
true
,
"service_tier"
:
"priority"
},
"samplingParams"
: {
"temperature"
:
0.2
,
"top_p"
:
0.8
,
"max_tokens"
:
4096
,
"presence_penalty"
:
0.1
,
"frequency_penalty"
:
0.1
}
}
},
{
"id"
:
"gpt-4o-mini"
,
"name"
:
"GPT-4o Mini"
,
"envKey"
:
"OPENAI_API_KEY"
,
"baseUrl"
:
"https://api.openai.com/v1"
,
"generationConfig"
: {
"timeout"
:
30000
,
"samplingParams"
: {
"temperature"
:
0.5
,
"max_tokens"
:
2048
}
}
},
{
"id"
:
"openai/gpt-4o"
,
"name"
:
"GPT-4o (via OpenRouter)"
,
"envKey"
:
"OPENROUTER_API_KEY"
,
"baseUrl"
:
"https://openrouter.ai/api/v1"
,
"generationConfig"
: {
"timeout"
:
120000
,
"maxRetries"
:
3
,
"samplingParams"
: {
"temperature"
:
0.7
}
}
}
]
}
}
Anthropic (
anthropic
)
{
"env"
: {
"ANTHROPIC_API_KEY"
:
"sk-ant-your-actual-anthropic-key-here"
},
"modelProviders"
: {
"anthropic"
: [
{
"id"
:
"claude-3-5-sonnet"
,
"name"
:
"Claude 3.5 Sonnet"
,
"envKey"
:
"ANTHROPIC_API_KEY"
,
"baseUrl"
:
"https://api.anthropic.com/v1"
,
"generationConfig"
: {
"timeout"
:
120000
,
"maxRetries"
:
3
,
"contextWindowSize"
:
200000
,
"samplingParams"
: {
"temperature"
:
0.7
,
"max_tokens"
:
8192
,
"top_p"
:
0.9
}
}
},
{
"id"
:
"claude-3-opus"
,
"name"
:
"Claude 3 Opus"
,
"envKey"
:
"ANTHROPIC_API_KEY"
,
"baseUrl"
:
"https://api.anthropic.com/v1"
,
"generationConfig"
: {
"timeout"
:
180000
,
"samplingParams"
: {
"temperature"
:
0.3
,
"max_tokens"
:
4096
}
}
}
]
}
}
Google Gemini (
gemini
)
{
"env"
: {
"GEMINI_API_KEY"
:
"AIza-your-actual-gemini-key-here"
},
"modelProviders"
: {
"gemini"
: [
{
"id"
:
"gemini-2.0-flash"
,
"name"
:
"Gemini 2.0 Flash"
,
"envKey"
:
"GEMINI_API_KEY"
,
"baseUrl"
:
"https://generativelanguage.googleapis.com"
,
"capabilities"
: {
"vision"
:
true
},
"generationConfig"
: {
"timeout"
:
60000
,
"maxRetries"
:
2
,
"contextWindowSize"
:
1000000
,
"schemaCompliance"
:
"auto"
,
"samplingParams"
: {
"temperature"
:
0.4
,
"top_p"
:
0.95
,
"max_tokens"
:
8192
,
"top_k"
:
40
}
}
}
]
}
}
Local Self-Hosted Models (via OpenAI-compatible API)
Most local inference servers (vLLM, Ollama, LM Studio, etc.) provide an OpenAI-compatible API endpoint. Configure them using the
openai
auth type with a local
baseUrl
:
{
"env"
: {
"OLLAMA_API_KEY"
:
"ollama"
,
"VLLM_API_KEY"
:
"not-needed"
,
"LMSTUDIO_API_KEY"
:
"lm-studio"
},
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen2.5-7b"
,
"name"
:
"Qwen2.5 7B (Ollama)"
,
"envKey"
:
"OLLAMA_API_KEY"
,
"baseUrl"
:
"http://localhost:11434/v1"
,
"generationConfig"
: {
"timeout"
:
300000
,
"maxRetries"
:
1
,
"contextWindowSize"
:
32768
,
"samplingParams"
: {
"temperature"
:
0.7
,
"top_p"
:
0.9
,
"max_tokens"
:
4096
}
}
},
{
"id"
:
"llama-3.1-8b"
,
"name"
:
"Llama 3.1 8B (vLLM)"
,
"envKey"
:
"VLLM_API_KEY"
,
"baseUrl"
:
"http://localhost:8000/v1"
,
"generationConfig"
: {
"timeout"
:
120000
,
"maxRetries"
:
2
,
"contextWindowSize"
:
128000
,
"samplingParams"
: {
"temperature"
:
0.6
,
"max_tokens"
:
8192
}
}
},
{
"id"
:
"local-model"
,
"name"
:
"Local Model (LM Studio)"
,
"envKey"
:
"LMSTUDIO_API_KEY"
,
"baseUrl"
:
"http://localhost:1234/v1"
,
"generationConfig"
: {
"timeout"
:
60000
,
"samplingParams"
: {
"temperature"
:
0.5
}
}
}
]
}
}
For local servers that don’t require authentication, you can use any placeholder value for the API key:
# For Ollama (no auth required)
export
OLLAMA_API_KEY
=
"ollama"
# For vLLM (if no auth is configured)
export
VLLM_API_KEY
=
"not-needed"
Note
The
extra_body
parameter is
only supported for OpenAI-compatible providers
(
openai
,
qwen-oauth
). It is ignored for Anthropic, and Gemini providers.
Note
About
envKey
: The
envKey
field specifies the
name of an environment variable
, not the actual API key value. For the configuration to work, you need to ensure the corresponding environment variable is set with your real API key. There are two ways to do this:
Option 1: Using a
.env
file
(recommended for security):
# ~/.qwen/.env (or project root)
OPENAI_API_KEY
=
sk-your-actual-key-here
Be sure to add
.env
to your
.gitignore
to prevent accidentally committing secrets.
Option 2: Using the
env
field in
settings.json
(as shown in the examples above):
{
"env"
: {
"OPENAI_API_KEY"
:
"sk-your-actual-key-here"
}
}
Each provider example includes an
env
field to illustrate how the API key should be configured.
Alibaba Cloud Coding Plan
Alibaba Cloud Coding Plan provides a pre-configured set of Qwen models optimized for coding tasks. This feature is available for users with Alibaba Cloud Coding Plan API access and offers a simplified setup experience with automatic model configuration updates.
Overview
When you authenticate with an Alibaba Cloud Coding Plan API key using the
/auth
command, Qwen Code automatically configures the following models:
Model ID
Name
Description
qwen3.5-plus
qwen3.5-plus
Advanced model with thinking enabled
qwen3-coder-plus
qwen3-coder-plus
Optimized for coding tasks
qwen3-max-2026-01-23
qwen3-max-2026-01-23
Latest max model with thinking enabled
Setup
Obtain an Alibaba Cloud Coding Plan API key:
China
:
https://bailian.console.aliyun.com/?tab=model#/efm/coding_plan
International
:
https://modelstudio.console.alibabacloud.com/?tab=dashboard#/efm/coding_plan
Run the
/auth
command in Qwen Code
Select
Alibaba Cloud Coding Plan
Select your region
Enter your API key when prompted
The models will be automatically configured and added to your
/model
picker.
Regions
Alibaba Cloud Coding Plan supports two regions:
Region
Endpoint
Description
China
https://coding.dashscope.aliyuncs.com/v1
Mainland China endpoint
Global/International
https://coding-intl.dashscope.aliyuncs.com/v1
International endpoint
The region is selected during authentication and stored in
settings.json
under
codingPlan.region
. To switch regions, re-run the
/auth
command and select a different region.
API Key Storage
When you configure Coding Plan through the
/auth
command, the API key is stored using the reserved environment variable name
BAILIAN_CODING_PLAN_API_KEY
. By default, it is stored in the
env
field of your
settings.json
file.
Warning
Security Recommendation
: For better security, it is recommended to move the API key from
settings.json
to a separate
.env
file and load it as an environment variable. For example:
# ~/.qwen/.env
BAILIAN_CODING_PLAN_API_KEY
=
your-api-key-here
Then ensure this file is added to your
.gitignore
if you’re using project-level settings.
Automatic Updates
Coding Plan model configurations are versioned. When Qwen Code detects a newer version of the model template, you will be prompted to update. Accepting the update will:
Replace the existing Coding Plan model configurations with the latest versions
Preserve any custom model configurations you’ve added manually
Automatically switch to the first model in the updated configuration
The update process ensures you always have access to the latest model configurations and features without manual intervention.
Manual Configuration (Advanced)
If you prefer to manually configure Coding Plan models, you can add them to your
settings.json
like any OpenAI-compatible provider:
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen3-coder-plus"
,
"name"
:
"qwen3-coder-plus"
,
"description"
:
"Qwen3-Coder via Alibaba Cloud Coding Plan"
,
"envKey"
:
"YOUR_CUSTOM_ENV_KEY"
,
"baseUrl"
:
"https://coding.dashscope.aliyuncs.com/v1"
}
]
}
}
Note
When using manual configuration:
You can use any environment variable name for
envKey
You do not need to configure
codingPlan.*
Automatic updates will not apply
to manually configured Coding Plan models
Warning
If you also use automatic Coding Plan configuration, automatic updates may overwrite your manual configurations if they use the same
envKey
and
baseUrl
as the automatic configuration. To avoid this, ensure your manual configuration uses a different
envKey
if possible.
Resolution Layers and Atomicity
The effective auth/model/credential values are chosen per field using the following precedence (first present wins). You can combine
--auth-type
with
--model
to point directly at a provider entry; these CLI flags run before other layers.
Layer (highest → lowest)
authType
model
apiKey
baseUrl
apiKeyEnvKey
proxy
Programmatic overrides
/auth
/auth
input
/auth
input
/auth
input
—
—
Model provider selection
—
modelProvider.id
env[modelProvider.envKey]
modelProvider.baseUrl
modelProvider.envKey
—
CLI arguments
--auth-type
--model
--openaiApiKey
(or provider-specific equivalents)
--openaiBaseUrl
(or provider-specific equivalents)
—
—
Environment variables
—
Provider-specific mapping (e.g.
OPENAI_MODEL
)
Provider-specific mapping (e.g.
OPENAI_API_KEY
)
Provider-specific mapping (e.g.
OPENAI_BASE_URL
)
—
—
Settings (
settings.json
)
security.auth.selectedType
model.name
security.auth.apiKey
security.auth.baseUrl
—
—
Default / computed
Falls back to
AuthType.QWEN_OAUTH
Built-in default (OpenAI ⇒
qwen3-coder-plus
)
—
—
—
Config.getProxy()
if configured
*When present, CLI auth flags override settings. Otherwise,
security.auth.selectedType
or the implicit default determine the auth type. Qwen OAuth and OpenAI are the only auth types surfaced without extra configuration.
Warning
Deprecation of
security.auth.apiKey
and
security.auth.baseUrl
:
Directly configuring API credentials via
security.auth.apiKey
and
security.auth.baseUrl
in
settings.json
is deprecated. These settings were used in historical versions for credentials entered through the UI, but the credential input flow was removed in version 0.10.1. These fields will be fully removed in a future release.
It is strongly recommended to migrate to
modelProviders
for all model and credential configurations. Use
envKey
in
modelProviders
to reference environment variables for secure credential management instead of hardcoding credentials in settings files.
Generation Config Layering: The Impermeable Provider Layer
The configuration resolution follows a strict layering model with one crucial rule:
the modelProvider layer is impermeable
.
How it works
When a modelProvider model IS selected
(e.g., via
/model
command choosing a provider-configured model):
The entire
generationConfig
from the provider is applied
atomically
The provider layer is completely impermeable
— lower layers (CLI, env, settings) do not participate in generationConfig resolution at all
All fields defined in
modelProviders[].generationConfig
use the provider’s values
All fields
not defined
by the provider are set to
undefined
(not inherited from settings)
This ensures provider configurations act as a complete, self-contained “sealed package”
When NO modelProvider model is selected
(e.g., using
--model
with a raw model ID, or using CLI/env/settings directly):
The resolution falls through to lower layers
Fields are populated from CLI → env → settings → defaults
This creates a
Runtime Model
(see next section)
Per-field precedence for
generationConfig
Priority
Source
Behavior
1
Programmatic overrides
Runtime
/model
,
/auth
changes
2
modelProviders[authType][].generationConfig
Impermeable layer
- completely replaces all generationConfig fields; lower layers do not participate
3
settings.model.generationConfig
Only used for
Runtime Models
(when no provider model is selected)
4
Content-generator defaults
Provider-specific defaults (e.g., OpenAI vs Gemini) - only for Runtime Models
Atomic field treatment
The following fields are treated as atomic objects - provider values completely replace the entire object, no merging occurs:
samplingParams
- Temperature, top_p, max_tokens, etc.
customHeaders
- Custom HTTP headers
extra_body
- Extra request body parameters
Example
// User settings (~/.qwen/settings.json)
{
"model"
: {
"generationConfig"
: {
"timeout"
:
30000
,
"samplingParams"
: {
"temperature"
:
0.5
,
"max_tokens"
:
1000
}
}
}
}
// modelProviders configuration
{
"modelProviders"
: {
"openai"
: [{
"id"
:
"gpt-4o"
,
"envKey"
:
"OPENAI_API_KEY"
,
"generationConfig"
: {
"timeout"
:
60000
,
"samplingParams"
: {
"temperature"
:
0.2
}
}
}]
}
}
When
gpt-4o
is selected from modelProviders:
timeout
= 60000 (from provider, overrides settings)
samplingParams.temperature
= 0.2 (from provider, completely replaces settings object)
samplingParams.max_tokens
=
undefined
(not defined in provider, and provider layer does not inherit from settings — fields are explicitly set to undefined if not provided)
When using a raw model via
--model gpt-4
(not from modelProviders, creates a Runtime Model):
timeout
= 30000 (from settings)
samplingParams.temperature
= 0.5 (from settings)
samplingParams.max_tokens
= 1000 (from settings)
The merge strategy for
modelProviders
itself is REPLACE: the entire
modelProviders
from project settings will override the corresponding section in user settings, rather than merging the two.
Reasoning / thinking configuration
The optional
reasoning
field under
generationConfig
controls how aggressively the model reasons before responding. The Anthropic and Gemini converters always honor it. The OpenAI-compatible pipeline honors it
unless
generationConfig.samplingParams
is set — see the “Interaction with
samplingParams
” caveat below.
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"deepseek-v4-pro"
,
"name"
:
"DeepSeek V4 Pro"
,
"baseUrl"
:
"https://api.deepseek.com/v1"
,
"envKey"
:
"DEEPSEEK_API_KEY"
,
"generationConfig"
: {
// The four-tier scale:
//   'low'    | 'medium' — server-mapped to 'high' on DeepSeek
//   'high'   — default reasoning intensity
//   'max'    — DeepSeek-specific extra-strong tier
// Or set `false` to disable reasoning entirely.
"reasoning"
: {
"effort"
:
"max"
},
},
},
],
},
}
Per-provider behavior
Protocol / provider
Wire shape
Notes
OpenAI / DeepSeek
(
api.deepseek.com
)
Flat
reasoning_effort: <effort>
body parameter
When
reasoning.effort
is set in the nested config shape, it’s rewritten to flat
reasoning_effort
and
'low'
/
'medium'
are normalized to
'high'
,
'xhigh'
to
'max'
— mirroring DeepSeek’s
server-side back-compat
. Top-level
samplingParams.reasoning_effort
or
extra_body.reasoning_effort
overrides skip this normalization and ship verbatim.
OpenAI
(other compatible servers)
reasoning: { effort, ... }
passed through verbatim
Set via
samplingParams
(e.g.
samplingParams.reasoning_effort
for GPT-5/o-series) when the provider expects a different shape.
Anthropic
(real
api.anthropic.com
)
output_config: { effort }
plus the
effort-2025-11-24
beta header
Real Anthropic accepts
'low'
/
'medium'
/
'high'
only.
'max'
is
clamped to
'high'
with a
debugLogger.warn
line (once per generator); if you want max effort, switch the baseURL to a DeepSeek-compatible endpoint that supports it.
Anthropic
(
api.deepseek.com/anthropic
)
Same
output_config: { effort }
+ beta header
'max'
is passed through unchanged.
Gemini
(
@google/genai
)
thinkingConfig: { includeThoughts: true, thinkingLevel }
'low'
→
LOW
,
'high'
/
'max'
→
HIGH
, others →
THINKING_LEVEL_UNSPECIFIED
(Gemini has no
MAX
tier).
reasoning: false
Setting
reasoning: false
(the literal boolean) explicitly disables thinking on every provider — useful for cheap side queries that don’t benefit from reasoning. This is honored at the request level too via
request.config.thinkingConfig.includeThoughts: false
for one-off calls (e.g. suggestion generation).
On a
api.deepseek.com
baseURL, the OpenAI pipeline emits the explicit
thinking: { type: 'disabled' }
field that DeepSeek V4+ requires — the server-side default is
'enabled'
, so simply omitting
reasoning_effort
would still pay thinking latency/cost. Self-hosted DeepSeek backends (sglang/vllm) and other OpenAI-compatible servers do
not
receive this field; if you need to disable thinking on those, inject
thinking: { type: 'disabled' }
(or whatever knob your inference framework exposes) via
samplingParams
/
extra_body
.
Interaction with
samplingParams
(OpenAI-compatible only)
Warning
When
generationConfig.samplingParams
is set on an OpenAI-compatible provider, the pipeline ships those keys to the wire
verbatim
and skips the separate
reasoning
injection entirely. So a config like
{ samplingParams: { temperature: 0.5 }, reasoning: { effort: 'max' } }
will silently drop the reasoning field on OpenAI/DeepSeek requests.
If you set
samplingParams
, include the reasoning knob inside it directly — for DeepSeek that’s
samplingParams.reasoning_effort
, for GPT-5/o-series it’s
samplingParams.reasoning_effort
(their flat field) or
samplingParams.reasoning
(the nested object). For OpenRouter and other providers the field name varies; consult the provider docs.
The Anthropic and Gemini converters are unaffected — they always read
reasoning.effort
directly regardless of
samplingParams
.
budget_tokens
You can pin an exact thinking-token budget by including
budget_tokens
alongside
effort
:
"reasoning"
: {
"effort"
:
"high"
,
"budget_tokens"
:
50000
}
For Anthropic this becomes
thinking.budget_tokens
. For OpenAI/DeepSeek the field is preserved but currently ignored by the server —
reasoning_effort
is the load-bearing knob.
Provider Models vs Runtime Models
Qwen Code distinguishes between two types of model configurations:
Provider Model
Defined in
modelProviders
configuration
Has a complete, atomic configuration package
When selected, its configuration is applied as an impermeable layer
Appears in
/model
command list with full metadata (name, description, capabilities)
Recommended for multi-model workflows and team consistency
Runtime Model
Created dynamically when using raw model IDs via CLI (
--model
), environment variables, or settings
Not defined in
modelProviders
Configuration is built by “projecting” through resolution layers (CLI → env → settings → defaults)
Automatically captured as a
RuntimeModelSnapshot
when a complete configuration is detected
Allows reuse without re-entering credentials
RuntimeModelSnapshot lifecycle
When you configure a model without using
modelProviders
, Qwen Code automatically creates a RuntimeModelSnapshot to preserve your configuration:
# This creates a RuntimeModelSnapshot with ID: $runtime|openai|my-custom-model
qwen
--auth-type
openai
--model
my-custom-model
--openaiApiKey
$KEY
--openaiBaseUrl
https://api.example.com/v1
The snapshot:
Captures model ID, API key, base URL, and generation config
Persists across sessions (stored in memory during runtime)
Appears in the
/model
command list as a runtime option
Can be switched to using
/model $runtime|openai|my-custom-model
Key differences
Aspect
Provider Model
Runtime Model
Configuration source
modelProviders
in settings
CLI, env, settings layers
Configuration atomicity
Complete, impermeable package
Layered, each field resolved independently
Reusability
Always available in
/model
list
Captured as snapshot, appears if complete
Team sharing
Yes (via committed settings)
No (user-local)
Credential storage
Reference via
envKey
only
May capture actual key in snapshot
When to use each
Use Provider Models
when: You have standard models shared across a team, need consistent configurations, or want to prevent accidental overrides
Use Runtime Models
when: Quickly testing a new model, using temporary credentials, or working with ad-hoc endpoints
Selection Persistence and Recommendations
Important
Define
modelProviders
in the user-scope
~/.qwen/settings.json
whenever possible and avoid persisting credential overrides in any scope. Keeping the provider catalog in user settings prevents merge/override conflicts between project and user scopes and ensures
/auth
and
/model
updates always write back to a consistent scope.
/model
and
/auth
persist
model.name
(where applicable) and
security.auth.selectedType
to the closest writable scope that already defines
modelProviders
; otherwise they fall back to the user scope. This keeps workspace/user files in sync with the active provider catalog.
Without
modelProviders
, the resolver mixes CLI/env/settings layers, creating Runtime Models. This is fine for single-provider setups but cumbersome when frequently switching. Define provider catalogs whenever multi-model workflows are common so that switches stay atomic, source-attributed, and debuggable.
Last updated on
May 18, 2026
Themes
Introduction</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/model-providers/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Model Providers
Model Providers
Qwen Code allows you to configure multiple model providers through the
modelProviders
setting in your
settings.json
. This enables you to switch between different AI models and providers using the
/model
command.
Overview
Use
modelProviders
to declare curated model lists per auth type that the
/model
picker can switch between. Keys must be valid auth types (
openai
,
anthropic
,
gemini
, etc.). Each entry requires an
id
and
must include
en...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Model Providers
Model Providers
Qwen Code allows you to configure multiple model providers through the
modelProviders
setting in your
settings.json
. This enables you to switch between different AI models and providers using the
/model
command.
Overview
Use
modelProviders
to declare curated model lists per auth type that the
/model
picker can switch between. Keys must be valid auth types (
openai
,
anthropic
,
gemini
, etc.). Each entry requires an
id
and
must include
envKey
, with optional
name
,
description
,
baseUrl
, and
generationConfig
. Credentials are never persisted in settings; the runtime reads them from
process.env[envKey]
. Qwen OAuth models remain hard-coded and cannot be overridden.
Note
Only the
/model
command exposes non-default auth types. Anthropic, Gemini, etc., must be defined via
modelProviders
. The
/auth
command lists Qwen OAuth, Alibaba Cloud Coding Plan, and API Key as the built-in authentication options.
Warning
Duplicate model IDs within the same authType:
Defining multiple models with the same
id
under a single
authType
(e.g., two entries with
"id": "gpt-4o"
in
openai
) is currently not supported. If duplicates exist,
the first occurrence wins
and subsequent duplicates are skipped with a warning. Note that the
id
field is used both as the configuration identifier and as the actual model name sent to the API, so using unique IDs (e.g.,
gpt-4o-creative
,
gpt-4o-balanced
) is not a viable workaround. This is a known limitation that we plan to address in a future release.
Configuration Examples by Auth Type
Below are comprehensive configuration examples for different authentication types, showing the available parameters and their combinations.
Supported Auth Types
The
modelProviders
object keys must be valid
authType
values. Currently supported auth types are:
Auth Type
Description
openai
OpenAI-compatible APIs (OpenAI, Azure OpenAI, local inference servers like vLLM/Ollama)
anthropic
Anthropic Claude API
gemini
Google Gemini API
qwen-oauth
Qwen OAuth (hard-coded, cannot be overridden in
modelProviders
)
[!warning]
If an invalid auth type key is used (e.g., a typo like
"openai-custom"
), the configuration will be
silently skipped
and the models will not appear in the
/model
picker. Always use one of the supported auth type values listed above.
SDKs Used for API Requests
Qwen Code uses the following official SDKs to send requests to each provider:
Auth Type
SDK Package
openai
openai
- Official OpenAI Node.js SDK
anthropic
@anthropic-ai/sdk
- Official Anthropic SDK
gemini
@google/genai
- Official Google GenAI SDK
qwen-oauth
openai
with custom provider (DashScope-compatible)
This means the
baseUrl
you configure should be compatible with the corresponding SDK’s expected API format. For example, when using
openai
auth type, the endpoint must accept OpenAI API format requests.
OpenAI-compatible providers (
openai
)
This auth type supports not only OpenAI’s official API but also any OpenAI-compatible endpoint, including aggregated model providers like OpenRouter.
{
"env"
: {
"OPENAI_API_KEY"
:
"sk-your-actual-openai-key-here"
,
"OPENROUTER_API_KEY"
:
"sk-or-your-actual-openrouter-key-here"
},
"modelProviders"
: {
"openai"
: [
{
"id"
:
"gpt-4o"
,
"name"
:
"GPT-4o"
,
"envKey"
:
"OPENAI_API_KEY"
,
"baseUrl"
:
"https://api.openai.com/v1"
,
"generationConfig"
: {
"timeout"
:
60000
,
"maxRetries"
:
3
,
"enableCacheControl"
:
true
,
"contextWindowSize"
:
128000
,
"modalities"
: {
"image"
:
true
},
"customHeaders"
: {
"X-Client-Request-ID"
:
"req-123"
},
"extra_body"
: {
"enable_thinking"
:
true
,
"service_tier"
:
"priority"
},
"samplingParams"
: {
"temperature"
:
0.2
,
"top_p"
:
0.8
,
"max_tokens"
:
4096
,
"presence_penalty"
:
0.1
,
"frequency_penalty"
:
0.1
}
}
},
{
"id"
:
"gpt-4o-mini"
,
"name"
:
"GPT-4o Mini"
,
"envKey"
:
"OPENAI_API_KEY"
,
"baseUrl"
:
"https://api.openai.com/v1"
,
"generationConfig"
: {
"timeout"
:
30000
,
"samplingParams"
: {
"temperature"
:
0.5
,
"max_tokens"
:
2048
}
}
},
{
"id"
:
"openai/gpt-4o"
,
"name"
:
"GPT-4o (via OpenRouter)"
,
"envKey"
:
"OPENROUTER_API_KEY"
,
"baseUrl"
:
"https://openrouter.ai/api/v1"
,
"generationConfig"
: {
"timeout"
:
120000
,
"maxRetries"
:
3
,
"samplingParams"
: {
"temperature"
:
0.7
}
}
}
]
}
}
Anthropic (
anthropic
)
{
"env"
: {
"ANTHROPIC_API_KEY"
:
"sk-ant-your-actual-anthropic-key-here"
},
"modelProviders"
: {
"anthropic"
: [
{
"id"
:
"claude-3-5-sonnet"
,
"name"
:
"Claude 3.5 Sonnet"
,
"envKey"
:
"ANTHROPIC_API_KEY"
,
"baseUrl"
:
"https://api.anthropic.com/v1"
,
"generationConfig"
: {
"timeout"
:
120000
,
"maxRetries"
:
3
,
"contextWindowSize"
:
200000
,
"samplingParams"
: {
"temperature"
:
0.7
,
"max_tokens"
:
8192
,
"top_p"
:
0.9
}
}
},
{
"id"
:
"claude-3-opus"
,
"name"
:
"Claude 3 Opus"
,
"envKey"
:
"ANTHROPIC_API_KEY"
,
"baseUrl"
:
"https://api.anthropic.com/v1"
,
"generationConfig"
: {
"timeout"
:
180000
,
"samplingParams"
: {
"temperature"
:
0.3
,
"max_tokens"
:
4096
}
}
}
]
}
}
Google Gemini (
gemini
)
{
"env"
: {
"GEMINI_API_KEY"
:
"AIza-your-actual-gemini-key-here"
},
"modelProviders"
: {
"gemini"
: [
{
"id"
:
"gemini-2.0-flash"
,
"name"
:
"Gemini 2.0 Flash"
,
"envKey"
:
"GEMINI_API_KEY"
,
"baseUrl"
:
"https://generativelanguage.googleapis.com"
,
"capabilities"
: {
"vision"
:
true
},
"generationConfig"
: {
"timeout"
:
60000
,
"maxRetries"
:
2
,
"contextWindowSize"
:
1000000
,
"schemaCompliance"
:
"auto"
,
"samplingParams"
: {
"temperature"
:
0.4
,
"top_p"
:
0.95
,
"max_tokens"
:
8192
,
"top_k"
:
40
}
}
}
]
}
}
Local Self-Hosted Models (via OpenAI-compatible API)
Most local inference servers (vLLM, Ollama, LM Studio, etc.) provide an OpenAI-compatible API endpoint. Configure them using the
openai
auth type with a local
baseUrl
:
{
"env"
: {
"OLLAMA_API_KEY"
:
"ollama"
,
"VLLM_API_KEY"
:
"not-needed"
,
"LMSTUDIO_API_KEY"
:
"lm-studio"
},
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen2.5-7b"
,
"name"
:
"Qwen2.5 7B (Ollama)"
,
"envKey"
:
"OLLAMA_API_KEY"
,
"baseUrl"
:
"http://localhost:11434/v1"
,
"generationConfig"
: {
"timeout"
:
300000
,
"maxRetries"
:
1
,
"contextWindowSize"
:
32768
,
"samplingParams"
: {
"temperature"
:
0.7
,
"top_p"
:
0.9
,
"max_tokens"
:
4096
}
}
},
{
"id"
:
"llama-3.1-8b"
,
"name"
:
"Llama 3.1 8B (vLLM)"
,
"envKey"
:
"VLLM_API_KEY"
,
"baseUrl"
:
"http://localhost:8000/v1"
,
"generationConfig"
: {
"timeout"
:
120000
,
"maxRetries"
:
2
,
"contextWindowSize"
:
128000
,
"samplingParams"
: {
"temperature"
:
0.6
,
"max_tokens"
:
8192
}
}
},
{
"id"
:
"local-model"
,
"name"
:
"Local Model (LM Studio)"
,
"envKey"
:
"LMSTUDIO_API_KEY"
,
"baseUrl"
:
"http://localhost:1234/v1"
,
"generationConfig"
: {
"timeout"
:
60000
,
"samplingParams"
: {
"temperature"
:
0.5
}
}
}
]
}
}
For local servers that don’t require authentication, you can use any placeholder value for the API key:
# For Ollama (no auth required)
export
OLLAMA_API_KEY
=
"ollama"
# For vLLM (if no auth is configured)
export
VLLM_API_KEY
=
"not-needed"
Note
The
extra_body
parameter is
only supported for OpenAI-compatible providers
(
openai
,
qwen-oauth
). It is ignored for Anthropic, and Gemini providers.
Note
About
envKey
: The
envKey
field specifies the
name of an environment variable
, not the actual API key value. For the configuration to work, you need to ensure the corresponding environment variable is set with your real API key. There are two ways to do this:
Option 1: Using a
.env
file
(recommended for security):
# ~/.qwen/.env (or project root)
OPENAI_API_KEY
=
sk-your-actual-key-here
Be sure to add
.env
to your
.gitignore
to prevent accidentally committing secrets.
Option 2: Using the
env
field in
settings.json
(as shown in the examples above):
{
"env"
: {
"OPENAI_API_KEY"
:
"sk-your-actual-key-here"
}
}
Each provider example includes an
env
field to illustrate how the API key should be configured.
Alibaba Cloud Coding Plan
Alibaba Cloud Coding Plan provides a pre-configured set of Qwen models optimized for coding tasks. This feature is available for users with Alibaba Cloud Coding Plan API access and offers a simplified setup experience with automatic model configuration updates.
Overview
When you authenticate with an Alibaba Cloud Coding Plan API key using the
/auth
command, Qwen Code automatically configures the following models:
Model ID
Name
Description
qwen3.5-plus
qwen3.5-plus
Advanced model with thinking enabled
qwen3-coder-plus
qwen3-coder-plus
Optimized for coding tasks
qwen3-max-2026-01-23
qwen3-max-2026-01-23
Latest max model with thinking enabled
Setup
Obtain an Alibaba Cloud Coding Plan API key:
China
:
https://bailian.console.aliyun.com/?tab=model#/efm/coding_plan
International
:
https://modelstudio.console.alibabacloud.com/?tab=dashboard#/efm/coding_plan
Run the
/auth
command in Qwen Code
Select
Alibaba Cloud Coding Plan
Select your region
Enter your API key when prompted
The models will be automatically configured and added to your
/model
picker.
Regions
Alibaba Cloud Coding Plan supports two regions:
Region
Endpoint
Description
China
https://coding.dashscope.aliyuncs.com/v1
Mainland China endpoint
Global/International
https://coding-intl.dashscope.aliyuncs.com/v1
International endpoint
The region is selected during authentication and stored in
settings.json
under
codingPlan.region
. To switch regions, re-run the
/auth
command and select a different region.
API Key Storage
When you configure Coding Plan through the
/auth
command, the API key is stored using the reserved environment variable name
BAILIAN_CODING_PLAN_API_KEY
. By default, it is stored in the
env
field of your
settings.json
file.
Warning
Security Recommendation
: For better security, it is recommended to move the API key from
settings.json
to a separate
.env
file and load it as an environment variable. For example:
# ~/.qwen/.env
BAILIAN_CODING_PLAN_API_KEY
=
your-api-key-here
Then ensure this file is added to your
.gitignore
if you’re using project-level settings.
Automatic Updates
Coding Plan model configurations are versioned. When Qwen Code detects a newer version of the model template, you will be prompted to update. Accepting the update will:
Replace the existing Coding Plan model configurations with the latest versions
Preserve any custom model configurations you’ve added manually
Automatically switch to the first model in the updated configuration
The update process ensures you always have access to the latest model configurations and features without manual intervention.
Manual Configuration (Advanced)
If you prefer to manually configure Coding Plan models, you can add them to your
settings.json
like any OpenAI-compatible provider:
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"qwen3-coder-plus"
,
"name"
:
"qwen3-coder-plus"
,
"description"
:
"Qwen3-Coder via Alibaba Cloud Coding Plan"
,
"envKey"
:
"YOUR_CUSTOM_ENV_KEY"
,
"baseUrl"
:
"https://coding.dashscope.aliyuncs.com/v1"
}
]
}
}
Note
When using manual configuration:
You can use any environment variable name for
envKey
You do not need to configure
codingPlan.*
Automatic updates will not apply
to manually configured Coding Plan models
Warning
If you also use automatic Coding Plan configuration, automatic updates may overwrite your manual configurations if they use the same
envKey
and
baseUrl
as the automatic configuration. To avoid this, ensure your manual configuration uses a different
envKey
if possible.
Resolution Layers and Atomicity
The effective auth/model/credential values are chosen per field using the following precedence (first present wins). You can combine
--auth-type
with
--model
to point directly at a provider entry; these CLI flags run before other layers.
Layer (highest → lowest)
authType
model
apiKey
baseUrl
apiKeyEnvKey
proxy
Programmatic overrides
/auth
/auth
input
/auth
input
/auth
input
—
—
Model provider selection
—
modelProvider.id
env[modelProvider.envKey]
modelProvider.baseUrl
modelProvider.envKey
—
CLI arguments
--auth-type
--model
--openaiApiKey
(or provider-specific equivalents)
--openaiBaseUrl
(or provider-specific equivalents)
—
—
Environment variables
—
Provider-specific mapping (e.g.
OPENAI_MODEL
)
Provider-specific mapping (e.g.
OPENAI_API_KEY
)
Provider-specific mapping (e.g.
OPENAI_BASE_URL
)
—
—
Settings (
settings.json
)
security.auth.selectedType
model.name
security.auth.apiKey
security.auth.baseUrl
—
—
Default / computed
Falls back to
AuthType.QWEN_OAUTH
Built-in default (OpenAI ⇒
qwen3-coder-plus
)
—
—
—
Config.getProxy()
if configured
*When present, CLI auth flags override settings. Otherwise,
security.auth.selectedType
or the implicit default determine the auth type. Qwen OAuth and OpenAI are the only auth types surfaced without extra configuration.
Warning
Deprecation of
security.auth.apiKey
and
security.auth.baseUrl
:
Directly configuring API credentials via
security.auth.apiKey
and
security.auth.baseUrl
in
settings.json
is deprecated. These settings were used in historical versions for credentials entered through the UI, but the credential input flow was removed in version 0.10.1. These fields will be fully removed in a future release.
It is strongly recommended to migrate to
modelProviders
for all model and credential configurations. Use
envKey
in
modelProviders
to reference environment variables for secure credential management instead of hardcoding credentials in settings files.
Generation Config Layering: The Impermeable Provider Layer
The configuration resolution follows a strict layering model with one crucial rule:
the modelProvider layer is impermeable
.
How it works
When a modelProvider model IS selected
(e.g., via
/model
command choosing a provider-configured model):
The entire
generationConfig
from the provider is applied
atomically
The provider layer is completely impermeable
— lower layers (CLI, env, settings) do not participate in generationConfig resolution at all
All fields defined in
modelProviders[].generationConfig
use the provider’s values
All fields
not defined
by the provider are set to
undefined
(not inherited from settings)
This ensures provider configurations act as a complete, self-contained “sealed package”
When NO modelProvider model is selected
(e.g., using
--model
with a raw model ID, or using CLI/env/settings directly):
The resolution falls through to lower layers
Fields are populated from CLI → env → settings → defaults
This creates a
Runtime Model
(see next section)
Per-field precedence for
generationConfig
Priority
Source
Behavior
1
Programmatic overrides
Runtime
/model
,
/auth
changes
2
modelProviders[authType][].generationConfig
Impermeable layer
- completely replaces all generationConfig fields; lower layers do not participate
3
settings.model.generationConfig
Only used for
Runtime Models
(when no provider model is selected)
4
Content-generator defaults
Provider-specific defaults (e.g., OpenAI vs Gemini) - only for Runtime Models
Atomic field treatment
The following fields are treated as atomic objects - provider values completely replace the entire object, no merging occurs:
samplingParams
- Temperature, top_p, max_tokens, etc.
customHeaders
- Custom HTTP headers
extra_body
- Extra request body parameters
Example
// User settings (~/.qwen/settings.json)
{
"model"
: {
"generationConfig"
: {
"timeout"
:
30000
,
"samplingParams"
: {
"temperature"
:
0.5
,
"max_tokens"
:
1000
}
}
}
}
// modelProviders configuration
{
"modelProviders"
: {
"openai"
: [{
"id"
:
"gpt-4o"
,
"envKey"
:
"OPENAI_API_KEY"
,
"generationConfig"
: {
"timeout"
:
60000
,
"samplingParams"
: {
"temperature"
:
0.2
}
}
}]
}
}
When
gpt-4o
is selected from modelProviders:
timeout
= 60000 (from provider, overrides settings)
samplingParams.temperature
= 0.2 (from provider, completely replaces settings object)
samplingParams.max_tokens
=
undefined
(not defined in provider, and provider layer does not inherit from settings — fields are explicitly set to undefined if not provided)
When using a raw model via
--model gpt-4
(not from modelProviders, creates a Runtime Model):
timeout
= 30000 (from settings)
samplingParams.temperature
= 0.5 (from settings)
samplingParams.max_tokens
= 1000 (from settings)
The merge strategy for
modelProviders
itself is REPLACE: the entire
modelProviders
from project settings will override the corresponding section in user settings, rather than merging the two.
Reasoning / thinking configuration
The optional
reasoning
field under
generationConfig
controls how aggressively the model reasons before responding. The Anthropic and Gemini converters always honor it. The OpenAI-compatible pipeline honors it
unless
generationConfig.samplingParams
is set — see the “Interaction with
samplingParams
” caveat below.
{
"modelProviders"
: {
"openai"
: [
{
"id"
:
"deepseek-v4-pro"
,
"name"
:
"DeepSeek V4 Pro"
,
"baseUrl"
:
"https://api.deepseek.com/v1"
,
"envKey"
:
"DEEPSEEK_API_KEY"
,
"generationConfig"
: {
// The four-tier scale:
//   'low'    | 'medium' — server-mapped to 'high' on DeepSeek
//   'high'   — default reasoning intensity
//   'max'    — DeepSeek-specific extra-strong tier
// Or set `false` to disable reasoning entirely.
"reasoning"
: {
"effort"
:
"max"
},
},
},
],
},
}
Per-provider behavior
Protocol / provider
Wire shape
Notes
OpenAI / DeepSeek
(
api.deepseek.com
)
Flat
reasoning_effort: <effort>
body parameter
When
reasoning.effort
is set in the nested config shape, it’s rewritten to flat
reasoning_effort
and
'low'
/
'medium'
are normalized to
'high'
,
'xhigh'
to
'max'
— mirroring DeepSeek’s
server-side back-compat
. Top-level
samplingParams.reasoning_effort
or
extra_body.reasoning_effort
overrides skip this normalization and ship verbatim.
OpenAI
(other compatible servers)
reasoning: { effort, ... }
passed through verbatim
Set via
samplingParams
(e.g.
samplingParams.reasoning_effort
for GPT-5/o-series) when the provider expects a different shape.
Anthropic
(real
api.anthropic.com
)
output_config: { effort }
plus the
effort-2025-11-24
beta header
Real Anthropic accepts
'low'
/
'medium'
/
'high'
only.
'max'
is
clamped to
'high'
with a
debugLogger.warn
line (once per generator); if you want max effort, switch the baseURL to a DeepSeek-compatible endpoint that supports it.
Anthropic
(
api.deepseek.com/anthropic
)
Same
output_config: { effort }
+ beta header
'max'
is passed through unchanged.
Gemini
(
@google/genai
)
thinkingConfig: { includeThoughts: true, thinkingLevel }
'low'
→
LOW
,
'high'
/
'max'
→
HIGH
, others →
THINKING_LEVEL_UNSPECIFIED
(Gemini has no
MAX
tier).
reasoning: false
Setting
reasoning: false
(the literal boolean) explicitly disables thinking on every provider — useful for cheap side queries that don’t benefit from reasoning. This is honored at the request level too via
request.config.thinkingConfig.includeThoughts: false
for one-off calls (e.g. suggestion generation).
On a
api.deepseek.com
baseURL, the OpenAI pipeline emits the explicit
thinking: { type: 'disabled' }
field that DeepSeek V4+ requires — the server-side default is
'enabled'
, so simply omitting
reasoning_effort
would still pay thinking latency/cost. Self-hosted DeepSeek backends (sglang/vllm) and other OpenAI-compatible servers do
not
receive this field; if you need to disable thinking on those, inject
thinking: { type: 'disabled' }
(or whatever knob your inference framework exposes) via
samplingParams
/
extra_body
.
Interaction with
samplingParams
(OpenAI-compatible only)
Warning
When
generationConfig.samplingParams
is set on an OpenAI-compatible provider, the pipeline ships those keys to the wire
verbatim
and skips the separate
reasoning
injection entirely. So a config like
{ samplingParams: { temperature: 0.5 }, reasoning: { effort: 'max' } }
will silently drop the reasoning field on OpenAI/DeepSeek requests.
If you set
samplingParams
, include the reasoning knob inside it directly — for DeepSeek that’s
samplingParams.reasoning_effort
, for GPT-5/o-series it’s
samplingParams.reasoning_effort
(their flat field) or
samplingParams.reasoning
(the nested object). For OpenRouter and other providers the field name varies; consult the provider docs.
The Anthropic and Gemini converters are unaffected — they always read
reasoning.effort
directly regardless of
samplingParams
.
budget_tokens
You can pin an exact thinking-token budget by including
budget_tokens
alongside
effort
:
"reasoning"
: {
"effort"
:
"high"
,
"budget_tokens"
:
50000
}
For Anthropic this becomes
thinking.budget_tokens
. For OpenAI/DeepSeek the field is preserved but currently ignored by the server —
reasoning_effort
is the load-bearing knob.
Provider Models vs Runtime Models
Qwen Code distinguishes between two types of model configurations:
Provider Model
Defined in
modelProviders
configuration
Has a complete, atomic configuration package
When selected, its configuration is applied as an impermeable layer
Appears in
/model
command list with full metadata (name, description, capabilities)
Recommended for multi-model workflows and team consistency
Runtime Model
Created dynamically when using raw model IDs via CLI (
--model
), environment variables, or settings
Not defined in
modelProviders
Configuration is built by “projecting” through resolution layers (CLI → env → settings → defaults)
Automatically captured as a
RuntimeModelSnapshot
when a complete configuration is detected
Allows reuse without re-entering credentials
RuntimeModelSnapshot lifecycle
When you configure a model without using
modelProviders
, Qwen Code automatically creates a RuntimeModelSnapshot to preserve your configuration:
# This creates a RuntimeModelSnapshot with ID: $runtime|openai|my-custom-model
qwen
--auth-type
openai
--model
my-custom-model
--openaiApiKey
$KEY
--openaiBaseUrl
https://api.example.com/v1
The snapshot:
Captures model ID, API key, base URL, and generation config
Persists across sessions (stored in memory during runtime)
Appears in the
/model
command list as a runtime option
Can be switched to using
/model $runtime|openai|my-custom-model
Key differences
Aspect
Provider Model
Runtime Model
Configuration source
modelProviders
in settings
CLI, env, settings layers
Configuration atomicity
Complete, impermeable package
Layered, each field resolved independently
Reusability
Always available in
/model
list
Captured as snapshot, appears if complete
Team sharing
Yes (via committed settings)
No (user-local)
Credential storage
Reference via
envKey
only
May capture actual key in snapshot
When to use each
Use Provider Models
when: You have standard models shared across a team, need consistent configurations, or want to prevent accidental overrides
Use Runtime Models
when: Quickly testing a new model, using temporary credentials, or working with ad-hoc endpoints
Selection Persistence and Recommendations
Important
Define
modelProviders
in the user-scope
~/.qwen/settings.json
whenever possible and avoid persisting credential overrides in any scope. Keeping the provider catalog in user settings prevents merge/override conflicts between project and user scopes and ensures
/auth
and
/model
updates always write back to a consistent scope.
/model
and
/auth
persist
model.name
(where applicable) and
security.auth.selectedType
to the closest writable scope that already defines
modelProviders
; otherwise they fall back to the user scope. This keeps workspace/user files in sync with the active provider catalog.
Without
modelProviders
, the resolver mixes CLI/env/settings layers, creating Runtime Models. This is fine for single-provider setups but cumbersome when frequently switching. Define provider catalogs whenever multi-model workflows are common so that switches stay atomic, source-attributed, and debuggable.
Last updated on
May 18, 2026
Themes
Introduction</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/model-providers/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Fork Subagent Design</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/fork-subagent/fork-subagent-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/fork-subagent/fork-subagent-design/</guid>
  <pubDate>Mon, 26 Feb 2024 00:00:00 +0000</pubDate>
  <category>Agent</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Fork Subagent
Fork Subagent Design
Fork Subagent Design
Implicit fork subagent that inherits the parent’s full conversation context and shares prompt cache for cost-efficient parallel task execution.
Overview
When the Agent tool is called without
subagent_type
, it triggers an implicit
fork
— a background subagent that inherits the parent’s conversation history, system prompt, and tool definitions. The fork uses
CacheSafeParams
to ensure its API requests share the same prefix as the paren...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Fork Subagent
Fork Subagent Design
Fork Subagent Design
Implicit fork subagent that inherits the parent’s full conversation context and shares prompt cache for cost-efficient parallel task execution.
Overview
When the Agent tool is called without
subagent_type
, it triggers an implicit
fork
— a background subagent that inherits the parent’s conversation history, system prompt, and tool definitions. The fork uses
CacheSafeParams
to ensure its API requests share the same prefix as the parent’s, enabling DashScope prompt cache hits.
Architecture
Parent conversation: [SystemPrompt | Tools | Msg1 | Msg2 | ... | MsgN (model)]
↑ identical prefix for all forks ↑
Fork A: [...MsgN | placeholder results | "Research A"]  ← shared cache
Fork B: [...MsgN | placeholder results | "Modify B"]    ← shared cache
Fork C: [...MsgN | placeholder results | "Test C"]      ← shared cache
Key Components
1. FORK_AGENT (
forkSubagent.ts
)
Synthetic agent config, not registered in
builtInAgents
. Has a fallback
systemPrompt
but in practice uses the parent’s rendered system prompt via
generationConfigOverride
.
2. CacheSafeParams Integration (
agent.ts
+
forkedQuery.ts
)
agent.ts (fork path)
│
├── getCacheSafeParams()          ← parent's generationConfig snapshot
│     ├── generationConfig        ← systemInstruction + tools + temp/topP
│     └── history                 ← (not used — we build extraHistory instead)
│
├── forkGenerationConfig          ← passed as generationConfigOverride
└── forkToolsOverride             ← FunctionDeclaration[] extracted from tools
│
▼
AgentHeadless.execute(context, signal, {
extraHistory,                   ← parent conversation history
generationConfigOverride,       ← parent's exact systemInstruction + tools
toolsOverride,                  ← parent's exact tool declarations
})
│
▼
AgentCore.createChat(context, {
extraHistory,
generationConfigOverride,       ← bypasses buildChatSystemPrompt()
})                                   AND skips getInitialChatHistory()
│                              (extraHistory already has env context)
▼
new GeminiChat(config, generationConfig, startHistory)
↑ byte-identical to parent's config
3. History Construction (
agent.ts
+
forkSubagent.ts
)
The fork’s
extraHistory
must end with a model message to maintain Gemini API’s user/model alternation when
agent-headless
sends the
task_prompt
.
Three cases:
Parent history ends with
extraHistory construction
task_prompt
model
(no function calls)
[...rawHistory]
(unchanged)
buildChildMessage(directive)
model
(with function calls)
[...rawHistory, model(clone), user(responses+directive), model(ack)]
'Begin.'
user
(unusual)
rawHistory.slice(0, -1)
(drop trailing user)
buildChildMessage(directive)
4. Recursive Fork Prevention (
forkSubagent.ts
)
isInForkChild()
scans conversation history for the
<fork-boilerplate>
tag. If found, the fork attempt is rejected with an error message.
5. Background Execution (
agent.ts
)
Fork uses
void executeSubagent()
(fire-and-forget) and returns
FORK_PLACEHOLDER_RESULT
immediately to the parent. Errors in the background task are caught, logged, and reflected in the display state.
Data Flow
1. Model calls Agent tool (no subagent_type)
2. agent.ts: import forkSubagent.js
3. agent.ts: getCacheSafeParams() → forkGenerationConfig + forkToolsOverride
4. agent.ts: build extraHistory from parent's getHistory(true)
5. agent.ts: build forkTaskPrompt (directive or 'Begin.')
6. agent.ts: createAgentHeadless(FORK_AGENT, ...)
7. agent.ts: void executeSubagent() — background
8. agent.ts: return FORK_PLACEHOLDER_RESULT to parent immediately
9. Background:
a. AgentHeadless.execute(context, signal, {extraHistory, generationConfigOverride, toolsOverride})
b. AgentCore.createChat() — uses parent's generationConfig (cache-shared)
c. runReasoningLoop() — uses parent's tool declarations
d. Fork executes tools, produces result
e. updateDisplay() with final status
Graceful Degradation
If
getCacheSafeParams()
returns null (first turn, no history yet), the fork falls back to:
FORK_AGENT.systemPrompt
for system instruction
prepareTools()
for tool declarations
This ensures the fork always works, even without cache sharing.
Files
File
Role
packages/core/src/agents/runtime/forkSubagent.ts
FORK_AGENT config, buildForkedMessages(), isInForkChild(), buildChildMessage()
packages/core/src/tools/agent.ts
Fork path: CacheSafeParams retrieval, extraHistory construction, background execution
packages/core/src/agents/runtime/agent-headless.ts
execute() options: generationConfigOverride, toolsOverride
packages/core/src/agents/runtime/agent-core.ts
CreateChatOptions.generationConfigOverride
packages/core/src/followup/forkedQuery.ts
CacheSafeParams infrastructure (existing, no changes)
Last updated on
May 18, 2026
Compact Mode Design: Competitive Analysis & Optimization
OpenRouter Auth and Model Management Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/fork-subagent/fork-subagent-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Fork Subagent
Fork Subagent Design
Fork Subagent Design
Implicit fork subagent that inherits the parent’s full conversation context and shares prompt cache for cost-efficient parallel task execution.
Overview
When the Agent tool is called without
subagent_type
, it triggers an implicit
fork
— a background subagent that inherits the parent’s conversation history, system prompt, and tool definitions. The fork uses
CacheSafeParams
to ensure its API requests share the same prefix as the paren...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Fork Subagent
Fork Subagent Design
Fork Subagent Design
Implicit fork subagent that inherits the parent’s full conversation context and shares prompt cache for cost-efficient parallel task execution.
Overview
When the Agent tool is called without
subagent_type
, it triggers an implicit
fork
— a background subagent that inherits the parent’s conversation history, system prompt, and tool definitions. The fork uses
CacheSafeParams
to ensure its API requests share the same prefix as the parent’s, enabling DashScope prompt cache hits.
Architecture
Parent conversation: [SystemPrompt | Tools | Msg1 | Msg2 | ... | MsgN (model)]
↑ identical prefix for all forks ↑
Fork A: [...MsgN | placeholder results | "Research A"]  ← shared cache
Fork B: [...MsgN | placeholder results | "Modify B"]    ← shared cache
Fork C: [...MsgN | placeholder results | "Test C"]      ← shared cache
Key Components
1. FORK_AGENT (
forkSubagent.ts
)
Synthetic agent config, not registered in
builtInAgents
. Has a fallback
systemPrompt
but in practice uses the parent’s rendered system prompt via
generationConfigOverride
.
2. CacheSafeParams Integration (
agent.ts
+
forkedQuery.ts
)
agent.ts (fork path)
│
├── getCacheSafeParams()          ← parent's generationConfig snapshot
│     ├── generationConfig        ← systemInstruction + tools + temp/topP
│     └── history                 ← (not used — we build extraHistory instead)
│
├── forkGenerationConfig          ← passed as generationConfigOverride
└── forkToolsOverride             ← FunctionDeclaration[] extracted from tools
│
▼
AgentHeadless.execute(context, signal, {
extraHistory,                   ← parent conversation history
generationConfigOverride,       ← parent's exact systemInstruction + tools
toolsOverride,                  ← parent's exact tool declarations
})
│
▼
AgentCore.createChat(context, {
extraHistory,
generationConfigOverride,       ← bypasses buildChatSystemPrompt()
})                                   AND skips getInitialChatHistory()
│                              (extraHistory already has env context)
▼
new GeminiChat(config, generationConfig, startHistory)
↑ byte-identical to parent's config
3. History Construction (
agent.ts
+
forkSubagent.ts
)
The fork’s
extraHistory
must end with a model message to maintain Gemini API’s user/model alternation when
agent-headless
sends the
task_prompt
.
Three cases:
Parent history ends with
extraHistory construction
task_prompt
model
(no function calls)
[...rawHistory]
(unchanged)
buildChildMessage(directive)
model
(with function calls)
[...rawHistory, model(clone), user(responses+directive), model(ack)]
'Begin.'
user
(unusual)
rawHistory.slice(0, -1)
(drop trailing user)
buildChildMessage(directive)
4. Recursive Fork Prevention (
forkSubagent.ts
)
isInForkChild()
scans conversation history for the
<fork-boilerplate>
tag. If found, the fork attempt is rejected with an error message.
5. Background Execution (
agent.ts
)
Fork uses
void executeSubagent()
(fire-and-forget) and returns
FORK_PLACEHOLDER_RESULT
immediately to the parent. Errors in the background task are caught, logged, and reflected in the display state.
Data Flow
1. Model calls Agent tool (no subagent_type)
2. agent.ts: import forkSubagent.js
3. agent.ts: getCacheSafeParams() → forkGenerationConfig + forkToolsOverride
4. agent.ts: build extraHistory from parent's getHistory(true)
5. agent.ts: build forkTaskPrompt (directive or 'Begin.')
6. agent.ts: createAgentHeadless(FORK_AGENT, ...)
7. agent.ts: void executeSubagent() — background
8. agent.ts: return FORK_PLACEHOLDER_RESULT to parent immediately
9. Background:
a. AgentHeadless.execute(context, signal, {extraHistory, generationConfigOverride, toolsOverride})
b. AgentCore.createChat() — uses parent's generationConfig (cache-shared)
c. runReasoningLoop() — uses parent's tool declarations
d. Fork executes tools, produces result
e. updateDisplay() with final status
Graceful Degradation
If
getCacheSafeParams()
returns null (first turn, no history yet), the fork falls back to:
FORK_AGENT.systemPrompt
for system instruction
prepareTools()
for tool declarations
This ensures the fork always works, even without cache sharing.
Files
File
Role
packages/core/src/agents/runtime/forkSubagent.ts
FORK_AGENT config, buildForkedMessages(), isInForkChild(), buildChildMessage()
packages/core/src/tools/agent.ts
Fork path: CacheSafeParams retrieval, extraHistory construction, background execution
packages/core/src/agents/runtime/agent-headless.ts
execute() options: generationConfigOverride, toolsOverride
packages/core/src/agents/runtime/agent-core.ts
CreateChatOptions.generationConfigOverride
packages/core/src/followup/forkedQuery.ts
CacheSafeParams infrastructure (existing, no changes)
Last updated on
May 18, 2026
Compact Mode Design: Competitive Analysis & Optimization
OpenRouter Auth and Model Management Design</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/fork-subagent/fork-subagent-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code tools</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/introduction/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/introduction/</guid>
  <pubDate>Sat, 24 Feb 2024 00:00:00 +0000</pubDate>
  <category>Getting Started</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Introduction
Qwen Code tools
Qwen Code includes built-in tools that the model uses to interact with your local environment, access information, and perform actions. These tools enhance the CLI’s capabilities, enabling it to go beyond text generation and assist with a wide range of tasks.
Overview of Qwen Code tools
In the context of Qwen Code, tools are specific functions or modules that the model can request to be executed. For example, if you ask the model to “Summarize t...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Introduction
Qwen Code tools
Qwen Code includes built-in tools that the model uses to interact with your local environment, access information, and perform actions. These tools enhance the CLI’s capabilities, enabling it to go beyond text generation and assist with a wide range of tasks.
Overview of Qwen Code tools
In the context of Qwen Code, tools are specific functions or modules that the model can request to be executed. For example, if you ask the model to “Summarize the contents of
my_document.txt
,” it will likely identify the need to read that file and will request the execution of the
read_file
tool.
The core component (
packages/core
) manages these tools, presents their definitions (schemas) to the model, executes them when requested, and returns the results to the model for further processing into a user-facing response.
These tools provide the following capabilities:
Access local information:
Tools allow the model to access your local file system, read file contents, list directories, etc.
Execute commands:
With tools like
run_shell_command
, the model can run shell commands (with appropriate safety measures and user confirmation).
Interact with the web:
Tools can fetch content from URLs.
Take actions:
Tools can modify files, write new files, or perform other actions on your system (again, typically with safeguards).
Ground responses:
By using tools to fetch real-time or specific local data, responses can be more accurate, relevant, and grounded in your actual context.
How to use Qwen Code tools
To use Qwen Code tools, provide a prompt to the CLI. The process works as follows:
You provide a prompt to the CLI.
The CLI sends the prompt to the core.
The core, along with your prompt and conversation history, sends a list of available tools and their descriptions/schemas to the configured model API.
The model analyzes your request. If it determines that a tool is needed, its response will include a request to execute a specific tool with certain parameters.
The core receives this tool request, validates it, and (often after user confirmation for sensitive operations) executes the tool.
The output from the tool is sent back to the model.
The model uses the tool’s output to formulate its final answer, which is then sent back through the core to the CLI and displayed to you.
You will typically see messages in the CLI indicating when a tool is being called and whether it succeeded or failed.
Security and confirmation
Many tools, especially those that can modify your file system or execute commands (
write_file
,
edit
,
run_shell_command
), are designed with safety in mind. Qwen Code will typically:
Require confirmation:
Prompt you before executing potentially sensitive operations, showing you what action is about to be taken.
Utilize sandboxing:
All tools are subject to restrictions enforced by sandboxing (see
Sandboxing in Qwen Code
). This means that when operating in a sandbox, any tools (including MCP servers) you wish to use must be available
inside
the sandbox environment. For example, to run an MCP server through
npx
, the
npx
executable must be installed within the sandbox’s Docker image or be available in the
sandbox-exec
environment.
It’s important to always review confirmation prompts carefully before allowing a tool to proceed.
Learn more about Qwen Code’s tools
Qwen Code’s built-in tools can be broadly categorized as follows:
File System Tools
:
For interacting with files and directories (reading, writing, listing, searching, etc.).
Shell Tool
(
run_shell_command
):
For executing shell commands.
Web Fetch Tool
(
web_fetch
):
For retrieving content from URLs.
Multi-File Read Tool
(
read_many_files
):
A specialized tool for reading content from multiple files or directories, often used by the
@
command.
Memory Tool
(
save_memory
):
For saving and recalling information across sessions.
Todo Write Tool
(
todo_write
):
For creating and managing structured task lists during coding sessions.
Task Tool
(
task
):
For delegating complex tasks to specialized subagents.
Exit Plan Mode Tool
(
exit_plan_mode
):
For exiting plan mode and proceeding with implementation.
Additionally, these tools incorporate:
MCP servers
: MCP servers act as a bridge between the model and your local environment or other services like APIs.
MCP Quick Start Guide
: Get started with MCP in 5 minutes with practical examples
MCP Example Configurations
: Ready-to-use configurations for common scenarios
Web Search via MCP
: Connect to web search services (Bailian, Tavily, GLM) through MCP
MCP Testing & Validation
: Test and validate your MCP server setups
Sandboxing
: Sandboxing isolates the model and its changes from your environment to reduce potential risk.
Last updated on
May 18, 2026
Channel Plugin Guide
File System</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/introduction/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Introduction
Qwen Code tools
Qwen Code includes built-in tools that the model uses to interact with your local environment, access information, and perform actions. These tools enhance the CLI’s capabilities, enabling it to go beyond text generation and assist with a wide range of tasks.
Overview of Qwen Code tools
In the context of Qwen Code, tools are specific functions or modules that the model can request to be executed. For example, if you ask the model to “Summarize t...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Introduction
Qwen Code tools
Qwen Code includes built-in tools that the model uses to interact with your local environment, access information, and perform actions. These tools enhance the CLI’s capabilities, enabling it to go beyond text generation and assist with a wide range of tasks.
Overview of Qwen Code tools
In the context of Qwen Code, tools are specific functions or modules that the model can request to be executed. For example, if you ask the model to “Summarize the contents of
my_document.txt
,” it will likely identify the need to read that file and will request the execution of the
read_file
tool.
The core component (
packages/core
) manages these tools, presents their definitions (schemas) to the model, executes them when requested, and returns the results to the model for further processing into a user-facing response.
These tools provide the following capabilities:
Access local information:
Tools allow the model to access your local file system, read file contents, list directories, etc.
Execute commands:
With tools like
run_shell_command
, the model can run shell commands (with appropriate safety measures and user confirmation).
Interact with the web:
Tools can fetch content from URLs.
Take actions:
Tools can modify files, write new files, or perform other actions on your system (again, typically with safeguards).
Ground responses:
By using tools to fetch real-time or specific local data, responses can be more accurate, relevant, and grounded in your actual context.
How to use Qwen Code tools
To use Qwen Code tools, provide a prompt to the CLI. The process works as follows:
You provide a prompt to the CLI.
The CLI sends the prompt to the core.
The core, along with your prompt and conversation history, sends a list of available tools and their descriptions/schemas to the configured model API.
The model analyzes your request. If it determines that a tool is needed, its response will include a request to execute a specific tool with certain parameters.
The core receives this tool request, validates it, and (often after user confirmation for sensitive operations) executes the tool.
The output from the tool is sent back to the model.
The model uses the tool’s output to formulate its final answer, which is then sent back through the core to the CLI and displayed to you.
You will typically see messages in the CLI indicating when a tool is being called and whether it succeeded or failed.
Security and confirmation
Many tools, especially those that can modify your file system or execute commands (
write_file
,
edit
,
run_shell_command
), are designed with safety in mind. Qwen Code will typically:
Require confirmation:
Prompt you before executing potentially sensitive operations, showing you what action is about to be taken.
Utilize sandboxing:
All tools are subject to restrictions enforced by sandboxing (see
Sandboxing in Qwen Code
). This means that when operating in a sandbox, any tools (including MCP servers) you wish to use must be available
inside
the sandbox environment. For example, to run an MCP server through
npx
, the
npx
executable must be installed within the sandbox’s Docker image or be available in the
sandbox-exec
environment.
It’s important to always review confirmation prompts carefully before allowing a tool to proceed.
Learn more about Qwen Code’s tools
Qwen Code’s built-in tools can be broadly categorized as follows:
File System Tools
:
For interacting with files and directories (reading, writing, listing, searching, etc.).
Shell Tool
(
run_shell_command
):
For executing shell commands.
Web Fetch Tool
(
web_fetch
):
For retrieving content from URLs.
Multi-File Read Tool
(
read_many_files
):
A specialized tool for reading content from multiple files or directories, often used by the
@
command.
Memory Tool
(
save_memory
):
For saving and recalling information across sessions.
Todo Write Tool
(
todo_write
):
For creating and managing structured task lists during coding sessions.
Task Tool
(
task
):
For delegating complex tasks to specialized subagents.
Exit Plan Mode Tool
(
exit_plan_mode
):
For exiting plan mode and proceeding with implementation.
Additionally, these tools incorporate:
MCP servers
: MCP servers act as a bridge between the model and your local environment or other services like APIs.
MCP Quick Start Guide
: Get started with MCP in 5 minutes with practical examples
MCP Example Configurations
: Ready-to-use configurations for common scenarios
Web Search via MCP
: Connect to web search services (Bailian, Tavily, GLM) through MCP
MCP Testing & Validation
: Test and validate your MCP server setups
Sandboxing
: Sandboxing isolates the model and its changes from your environment to reduce potential risk.
Last updated on
May 18, 2026
Channel Plugin Guide
File System</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/introduction/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Automation and Triage Processes</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/development/issue-and-pr-automation/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/development/issue-and-pr-automation/</guid>
  <pubDate>Wed, 21 Feb 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Issue and PR Automation
Automation and Triage Processes
This document provides a detailed overview of the automated processes we use to manage and triage issues and pull requests. Our goal is to provide prompt feedback and ensure that contributions are reviewed and integrated efficiently. Understanding this automation will help you as a contributor know what to expect and how to best interact with our repository bots.
Guiding Principle: Issues and Pull Requests
First ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Issue and PR Automation
Automation and Triage Processes
This document provides a detailed overview of the automated processes we use to manage and triage issues and pull requests. Our goal is to provide prompt feedback and ensure that contributions are reviewed and integrated efficiently. Understanding this automation will help you as a contributor know what to expect and how to best interact with our repository bots.
Guiding Principle: Issues and Pull Requests
First and foremost, almost every Pull Request (PR) should be linked to a corresponding Issue. The issue describes the “what” and the “why” (the bug or feature), while the PR is the “how” (the implementation). This separation helps us track work, prioritize features, and maintain clear historical context. Our automation is built around this principle.
Detailed Automation Workflows
Here is a breakdown of the specific automation workflows that run in our repository.
1. When you open an Issue:
Automated Issue Triage
This is the first bot you will interact with when you create an issue. Its job is to perform an initial analysis and apply the correct labels.
Workflow File
:
.github/workflows/qwen-automated-issue-triage.yml
When it runs
: Immediately after an issue is created or reopened.
What it does
:
It uses a Qwen model to analyze the issue’s title and body against a detailed set of guidelines.
Applies one
area/*
label
: Categorizes the issue into a functional area of the project (e.g.,
area/ux
,
area/models
,
area/platform
).
Applies one
kind/*
label
: Identifies the type of issue (e.g.,
kind/bug
,
kind/enhancement
,
kind/question
).
Applies one
priority/*
label
: Assigns a priority from P0 (critical) to P3 (low) based on the described impact.
May apply
status/need-information
: If the issue lacks critical details (like logs or reproduction steps), it will be flagged for more information.
May apply
status/need-retesting
: If the issue references a CLI version that is more than six versions old, it will be flagged for retesting on a current version.
What you should do
:
Fill out the issue template as completely as possible. The more detail you provide, the more accurate the triage will be.
If the
status/need-information
label is added, please provide the requested details in a comment.
2. When you open a Pull Request:
Continuous Integration (CI)
This workflow ensures that all changes meet our quality standards before they can be merged.
Workflow File
:
.github/workflows/ci.yml
When it runs
: On every push to a pull request.
What it does
:
Lint
: Checks that your code adheres to our project’s formatting and style rules.
Test
: Runs our full suite of automated tests across macOS, Windows, and Linux, and on multiple Node.js versions. This is the most time-consuming part of the CI process.
Post Coverage Comment
: After all tests have successfully passed, a bot will post a comment on your PR. This comment provides a summary of how well your changes are covered by tests.
What you should do
:
Ensure all CI checks pass. A green checkmark ✅ will appear next to your commit when everything is successful.
If a check fails (a red “X” ❌), click the “Details” link next to the failed check to view the logs, identify the problem, and push a fix.
3. Ongoing Triage for Pull Requests:
PR Auditing and Label Sync
This workflow runs periodically to ensure all open PRs are correctly linked to issues and have consistent labels.
Workflow File
:
.github/workflows/qwen-scheduled-pr-triage.yml
When it runs
: Every 15 minutes on all open pull requests.
What it does
:
Checks for a linked issue
: The bot scans your PR description for a keyword that links it to an issue (e.g.,
Fixes #123
,
Closes #456
).
Adds
status/need-issue
: If no linked issue is found, the bot will add the
status/need-issue
label to your PR. This is a clear signal that an issue needs to be created and linked.
Synchronizes labels
: If an issue
is
linked, the bot ensures the PR’s labels perfectly match the issue’s labels. It will add any missing labels and remove any that don’t belong, and it will remove the
status/need-issue
label if it was present.
What you should do
:
Always link your PR to an issue.
This is the most important step. Add a line like
Resolves #<issue-number>
to your PR description.
This will ensure your PR is correctly categorized and moves through the review process smoothly.
4. Ongoing Triage for Issues:
Scheduled Issue Triage
This is a fallback workflow to ensure that no issue gets missed by the triage process.
Workflow File
:
.github/workflows/qwen-scheduled-issue-triage.yml
When it runs
: Every hour on all open issues.
What it does
:
It actively seeks out issues that either have no labels at all or still have the
status/need-triage
label.
It then triggers the same powerful QwenCode-based analysis as the initial triage bot to apply the correct labels.
What you should do
:
You typically don’t need to do anything. This workflow is a safety net to ensure every issue is eventually categorized, even if the initial triage fails.
5. Release Automation
This workflow handles the process of packaging and publishing new versions of Qwen Code.
Workflow File
:
.github/workflows/release.yml
When it runs
: On a daily schedule for “nightly” releases, and manually for official patch/minor releases.
What it does
:
Automatically builds the project, bumps the version numbers, and publishes the packages to npm.
Creates a corresponding release on GitHub with generated release notes.
What you should do
:
As a contributor, you don’t need to do anything for this process. You can be confident that once your PR is merged into the
main
branch, your changes will be included in the very next nightly release.
We hope this detailed overview is helpful. If you have any questions about our automation or processes, please don’t hesitate to ask!
Last updated on
May 18, 2026
Integration Tests
Qwen Code Extensions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/issue-and-pr-automation/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Issue and PR Automation
Automation and Triage Processes
This document provides a detailed overview of the automated processes we use to manage and triage issues and pull requests. Our goal is to provide prompt feedback and ensure that contributions are reviewed and integrated efficiently. Understanding this automation will help you as a contributor know what to expect and how to best interact with our repository bots.
Guiding Principle: Issues and Pull Requests
First ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Issue and PR Automation
Automation and Triage Processes
This document provides a detailed overview of the automated processes we use to manage and triage issues and pull requests. Our goal is to provide prompt feedback and ensure that contributions are reviewed and integrated efficiently. Understanding this automation will help you as a contributor know what to expect and how to best interact with our repository bots.
Guiding Principle: Issues and Pull Requests
First and foremost, almost every Pull Request (PR) should be linked to a corresponding Issue. The issue describes the “what” and the “why” (the bug or feature), while the PR is the “how” (the implementation). This separation helps us track work, prioritize features, and maintain clear historical context. Our automation is built around this principle.
Detailed Automation Workflows
Here is a breakdown of the specific automation workflows that run in our repository.
1. When you open an Issue:
Automated Issue Triage
This is the first bot you will interact with when you create an issue. Its job is to perform an initial analysis and apply the correct labels.
Workflow File
:
.github/workflows/qwen-automated-issue-triage.yml
When it runs
: Immediately after an issue is created or reopened.
What it does
:
It uses a Qwen model to analyze the issue’s title and body against a detailed set of guidelines.
Applies one
area/*
label
: Categorizes the issue into a functional area of the project (e.g.,
area/ux
,
area/models
,
area/platform
).
Applies one
kind/*
label
: Identifies the type of issue (e.g.,
kind/bug
,
kind/enhancement
,
kind/question
).
Applies one
priority/*
label
: Assigns a priority from P0 (critical) to P3 (low) based on the described impact.
May apply
status/need-information
: If the issue lacks critical details (like logs or reproduction steps), it will be flagged for more information.
May apply
status/need-retesting
: If the issue references a CLI version that is more than six versions old, it will be flagged for retesting on a current version.
What you should do
:
Fill out the issue template as completely as possible. The more detail you provide, the more accurate the triage will be.
If the
status/need-information
label is added, please provide the requested details in a comment.
2. When you open a Pull Request:
Continuous Integration (CI)
This workflow ensures that all changes meet our quality standards before they can be merged.
Workflow File
:
.github/workflows/ci.yml
When it runs
: On every push to a pull request.
What it does
:
Lint
: Checks that your code adheres to our project’s formatting and style rules.
Test
: Runs our full suite of automated tests across macOS, Windows, and Linux, and on multiple Node.js versions. This is the most time-consuming part of the CI process.
Post Coverage Comment
: After all tests have successfully passed, a bot will post a comment on your PR. This comment provides a summary of how well your changes are covered by tests.
What you should do
:
Ensure all CI checks pass. A green checkmark ✅ will appear next to your commit when everything is successful.
If a check fails (a red “X” ❌), click the “Details” link next to the failed check to view the logs, identify the problem, and push a fix.
3. Ongoing Triage for Pull Requests:
PR Auditing and Label Sync
This workflow runs periodically to ensure all open PRs are correctly linked to issues and have consistent labels.
Workflow File
:
.github/workflows/qwen-scheduled-pr-triage.yml
When it runs
: Every 15 minutes on all open pull requests.
What it does
:
Checks for a linked issue
: The bot scans your PR description for a keyword that links it to an issue (e.g.,
Fixes #123
,
Closes #456
).
Adds
status/need-issue
: If no linked issue is found, the bot will add the
status/need-issue
label to your PR. This is a clear signal that an issue needs to be created and linked.
Synchronizes labels
: If an issue
is
linked, the bot ensures the PR’s labels perfectly match the issue’s labels. It will add any missing labels and remove any that don’t belong, and it will remove the
status/need-issue
label if it was present.
What you should do
:
Always link your PR to an issue.
This is the most important step. Add a line like
Resolves #<issue-number>
to your PR description.
This will ensure your PR is correctly categorized and moves through the review process smoothly.
4. Ongoing Triage for Issues:
Scheduled Issue Triage
This is a fallback workflow to ensure that no issue gets missed by the triage process.
Workflow File
:
.github/workflows/qwen-scheduled-issue-triage.yml
When it runs
: Every hour on all open issues.
What it does
:
It actively seeks out issues that either have no labels at all or still have the
status/need-triage
label.
It then triggers the same powerful QwenCode-based analysis as the initial triage bot to apply the correct labels.
What you should do
:
You typically don’t need to do anything. This workflow is a safety net to ensure every issue is eventually categorized, even if the initial triage fails.
5. Release Automation
This workflow handles the process of packaging and publishing new versions of Qwen Code.
Workflow File
:
.github/workflows/release.yml
When it runs
: On a daily schedule for “nightly” releases, and manually for official patch/minor releases.
What it does
:
Automatically builds the project, bumps the version numbers, and publishes the packages to npm.
Creates a corresponding release on GitHub with generated release notes.
What you should do
:
As a contributor, you don’t need to do anything for this process. You can be confident that once your PR is merged into the
main
branch, your changes will be included in the very next nightly release.
We hope this detailed overview is helpful. If you have any questions about our automation or processes, please don’t hesitate to ask!
Last updated on
May 18, 2026
Integration Tests
Qwen Code Extensions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/issue-and-pr-automation/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Getting Started with Qwen Code Extensions</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/getting-started-extensions/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/getting-started-extensions/</guid>
  <pubDate>Mon, 19 Feb 2024 00:00:00 +0000</pubDate>
  <category>Getting Started</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Extensions
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extens...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Extensions
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extension
The easiest way to start is by using one of the built-in templates. We’ll use the
mcp-server
example as our foundation.
Run the following command to create a new directory called
my-first-extension
with the template files:
qwen
extensions
new
my-first-extension
mcp-server
This will create a new directory with the following structure:
my-first-extension/
├── example.ts
├── qwen-extension.json
├── package.json
└── tsconfig.json
Step 2: Understand the Extension Files
Let’s look at the key files in your new extension.
qwen-extension.json
This is the manifest file for your extension. It tells Qwen Code how to load and use your extension.
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
name
: The unique name for your extension.
version
: The version of your extension.
mcpServers
: This section defines one or more Model Context Protocol (MCP) servers. MCP servers are how you can add new tools for the model to use.
command
,
args
,
cwd
: These fields specify how to start your server. Notice the use of the
${extensionPath}
variable, which Qwen Code replaces with the absolute path to your extension’s installation directory. This allows your extension to work regardless of where it’s installed.
example.ts
This file contains the source code for your MCP server. It’s a simple Node.js server that uses the
@modelcontextprotocol/sdk
.
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import
{ McpServer }
from
'@modelcontextprotocol/sdk/server/mcp.js'
;
import
{ StdioServerTransport }
from
'@modelcontextprotocol/sdk/server/stdio.js'
;
import
{ z }
from
'zod'
;
const
server
=
new
McpServer
({
name:
'prompt-server'
,
version:
'1.0.0'
,
});
// Registers a new tool named 'fetch_posts'
server.
registerTool
(
'fetch_posts'
,
{
description:
'Fetches a list of posts from a public API.'
,
inputSchema: z.
object
({}).shape,
},
async
()
=>
{
const
apiResponse
=
await
fetch
(
'https://jsonplaceholder.typicode.com/posts'
,
);
const
posts
=
await
apiResponse.
json
();
const
response
=
{ posts: posts.
slice
(
0
,
5
) };
return
{
content: [
{
type:
'text'
,
text:
JSON
.
stringify
(response),
},
],
};
},
);
// ... (prompt registration omitted for brevity)
const
transport
=
new
StdioServerTransport
();
await
server.
connect
(transport);
This server defines a single tool called
fetch_posts
that fetches data from a public API.
package.json
and
tsconfig.json
These are standard configuration files for a TypeScript project. The
package.json
file defines dependencies and a
build
script, and
tsconfig.json
configures the TypeScript compiler.
Step 3: Build and Link Your Extension
Before you can use the extension, you need to compile the TypeScript code and link the extension to your Qwen Code installation for local development.
Install dependencies:
cd
my-first-extension
npm
install
Build the server:
npm
run
build
This will compile
example.ts
into
dist/example.js
, which is the file referenced in your
qwen-extension.json
.
Link the extension:
The
link
command creates a symbolic link from the Qwen Code extensions directory to your development directory. This means any changes you make will be reflected immediately without needing to reinstall.
qwen
extensions
link
.
Now, restart your Qwen Code session. The new
fetch_posts
tool will be available. You can test it by asking: “fetch posts”.
Step 4: Add a Custom Command
Custom commands provide a way to create shortcuts for complex prompts. Let’s add a command that searches for a pattern in your code.
Create a
commands
directory and a subdirectory for your command group:
mkdir
-p
commands/fs
Create a file named
commands/fs/grep-code.toml
:
prompt =
"""
Please summarize the findings for the pattern `{{args}}`.
Search Results:
!{grep -r {{args}} .}
"""
This command,
/fs:grep-code
, will take an argument, run the
grep
shell command with it, and pipe the results into a prompt for summarization.
After saving the file, restart the Qwen Code. You can now run
/fs:grep-code "some pattern"
to use your new command.
Step 5: Add a Custom
QWEN.md
You can provide persistent context to the model by adding a
QWEN.md
file to your extension. This is useful for giving the model instructions on how to behave or information about your extension’s tools. Note that you may not always need this for extensions built to expose commands and prompts.
Create a file named
QWEN.md
in the root of your extension directory:
# My First Extension Instructions
You are an expert developer assistant. When the user asks you to fetch posts, use the
`fetch_posts`
tool. Be concise in your responses.
Update your
qwen-extension.json
to tell the CLI to load this file:
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"contextFileName"
:
"QWEN.md"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
Restart the CLI again. The model will now have the context from your
QWEN.md
file in every session where the extension is active.
Step 6: Releasing Your Extension
Once you are happy with your extension, you can share it with others. The two primary ways of releasing extensions are via a Git repository or through GitHub Releases. Using a public Git repository is the simplest method.
For detailed instructions on both methods, please refer to the
Extension Releasing Guide
.
Conclusion
You’ve successfully created a Qwen Code extension! You learned how to:
Bootstrap a new extension from a template.
Add custom tools with an MCP server.
Create convenient custom commands.
Provide persistent context to the model.
Link your extension for local development.
From here, you can explore more advanced features and build powerful new capabilities into the Qwen Code.
Last updated on
May 18, 2026
Extension Releasing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/getting-started-extensions/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Extensions
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extens...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Extensions
Getting Started with Qwen Code Extensions
Getting Started with Qwen Code Extensions
This guide will walk you through creating your first Qwen Code extension. You’ll learn how to set up a new extension, add a custom tool via an MCP server, create a custom command, and provide context to the model with a
QWEN.md
file.
Prerequisites
Before you start, make sure you have the Qwen Code installed and a basic understanding of Node.js and TypeScript.
Step 1: Create a New Extension
The easiest way to start is by using one of the built-in templates. We’ll use the
mcp-server
example as our foundation.
Run the following command to create a new directory called
my-first-extension
with the template files:
qwen
extensions
new
my-first-extension
mcp-server
This will create a new directory with the following structure:
my-first-extension/
├── example.ts
├── qwen-extension.json
├── package.json
└── tsconfig.json
Step 2: Understand the Extension Files
Let’s look at the key files in your new extension.
qwen-extension.json
This is the manifest file for your extension. It tells Qwen Code how to load and use your extension.
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
name
: The unique name for your extension.
version
: The version of your extension.
mcpServers
: This section defines one or more Model Context Protocol (MCP) servers. MCP servers are how you can add new tools for the model to use.
command
,
args
,
cwd
: These fields specify how to start your server. Notice the use of the
${extensionPath}
variable, which Qwen Code replaces with the absolute path to your extension’s installation directory. This allows your extension to work regardless of where it’s installed.
example.ts
This file contains the source code for your MCP server. It’s a simple Node.js server that uses the
@modelcontextprotocol/sdk
.
/**
*
@license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import
{ McpServer }
from
'@modelcontextprotocol/sdk/server/mcp.js'
;
import
{ StdioServerTransport }
from
'@modelcontextprotocol/sdk/server/stdio.js'
;
import
{ z }
from
'zod'
;
const
server
=
new
McpServer
({
name:
'prompt-server'
,
version:
'1.0.0'
,
});
// Registers a new tool named 'fetch_posts'
server.
registerTool
(
'fetch_posts'
,
{
description:
'Fetches a list of posts from a public API.'
,
inputSchema: z.
object
({}).shape,
},
async
()
=>
{
const
apiResponse
=
await
fetch
(
'https://jsonplaceholder.typicode.com/posts'
,
);
const
posts
=
await
apiResponse.
json
();
const
response
=
{ posts: posts.
slice
(
0
,
5
) };
return
{
content: [
{
type:
'text'
,
text:
JSON
.
stringify
(response),
},
],
};
},
);
// ... (prompt registration omitted for brevity)
const
transport
=
new
StdioServerTransport
();
await
server.
connect
(transport);
This server defines a single tool called
fetch_posts
that fetches data from a public API.
package.json
and
tsconfig.json
These are standard configuration files for a TypeScript project. The
package.json
file defines dependencies and a
build
script, and
tsconfig.json
configures the TypeScript compiler.
Step 3: Build and Link Your Extension
Before you can use the extension, you need to compile the TypeScript code and link the extension to your Qwen Code installation for local development.
Install dependencies:
cd
my-first-extension
npm
install
Build the server:
npm
run
build
This will compile
example.ts
into
dist/example.js
, which is the file referenced in your
qwen-extension.json
.
Link the extension:
The
link
command creates a symbolic link from the Qwen Code extensions directory to your development directory. This means any changes you make will be reflected immediately without needing to reinstall.
qwen
extensions
link
.
Now, restart your Qwen Code session. The new
fetch_posts
tool will be available. You can test it by asking: “fetch posts”.
Step 4: Add a Custom Command
Custom commands provide a way to create shortcuts for complex prompts. Let’s add a command that searches for a pattern in your code.
Create a
commands
directory and a subdirectory for your command group:
mkdir
-p
commands/fs
Create a file named
commands/fs/grep-code.toml
:
prompt =
"""
Please summarize the findings for the pattern `{{args}}`.
Search Results:
!{grep -r {{args}} .}
"""
This command,
/fs:grep-code
, will take an argument, run the
grep
shell command with it, and pipe the results into a prompt for summarization.
After saving the file, restart the Qwen Code. You can now run
/fs:grep-code "some pattern"
to use your new command.
Step 5: Add a Custom
QWEN.md
You can provide persistent context to the model by adding a
QWEN.md
file to your extension. This is useful for giving the model instructions on how to behave or information about your extension’s tools. Note that you may not always need this for extensions built to expose commands and prompts.
Create a file named
QWEN.md
in the root of your extension directory:
# My First Extension Instructions
You are an expert developer assistant. When the user asks you to fetch posts, use the
`fetch_posts`
tool. Be concise in your responses.
Update your
qwen-extension.json
to tell the CLI to load this file:
{
"name"
:
"my-first-extension"
,
"version"
:
"1.0.0"
,
"contextFileName"
:
"QWEN.md"
,
"mcpServers"
: {
"nodeServer"
: {
"command"
:
"node"
,
"args"
: [
"${extensionPath}${/}dist${/}example.js"
],
"cwd"
:
"${extensionPath}"
}
}
}
Restart the CLI again. The model will now have the context from your
QWEN.md
file in every session where the extension is active.
Step 6: Releasing Your Extension
Once you are happy with your extension, you can share it with others. The two primary ways of releasing extensions are via a Git repository or through GitHub Releases. Using a public Git repository is the simplest method.
For detailed instructions on both methods, please refer to the
Extension Releasing Guide
.
Conclusion
You’ve successfully created a Qwen Code extension! You learned how to:
Bootstrap a new extension from a template.
Add custom tools with an MCP server.
Create convenient custom commands.
Provide persistent context to the model.
Link your extension for local development.
From here, you can explore more advanced features and build powerful new capabilities into the Qwen Code.
Last updated on
May 18, 2026
Extension Releasing</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/extensions/getting-started-extensions/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Agent Tool Display Implementation Plan</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/plans/2026-03-22-agent-tool-display-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/plans/2026-03-22-agent-tool-display-design/</guid>
  <pubDate>Mon, 19 Feb 2024 00:00:00 +0000</pubDate>
  <category>Agent</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Plans
Agent Tool Display Implementation Plan
Agent Tool Display Implementation Plan
For Claude:
REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal:
Add a dedicated VSCode/web UI display for Agent tool executions so subagent progress, summaries, and failures render from structured
rawOutput
instead of falling back to the generic tool card.
Architecture:
Preserve ACP
rawOutput
through the VSCode session/update pipeline into
ToolCallData
, then let the sha...</p><div style="font-size:16px;line-height:1.8;color:#333">Plans
Agent Tool Display Implementation Plan
Agent Tool Display Implementation Plan
For Claude:
REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal:
Add a dedicated VSCode/web UI display for Agent tool executions so subagent progress, summaries, and failures render from structured
rawOutput
instead of falling back to the generic tool card.
Architecture:
Preserve ACP
rawOutput
through the VSCode session/update pipeline into
ToolCallData
, then let the shared web UI router detect
task_execution
payloads and render a dedicated
AgentToolCall
component. Keep the change shared in
packages/webui
so VSCode and
ChatViewer
stay aligned.
Tech Stack:
TypeScript, React, Vitest, shared
@qwen-code/webui
tool-call components.
Task 1: Lock in the failing data-flow behavior
Files:
Modify:
packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.test.ts
Create:
packages/vscode-ide-companion/src/webview/hooks/useToolCalls.test.tsx
Step 1: Write the failing tests
Add a session handler test asserting
tool_call_update
forwards
rawOutput
when ACP sends a
task_execution
payload.
Add a hook test asserting
useToolCalls
stores and updates
rawOutput
for an agent tool call.
Step 2: Run test to verify it fails
Run:
npm test --workspace=packages/vscode-ide-companion -- --run qwenSessionUpdateHandler.test.ts useToolCalls.test.tsx
Expected: failures because
rawOutput
is not preserved in the current handler/hook pipeline.
Task 2: Lock in the failing renderer behavior
Files:
Create:
packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Step 1: Write the failing test
Render the routed tool call with
kind: 'other'
plus
rawOutput.type === 'task_execution'
.
Assert the task description, active child tool, summary, and failure reason render from a dedicated agent display instead of generic text output.
Step 2: Run test to verify it fails
Run:
npm test --workspace=packages/vscode-ide-companion -- --run packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Expected: failure because the router only keys off
kind
and no dedicated agent component exists.
Task 3: Preserve structured agent output end-to-end
Files:
Modify:
packages/vscode-ide-companion/src/types/chatTypes.ts
Modify:
packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.ts
Modify:
packages/vscode-ide-companion/src/webview/hooks/useToolCalls.ts
Modify:
packages/webui/src/components/toolcalls/shared/types.ts
Step 1: Implement the minimal data model changes
Add optional
rawOutput
to the VSCode session/webview tool-call types.
Forward
rawOutput
in
QwenSessionUpdateHandler
.
Store/merge
rawOutput
in
useToolCalls
.
Expose
rawOutput
in shared web UI tool-call data types.
Step 2: Run the focused tests
Run:
npm test --workspace=packages/vscode-ide-companion -- --run qwenSessionUpdateHandler.test.ts useToolCalls.test.tsx
Expected: pass.
Task 4: Add the shared agent tool-call UI
Files:
Create:
packages/webui/src/components/toolcalls/AgentToolCall.tsx
Modify:
packages/webui/src/components/toolcalls/index.ts
Modify:
packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.tsx
Modify:
packages/webui/src/components/ChatViewer/ChatViewer.tsx
Step 1: Implement the minimal renderer
Add a guard for
rawOutput.type === 'task_execution'
.
Render task description as the header.
Show agent name + status, currently running child tools, completion summary, and failure/cancel reason.
Keep the layout compatible with multiple parallel agent cards by rendering each tool call independently.
Step 2: Run the focused renderer test
Run:
npm test --workspace=packages/vscode-ide-companion -- --run packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Expected: pass.
Task 5: Verify the integrated surface
Files:
Modify:
packages/webui/src/index.ts
Step 1: Export the new shared component if needed
Re-export any new component/types needed by VSCode or
ChatViewer
.
Step 2: Run package verification
Run:
npm test --workspace=packages/vscode-ide-companion -- --run qwenSessionUpdateHandler.test.ts useToolCalls.test.tsx packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Run:
npm run check-types --workspace=packages/vscode-ide-companion
Run:
npm run typecheck --workspace=packages/webui
Expected: all targeted tests and typechecks pass.
Last updated on
May 18, 2026</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/plans/2026-03-22-agent-tool-display-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Plans
Agent Tool Display Implementation Plan
Agent Tool Display Implementation Plan
For Claude:
REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal:
Add a dedicated VSCode/web UI display for Agent tool executions so subagent progress, summaries, and failures render from structured
rawOutput
instead of falling back to the generic tool card.
Architecture:
Preserve ACP
rawOutput
through the VSCode session/update pipeline into
ToolCallData
, then let the sha...</p><div style="font-size:16px;line-height:1.8;color:#333">Plans
Agent Tool Display Implementation Plan
Agent Tool Display Implementation Plan
For Claude:
REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal:
Add a dedicated VSCode/web UI display for Agent tool executions so subagent progress, summaries, and failures render from structured
rawOutput
instead of falling back to the generic tool card.
Architecture:
Preserve ACP
rawOutput
through the VSCode session/update pipeline into
ToolCallData
, then let the shared web UI router detect
task_execution
payloads and render a dedicated
AgentToolCall
component. Keep the change shared in
packages/webui
so VSCode and
ChatViewer
stay aligned.
Tech Stack:
TypeScript, React, Vitest, shared
@qwen-code/webui
tool-call components.
Task 1: Lock in the failing data-flow behavior
Files:
Modify:
packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.test.ts
Create:
packages/vscode-ide-companion/src/webview/hooks/useToolCalls.test.tsx
Step 1: Write the failing tests
Add a session handler test asserting
tool_call_update
forwards
rawOutput
when ACP sends a
task_execution
payload.
Add a hook test asserting
useToolCalls
stores and updates
rawOutput
for an agent tool call.
Step 2: Run test to verify it fails
Run:
npm test --workspace=packages/vscode-ide-companion -- --run qwenSessionUpdateHandler.test.ts useToolCalls.test.tsx
Expected: failures because
rawOutput
is not preserved in the current handler/hook pipeline.
Task 2: Lock in the failing renderer behavior
Files:
Create:
packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Step 1: Write the failing test
Render the routed tool call with
kind: 'other'
plus
rawOutput.type === 'task_execution'
.
Assert the task description, active child tool, summary, and failure reason render from a dedicated agent display instead of generic text output.
Step 2: Run test to verify it fails
Run:
npm test --workspace=packages/vscode-ide-companion -- --run packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Expected: failure because the router only keys off
kind
and no dedicated agent component exists.
Task 3: Preserve structured agent output end-to-end
Files:
Modify:
packages/vscode-ide-companion/src/types/chatTypes.ts
Modify:
packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.ts
Modify:
packages/vscode-ide-companion/src/webview/hooks/useToolCalls.ts
Modify:
packages/webui/src/components/toolcalls/shared/types.ts
Step 1: Implement the minimal data model changes
Add optional
rawOutput
to the VSCode session/webview tool-call types.
Forward
rawOutput
in
QwenSessionUpdateHandler
.
Store/merge
rawOutput
in
useToolCalls
.
Expose
rawOutput
in shared web UI tool-call data types.
Step 2: Run the focused tests
Run:
npm test --workspace=packages/vscode-ide-companion -- --run qwenSessionUpdateHandler.test.ts useToolCalls.test.tsx
Expected: pass.
Task 4: Add the shared agent tool-call UI
Files:
Create:
packages/webui/src/components/toolcalls/AgentToolCall.tsx
Modify:
packages/webui/src/components/toolcalls/index.ts
Modify:
packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.tsx
Modify:
packages/webui/src/components/ChatViewer/ChatViewer.tsx
Step 1: Implement the minimal renderer
Add a guard for
rawOutput.type === 'task_execution'
.
Render task description as the header.
Show agent name + status, currently running child tools, completion summary, and failure/cancel reason.
Keep the layout compatible with multiple parallel agent cards by rendering each tool call independently.
Step 2: Run the focused renderer test
Run:
npm test --workspace=packages/vscode-ide-companion -- --run packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Expected: pass.
Task 5: Verify the integrated surface
Files:
Modify:
packages/webui/src/index.ts
Step 1: Export the new shared component if needed
Re-export any new component/types needed by VSCode or
ChatViewer
.
Step 2: Run package verification
Run:
npm test --workspace=packages/vscode-ide-companion -- --run qwenSessionUpdateHandler.test.ts useToolCalls.test.tsx packages/vscode-ide-companion/src/webview/components/messages/toolcalls/index.test.tsx
Run:
npm run check-types --workspace=packages/vscode-ide-companion
Run:
npm run typecheck --workspace=packages/webui
Expected: all targeted tests and typechecks pass.
Last updated on
May 18, 2026</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/plans/2026-03-22-agent-tool-display-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Qwen Code Companion Plugin: Interface Specification</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-companion-spec/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-companion-spec/</guid>
  <pubDate>Thu, 01 Feb 2024 00:00:00 +0000</pubDate>
  <category>Integrations</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Qwen Code Companion Plugin: Interface Specification
Last Updated: September 15, 2025
This document defines the contract for building a companion plugin to enable Qwen Code’s IDE mode. For VS Code, these features (native diffing, context awareness) are provided by the official extension (
marketplace
). This specification is for contributors who wish to bring similar functionality to other editors like JetBrains IDEs, Sublime Text, etc.
I. The Communication Interface
Qwen Code and the ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Qwen Code Companion Plugin: Interface Specification
Last Updated: September 15, 2025
This document defines the contract for building a companion plugin to enable Qwen Code’s IDE mode. For VS Code, these features (native diffing, context awareness) are provided by the official extension (
marketplace
). This specification is for contributors who wish to bring similar functionality to other editors like JetBrains IDEs, Sublime Text, etc.
I. The Communication Interface
Qwen Code and the IDE plugin communicate through a local communication channel.
1. Transport Layer: MCP over HTTP
The plugin
MUST
run a local HTTP server that implements the
Model Context Protocol (MCP)
.
Protocol:
The server must be a valid MCP server. We recommend using an existing MCP SDK for your language of choice if available.
Endpoint:
The server should expose a single endpoint (e.g.,
/mcp
) for all MCP communication.
Port:
The server
MUST
listen on a dynamically assigned port (i.e., listen on port
0
).
2. Discovery Mechanism: The Lock File
For Qwen Code to connect, it needs to discover what port your server is using. The plugin
MUST
facilitate this by creating a “lock file” and setting the port environment variable.
How the CLI Finds the File:
The CLI reads the port from
QWEN_CODE_IDE_SERVER_PORT
, then reads
~/.qwen/ide/<PORT>.lock
. (Legacy fallbacks exist for older extensions; see note below.)
File Location:
The file must be created in a specific directory:
~/.qwen/ide/
. Your plugin must create this directory if it doesn’t exist.
File Naming Convention:
The filename is critical and
MUST
follow the pattern:
<PORT>.lock
<PORT>
: The port your MCP server is listening on.
File Content & Workspace Validation:
The file
MUST
contain a JSON object with the following structure:
{
"port"
:
12345
,
"workspacePath"
:
"/path/to/project1:/path/to/project2"
,
"authToken"
:
"a-very-secret-token"
,
"ppid"
:
1234
,
"ideName"
:
"VS Code"
}
port
(number, required): The port of the MCP server.
workspacePath
(string, required): A list of all open workspace root paths, delimited by the OS-specific path separator (
:
for Linux/macOS,
;
for Windows). The CLI uses this path to ensure it’s running in the same project folder that’s open in the IDE. If the CLI’s current working directory is not a sub-directory of
workspacePath
, the connection will be rejected. Your plugin
MUST
provide the correct, absolute path(s) to the root of the open workspace(s).
authToken
(string, required): A secret token for securing the connection. The CLI will include this token in an
Authorization: Bearer <token>
header on all requests.
ppid
(number, required): The parent process ID of the IDE process.
ideName
(string, required): A user-friendly name for the IDE (e.g.,
VS Code
,
JetBrains IDE
).
Authentication:
To secure the connection, the plugin
MUST
generate a unique, secret token and include it in the discovery file. The CLI will then include this token in the
Authorization
header for all requests to the MCP server (e.g.,
Authorization: Bearer a-very-secret-token
). Your server
MUST
validate this token on every request and reject any that are unauthorized.
Environment Variables (Required):
Your plugin
MUST
set
QWEN_CODE_IDE_SERVER_PORT
in the integrated terminal so the CLI can locate the correct
<PORT>.lock
file.
Legacy note:
For extensions older than v0.5.1, Qwen Code may fall back to reading JSON files in the system temp directory named
qwen-code-ide-server-<PID>.json
or
qwen-code-ide-server-<PORT>.json
. New integrations should not rely on these legacy files.
II. The Context Interface
To enable context awareness, the plugin
MAY
provide the CLI with real-time information about the user’s activity in the IDE.
ide/contextUpdate
Notification
The plugin
MAY
send an
ide/contextUpdate
notification
to the CLI whenever the user’s context changes.
Triggering Events:
This notification should be sent (with a recommended debounce of 50ms) when:
A file is opened, closed, or focused.
The user’s cursor position or text selection changes in the active file.
Payload (
IdeContext
):
The notification parameters
MUST
be an
IdeContext
object:
interface
IdeContext
{
workspaceState
?:
{
openFiles
?:
File
[];
isTrusted
?:
boolean
;
};
}
interface
File
{
// Absolute path to the file
path
:
string
;
// Last focused Unix timestamp (for ordering)
timestamp
:
number
;
// True if this is the currently focused file
isActive
?:
boolean
;
cursor
?:
{
// 1-based line number
line
:
number
;
// 1-based character number
character
:
number
;
};
// The text currently selected by the user
selectedText
?:
string
;
}
Note:
The
openFiles
list should only include files that exist on disk. Virtual files (e.g., unsaved files without a path, editor settings pages)
MUST
be excluded.
How the CLI Uses This Context
After receiving the
IdeContext
object, the CLI performs several normalization and truncation steps before sending the information to the model.
File Ordering:
The CLI uses the
timestamp
field to determine the most recently used files. It sorts the
openFiles
list based on this value. Therefore, your plugin
MUST
provide an accurate Unix timestamp for when a file was last focused.
Active File:
The CLI considers only the most recent file (after sorting) to be the “active” file. It will ignore the
isActive
flag on all other files and clear their
cursor
and
selectedText
fields. Your plugin should focus on setting
isActive: true
and providing cursor/selection details only for the currently focused file.
Truncation:
To manage token limits, the CLI truncates both the file list (to 10 files) and the
selectedText
(to 16KB).
While the CLI handles the final truncation, it is highly recommended that your plugin also limits the amount of context it sends.
III. The Diffing Interface
To enable interactive code modifications, the plugin
MAY
expose a diffing interface. This allows the CLI to request that the IDE open a diff view, showing proposed changes to a file. The user can then review, edit, and ultimately accept or reject these changes directly within the IDE.
openDiff
Tool
The plugin
MUST
register an
openDiff
tool on its MCP server.
Description:
This tool instructs the IDE to open a modifiable diff view for a specific file.
Request (
OpenDiffRequest
):
The tool is invoked via a
tools/call
request. The
arguments
field within the request’s
params
MUST
be an
OpenDiffRequest
object.
interface
OpenDiffRequest
{
// The absolute path to the file to be diffed.
filePath
:
string
;
// The proposed new content for the file.
newContent
:
string
;
}
Response (
CallToolResult
):
The tool
MUST
immediately return a
CallToolResult
to acknowledge the request and report whether the diff view was successfully opened.
On Success: If the diff view was opened successfully, the response
MUST
contain empty content (i.e.,
content: []
).
On Failure: If an error prevented the diff view from opening, the response
MUST
have
isError: true
and include a
TextContent
block in the
content
array describing the error.
The actual outcome of the diff (acceptance or rejection) is communicated asynchronously via notifications.
closeDiff
Tool
The plugin
MUST
register a
closeDiff
tool on its MCP server.
Description:
This tool instructs the IDE to close an open diff view for a specific file.
Request (
CloseDiffRequest
):
The tool is invoked via a
tools/call
request. The
arguments
field within the request’s
params
MUST
be an
CloseDiffRequest
object.
interface
CloseDiffRequest
{
// The absolute path to the file whose diff view should be closed.
filePath
:
string
;
}
Response (
CallToolResult
):
The tool
MUST
return a
CallToolResult
.
On Success: If the diff view was closed successfully, the response
MUST
include a single
TextContent
block in the content array containing the file’s final content before closing.
On Failure: If an error prevented the diff view from closing, the response
MUST
have
isError: true
and include a
TextContent
block in the
content
array describing the error.
ide/diffAccepted
Notification
When the user accepts the changes in a diff view (e.g., by clicking an “Apply” or “Save” button), the plugin
MUST
send an
ide/diffAccepted
notification to the CLI.
Payload:
The notification parameters
MUST
include the file path and the final content of the file. The content may differ from the original
newContent
if the user made manual edits in the diff view.
{
// The absolute path to the file that was diffed.
filePath
: string;
// The full content of the file after acceptance.
content
: string;
}
ide/diffRejected
Notification
When the user rejects the changes (e.g., by closing the diff view without accepting), the plugin
MUST
send an
ide/diffRejected
notification to the CLI.
Payload:
The notification parameters
MUST
include the file path of the rejected diff.
{
// The absolute path to the file that was diffed.
filePath
: string;
}
IV. The Lifecycle Interface
The plugin
MUST
manage its resources and the discovery file correctly based on the IDE’s lifecycle.
On Activation (IDE startup/plugin enabled):
Start the MCP server.
Create the discovery file.
On Deactivation (IDE shutdown/plugin disabled):
Stop the MCP server.
Delete the discovery file.
Last updated on
May 18, 2026
QuickStart</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-companion-spec/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Qwen Code Companion Plugin: Interface Specification
Last Updated: September 15, 2025
This document defines the contract for building a companion plugin to enable Qwen Code’s IDE mode. For VS Code, these features (native diffing, context awareness) are provided by the official extension (
marketplace
). This specification is for contributors who wish to bring similar functionality to other editors like JetBrains IDEs, Sublime Text, etc.
I. The Communication Interface
Qwen Code and the ...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Qwen Code Companion Plugin: Interface Specification
Last Updated: September 15, 2025
This document defines the contract for building a companion plugin to enable Qwen Code’s IDE mode. For VS Code, these features (native diffing, context awareness) are provided by the official extension (
marketplace
). This specification is for contributors who wish to bring similar functionality to other editors like JetBrains IDEs, Sublime Text, etc.
I. The Communication Interface
Qwen Code and the IDE plugin communicate through a local communication channel.
1. Transport Layer: MCP over HTTP
The plugin
MUST
run a local HTTP server that implements the
Model Context Protocol (MCP)
.
Protocol:
The server must be a valid MCP server. We recommend using an existing MCP SDK for your language of choice if available.
Endpoint:
The server should expose a single endpoint (e.g.,
/mcp
) for all MCP communication.
Port:
The server
MUST
listen on a dynamically assigned port (i.e., listen on port
0
).
2. Discovery Mechanism: The Lock File
For Qwen Code to connect, it needs to discover what port your server is using. The plugin
MUST
facilitate this by creating a “lock file” and setting the port environment variable.
How the CLI Finds the File:
The CLI reads the port from
QWEN_CODE_IDE_SERVER_PORT
, then reads
~/.qwen/ide/<PORT>.lock
. (Legacy fallbacks exist for older extensions; see note below.)
File Location:
The file must be created in a specific directory:
~/.qwen/ide/
. Your plugin must create this directory if it doesn’t exist.
File Naming Convention:
The filename is critical and
MUST
follow the pattern:
<PORT>.lock
<PORT>
: The port your MCP server is listening on.
File Content & Workspace Validation:
The file
MUST
contain a JSON object with the following structure:
{
"port"
:
12345
,
"workspacePath"
:
"/path/to/project1:/path/to/project2"
,
"authToken"
:
"a-very-secret-token"
,
"ppid"
:
1234
,
"ideName"
:
"VS Code"
}
port
(number, required): The port of the MCP server.
workspacePath
(string, required): A list of all open workspace root paths, delimited by the OS-specific path separator (
:
for Linux/macOS,
;
for Windows). The CLI uses this path to ensure it’s running in the same project folder that’s open in the IDE. If the CLI’s current working directory is not a sub-directory of
workspacePath
, the connection will be rejected. Your plugin
MUST
provide the correct, absolute path(s) to the root of the open workspace(s).
authToken
(string, required): A secret token for securing the connection. The CLI will include this token in an
Authorization: Bearer <token>
header on all requests.
ppid
(number, required): The parent process ID of the IDE process.
ideName
(string, required): A user-friendly name for the IDE (e.g.,
VS Code
,
JetBrains IDE
).
Authentication:
To secure the connection, the plugin
MUST
generate a unique, secret token and include it in the discovery file. The CLI will then include this token in the
Authorization
header for all requests to the MCP server (e.g.,
Authorization: Bearer a-very-secret-token
). Your server
MUST
validate this token on every request and reject any that are unauthorized.
Environment Variables (Required):
Your plugin
MUST
set
QWEN_CODE_IDE_SERVER_PORT
in the integrated terminal so the CLI can locate the correct
<PORT>.lock
file.
Legacy note:
For extensions older than v0.5.1, Qwen Code may fall back to reading JSON files in the system temp directory named
qwen-code-ide-server-<PID>.json
or
qwen-code-ide-server-<PORT>.json
. New integrations should not rely on these legacy files.
II. The Context Interface
To enable context awareness, the plugin
MAY
provide the CLI with real-time information about the user’s activity in the IDE.
ide/contextUpdate
Notification
The plugin
MAY
send an
ide/contextUpdate
notification
to the CLI whenever the user’s context changes.
Triggering Events:
This notification should be sent (with a recommended debounce of 50ms) when:
A file is opened, closed, or focused.
The user’s cursor position or text selection changes in the active file.
Payload (
IdeContext
):
The notification parameters
MUST
be an
IdeContext
object:
interface
IdeContext
{
workspaceState
?:
{
openFiles
?:
File
[];
isTrusted
?:
boolean
;
};
}
interface
File
{
// Absolute path to the file
path
:
string
;
// Last focused Unix timestamp (for ordering)
timestamp
:
number
;
// True if this is the currently focused file
isActive
?:
boolean
;
cursor
?:
{
// 1-based line number
line
:
number
;
// 1-based character number
character
:
number
;
};
// The text currently selected by the user
selectedText
?:
string
;
}
Note:
The
openFiles
list should only include files that exist on disk. Virtual files (e.g., unsaved files without a path, editor settings pages)
MUST
be excluded.
How the CLI Uses This Context
After receiving the
IdeContext
object, the CLI performs several normalization and truncation steps before sending the information to the model.
File Ordering:
The CLI uses the
timestamp
field to determine the most recently used files. It sorts the
openFiles
list based on this value. Therefore, your plugin
MUST
provide an accurate Unix timestamp for when a file was last focused.
Active File:
The CLI considers only the most recent file (after sorting) to be the “active” file. It will ignore the
isActive
flag on all other files and clear their
cursor
and
selectedText
fields. Your plugin should focus on setting
isActive: true
and providing cursor/selection details only for the currently focused file.
Truncation:
To manage token limits, the CLI truncates both the file list (to 10 files) and the
selectedText
(to 16KB).
While the CLI handles the final truncation, it is highly recommended that your plugin also limits the amount of context it sends.
III. The Diffing Interface
To enable interactive code modifications, the plugin
MAY
expose a diffing interface. This allows the CLI to request that the IDE open a diff view, showing proposed changes to a file. The user can then review, edit, and ultimately accept or reject these changes directly within the IDE.
openDiff
Tool
The plugin
MUST
register an
openDiff
tool on its MCP server.
Description:
This tool instructs the IDE to open a modifiable diff view for a specific file.
Request (
OpenDiffRequest
):
The tool is invoked via a
tools/call
request. The
arguments
field within the request’s
params
MUST
be an
OpenDiffRequest
object.
interface
OpenDiffRequest
{
// The absolute path to the file to be diffed.
filePath
:
string
;
// The proposed new content for the file.
newContent
:
string
;
}
Response (
CallToolResult
):
The tool
MUST
immediately return a
CallToolResult
to acknowledge the request and report whether the diff view was successfully opened.
On Success: If the diff view was opened successfully, the response
MUST
contain empty content (i.e.,
content: []
).
On Failure: If an error prevented the diff view from opening, the response
MUST
have
isError: true
and include a
TextContent
block in the
content
array describing the error.
The actual outcome of the diff (acceptance or rejection) is communicated asynchronously via notifications.
closeDiff
Tool
The plugin
MUST
register a
closeDiff
tool on its MCP server.
Description:
This tool instructs the IDE to close an open diff view for a specific file.
Request (
CloseDiffRequest
):
The tool is invoked via a
tools/call
request. The
arguments
field within the request’s
params
MUST
be an
CloseDiffRequest
object.
interface
CloseDiffRequest
{
// The absolute path to the file whose diff view should be closed.
filePath
:
string
;
}
Response (
CallToolResult
):
The tool
MUST
return a
CallToolResult
.
On Success: If the diff view was closed successfully, the response
MUST
include a single
TextContent
block in the content array containing the file’s final content before closing.
On Failure: If an error prevented the diff view from closing, the response
MUST
have
isError: true
and include a
TextContent
block in the
content
array describing the error.
ide/diffAccepted
Notification
When the user accepts the changes in a diff view (e.g., by clicking an “Apply” or “Save” button), the plugin
MUST
send an
ide/diffAccepted
notification to the CLI.
Payload:
The notification parameters
MUST
include the file path and the final content of the file. The content may differ from the original
newContent
if the user made manual edits in the diff view.
{
// The absolute path to the file that was diffed.
filePath
: string;
// The full content of the file after acceptance.
content
: string;
}
ide/diffRejected
Notification
When the user rejects the changes (e.g., by closing the diff view without accepting), the plugin
MUST
send an
ide/diffRejected
notification to the CLI.
Payload:
The notification parameters
MUST
include the file path of the rejected diff.
{
// The absolute path to the file that was diffed.
filePath
: string;
}
IV. The Lifecycle Interface
The plugin
MUST
manage its resources and the discovery file correctly based on the IDE’s lifecycle.
On Activation (IDE startup/plugin enabled):
Start the MCP server.
Create the discovery file.
On Deactivation (IDE shutdown/plugin disabled):
Stop the MCP server.
Delete the discovery file.
Last updated on
May 18, 2026
QuickStart</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/ide-integration/ide-companion-spec/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Exit Plan Mode Tool ( exit_plan_mode )</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/tools/exit-plan-mode/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/tools/exit-plan-mode/</guid>
  <pubDate>Wed, 24 Jan 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Exit Plan Mode
Exit Plan Mode Tool (
exit_plan_mode
)
This document describes the
exit_plan_mode
tool for Qwen Code.
Description
Use
exit_plan_mode
when you are in plan mode and have finished presenting your implementation plan. This tool prompts the user to approve or reject the plan and transitions from planning mode to implementation mode.
The tool is specifically designed for tasks that require planning implementation steps before writing code. It should NOT be used for...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Exit Plan Mode
Exit Plan Mode Tool (
exit_plan_mode
)
This document describes the
exit_plan_mode
tool for Qwen Code.
Description
Use
exit_plan_mode
when you are in plan mode and have finished presenting your implementation plan. This tool prompts the user to approve or reject the plan and transitions from planning mode to implementation mode.
The tool is specifically designed for tasks that require planning implementation steps before writing code. It should NOT be used for research or information-gathering tasks.
Arguments
exit_plan_mode
takes one argument:
plan
(string, required): The implementation plan you want to present to the user for approval. This should be a concise, markdown-formatted plan describing the implementation steps.
How to use
exit_plan_mode
with Qwen Code
The Exit Plan Mode tool is part of Qwen Code’s planning workflow. When you’re in plan mode (typically after exploring a codebase and designing an implementation approach), you use this tool to:
Present your implementation plan to the user
Request approval to proceed with implementation
Transition from plan mode to implementation mode based on user response
The tool will prompt the user with your plan and provide options to:
Proceed Once
: Approve the plan for this session only
Proceed Always
: Approve the plan and enable auto-approval for future edit operations
Cancel
: Reject the plan and remain in planning mode
Usage:
exit_plan_mode(plan="Your detailed implementation plan here...")
When to Use This Tool
Use
exit_plan_mode
when:
Implementation tasks
: You are planning the implementation steps for a coding task
Plan completion
: You have finished exploring and designing your implementation approach
User approval needed
: You need user confirmation before proceeding with code changes
Code writing tasks
: The task involves writing, modifying, or refactoring code
Examples of appropriate usage:
“Help me implement user authentication” → Use after planning the auth system implementation
“Add a new API endpoint for user management” → Use after designing the endpoint structure
“Refactor the database layer to use TypeORM” → Use after planning the refactoring approach
When NOT to Use This Tool
Do NOT use
exit_plan_mode
for:
Research tasks
: Tasks focused on understanding or exploring existing code
Information gathering
: When you’re searching, reading, or analyzing without implementing
Documentation tasks
: When creating documentation without code changes
Analysis tasks
: When providing explanations or reviews without implementation
Examples of inappropriate usage:
“Search for and understand the implementation of vim mode in the codebase” → Don’t use (research task)
“Explain how the authentication system works” → Don’t use (analysis task)
“Find all TODO comments in the project” → Don’t use (information gathering)
exit_plan_mode
examples
Implementation planning example
exit_plan_mode(plan="## Implementation Plan for User Dashboard
### 1. Create Dashboard Components
- Create `UserDashboard.tsx` with main dashboard layout
- Add `DashboardStats.tsx` for displaying user statistics
- Implement `ActivityFeed.tsx` for recent user activities
### 2. Add API Integration
- Create `dashboardApi.ts` for dashboard data fetching
- Add endpoints for user stats and activity data
- Implement error handling and loading states
### 3. Update Routing
- Add dashboard route in `App.tsx`
- Update navigation component with dashboard link
- Add route protection for authenticated users
### 4. Testing and Documentation
- Write unit tests for dashboard components
- Add integration tests for API calls
- Update user documentation with dashboard features
This implementation will take approximately 4-6 hours and requires changes to 8-10 files.")
Refactoring plan example
exit_plan_mode(plan="## Database Layer Refactoring Plan
### Phase 1: Setup and Configuration
- Install and configure TypeORM dependencies
- Create database connection configuration
- Set up entity definitions for existing models
### Phase 2: Migration
- Create migration scripts for existing data
- Implement new repository pattern using TypeORM
- Replace raw SQL queries with TypeORM query builder
### Phase 3: Testing and Validation
- Update all database tests to use TypeORM
- Validate data integrity after migration
- Performance testing to ensure no regressions
This refactoring will modernize our database layer while maintaining backward compatibility.")
User Response Handling
After calling
exit_plan_mode
, the user can respond in several ways:
Proceed Once
: The plan is approved for immediate implementation with default confirmation settings
Proceed Always
: The plan is approved and auto-approval is enabled for subsequent edit operations
Cancel
: The plan is rejected, and the system remains in plan mode for further planning
The tool automatically adjusts the approval mode based on the user’s choice, streamlining the implementation process according to user preferences.
Important Notes
Plan mode only
: This tool should only be used when you are currently in plan mode
Implementation focus
: Only use for tasks that involve writing or modifying code
Concise plans
: Keep plans focused and concise - aim for clarity over exhaustive detail
Markdown support
: Plans support markdown formatting for better readability
Single use
: The tool should be used once per planning session when ready to proceed
User control
: The final decision to proceed always rests with the user
Integration with Planning Workflow
The Exit Plan Mode tool is part of a larger planning workflow:
Enter Plan Mode
: User requests or system determines planning is needed
Exploration Phase
: Analyze codebase, understand requirements, explore options
Plan Design
: Create implementation strategy based on exploration
Plan Presentation
: Use
exit_plan_mode
to present plan to user
Implementation Phase
: Upon approval, proceed with planned implementation
This workflow ensures thoughtful implementation approaches and gives users control over significant code changes.
Last updated on
May 18, 2026
Task
Web Fetch</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/exit-plan-mode/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Tools
Exit Plan Mode
Exit Plan Mode Tool (
exit_plan_mode
)
This document describes the
exit_plan_mode
tool for Qwen Code.
Description
Use
exit_plan_mode
when you are in plan mode and have finished presenting your implementation plan. This tool prompts the user to approve or reject the plan and transitions from planning mode to implementation mode.
The tool is specifically designed for tasks that require planning implementation steps before writing code. It should NOT be used for...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Tools
Exit Plan Mode
Exit Plan Mode Tool (
exit_plan_mode
)
This document describes the
exit_plan_mode
tool for Qwen Code.
Description
Use
exit_plan_mode
when you are in plan mode and have finished presenting your implementation plan. This tool prompts the user to approve or reject the plan and transitions from planning mode to implementation mode.
The tool is specifically designed for tasks that require planning implementation steps before writing code. It should NOT be used for research or information-gathering tasks.
Arguments
exit_plan_mode
takes one argument:
plan
(string, required): The implementation plan you want to present to the user for approval. This should be a concise, markdown-formatted plan describing the implementation steps.
How to use
exit_plan_mode
with Qwen Code
The Exit Plan Mode tool is part of Qwen Code’s planning workflow. When you’re in plan mode (typically after exploring a codebase and designing an implementation approach), you use this tool to:
Present your implementation plan to the user
Request approval to proceed with implementation
Transition from plan mode to implementation mode based on user response
The tool will prompt the user with your plan and provide options to:
Proceed Once
: Approve the plan for this session only
Proceed Always
: Approve the plan and enable auto-approval for future edit operations
Cancel
: Reject the plan and remain in planning mode
Usage:
exit_plan_mode(plan="Your detailed implementation plan here...")
When to Use This Tool
Use
exit_plan_mode
when:
Implementation tasks
: You are planning the implementation steps for a coding task
Plan completion
: You have finished exploring and designing your implementation approach
User approval needed
: You need user confirmation before proceeding with code changes
Code writing tasks
: The task involves writing, modifying, or refactoring code
Examples of appropriate usage:
“Help me implement user authentication” → Use after planning the auth system implementation
“Add a new API endpoint for user management” → Use after designing the endpoint structure
“Refactor the database layer to use TypeORM” → Use after planning the refactoring approach
When NOT to Use This Tool
Do NOT use
exit_plan_mode
for:
Research tasks
: Tasks focused on understanding or exploring existing code
Information gathering
: When you’re searching, reading, or analyzing without implementing
Documentation tasks
: When creating documentation without code changes
Analysis tasks
: When providing explanations or reviews without implementation
Examples of inappropriate usage:
“Search for and understand the implementation of vim mode in the codebase” → Don’t use (research task)
“Explain how the authentication system works” → Don’t use (analysis task)
“Find all TODO comments in the project” → Don’t use (information gathering)
exit_plan_mode
examples
Implementation planning example
exit_plan_mode(plan="## Implementation Plan for User Dashboard
### 1. Create Dashboard Components
- Create `UserDashboard.tsx` with main dashboard layout
- Add `DashboardStats.tsx` for displaying user statistics
- Implement `ActivityFeed.tsx` for recent user activities
### 2. Add API Integration
- Create `dashboardApi.ts` for dashboard data fetching
- Add endpoints for user stats and activity data
- Implement error handling and loading states
### 3. Update Routing
- Add dashboard route in `App.tsx`
- Update navigation component with dashboard link
- Add route protection for authenticated users
### 4. Testing and Documentation
- Write unit tests for dashboard components
- Add integration tests for API calls
- Update user documentation with dashboard features
This implementation will take approximately 4-6 hours and requires changes to 8-10 files.")
Refactoring plan example
exit_plan_mode(plan="## Database Layer Refactoring Plan
### Phase 1: Setup and Configuration
- Install and configure TypeORM dependencies
- Create database connection configuration
- Set up entity definitions for existing models
### Phase 2: Migration
- Create migration scripts for existing data
- Implement new repository pattern using TypeORM
- Replace raw SQL queries with TypeORM query builder
### Phase 3: Testing and Validation
- Update all database tests to use TypeORM
- Validate data integrity after migration
- Performance testing to ensure no regressions
This refactoring will modernize our database layer while maintaining backward compatibility.")
User Response Handling
After calling
exit_plan_mode
, the user can respond in several ways:
Proceed Once
: The plan is approved for immediate implementation with default confirmation settings
Proceed Always
: The plan is approved and auto-approval is enabled for subsequent edit operations
Cancel
: The plan is rejected, and the system remains in plan mode for further planning
The tool automatically adjusts the approval mode based on the user’s choice, streamlining the implementation process according to user preferences.
Important Notes
Plan mode only
: This tool should only be used when you are currently in plan mode
Implementation focus
: Only use for tasks that involve writing or modifying code
Concise plans
: Keep plans focused and concise - aim for clarity over exhaustive detail
Markdown support
: Plans support markdown formatting for better readability
Single use
: The tool should be used once per planning session when ready to proceed
User control
: The final decision to proceed always rests with the user
Integration with Planning Workflow
The Exit Plan Mode tool is part of a larger planning workflow:
Enter Plan Mode
: User requests or system determines planning is needed
Exploration Phase
: Analyze codebase, understand requirements, explore options
Plan Design
: Create implementation strategy based on exploration
Plan Presentation
: Use
exit_plan_mode
to present plan to user
Implementation Phase
: Upon approval, proceed with planned implementation
This workflow ensures thoughtful implementation approaches and gives users control over significant code changes.
Last updated on
May 18, 2026
Task
Web Fetch</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/tools/exit-plan-mode/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Memory</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/memory/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/memory/</guid>
  <pubDate>Sun, 21 Jan 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Memory
Memory
Every Qwen Code session starts with a fresh context window. Two mechanisms carry knowledge across sessions so you don’t have to re-explain yourself every time:
QWEN.md
— instructions
you
write once and Qwen reads every session
Auto-memory
— notes Qwen writes itself based on what it learns from you
QWEN.md: your instructions to Qwen
QWEN.md is a plain text file where you write things Qwen should always know about your project or your preferences. Think of it as a...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Memory
Memory
Every Qwen Code session starts with a fresh context window. Two mechanisms carry knowledge across sessions so you don’t have to re-explain yourself every time:
QWEN.md
— instructions
you
write once and Qwen reads every session
Auto-memory
— notes Qwen writes itself based on what it learns from you
QWEN.md: your instructions to Qwen
QWEN.md is a plain text file where you write things Qwen should always know about your project or your preferences. Think of it as a permanent briefing that loads at the start of every conversation.
What to put in QWEN.md
Add things you’d otherwise have to repeat every session:
Build and test commands (
npm run test
,
make build
)
Coding conventions your team follows (“all new files must have JSDoc comments”)
Architectural decisions (“we use the repository pattern, never call the database directly from controllers”)
Personal preferences (“always use pnpm, not npm”)
Don’t include things Qwen can figure out by reading your code. QWEN.md works best when it’s short and specific — the longer it gets, the less reliably Qwen follows it.
Where to create QWEN.md
File
Who it applies to
~/.qwen/QWEN.md
You, across all your projects
QWEN.md
in the project root
Your whole team (commit it to source control)
You can have both. Qwen loads all QWEN.md files it finds when you start a session — your personal one plus any in the project.
If your repository already has an
AGENTS.md
file for other AI tools, Qwen reads that too. No need to duplicate instructions.
Generate one automatically with
/init
Run
/init
and Qwen will analyze your codebase to create a starter QWEN.md with build commands, test instructions, and conventions it finds. If one already exists, it suggests additions instead of overwriting.
Reference other files
You can point QWEN.md at other files so Qwen reads them too:
See @README.md for project overview.
# Conventions
-
Git workflow: @docs/git-workflow.md
Use
@path/to/file
anywhere in QWEN.md. Relative paths resolve from the QWEN.md file itself.
Auto-memory: what Qwen learns about you
Auto-memory runs in the background. After each of your conversations, Qwen quietly saves useful things it learned — your preferences, feedback you gave, project context — so it can use them in future sessions without you repeating yourself.
This is different from QWEN.md: you don’t write it, Qwen does.
What Qwen saves
Qwen looks for four kinds of things worth remembering:
What
Examples
About you
Your role, background, how you like to work
Your feedback
Corrections you made, approaches you confirmed
Project context
Ongoing work, decisions, goals not obvious from the code
External references
Dashboards, ticket trackers, docs links you mentioned
Qwen doesn’t save everything — only things that would actually be useful next time.
Where it’s stored
Auto-memory files live at
~/.qwen/projects/<project>/memory/
. All branches and worktrees of the same repository share the same memory folder, so what Qwen learns in one branch is available in others.
Everything saved is plain markdown — you can open, edit, or delete any file at any time.
Periodic cleanup
Qwen periodically goes through its saved memories to remove duplicates and clean up outdated entries. This runs automatically in the background once a day after enough sessions have accumulated. You can trigger it manually with
/dream
if you want it to run now.
While cleanup is running,
✦ dreaming
appears in the corner of the screen. Your session continues normally.
Turning it on or off
Auto-memory is on by default. To toggle it, open
/memory
and use the switches at the top. You can turn off just the automatic saving, just the periodic cleanup, or both.
You can also set them in
~/.qwen/settings.json
(applies to all projects) or
.qwen/settings.json
(this project only):
{
"memory"
: {
"enableManagedAutoMemory"
:
true
,
"enableManagedAutoDream"
:
true
}
}
Commands
/memory
Opens the Memory panel. From here you can:
Turn auto-memory saving on or off
Turn periodic cleanup (dream) on or off
Open your personal QWEN.md (
~/.qwen/QWEN.md
)
Open the project QWEN.md
Browse the auto-memory folder
/init
Generates a starter QWEN.md for your project. Qwen reads your codebase and fills in build commands, test instructions, and conventions it discovers.
/remember <text>
Immediately saves something to auto-memory without waiting for Qwen to pick it up automatically:
/remember always use snake_case for Python variable names
/remember the staging environment is at staging.example.com
/forget <text>
Removes auto-memory entries that match your description:
/forget old workaround for the login bug
/dream
Runs the memory cleanup now instead of waiting for the automatic schedule:
/dream
Troubleshooting
Qwen isn’t following my QWEN.md
Open
/memory
to see which files are loaded. If your file isn’t listed, Qwen can’t see it — make sure it’s in the project root or
~/.qwen/
.
Instructions work better when they’re specific:
✓
Use 2-space indentation for TypeScript files
✗
Format code nicely
If you have multiple QWEN.md files with conflicting instructions, Qwen may behave inconsistently. Review them and remove any contradictions.
I want to see what Qwen has saved
Run
/memory
and select
Open auto-memory folder
. All saved memories are readable markdown files you can browse, edit, or delete.
Qwen keeps forgetting things
If auto-memory is on but Qwen doesn’t seem to remember things across sessions, try running
/dream
to force a cleanup pass. Also check
/memory
to confirm both toggles are enabled.
For things you always want Qwen to remember, add them to QWEN.md instead — auto-memory is best-effort, QWEN.md is guaranteed.
Last updated on
May 18, 2026
Skills
Headless Mode</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/memory/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Memory
Memory
Every Qwen Code session starts with a fresh context window. Two mechanisms carry knowledge across sessions so you don’t have to re-explain yourself every time:
QWEN.md
— instructions
you
write once and Qwen reads every session
Auto-memory
— notes Qwen writes itself based on what it learns from you
QWEN.md: your instructions to Qwen
QWEN.md is a plain text file where you write things Qwen should always know about your project or your preferences. Think of it as a...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Memory
Memory
Every Qwen Code session starts with a fresh context window. Two mechanisms carry knowledge across sessions so you don’t have to re-explain yourself every time:
QWEN.md
— instructions
you
write once and Qwen reads every session
Auto-memory
— notes Qwen writes itself based on what it learns from you
QWEN.md: your instructions to Qwen
QWEN.md is a plain text file where you write things Qwen should always know about your project or your preferences. Think of it as a permanent briefing that loads at the start of every conversation.
What to put in QWEN.md
Add things you’d otherwise have to repeat every session:
Build and test commands (
npm run test
,
make build
)
Coding conventions your team follows (“all new files must have JSDoc comments”)
Architectural decisions (“we use the repository pattern, never call the database directly from controllers”)
Personal preferences (“always use pnpm, not npm”)
Don’t include things Qwen can figure out by reading your code. QWEN.md works best when it’s short and specific — the longer it gets, the less reliably Qwen follows it.
Where to create QWEN.md
File
Who it applies to
~/.qwen/QWEN.md
You, across all your projects
QWEN.md
in the project root
Your whole team (commit it to source control)
You can have both. Qwen loads all QWEN.md files it finds when you start a session — your personal one plus any in the project.
If your repository already has an
AGENTS.md
file for other AI tools, Qwen reads that too. No need to duplicate instructions.
Generate one automatically with
/init
Run
/init
and Qwen will analyze your codebase to create a starter QWEN.md with build commands, test instructions, and conventions it finds. If one already exists, it suggests additions instead of overwriting.
Reference other files
You can point QWEN.md at other files so Qwen reads them too:
See @README.md for project overview.
# Conventions
-
Git workflow: @docs/git-workflow.md
Use
@path/to/file
anywhere in QWEN.md. Relative paths resolve from the QWEN.md file itself.
Auto-memory: what Qwen learns about you
Auto-memory runs in the background. After each of your conversations, Qwen quietly saves useful things it learned — your preferences, feedback you gave, project context — so it can use them in future sessions without you repeating yourself.
This is different from QWEN.md: you don’t write it, Qwen does.
What Qwen saves
Qwen looks for four kinds of things worth remembering:
What
Examples
About you
Your role, background, how you like to work
Your feedback
Corrections you made, approaches you confirmed
Project context
Ongoing work, decisions, goals not obvious from the code
External references
Dashboards, ticket trackers, docs links you mentioned
Qwen doesn’t save everything — only things that would actually be useful next time.
Where it’s stored
Auto-memory files live at
~/.qwen/projects/<project>/memory/
. All branches and worktrees of the same repository share the same memory folder, so what Qwen learns in one branch is available in others.
Everything saved is plain markdown — you can open, edit, or delete any file at any time.
Periodic cleanup
Qwen periodically goes through its saved memories to remove duplicates and clean up outdated entries. This runs automatically in the background once a day after enough sessions have accumulated. You can trigger it manually with
/dream
if you want it to run now.
While cleanup is running,
✦ dreaming
appears in the corner of the screen. Your session continues normally.
Turning it on or off
Auto-memory is on by default. To toggle it, open
/memory
and use the switches at the top. You can turn off just the automatic saving, just the periodic cleanup, or both.
You can also set them in
~/.qwen/settings.json
(applies to all projects) or
.qwen/settings.json
(this project only):
{
"memory"
: {
"enableManagedAutoMemory"
:
true
,
"enableManagedAutoDream"
:
true
}
}
Commands
/memory
Opens the Memory panel. From here you can:
Turn auto-memory saving on or off
Turn periodic cleanup (dream) on or off
Open your personal QWEN.md (
~/.qwen/QWEN.md
)
Open the project QWEN.md
Browse the auto-memory folder
/init
Generates a starter QWEN.md for your project. Qwen reads your codebase and fills in build commands, test instructions, and conventions it discovers.
/remember <text>
Immediately saves something to auto-memory without waiting for Qwen to pick it up automatically:
/remember always use snake_case for Python variable names
/remember the staging environment is at staging.example.com
/forget <text>
Removes auto-memory entries that match your description:
/forget old workaround for the login bug
/dream
Runs the memory cleanup now instead of waiting for the automatic schedule:
/dream
Troubleshooting
Qwen isn’t following my QWEN.md
Open
/memory
to see which files are loaded. If your file isn’t listed, Qwen can’t see it — make sure it’s in the project root or
~/.qwen/
.
Instructions work better when they’re specific:
✓
Use 2-space indentation for TypeScript files
✗
Format code nicely
If you have multiple QWEN.md files with conflicting instructions, Qwen may behave inconsistently. Review them and remove any contradictions.
I want to see what Qwen has saved
Run
/memory
and select
Open auto-memory folder
. All saved memories are readable markdown files you can browse, edit, or delete.
Qwen keeps forgetting things
If auto-memory is on but Qwen doesn’t seem to remember things across sessions, try running
/dream
to force a cleanup pass. Also check
/memory
to confirm both toggles are enabled.
For things you always want Qwen to remember, add them to QWEN.md instead — auto-memory is best-effort, QWEN.md is guaranteed.
Last updated on
May 18, 2026
Skills
Headless Mode</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/memory/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Phase 2 技术设计文档：能力扩展</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase2-technical-design/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase2-technical-design/</guid>
  <pubDate>Sat, 20 Jan 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Phase 2 技术设计文档：能力扩展
Phase 2 技术设计文档：能力扩展
1. 设计目标与约束
1.1 目标
将 13 个 built-in 命令的
supportedModes
扩展到包含
non_interactive
和/或
acp
确保每个扩展命令在 ACP/non-interactive 路径下返回适合 IDE 消费的文本内容
打通 prompt command 的模型调用通路（
SkillTool
消费
getModelInvocableCommands()
）
实现 mid-input slash command 基础检测
1.2 硬性约束
interactive 路径零退化
：所有扩展命令的现有 interactive 行为严格不变，只在 action 内部新增模式分支，不触碰 interactive 路径代码
实现策略：模式分支，而非双注册
：13 个命令均采用在
action
内部增加
executionMode
判断的方式，不使用 Phase 1 设计文档 §10.2 描述的双注册模式（双注册仅在 interacti...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Phase 2 技术设计文档：能力扩展
Phase 2 技术设计文档：能力扩展
1. 设计目标与约束
1.1 目标
将 13 个 built-in 命令的
supportedModes
扩展到包含
non_interactive
和/或
acp
确保每个扩展命令在 ACP/non-interactive 路径下返回适合 IDE 消费的文本内容
打通 prompt command 的模型调用通路（
SkillTool
消费
getModelInvocableCommands()
）
实现 mid-input slash command 基础检测
1.2 硬性约束
interactive 路径零退化
：所有扩展命令的现有 interactive 行为严格不变，只在 action 内部新增模式分支，不触碰 interactive 路径代码
实现策略：模式分支，而非双注册
：13 个命令均采用在
action
内部增加
executionMode
判断的方式，不使用 Phase 1 设计文档 §10.2 描述的双注册模式（双注册仅在 interactive 和 non-interactive 逻辑差异极大时才有必要，本阶段命令复杂度不达到该门槛）
ACP 消息格式
：ACP 路径返回的文本内容不含 ANSI 样式，以 Markdown 或纯文本为宜，面向 IDE 插件消费
跳过环境相关副作用
：打开浏览器（
open()
）、操作剪贴板（
copyToClipboard()
）等依赖图形环境的操作，在 non-interactive/ACP 路径下必须跳过
2. Phase 1 完成后的基础状态
Phase 1 结束后的架构要点（Phase 2 直接在此基础上扩展）：
commandType
字段已从
SlashCommand
接口中删除，所有命令改用显式
supportedModes
getEffectiveSupportedModes()
为两级推断：显式
supportedModes
→
CommandKind
兜底
CommandService.getCommandsForMode(mode)
取代原
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
白名单
btw
、
bug
、
compress
、
context
、
init
、
summary
已在 Phase 1 中扩展到全模式，
不在本阶段列表中
createNonInteractiveUI()
中各方法均为 no-op：
addItem
、
clear
、
setDebugMessage
、
setPendingItem
、
reloadCommands
均静默忽略调用
3. 变更范围总览
本阶段共涉及 13 个命令，按实现复杂度分为四类：
类别
命令
变更要点
A 类
export
只改
supportedModes
，action 所有路径已返回合法类型
仅交互
plan
、
statusline
设计决策：这两个命令语义上与交互界面紧密耦合，保持
supportedModes: ['interactive']
A+ 类
language
改
supportedModes
+ 少量 non-interactive 分支处理
仅交互
copy
、
restore
设计决策：剥贴板和快照恢复本质上是交互操作，保持
supportedModes: ['interactive']
A’ 类
model
、
approval-mode
有参数路径已返回
message
，无参数路径需新增 non-interactive 分支（现触发 dialog）
B 类
about
、
stats
、
insight
、
docs
、
clear
action 所有路径均无返回值或调用
addItem
/
clear
，需新增完整 non-interactive 分支
4. A 类：只改
supportedModes
这三个命令的所有
action
路径已经返回
message
或
submit_prompt
，完全无 UI 依赖，
handleCommandResult
可直接处理。
4.1
/export
（及子命令）
当前状态
：
supportedModes: ['interactive']
，所有子命令 action 均返回
MessageActionReturn
。
变更
：将父命令及所有四个子命令（
md
、
html
、
json
、
jsonl
）的
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
ACP 消息内容
：action 现有返回内容已包含完整文件路径（如
Session exported to markdown: qwen-export-2024-01-01T12-00-00.md
），对 IDE 消费友好，无需修改文本。
注意
：
/export
父命令本身没有
action
，只有子命令。将父命令
supportedModes
改为全模式后，
parseSlashCommand
能够匹配子命令路由，但若用户只输入
/export
不带子命令，
commandToExecute.action
为 undefined，
handleSlashCommand
返回
no_command
，调用方会显示可用子命令提示。这是预期行为。
4.2
/plan
当前状态
：
supportedModes: ['interactive']
，action 所有路径返回
MessageActionReturn
或
SubmitPromptActionReturn
。
设计决策
：
/plan
是引导用户进行多轮交互规划的命令，语义上与交互界面紧密耦合。经讨论决定保持
supportedModes: ['interactive']
，不扩展至 non-interactive/acp 模式。
4.3
/statusline
当前状态
：
supportedModes: ['interactive']
，action 始终返回
SubmitPromptActionReturn
（将 subagent 调用 prompt 提交给模型）。
设计决策
：
/statusline
是触发 subagent 对当前状态进行总结的命令，语义上与交互界面紧密耦合。经讨论决定保持
supportedModes: ['interactive']
，不扩展至 non-interactive/acp 模式。
5. A+ 类：少量 non-interactive 分支处理
5.1
/language
当前状态
：action 所有路径均返回
MessageActionReturn
（读取/设置语言设置）。
需要处理的副作用
：
setUiLanguage()
内调用
context.ui.reloadCommands()
，在非交互 UI 中已是 no-op，无需额外处理。
变更
：
将父命令及子命令（
ui
、
output
，以及
SUPPORTED_LANGUAGES
动态生成的子命令）的
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
action 无需添加模式分支，现有返回文本已适合机器消费。
ACP 语义说明
：在 non-interactive（单次调用）中执行
/language ui zh-CN
会修改持久化设置（写入 settings 文件），该变更对后续 session 生效，本次 session 内 i18n 也立即生效。这与用户预期一致。
5.2
/copy
当前状态
：action 调用
copyToClipboard()
，在 ACP/headless 环境中可能抛出异常或无声失败（clipboard 不可用）。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在 action 内新增模式分支：
// 获取 last AI message（现有逻辑，可复用）
if
(context.executionMode
!==
'interactive'
) {
// 非交互/ACP：跳过剪贴板，返回内容本身
if
(
!
lastAiOutput) {
return
{
type:
'message'
,
messageType:
'info'
,
content:
'No output in history.'
,
};
}
return
{
type:
'message'
,
messageType:
'info'
,
content: lastAiOutput,
};
}
// interactive 路径：原有剪贴板逻辑不变
await
copyToClipboard
(lastAiOutput);
return
{
type:
'message'
,
messageType:
'info'
,
content:
'Last output copied to the clipboard'
,
};
ACP 语义
：IDE 收到最后一条模型输出的原文，可自行决定是否写入剪贴板或展示给用户。
5.3
/restore
当前状态
：
supportedModes: ['interactive']
。
设计决策
：快照恢复进一步会重新执行工具调用，语义上与交互界面紧密耦合。经讨论决定保持
supportedModes: ['interactive']
，不扩展至 non-interactive/acp 模式。
ACP 语义
：checkpoint 的 git 状态恢复和 gemini client history 设置均作为副作用执行；IDE 收到确认消息后可提示用户”状态已恢复”，工具重执行由 IDE 自行决定是否触发。
6. A’ 类：无参数 dialog 路径的 non-interactive 处理
6.1
/model
当前状态
：
输入
当前行为
/model
（无参数）
→
{ type: 'dialog', dialog: 'model' }
（non-interactive 下变 unsupported）
/model <model-id>
未实现（只有
--fast
分支）
/model --fast
（无 model name）
→
{ type: 'dialog', dialog: 'fast-model' }
（non-interactive 下变 unsupported）
/model --fast <model-id>
→
MessageActionReturn
✅
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在 action 内各 dialog 路径前插入 non-interactive 分支：
// 无参数路径（原返回 dialog: 'model'）
if
(
!
args.
trim
()) {
if
(context.executionMode
!==
'interactive'
) {
const
currentModel
=
config.
getModel
()
??
'unknown'
;
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Current model: ${
currentModel
}
\n
Use "/model <model-id>" to switch models.`
,
};
}
return
{ type:
'dialog'
, dialog:
'model'
};
}
// --fast 无参数路径（原返回 dialog: 'fast-model'）
if
(args.
startsWith
(
'--fast'
)
&&
!
modelName) {
if
(context.executionMode
!==
'interactive'
) {
const
fastModel
=
context.services.settings?.merged?.fastModel
??
'not set'
;
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Current fast model: ${
fastModel
}
\n
Use "/model --fast <model-id>" to set fast model.`
,
};
}
return
{ type:
'dialog'
, dialog:
'fast-model'
};
}
ACP 语义
：IDE 展示当前模型名称，供用户参考；切换模型通过带参数调用实现（
/model <model-id>
）。
注意
：
/model <model-id>
（不带
--fast
）目前没有实现设置当前 session 模型的逻辑，只有
--fast <model-id>
有。如果 Phase 2 要支持 ACP 下切换主模型，需要同步实现
/model <model-id>
的 set 逻辑。本设计预留此路径但标记为 Phase 2 可选项，优先保证”查看当前模型”的 read-only 路径。
6.2
/approval-mode
当前状态
：
输入
当前行为
/approval-mode
（无参数）
→
{ type: 'dialog', dialog: 'approval-mode' }
（non-interactive 下变 unsupported）
/approval-mode <mode>
→
MessageActionReturn
✅
/approval-mode <invalid>
→
MessageActionReturn
（error）✅
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在无参数路径（
!args.trim()
）插入 non-interactive 分支：
if
(
!
args.
trim
()) {
if
(context.executionMode
!==
'interactive'
) {
const
currentMode
=
config?.
getApprovalMode
()
??
'unknown'
;
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Current approval mode: ${
currentMode
}
\n
Available modes: ${
APPROVAL_MODES
.
join
(
', '
)
}
\n
Use "/approval-mode <mode>" to change.`
,
};
}
return
{ type:
'dialog'
, dialog:
'approval-mode'
};
}
7. B 类：需要完整 non-interactive 分支
这五个命令的 action 在 interactive 模式下通过
context.ui.addItem()
渲染 React 组件或调用
context.ui.clear()
，返回值为
void
。在 non-interactive 中，这些调用均为 no-op，导致
handleSlashCommand
将无返回值处理为
"Command executed successfully."
，无实际内容输出。
实现原则
：在 action
顶部
检查
executionMode
，非 interactive 时
提前 return
包含实际内容的
message
，interactive 路径代码完全不触碰。
7.1
/about
（altName:
status
）
数据来源
：
getExtendedSystemInfo(context)
返回
ExtendedSystemInfo
，包含：
cliVersion
、
osPlatform
、
osArch
、
osRelease
、
nodeVersion
、
modelVersion
、
selectedAuthType
、
ideClient
、
sessionId
、
memoryUsage
、
baseUrl
、
apiKeyEnvKey
、
gitCommit
、
fastModel
。所有字段在 non-interactive 中均可获取（context.services.config 和 settings 均已注入）。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在
getExtendedSystemInfo
调用后，interactive 路径之前插入模式分支：
action
:
async
(
context
)
=>
{
const
systemInfo
=
await
getExtendedSystemInfo
(context);
if
(context.executionMode
!==
'interactive'
) {
const
lines
=
[
`Qwen Code v${
systemInfo
.
cliVersion
}`
,
`Model: ${
systemInfo
.
modelVersion
}`
,
`Fast Model: ${
systemInfo
.
fastModel
??
'not set'}`
,
`Auth: ${
systemInfo
.
selectedAuthType
}`
,
`Platform: ${
systemInfo
.
osPlatform
} ${
systemInfo
.
osArch
} (${
systemInfo
.
osRelease
})`
,
`Node.js: ${
systemInfo
.
nodeVersion
}`
,
`Session: ${
systemInfo
.
sessionId
}`
,
...
(systemInfo.gitCommit
?
[
`Git commit: ${
systemInfo
.
gitCommit
}`
]
:
[]),
...
(systemInfo.ideClient
?
[
`IDE: ${
systemInfo
.
ideClient
}`
]
:
[]),
];
return
{
type:
'message'
,
messageType:
'info'
,
content: lines.
join
(
'
\n
'
),
};
}
// interactive 路径：原有 addItem 逻辑不变
const
aboutItem
:
Omit
<
HistoryItemAbout
,
'id'
>
=
{ type: MessageType.
ABOUT
, systemInfo };
context.ui.
addItem
(aboutItem, Date.
now
());
},
7.2
/stats
（及子命令
model
、
tools
）
数据来源
：
context.session.stats
（
SessionStatsState
）包含
sessionStartTime
、
metrics
（
SessionMetrics
：
models
、
tools
、
files
）、
promptCount
。在 non-interactive 中，
sessionStartTime
为当前调用时刻，
metrics
来自
uiTelemetryService.getMetrics()
（本次调用的累积值，通常为零），
promptCount
为 1。
变更
：
将父命令
stats
及子命令
model
、
tools
的
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
父命令和每个子命令的 action 均插入模式分支，提前返回文本格式统计：
// /stats 主命令
action
: (
context
)
=>
{
if
(context.executionMode
!==
'interactive'
) {
const
now
=
new
Date
();
const
{
sessionStartTime
,
promptCount
,
metrics
}
=
context.session.stats;
if
(
!
sessionStartTime) {
return
{ type:
'message'
, messageType:
'error'
, content:
'Session start time unavailable.'
};
}
const
wallDuration
=
now.
getTime
()
-
sessionStartTime.
getTime
();
// 汇总所有 model 的 token 数
let
totalPromptTokens
=
0
, totalCandidateTokens
=
0
, totalRequests
=
0
;
for
(
const
modelMetrics
of
Object.
values
(metrics.models)) {
totalPromptTokens
+=
modelMetrics.tokens.prompt;
totalCandidateTokens
+=
modelMetrics.tokens.candidates;
totalRequests
+=
modelMetrics.api.totalRequests;
}
const
lines
=
[
`Session duration: ${
formatDuration
(
wallDuration
)
}`
,
`Prompts: ${
promptCount
}`
,
`API requests: ${
totalRequests
}`
,
`Tokens — prompt: ${
totalPromptTokens
}, output: ${
totalCandidateTokens
}`
,
`Tool calls: ${
metrics
.
tools
.
totalCalls
} (${
metrics
.
tools
.
totalSuccess
} ok, ${
metrics
.
tools
.
totalFail
} fail)`
,
`Files: +${
metrics
.
files
.
totalLinesAdded
} / -${
metrics
.
files
.
totalLinesRemoved
} lines`
,
];
return
{ type:
'message'
, messageType:
'info'
, content: lines.
join
(
'
\n
'
) };
}
// interactive 路径：原有 addItem 逻辑不变
const
statsItem
:
HistoryItemStats
=
{ type: MessageType.
STATS
, duration:
formatDuration
(wallDuration) };
context.ui.
addItem
(statsItem, Date.
now
());
},
子命令
model
和
tools
也各自插入模式分支，返回对应维度的文本统计（model 维度按 model name 列出 token 用量；tools 维度列出各 tool 调用次数）。
说明
：在 non-interactive 单次调用中，metrics 通常为零（新 session），但结构完整，不影响格式。ACP Session 中可能有累积值，有实际意义。
7.3
/insight
当前状态
：action 返回
void
，通过
addItem
展示进度和结果，最后调用
open(outputPath)
打开浏览器。核心逻辑是
insightGenerator.generateStaticInsight()
生成 HTML 文件。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
按
executionMode
三路分叉：
non_interactive
：同步生成，忽略进度回调，不开浏览器，直接返回
message
（文件路径）
acp
：异步启动生成，通过
stream_messages
将进度（
encodeInsightProgressMessage
）和完成（
encodeInsightReadyMessage
）推送给 IDE
interactive
：原有
addItem
+
setPendingItem
+
open()
逻辑不变
// non_interactive 路径
if
(context.executionMode
===
'non_interactive'
) {
const
outputPath
=
await
insightGenerator.
generateStaticInsight
(
projectsDir,
()
=>
{},
// no-op progress
);
return
{
type:
'message'
,
messageType:
'info'
,
content:
t
(
'Insight report generated at: {{path}}'
, { path: outputPath }),
};
}
// acp 路径：stream_messages
if
(context.executionMode
===
'acp'
) {
// ... 构造 streamMessages async generator，yield encodeInsightProgressMessage / encodeInsightReadyMessage ...
return
{ type:
'stream_messages'
, messages:
streamMessages
() };
}
// interactive 路径：原有实现不变
设计理由
：
non_interactive
模式（CLI 管道）不支持
stream_messages
，只能返回单条
message
；ACP 模式（IDE 插件）能消费
stream_messages
并实时展示进度，因此为其保留 streaming 路径。
ACP 消息格式
：
encodeInsightProgressMessage(stage, progress, detail?)
产生 IDE 可解析的进度条消息；
encodeInsightReadyMessage(outputPath)
通知 IDE 文件已就绪，由 IDE 决定如何展示链接。
7.4
/docs
当前状态
：action 返回
void
，通过
addItem
显示消息并调用
open(docsUrl)
打开浏览器。有一个
SANDBOX
环境变量分支（沙盒下只 addItem，不开浏览器）。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
修改 action 返回类型为
Promise<void | MessageActionReturn>
。
在 action 开头插入 non-interactive 分支：
action
:
async
(
context
)
=>
{
const
langPath
=
getCurrentLanguage
()?.
startsWith
(
'zh'
)
?
'zh'
:
'en'
;
const
docsUrl
=
`https://qwenlm.github.io/qwen-code-docs/${
langPath
}`
;
if
(context.executionMode
!==
'interactive'
) {
// 非交互/ACP：直接返回 URL，不打开浏览器，不调用 addItem
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Qwen Code documentation: ${
docsUrl
}`
,
};
}
// interactive 路径：原有 SANDBOX 判断 + addItem + open() 不变
if
(process.env[
'SANDBOX'
]
&&
...
) {
context.ui.
addItem
(
...
);
}
else
{
context.ui.
addItem
(
...
);
await
open
(docsUrl);
}
},
7.5
/clear
（altNames:
reset
、
new
）
当前状态
：action 执行以下操作并返回
void
：
config.getHookSystem()?.fireSessionEndEvent()
— 触发 hook（有副作用）
config.startNewSession()
— 开始新 session ID（有副作用）
uiTelemetryService.reset()
— 重置 telemetry 计数器（有副作用）
skillTool.clearLoadedSkills()
— 清除 skill 缓存（有副作用）
context.ui.clear()
— 清空终端 UI（
UI 副作用，non-interactive 下为 no-op
）
geminiClient.resetChat()
— 重置 chat 历史（有副作用）
config.getHookSystem()?.fireSessionStartEvent()
— 触发 hook（有副作用）
non-interactive/ACP 语义分析
：
ui.clear()
在 non-interactive 中已是 no-op，不需要处理
geminiClient.resetChat()
：在 ACP Session 中是有意义的副作用（清空 chat 历史），应保留；在 non-interactive 单次调用中，每次调用都是全新 session，
resetChat
语义重复但无害
config.startNewSession()
：在 ACP 中有意义（开始新的 session ID）；在 non-interactive 单次调用中同样语义重复但无害
fireSessionEndEvent
/
fireSessionStartEvent
：在 ACP 中有意义（触发 hook）
决策
：non-interactive/ACP 路径保留所有有意义的副作用（resetChat、startNewSession、hook events），仅跳过
ui.clear()
（已是 no-op）并返回上下文边界标记 message。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
修改 action 返回类型为
Promise<void | MessageActionReturn>
。
在 action 内，
context.ui.clear()
调用后（或替代它）根据模式分支：
action
:
async
(
context
,
_args
)
=>
{
const
{
config
}
=
context.services;
if
(config) {
config.
getHookSystem
()?.
fireSessionEndEvent
(SessionEndReason.Clear).
catch
(
...
);
const
newSessionId
=
config.
startNewSession
();
uiTelemetryService.
reset
();
const
skillTool
=
config.
getToolRegistry
()?.
getAllTools
().
find
(
...
);
if
(skillTool
instanceof
SkillTool
) skillTool.
clearLoadedSkills
();
if
(newSessionId
&&
context.session.startNewSession) {
context.session.
startNewSession
(newSessionId);
}
// ui.clear() 在非交互下已是 no-op，但依然调用（不需要条件分支）
context.ui.
clear
();
const
geminiClient
=
config.
getGeminiClient
();
if
(geminiClient) {
await
geminiClient.
resetChat
();
}
config.
getHookSystem
()?.
fireSessionStartEvent
(
...
).
catch
(
...
);
}
else
{
context.ui.
clear
();
}
// 根据模式决定返回值
if
(context.executionMode
!==
'interactive'
) {
return
{
type:
'message'
,
messageType:
'info'
,
content:
'Context cleared. Previous messages are no longer in context.'
,
};
}
// interactive 路径：void（不返回，React UI 由 ui.clear() 驱动更新）
},
ACP 语义
：IDE 收到上下文边界标记后，可将其作为 session 分隔符展示（如”新会话开始”提示），并清空本地 chat 历史缓存。
8.
handleCommandResult
变更
结论：无需修改。
Phase 2 所有命令变更后，non-interactive/ACP 路径的返回类型均为
message
或
submit_prompt
，均已在
handleCommandResult
的 switch 中正确处理。
9.
createNonInteractiveUI()
变更
结论：无需修改。
当前 no-op 实现已足够。
addItem
、
clear
、
setPendingItem
等 no-op 在 B 类命令的 non-interactive 路径中不会被调用（因为提前 return）；interactive 路径中不受影响。
10. Phase 2.2：prompt command 模型调用打通
Phase 1 中
CommandService.getModelInvocableCommands()
已实现，
BundledSkillLoader
、
FileCommandLoader
（用户/项目命令）、
McpPromptLoader
已设置
modelInvocable: true
。
Phase 2.2 的工作是将
SkillTool
从只消费
SkillManager.listSkills()
改为同时消费
CommandService.getModelInvocableCommands()
，统一模型可调用命令的入口。
变更文件
：
packages/core/src/tools/SkillTool.ts
（或对应路径）
具体变更
：
SkillTool
在初始化时接收
CommandService
（或其
getModelInvocableCommands()
的结果）作为依赖注入
在构建 tool description 时，合并
listSkills()
和
getModelInvocableCommands()
的结果
确保 built-in commands（
modelInvocable: false
）不出现在 tool description 中
注
：
SkillTool
的具体实现依赖
packages/core
内部架构，详细设计在本文档中仅描述接口变更，实现细节需结合 core 包的现有结构确定。
11. Phase 2.3：mid-input slash command 检测（基础版）
在
InputPrompt
组件中检测光标附近的 slash token（不限于行首），触发补全菜单。
检测规则
：
当光标前存在以
/
开头、不含空格的 token 时，触发命令补全
补全候选来自
getCommandsForMode('interactive')
的可见命令列表
补全菜单展示命令名 + description（不含 argumentHint 等，Phase 3 补充）
本功能为 UI 层变更，属于 Phase 2.3 独立子任务，不影响其他 Phase 2.1/2.2 的实施。
12. 文件变更总览
12.1 命令文件变更（Phase 2.1）
文件
变更类型
具体内容
exportCommand.ts
A 类
父命令 + 4 个子命令：
supportedModes
→ all modes
planCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
statuslineCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
languageCommand.ts
A+ 类
父命令 +
ui
/
output
子命令 + 动态 language 子命令：
supportedModes
→ all modes
copyCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
restoreCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
modelCommand.ts
A’ 类
supportedModes
→ all modes + 无参数/无 fast model 路径新增非交互分支
approvalModeCommand.ts
A’ 类
supportedModes
→ all modes + 无参数路径新增非交互分支
aboutCommand.ts
B 类
supportedModes
→ all modes + 非交互路径返回
message
（版本/模型/环境摘要）
statsCommand.ts
B 类
supportedModes
→ all modes + 非交互路径返回
message
（stats 文本）；子命令同步处理
insightCommand.ts
B 类
supportedModes
→ all modes +
non_interactive
路径同步生成返回
message
（文件路径）；
acp
路径返回
stream_messages
带进度推送
docsCommand.ts
B 类
supportedModes
→ all modes + 非交互路径返回
message
（文档 URL），不打开浏览器
clearCommand.ts
B 类
supportedModes
→ all modes + action 末尾根据模式返回
message
或
void
12.2 其他文件变更
文件
变更内容
packages/core/src/tools/SkillTool.ts
Phase 2.2：接入
getModelInvocableCommands()
（详细设计另行确定）
packages/cli/src/ui/InputPrompt.tsx
（或同等组件）
Phase 2.3：mid-input slash 检测逻辑
12.3 不变的文件
packages/cli/src/nonInteractiveCliCommands.ts
（
handleCommandResult
、
handleSlashCommand
无需修改）
packages/cli/src/ui/noninteractive/nonInteractiveUi.ts
（stub UI 无需修改）
packages/cli/src/services/commandUtils.ts
（
filterCommandsForMode
、
getEffectiveSupportedModes
无需修改）
packages/cli/src/services/CommandService.ts
（
getCommandsForMode
、
getModelInvocableCommands
已在 Phase 1 实现）
13. 测试策略
13.1 命令单元测试
为每个变更的命令在同目录下新增或更新测试文件（
*.test.ts
），覆盖以下 case：
A/A+ 类命令
（
export
、
language
）：
supportedModes
正确包含
non_interactive
和
acp
在
executionMode: 'non_interactive'
下，action 返回
MessageActionReturn
或
SubmitPromptActionReturn
，不调用
ui.addItem
或
ui.clear
Interactive 路径行为与重构前完全一致（快照测试）
仅交互命令
（
plan
、
statusline
、
copy
、
restore
）：
supportedModes
为
['interactive']
，这是设计决策
验证 non-interactive 下执行时正确返回
unsupported
A’ 类命令
（
model
、
approval-mode
）：
无参数 +
executionMode: 'non_interactive'
→ 返回当前状态
message
，不返回
dialog
有参数 +
executionMode: 'non_interactive'
→ 原有
message
逻辑正常执行
Interactive 路径：无参数 →
dialog
，有参数 →
message
（不变）
B 类命令
（
about
、
stats
、
insight
、
docs
、
clear
）：
executionMode: 'non_interactive'
下，action 返回
MessageActionReturn
，不调用任何
ui.*
方法
返回的
content
字符串包含预期的关键字段（版本号、模型名、URL 等）
Interactive 路径：
ui.addItem
被调用，
action
返回
void
（不变）
clear
的特殊 case
：
executionMode: 'non_interactive'
下，
geminiClient.resetChat()
仍被调用（副作用保留）
返回上下文边界
message
，内容为
'Context cleared. Previous messages are no longer in context.'
13.2 集成测试（
handleSlashCommand
）
在
nonInteractiveCli.test.ts
或新建的集成测试文件中：
handleSlashCommand('/about', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 包含版本号 }
handleSlashCommand('/stats', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 包含 'Session duration' }
handleSlashCommand('/docs', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 包含 'qwenlm.github.io' }
handleSlashCommand('/clear', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 'Context cleared.' }
handleSlashCommand('/plan', ...)
在 non-interactive 模式下返回
unsupported
（仅交互命令）
现有 non-interactive 命令（
btw
、
bug
等）行为无退化
13.3
commandUtils
测试
commandUtils.test.ts
中新增（或已有的测试继续覆盖）：
扩展后的命令（
export
、
language
等）均能通过
filterCommandsForMode(commands, 'non_interactive')
和
filterCommandsForMode(commands, 'acp')
的过滤
仅交互命令（
plan
、
statusline
、
copy
、
restore
）在
filterCommandsForMode(commands, 'non_interactive')
下被正确过滤掉
14. 行为影响分析
场景
Phase 2 前行为
Phase 2 后行为
性质
non-interactive 下执行
/export md
❌ unsupported（被过滤）
✅ 返回文件路径 message
能力扩展
non-interactive 下执行
/plan <task>
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/statusline
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/language ui zh-CN
❌ unsupported
✅ 设置语言，返回确认 message
能力扩展
non-interactive 下执行
/copy
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/restore
（无参数）
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/restore <id>
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/model
❌ unsupported（dialog）
✅ 返回当前模型名称
能力扩展
non-interactive 下执行
/model <id>
❌ unsupported
🔄 Phase 2 可选：实现切换逻辑
能力扩展（可选）
non-interactive 下执行
/approval-mode
❌ unsupported（dialog）
✅ 返回当前审批模式
能力扩展
non-interactive 下执行
/approval-mode yolo
❌ unsupported
✅ 设置模式，返回确认
能力扩展
non-interactive 下执行
/about
❌ 返回 “Command executed successfully.”（addItem no-op）
✅ 返回版本/模型/环境摘要
Bug fix + 能力扩展
non-interactive 下执行
/stats
❌ 返回 “Command executed successfully.”
✅ 返回 session 统计文本
Bug fix + 能力扩展
non-interactive 下执行
/insight
❌ 返回 “Command executed successfully.”（生成但无输出）
✅ 生成并返回文件路径
Bug fix + 能力扩展
non-interactive 下执行
/docs
❌ 返回 “Command executed successfully.”
✅ 返回文档 URL
Bug fix + 能力扩展
non-interactive 下执行
/clear
❌ 返回 “Command executed successfully.”
✅ 返回上下文边界 message
Bug fix + 能力扩展
interactive 下执行任意以上命令
✅ 原有行为
✅ 原有行为（零退化）
不变
15. 实施顺序
建议按以下顺序实施，每组可独立 commit 和 review：
Batch 1
（~30min）：A 类 — 只改
supportedModes
修改
exportCommand.ts
（及其子命令），验证测试通过。
Batch 2
（~45min）：A+ 类 — 少量分支
修改
languageCommand.ts
，为有副作用的路径添加非交互分支，更新对应测试。（
copyCommand.ts
和
restoreCommand.ts
经讨论保持仅交互。）
Batch 3
（~45min）：A’ 类 — dialog 路径
修改
modelCommand.ts
、
approvalModeCommand.ts
，为无参数路径添加非交互分支，更新对应测试。
Batch 4
（~1.5h）：B 类 — 完整分支
修改
aboutCommand.ts
、
statsCommand.ts
（含子命令）、
docsCommand.ts
。
Batch 5
（~1h）：B 类特殊 —
insightCommand.ts
、
clearCommand.ts
这两个命令副作用较多，单独一个 commit，更新对应测试和集成测试。
Batch 6
（~2h）：Phase 2.2 — prompt command 模型调用打通
修改
SkillTool
，接入
getModelInvocableCommands()
，更新 SkillTool 测试。
Batch 7
（~2h）：Phase 2.3 — mid-input slash 检测
修改
InputPrompt
组件，新增补全触发逻辑和 UI 测试。
Batch 8
（~30min）：全量测试 + 类型检查
运行
npm run typecheck
、
cd packages/cli && npx vitest run
，修复剩余问题。
16. 验收 Checklist
Phase 2.1 命令扩展
A 类：
/export
（及子命令）、
/plan
、
/statusline
在 non-interactive 和 acp 模式下可正常执行并返回有意义输出
A+ 类：
/language
（及子命令）在 non-interactive 下正常执行，设置持久化
A+ 类：
/copy
在 non-interactive/acp 下返回最后 AI 输出文本（不操作剪贴板）
A+ 类：
/restore
无参数时在 non-interactive 下返回 checkpoint 列表；有参数时恢复状态并返回确认 message（不返回
type: 'tool'
）
A’ 类：
/model
无参数时在 non-interactive/acp 下返回当前模型名（不触发 dialog）；
/model --fast <id>
正常设置
A’ 类：
/approval-mode
无参数时在 non-interactive/acp 下返回当前模式（不触发 dialog）；有参数时正常设置
B 类：
/about
在 non-interactive/acp 下返回包含版本号、模型名的纯文本摘要
B 类：
/stats
（含子命令）在 non-interactive/acp 下返回纯文本统计数据
B 类：
/insight
在 non-interactive/acp 下生成 insight 文件并返回文件路径（不打开浏览器）
B 类：
/docs
在 non-interactive/acp 下返回文档 URL（不打开浏览器）
B 类：
/clear
在 non-interactive/acp 下返回上下文边界标记 message，
geminiClient.resetChat()
正常执行
所有 13 个命令在 interactive 模式下行为与重构前完全一致（无退化）
TypeScript 编译无错误（
npm run typecheck
）
npm run lint
无新增错误
所有现有测试通过（
cd packages/cli && npx vitest run
）
Phase 2.2 模型调用
模型在对话中可以通过
SkillTool
调用 bundled skill、file command（用户/项目）、MCP prompt
模型不可以调用 built-in commands
SkillTool
的 tool description 包含所有
modelInvocable: true
命令的名称和 description
Phase 2.3 mid-input slash
在输入框正文中输入
/
后触发命令补全菜单（不限行首）
补全菜单展示命令名 + description
补全选中后正确填充到输入框
Last updated on
May 18, 2026
Phase 1 技术设计文档：基础设施重建
Slash Command 重构路线图</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase2-technical-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Design
Slash Command
Phase 2 技术设计文档：能力扩展
Phase 2 技术设计文档：能力扩展
1. 设计目标与约束
1.1 目标
将 13 个 built-in 命令的
supportedModes
扩展到包含
non_interactive
和/或
acp
确保每个扩展命令在 ACP/non-interactive 路径下返回适合 IDE 消费的文本内容
打通 prompt command 的模型调用通路（
SkillTool
消费
getModelInvocableCommands()
）
实现 mid-input slash command 基础检测
1.2 硬性约束
interactive 路径零退化
：所有扩展命令的现有 interactive 行为严格不变，只在 action 内部新增模式分支，不触碰 interactive 路径代码
实现策略：模式分支，而非双注册
：13 个命令均采用在
action
内部增加
executionMode
判断的方式，不使用 Phase 1 设计文档 §10.2 描述的双注册模式（双注册仅在 interacti...</p><div style="font-size:16px;line-height:1.8;color:#333">Design
Slash Command
Phase 2 技术设计文档：能力扩展
Phase 2 技术设计文档：能力扩展
1. 设计目标与约束
1.1 目标
将 13 个 built-in 命令的
supportedModes
扩展到包含
non_interactive
和/或
acp
确保每个扩展命令在 ACP/non-interactive 路径下返回适合 IDE 消费的文本内容
打通 prompt command 的模型调用通路（
SkillTool
消费
getModelInvocableCommands()
）
实现 mid-input slash command 基础检测
1.2 硬性约束
interactive 路径零退化
：所有扩展命令的现有 interactive 行为严格不变，只在 action 内部新增模式分支，不触碰 interactive 路径代码
实现策略：模式分支，而非双注册
：13 个命令均采用在
action
内部增加
executionMode
判断的方式，不使用 Phase 1 设计文档 §10.2 描述的双注册模式（双注册仅在 interactive 和 non-interactive 逻辑差异极大时才有必要，本阶段命令复杂度不达到该门槛）
ACP 消息格式
：ACP 路径返回的文本内容不含 ANSI 样式，以 Markdown 或纯文本为宜，面向 IDE 插件消费
跳过环境相关副作用
：打开浏览器（
open()
）、操作剪贴板（
copyToClipboard()
）等依赖图形环境的操作，在 non-interactive/ACP 路径下必须跳过
2. Phase 1 完成后的基础状态
Phase 1 结束后的架构要点（Phase 2 直接在此基础上扩展）：
commandType
字段已从
SlashCommand
接口中删除，所有命令改用显式
supportedModes
getEffectiveSupportedModes()
为两级推断：显式
supportedModes
→
CommandKind
兜底
CommandService.getCommandsForMode(mode)
取代原
ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE
白名单
btw
、
bug
、
compress
、
context
、
init
、
summary
已在 Phase 1 中扩展到全模式，
不在本阶段列表中
createNonInteractiveUI()
中各方法均为 no-op：
addItem
、
clear
、
setDebugMessage
、
setPendingItem
、
reloadCommands
均静默忽略调用
3. 变更范围总览
本阶段共涉及 13 个命令，按实现复杂度分为四类：
类别
命令
变更要点
A 类
export
只改
supportedModes
，action 所有路径已返回合法类型
仅交互
plan
、
statusline
设计决策：这两个命令语义上与交互界面紧密耦合，保持
supportedModes: ['interactive']
A+ 类
language
改
supportedModes
+ 少量 non-interactive 分支处理
仅交互
copy
、
restore
设计决策：剥贴板和快照恢复本质上是交互操作，保持
supportedModes: ['interactive']
A’ 类
model
、
approval-mode
有参数路径已返回
message
，无参数路径需新增 non-interactive 分支（现触发 dialog）
B 类
about
、
stats
、
insight
、
docs
、
clear
action 所有路径均无返回值或调用
addItem
/
clear
，需新增完整 non-interactive 分支
4. A 类：只改
supportedModes
这三个命令的所有
action
路径已经返回
message
或
submit_prompt
，完全无 UI 依赖，
handleCommandResult
可直接处理。
4.1
/export
（及子命令）
当前状态
：
supportedModes: ['interactive']
，所有子命令 action 均返回
MessageActionReturn
。
变更
：将父命令及所有四个子命令（
md
、
html
、
json
、
jsonl
）的
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
ACP 消息内容
：action 现有返回内容已包含完整文件路径（如
Session exported to markdown: qwen-export-2024-01-01T12-00-00.md
），对 IDE 消费友好，无需修改文本。
注意
：
/export
父命令本身没有
action
，只有子命令。将父命令
supportedModes
改为全模式后，
parseSlashCommand
能够匹配子命令路由，但若用户只输入
/export
不带子命令，
commandToExecute.action
为 undefined，
handleSlashCommand
返回
no_command
，调用方会显示可用子命令提示。这是预期行为。
4.2
/plan
当前状态
：
supportedModes: ['interactive']
，action 所有路径返回
MessageActionReturn
或
SubmitPromptActionReturn
。
设计决策
：
/plan
是引导用户进行多轮交互规划的命令，语义上与交互界面紧密耦合。经讨论决定保持
supportedModes: ['interactive']
，不扩展至 non-interactive/acp 模式。
4.3
/statusline
当前状态
：
supportedModes: ['interactive']
，action 始终返回
SubmitPromptActionReturn
（将 subagent 调用 prompt 提交给模型）。
设计决策
：
/statusline
是触发 subagent 对当前状态进行总结的命令，语义上与交互界面紧密耦合。经讨论决定保持
supportedModes: ['interactive']
，不扩展至 non-interactive/acp 模式。
5. A+ 类：少量 non-interactive 分支处理
5.1
/language
当前状态
：action 所有路径均返回
MessageActionReturn
（读取/设置语言设置）。
需要处理的副作用
：
setUiLanguage()
内调用
context.ui.reloadCommands()
，在非交互 UI 中已是 no-op，无需额外处理。
变更
：
将父命令及子命令（
ui
、
output
，以及
SUPPORTED_LANGUAGES
动态生成的子命令）的
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
action 无需添加模式分支，现有返回文本已适合机器消费。
ACP 语义说明
：在 non-interactive（单次调用）中执行
/language ui zh-CN
会修改持久化设置（写入 settings 文件），该变更对后续 session 生效，本次 session 内 i18n 也立即生效。这与用户预期一致。
5.2
/copy
当前状态
：action 调用
copyToClipboard()
，在 ACP/headless 环境中可能抛出异常或无声失败（clipboard 不可用）。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在 action 内新增模式分支：
// 获取 last AI message（现有逻辑，可复用）
if
(context.executionMode
!==
'interactive'
) {
// 非交互/ACP：跳过剪贴板，返回内容本身
if
(
!
lastAiOutput) {
return
{
type:
'message'
,
messageType:
'info'
,
content:
'No output in history.'
,
};
}
return
{
type:
'message'
,
messageType:
'info'
,
content: lastAiOutput,
};
}
// interactive 路径：原有剪贴板逻辑不变
await
copyToClipboard
(lastAiOutput);
return
{
type:
'message'
,
messageType:
'info'
,
content:
'Last output copied to the clipboard'
,
};
ACP 语义
：IDE 收到最后一条模型输出的原文，可自行决定是否写入剪贴板或展示给用户。
5.3
/restore
当前状态
：
supportedModes: ['interactive']
。
设计决策
：快照恢复进一步会重新执行工具调用，语义上与交互界面紧密耦合。经讨论决定保持
supportedModes: ['interactive']
，不扩展至 non-interactive/acp 模式。
ACP 语义
：checkpoint 的 git 状态恢复和 gemini client history 设置均作为副作用执行；IDE 收到确认消息后可提示用户”状态已恢复”，工具重执行由 IDE 自行决定是否触发。
6. A’ 类：无参数 dialog 路径的 non-interactive 处理
6.1
/model
当前状态
：
输入
当前行为
/model
（无参数）
→
{ type: 'dialog', dialog: 'model' }
（non-interactive 下变 unsupported）
/model <model-id>
未实现（只有
--fast
分支）
/model --fast
（无 model name）
→
{ type: 'dialog', dialog: 'fast-model' }
（non-interactive 下变 unsupported）
/model --fast <model-id>
→
MessageActionReturn
✅
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在 action 内各 dialog 路径前插入 non-interactive 分支：
// 无参数路径（原返回 dialog: 'model'）
if
(
!
args.
trim
()) {
if
(context.executionMode
!==
'interactive'
) {
const
currentModel
=
config.
getModel
()
??
'unknown'
;
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Current model: ${
currentModel
}
\n
Use "/model <model-id>" to switch models.`
,
};
}
return
{ type:
'dialog'
, dialog:
'model'
};
}
// --fast 无参数路径（原返回 dialog: 'fast-model'）
if
(args.
startsWith
(
'--fast'
)
&&
!
modelName) {
if
(context.executionMode
!==
'interactive'
) {
const
fastModel
=
context.services.settings?.merged?.fastModel
??
'not set'
;
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Current fast model: ${
fastModel
}
\n
Use "/model --fast <model-id>" to set fast model.`
,
};
}
return
{ type:
'dialog'
, dialog:
'fast-model'
};
}
ACP 语义
：IDE 展示当前模型名称，供用户参考；切换模型通过带参数调用实现（
/model <model-id>
）。
注意
：
/model <model-id>
（不带
--fast
）目前没有实现设置当前 session 模型的逻辑，只有
--fast <model-id>
有。如果 Phase 2 要支持 ACP 下切换主模型，需要同步实现
/model <model-id>
的 set 逻辑。本设计预留此路径但标记为 Phase 2 可选项，优先保证”查看当前模型”的 read-only 路径。
6.2
/approval-mode
当前状态
：
输入
当前行为
/approval-mode
（无参数）
→
{ type: 'dialog', dialog: 'approval-mode' }
（non-interactive 下变 unsupported）
/approval-mode <mode>
→
MessageActionReturn
✅
/approval-mode <invalid>
→
MessageActionReturn
（error）✅
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在无参数路径（
!args.trim()
）插入 non-interactive 分支：
if
(
!
args.
trim
()) {
if
(context.executionMode
!==
'interactive'
) {
const
currentMode
=
config?.
getApprovalMode
()
??
'unknown'
;
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Current approval mode: ${
currentMode
}
\n
Available modes: ${
APPROVAL_MODES
.
join
(
', '
)
}
\n
Use "/approval-mode <mode>" to change.`
,
};
}
return
{ type:
'dialog'
, dialog:
'approval-mode'
};
}
7. B 类：需要完整 non-interactive 分支
这五个命令的 action 在 interactive 模式下通过
context.ui.addItem()
渲染 React 组件或调用
context.ui.clear()
，返回值为
void
。在 non-interactive 中，这些调用均为 no-op，导致
handleSlashCommand
将无返回值处理为
"Command executed successfully."
，无实际内容输出。
实现原则
：在 action
顶部
检查
executionMode
，非 interactive 时
提前 return
包含实际内容的
message
，interactive 路径代码完全不触碰。
7.1
/about
（altName:
status
）
数据来源
：
getExtendedSystemInfo(context)
返回
ExtendedSystemInfo
，包含：
cliVersion
、
osPlatform
、
osArch
、
osRelease
、
nodeVersion
、
modelVersion
、
selectedAuthType
、
ideClient
、
sessionId
、
memoryUsage
、
baseUrl
、
apiKeyEnvKey
、
gitCommit
、
fastModel
。所有字段在 non-interactive 中均可获取（context.services.config 和 settings 均已注入）。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
在
getExtendedSystemInfo
调用后，interactive 路径之前插入模式分支：
action
:
async
(
context
)
=>
{
const
systemInfo
=
await
getExtendedSystemInfo
(context);
if
(context.executionMode
!==
'interactive'
) {
const
lines
=
[
`Qwen Code v${
systemInfo
.
cliVersion
}`
,
`Model: ${
systemInfo
.
modelVersion
}`
,
`Fast Model: ${
systemInfo
.
fastModel
??
'not set'}`
,
`Auth: ${
systemInfo
.
selectedAuthType
}`
,
`Platform: ${
systemInfo
.
osPlatform
} ${
systemInfo
.
osArch
} (${
systemInfo
.
osRelease
})`
,
`Node.js: ${
systemInfo
.
nodeVersion
}`
,
`Session: ${
systemInfo
.
sessionId
}`
,
...
(systemInfo.gitCommit
?
[
`Git commit: ${
systemInfo
.
gitCommit
}`
]
:
[]),
...
(systemInfo.ideClient
?
[
`IDE: ${
systemInfo
.
ideClient
}`
]
:
[]),
];
return
{
type:
'message'
,
messageType:
'info'
,
content: lines.
join
(
'
\n
'
),
};
}
// interactive 路径：原有 addItem 逻辑不变
const
aboutItem
:
Omit
<
HistoryItemAbout
,
'id'
>
=
{ type: MessageType.
ABOUT
, systemInfo };
context.ui.
addItem
(aboutItem, Date.
now
());
},
7.2
/stats
（及子命令
model
、
tools
）
数据来源
：
context.session.stats
（
SessionStatsState
）包含
sessionStartTime
、
metrics
（
SessionMetrics
：
models
、
tools
、
files
）、
promptCount
。在 non-interactive 中，
sessionStartTime
为当前调用时刻，
metrics
来自
uiTelemetryService.getMetrics()
（本次调用的累积值，通常为零），
promptCount
为 1。
变更
：
将父命令
stats
及子命令
model
、
tools
的
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
父命令和每个子命令的 action 均插入模式分支，提前返回文本格式统计：
// /stats 主命令
action
: (
context
)
=>
{
if
(context.executionMode
!==
'interactive'
) {
const
now
=
new
Date
();
const
{
sessionStartTime
,
promptCount
,
metrics
}
=
context.session.stats;
if
(
!
sessionStartTime) {
return
{ type:
'message'
, messageType:
'error'
, content:
'Session start time unavailable.'
};
}
const
wallDuration
=
now.
getTime
()
-
sessionStartTime.
getTime
();
// 汇总所有 model 的 token 数
let
totalPromptTokens
=
0
, totalCandidateTokens
=
0
, totalRequests
=
0
;
for
(
const
modelMetrics
of
Object.
values
(metrics.models)) {
totalPromptTokens
+=
modelMetrics.tokens.prompt;
totalCandidateTokens
+=
modelMetrics.tokens.candidates;
totalRequests
+=
modelMetrics.api.totalRequests;
}
const
lines
=
[
`Session duration: ${
formatDuration
(
wallDuration
)
}`
,
`Prompts: ${
promptCount
}`
,
`API requests: ${
totalRequests
}`
,
`Tokens — prompt: ${
totalPromptTokens
}, output: ${
totalCandidateTokens
}`
,
`Tool calls: ${
metrics
.
tools
.
totalCalls
} (${
metrics
.
tools
.
totalSuccess
} ok, ${
metrics
.
tools
.
totalFail
} fail)`
,
`Files: +${
metrics
.
files
.
totalLinesAdded
} / -${
metrics
.
files
.
totalLinesRemoved
} lines`
,
];
return
{ type:
'message'
, messageType:
'info'
, content: lines.
join
(
'
\n
'
) };
}
// interactive 路径：原有 addItem 逻辑不变
const
statsItem
:
HistoryItemStats
=
{ type: MessageType.
STATS
, duration:
formatDuration
(wallDuration) };
context.ui.
addItem
(statsItem, Date.
now
());
},
子命令
model
和
tools
也各自插入模式分支，返回对应维度的文本统计（model 维度按 model name 列出 token 用量；tools 维度列出各 tool 调用次数）。
说明
：在 non-interactive 单次调用中，metrics 通常为零（新 session），但结构完整，不影响格式。ACP Session 中可能有累积值，有实际意义。
7.3
/insight
当前状态
：action 返回
void
，通过
addItem
展示进度和结果，最后调用
open(outputPath)
打开浏览器。核心逻辑是
insightGenerator.generateStaticInsight()
生成 HTML 文件。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
按
executionMode
三路分叉：
non_interactive
：同步生成，忽略进度回调，不开浏览器，直接返回
message
（文件路径）
acp
：异步启动生成，通过
stream_messages
将进度（
encodeInsightProgressMessage
）和完成（
encodeInsightReadyMessage
）推送给 IDE
interactive
：原有
addItem
+
setPendingItem
+
open()
逻辑不变
// non_interactive 路径
if
(context.executionMode
===
'non_interactive'
) {
const
outputPath
=
await
insightGenerator.
generateStaticInsight
(
projectsDir,
()
=>
{},
// no-op progress
);
return
{
type:
'message'
,
messageType:
'info'
,
content:
t
(
'Insight report generated at: {{path}}'
, { path: outputPath }),
};
}
// acp 路径：stream_messages
if
(context.executionMode
===
'acp'
) {
// ... 构造 streamMessages async generator，yield encodeInsightProgressMessage / encodeInsightReadyMessage ...
return
{ type:
'stream_messages'
, messages:
streamMessages
() };
}
// interactive 路径：原有实现不变
设计理由
：
non_interactive
模式（CLI 管道）不支持
stream_messages
，只能返回单条
message
；ACP 模式（IDE 插件）能消费
stream_messages
并实时展示进度，因此为其保留 streaming 路径。
ACP 消息格式
：
encodeInsightProgressMessage(stage, progress, detail?)
产生 IDE 可解析的进度条消息；
encodeInsightReadyMessage(outputPath)
通知 IDE 文件已就绪，由 IDE 决定如何展示链接。
7.4
/docs
当前状态
：action 返回
void
，通过
addItem
显示消息并调用
open(docsUrl)
打开浏览器。有一个
SANDBOX
环境变量分支（沙盒下只 addItem，不开浏览器）。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
修改 action 返回类型为
Promise<void | MessageActionReturn>
。
在 action 开头插入 non-interactive 分支：
action
:
async
(
context
)
=>
{
const
langPath
=
getCurrentLanguage
()?.
startsWith
(
'zh'
)
?
'zh'
:
'en'
;
const
docsUrl
=
`https://qwenlm.github.io/qwen-code-docs/${
langPath
}`
;
if
(context.executionMode
!==
'interactive'
) {
// 非交互/ACP：直接返回 URL，不打开浏览器，不调用 addItem
return
{
type:
'message'
,
messageType:
'info'
,
content:
`Qwen Code documentation: ${
docsUrl
}`
,
};
}
// interactive 路径：原有 SANDBOX 判断 + addItem + open() 不变
if
(process.env[
'SANDBOX'
]
&&
...
) {
context.ui.
addItem
(
...
);
}
else
{
context.ui.
addItem
(
...
);
await
open
(docsUrl);
}
},
7.5
/clear
（altNames:
reset
、
new
）
当前状态
：action 执行以下操作并返回
void
：
config.getHookSystem()?.fireSessionEndEvent()
— 触发 hook（有副作用）
config.startNewSession()
— 开始新 session ID（有副作用）
uiTelemetryService.reset()
— 重置 telemetry 计数器（有副作用）
skillTool.clearLoadedSkills()
— 清除 skill 缓存（有副作用）
context.ui.clear()
— 清空终端 UI（
UI 副作用，non-interactive 下为 no-op
）
geminiClient.resetChat()
— 重置 chat 历史（有副作用）
config.getHookSystem()?.fireSessionStartEvent()
— 触发 hook（有副作用）
non-interactive/ACP 语义分析
：
ui.clear()
在 non-interactive 中已是 no-op，不需要处理
geminiClient.resetChat()
：在 ACP Session 中是有意义的副作用（清空 chat 历史），应保留；在 non-interactive 单次调用中，每次调用都是全新 session，
resetChat
语义重复但无害
config.startNewSession()
：在 ACP 中有意义（开始新的 session ID）；在 non-interactive 单次调用中同样语义重复但无害
fireSessionEndEvent
/
fireSessionStartEvent
：在 ACP 中有意义（触发 hook）
决策
：non-interactive/ACP 路径保留所有有意义的副作用（resetChat、startNewSession、hook events），仅跳过
ui.clear()
（已是 no-op）并返回上下文边界标记 message。
变更
：
将
supportedModes
改为
['interactive', 'non_interactive', 'acp']
。
修改 action 返回类型为
Promise<void | MessageActionReturn>
。
在 action 内，
context.ui.clear()
调用后（或替代它）根据模式分支：
action
:
async
(
context
,
_args
)
=>
{
const
{
config
}
=
context.services;
if
(config) {
config.
getHookSystem
()?.
fireSessionEndEvent
(SessionEndReason.Clear).
catch
(
...
);
const
newSessionId
=
config.
startNewSession
();
uiTelemetryService.
reset
();
const
skillTool
=
config.
getToolRegistry
()?.
getAllTools
().
find
(
...
);
if
(skillTool
instanceof
SkillTool
) skillTool.
clearLoadedSkills
();
if
(newSessionId
&&
context.session.startNewSession) {
context.session.
startNewSession
(newSessionId);
}
// ui.clear() 在非交互下已是 no-op，但依然调用（不需要条件分支）
context.ui.
clear
();
const
geminiClient
=
config.
getGeminiClient
();
if
(geminiClient) {
await
geminiClient.
resetChat
();
}
config.
getHookSystem
()?.
fireSessionStartEvent
(
...
).
catch
(
...
);
}
else
{
context.ui.
clear
();
}
// 根据模式决定返回值
if
(context.executionMode
!==
'interactive'
) {
return
{
type:
'message'
,
messageType:
'info'
,
content:
'Context cleared. Previous messages are no longer in context.'
,
};
}
// interactive 路径：void（不返回，React UI 由 ui.clear() 驱动更新）
},
ACP 语义
：IDE 收到上下文边界标记后，可将其作为 session 分隔符展示（如”新会话开始”提示），并清空本地 chat 历史缓存。
8.
handleCommandResult
变更
结论：无需修改。
Phase 2 所有命令变更后，non-interactive/ACP 路径的返回类型均为
message
或
submit_prompt
，均已在
handleCommandResult
的 switch 中正确处理。
9.
createNonInteractiveUI()
变更
结论：无需修改。
当前 no-op 实现已足够。
addItem
、
clear
、
setPendingItem
等 no-op 在 B 类命令的 non-interactive 路径中不会被调用（因为提前 return）；interactive 路径中不受影响。
10. Phase 2.2：prompt command 模型调用打通
Phase 1 中
CommandService.getModelInvocableCommands()
已实现，
BundledSkillLoader
、
FileCommandLoader
（用户/项目命令）、
McpPromptLoader
已设置
modelInvocable: true
。
Phase 2.2 的工作是将
SkillTool
从只消费
SkillManager.listSkills()
改为同时消费
CommandService.getModelInvocableCommands()
，统一模型可调用命令的入口。
变更文件
：
packages/core/src/tools/SkillTool.ts
（或对应路径）
具体变更
：
SkillTool
在初始化时接收
CommandService
（或其
getModelInvocableCommands()
的结果）作为依赖注入
在构建 tool description 时，合并
listSkills()
和
getModelInvocableCommands()
的结果
确保 built-in commands（
modelInvocable: false
）不出现在 tool description 中
注
：
SkillTool
的具体实现依赖
packages/core
内部架构，详细设计在本文档中仅描述接口变更，实现细节需结合 core 包的现有结构确定。
11. Phase 2.3：mid-input slash command 检测（基础版）
在
InputPrompt
组件中检测光标附近的 slash token（不限于行首），触发补全菜单。
检测规则
：
当光标前存在以
/
开头、不含空格的 token 时，触发命令补全
补全候选来自
getCommandsForMode('interactive')
的可见命令列表
补全菜单展示命令名 + description（不含 argumentHint 等，Phase 3 补充）
本功能为 UI 层变更，属于 Phase 2.3 独立子任务，不影响其他 Phase 2.1/2.2 的实施。
12. 文件变更总览
12.1 命令文件变更（Phase 2.1）
文件
变更类型
具体内容
exportCommand.ts
A 类
父命令 + 4 个子命令：
supportedModes
→ all modes
planCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
statuslineCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
languageCommand.ts
A+ 类
父命令 +
ui
/
output
子命令 + 动态 language 子命令：
supportedModes
→ all modes
copyCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
restoreCommand.ts
仅交互
设计决策：保持
supportedModes: ['interactive']
，未变更
modelCommand.ts
A’ 类
supportedModes
→ all modes + 无参数/无 fast model 路径新增非交互分支
approvalModeCommand.ts
A’ 类
supportedModes
→ all modes + 无参数路径新增非交互分支
aboutCommand.ts
B 类
supportedModes
→ all modes + 非交互路径返回
message
（版本/模型/环境摘要）
statsCommand.ts
B 类
supportedModes
→ all modes + 非交互路径返回
message
（stats 文本）；子命令同步处理
insightCommand.ts
B 类
supportedModes
→ all modes +
non_interactive
路径同步生成返回
message
（文件路径）；
acp
路径返回
stream_messages
带进度推送
docsCommand.ts
B 类
supportedModes
→ all modes + 非交互路径返回
message
（文档 URL），不打开浏览器
clearCommand.ts
B 类
supportedModes
→ all modes + action 末尾根据模式返回
message
或
void
12.2 其他文件变更
文件
变更内容
packages/core/src/tools/SkillTool.ts
Phase 2.2：接入
getModelInvocableCommands()
（详细设计另行确定）
packages/cli/src/ui/InputPrompt.tsx
（或同等组件）
Phase 2.3：mid-input slash 检测逻辑
12.3 不变的文件
packages/cli/src/nonInteractiveCliCommands.ts
（
handleCommandResult
、
handleSlashCommand
无需修改）
packages/cli/src/ui/noninteractive/nonInteractiveUi.ts
（stub UI 无需修改）
packages/cli/src/services/commandUtils.ts
（
filterCommandsForMode
、
getEffectiveSupportedModes
无需修改）
packages/cli/src/services/CommandService.ts
（
getCommandsForMode
、
getModelInvocableCommands
已在 Phase 1 实现）
13. 测试策略
13.1 命令单元测试
为每个变更的命令在同目录下新增或更新测试文件（
*.test.ts
），覆盖以下 case：
A/A+ 类命令
（
export
、
language
）：
supportedModes
正确包含
non_interactive
和
acp
在
executionMode: 'non_interactive'
下，action 返回
MessageActionReturn
或
SubmitPromptActionReturn
，不调用
ui.addItem
或
ui.clear
Interactive 路径行为与重构前完全一致（快照测试）
仅交互命令
（
plan
、
statusline
、
copy
、
restore
）：
supportedModes
为
['interactive']
，这是设计决策
验证 non-interactive 下执行时正确返回
unsupported
A’ 类命令
（
model
、
approval-mode
）：
无参数 +
executionMode: 'non_interactive'
→ 返回当前状态
message
，不返回
dialog
有参数 +
executionMode: 'non_interactive'
→ 原有
message
逻辑正常执行
Interactive 路径：无参数 →
dialog
，有参数 →
message
（不变）
B 类命令
（
about
、
stats
、
insight
、
docs
、
clear
）：
executionMode: 'non_interactive'
下，action 返回
MessageActionReturn
，不调用任何
ui.*
方法
返回的
content
字符串包含预期的关键字段（版本号、模型名、URL 等）
Interactive 路径：
ui.addItem
被调用，
action
返回
void
（不变）
clear
的特殊 case
：
executionMode: 'non_interactive'
下，
geminiClient.resetChat()
仍被调用（副作用保留）
返回上下文边界
message
，内容为
'Context cleared. Previous messages are no longer in context.'
13.2 集成测试（
handleSlashCommand
）
在
nonInteractiveCli.test.ts
或新建的集成测试文件中：
handleSlashCommand('/about', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 包含版本号 }
handleSlashCommand('/stats', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 包含 'Session duration' }
handleSlashCommand('/docs', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 包含 'qwenlm.github.io' }
handleSlashCommand('/clear', ...)
在 non-interactive 模式下返回
{ type: 'message', content: 'Context cleared.' }
handleSlashCommand('/plan', ...)
在 non-interactive 模式下返回
unsupported
（仅交互命令）
现有 non-interactive 命令（
btw
、
bug
等）行为无退化
13.3
commandUtils
测试
commandUtils.test.ts
中新增（或已有的测试继续覆盖）：
扩展后的命令（
export
、
language
等）均能通过
filterCommandsForMode(commands, 'non_interactive')
和
filterCommandsForMode(commands, 'acp')
的过滤
仅交互命令（
plan
、
statusline
、
copy
、
restore
）在
filterCommandsForMode(commands, 'non_interactive')
下被正确过滤掉
14. 行为影响分析
场景
Phase 2 前行为
Phase 2 后行为
性质
non-interactive 下执行
/export md
❌ unsupported（被过滤）
✅ 返回文件路径 message
能力扩展
non-interactive 下执行
/plan <task>
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/statusline
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/language ui zh-CN
❌ unsupported
✅ 设置语言，返回确认 message
能力扩展
non-interactive 下执行
/copy
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/restore
（无参数）
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/restore <id>
❌ unsupported
❌ unsupported（设计决策：仅交互）
不变
non-interactive 下执行
/model
❌ unsupported（dialog）
✅ 返回当前模型名称
能力扩展
non-interactive 下执行
/model <id>
❌ unsupported
🔄 Phase 2 可选：实现切换逻辑
能力扩展（可选）
non-interactive 下执行
/approval-mode
❌ unsupported（dialog）
✅ 返回当前审批模式
能力扩展
non-interactive 下执行
/approval-mode yolo
❌ unsupported
✅ 设置模式，返回确认
能力扩展
non-interactive 下执行
/about
❌ 返回 “Command executed successfully.”（addItem no-op）
✅ 返回版本/模型/环境摘要
Bug fix + 能力扩展
non-interactive 下执行
/stats
❌ 返回 “Command executed successfully.”
✅ 返回 session 统计文本
Bug fix + 能力扩展
non-interactive 下执行
/insight
❌ 返回 “Command executed successfully.”（生成但无输出）
✅ 生成并返回文件路径
Bug fix + 能力扩展
non-interactive 下执行
/docs
❌ 返回 “Command executed successfully.”
✅ 返回文档 URL
Bug fix + 能力扩展
non-interactive 下执行
/clear
❌ 返回 “Command executed successfully.”
✅ 返回上下文边界 message
Bug fix + 能力扩展
interactive 下执行任意以上命令
✅ 原有行为
✅ 原有行为（零退化）
不变
15. 实施顺序
建议按以下顺序实施，每组可独立 commit 和 review：
Batch 1
（~30min）：A 类 — 只改
supportedModes
修改
exportCommand.ts
（及其子命令），验证测试通过。
Batch 2
（~45min）：A+ 类 — 少量分支
修改
languageCommand.ts
，为有副作用的路径添加非交互分支，更新对应测试。（
copyCommand.ts
和
restoreCommand.ts
经讨论保持仅交互。）
Batch 3
（~45min）：A’ 类 — dialog 路径
修改
modelCommand.ts
、
approvalModeCommand.ts
，为无参数路径添加非交互分支，更新对应测试。
Batch 4
（~1.5h）：B 类 — 完整分支
修改
aboutCommand.ts
、
statsCommand.ts
（含子命令）、
docsCommand.ts
。
Batch 5
（~1h）：B 类特殊 —
insightCommand.ts
、
clearCommand.ts
这两个命令副作用较多，单独一个 commit，更新对应测试和集成测试。
Batch 6
（~2h）：Phase 2.2 — prompt command 模型调用打通
修改
SkillTool
，接入
getModelInvocableCommands()
，更新 SkillTool 测试。
Batch 7
（~2h）：Phase 2.3 — mid-input slash 检测
修改
InputPrompt
组件，新增补全触发逻辑和 UI 测试。
Batch 8
（~30min）：全量测试 + 类型检查
运行
npm run typecheck
、
cd packages/cli && npx vitest run
，修复剩余问题。
16. 验收 Checklist
Phase 2.1 命令扩展
A 类：
/export
（及子命令）、
/plan
、
/statusline
在 non-interactive 和 acp 模式下可正常执行并返回有意义输出
A+ 类：
/language
（及子命令）在 non-interactive 下正常执行，设置持久化
A+ 类：
/copy
在 non-interactive/acp 下返回最后 AI 输出文本（不操作剪贴板）
A+ 类：
/restore
无参数时在 non-interactive 下返回 checkpoint 列表；有参数时恢复状态并返回确认 message（不返回
type: 'tool'
）
A’ 类：
/model
无参数时在 non-interactive/acp 下返回当前模型名（不触发 dialog）；
/model --fast <id>
正常设置
A’ 类：
/approval-mode
无参数时在 non-interactive/acp 下返回当前模式（不触发 dialog）；有参数时正常设置
B 类：
/about
在 non-interactive/acp 下返回包含版本号、模型名的纯文本摘要
B 类：
/stats
（含子命令）在 non-interactive/acp 下返回纯文本统计数据
B 类：
/insight
在 non-interactive/acp 下生成 insight 文件并返回文件路径（不打开浏览器）
B 类：
/docs
在 non-interactive/acp 下返回文档 URL（不打开浏览器）
B 类：
/clear
在 non-interactive/acp 下返回上下文边界标记 message，
geminiClient.resetChat()
正常执行
所有 13 个命令在 interactive 模式下行为与重构前完全一致（无退化）
TypeScript 编译无错误（
npm run typecheck
）
npm run lint
无新增错误
所有现有测试通过（
cd packages/cli && npx vitest run
）
Phase 2.2 模型调用
模型在对话中可以通过
SkillTool
调用 bundled skill、file command（用户/项目）、MCP prompt
模型不可以调用 built-in commands
SkillTool
的 tool description 包含所有
modelInvocable: true
命令的名称和 description
Phase 2.3 mid-input slash
在输入框正文中输入
/
后触发命令补全菜单（不限行首）
补全菜单展示命令名 + description
补全选中后正确填充到输入框
Last updated on
May 18, 2026
Phase 1 技术设计文档：基础设施重建
Slash Command 重构路线图</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/design/slash-command/phase2-technical-design/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Code Review</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/code-review/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/code-review/</guid>
  <pubDate>Tue, 16 Jan 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Code Review
Code Review
Review code changes for correctness, security, performance, and code quality using
/review
.
Quick Start
# Review local uncommitted changes
/review
# Review a pull request (by number or URL)
/review
123
/review
https://github.com/org/repo/pull/123
# Review and post inline comments on the PR
/review
123
--comment
# Review a specific file
/review
src/utils/auth.ts
If there are no uncommitted changes,
/review
will let you know and stop — no agents are lau...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Code Review
Code Review
Review code changes for correctness, security, performance, and code quality using
/review
.
Quick Start
# Review local uncommitted changes
/review
# Review a pull request (by number or URL)
/review
123
/review
https://github.com/org/repo/pull/123
# Review and post inline comments on the PR
/review
123
--comment
# Review a specific file
/review
src/utils/auth.ts
If there are no uncommitted changes,
/review
will let you know and stop — no agents are launched.
How It Works
The
/review
command runs a multi-stage pipeline:
Step 1:  Determine scope (local diff / PR worktree / file)
Step 2:  Load project review rules
Step 3:  Run deterministic analysis (linter, typecheck)    [zero LLM cost]
Step 4:  9 parallel review agents                          [9 LLM calls]
|-- Agent 1: Correctness
|-- Agent 2: Security
|-- Agent 3: Code Quality
|-- Agent 4: Performance & Efficiency
|-- Agent 5: Test Coverage
|-- Agent 6: Undirected Audit (3 personas: 6a/6b/6c)
'-- Agent 7: Build & Test (runs shell commands)
Step 5:  Deduplicate --> Batch verify --> Aggregate         [1 LLM call]
Step 6:  Iterative reverse audit (1-3 rounds, gap finding) [1-3 LLM calls]
Step 7:  Present findings + verdict
Step 8:  Autofix (user-confirmed, optional)
Step 9:  Post PR inline comments (if requested)
Step 10: Save report + incremental cache
Step 11: Clean up (remove worktree + temp files)
Review Agents
Agent
Focus
Agent 1: Correctness
Logic errors, edge cases, null handling, race conditions, type safety
Agent 2: Security
Injection, XSS, SSRF, auth bypass, sensitive data exposure
Agent 3: Code Quality
Style consistency, naming, duplication, dead code
Agent 4: Performance & Efficiency
N+1 queries, memory leaks, unnecessary re-renders, bundle size
Agent 5: Test Coverage
Untested code paths in the diff, missing branch coverage, weak assertions
Agent 6: Undirected Audit
3 parallel personas (attacker / 3am-oncall / maintainer) — catches cross-dimensional issues
Agent 7: Build & Test
Runs build and test commands, reports failures
All agents run in parallel (Agent 6 launches 3 persona variants concurrently, totaling 9 parallel tasks for same-repo reviews). Findings from Agents 1-6 are verified in a
single batch verification pass
(one agent reviews all findings at once, keeping verification cost fixed regardless of finding count). After verification,
iterative reverse audit
runs 1-3 rounds of gap-finding — each round receives the cumulative finding list from prior rounds, so successive rounds focus on whatever’s left undiscovered. The loop stops as soon as a round returns “No issues found”, or after 3 rounds (hard cap). Reverse audit findings skip verification (the agent already has full context) and are included as high-confidence results.
Deterministic Analysis
Before the LLM agents run,
/review
automatically runs your project’s existing linters and type checkers:
Language
Tools detected
TypeScript/JavaScript
tsc --noEmit
,
npm run lint
,
eslint
Python
ruff
,
mypy
,
flake8
Rust
cargo clippy
Go
go vet
,
golangci-lint
Java
mvn compile
,
checkstyle
,
spotbugs
,
pmd
C/C++
clang-tidy
(if
compile_commands.json
available)
Other
Auto-discovered from CI config (
.github/workflows/*.yml
, etc.)
For projects that don’t match standard patterns (e.g., OpenJDK),
/review
reads CI configuration files to discover what lint/check commands the project uses. No user configuration needed.
Deterministic findings are tagged with
[linter]
or
[typecheck]
and skip LLM verification — they are ground truth.
Errors
→ Critical severity
Warnings
→ Nice to have (terminal only, not posted as PR comments)
If a tool is not installed or times out, it is skipped with an informational note.
Severity Levels
Severity
Meaning
Posted as PR comment?
Critical
Must fix before merging (bugs, security, data loss, build failures)
Yes (high-confidence only)
Suggestion
Recommended improvement
Yes (high-confidence only)
Nice to have
Optional optimization
No (terminal only)
Low-confidence findings appear in a separate “Needs Human Review” section in the terminal and are never posted as PR comments.
Autofix
After presenting findings,
/review
offers to auto-apply fixes for Critical and Suggestion findings that have clear solutions:
Found 3 issues with auto-fixable suggestions. Apply auto-fixes? (y/n)
Fixes are applied using the
edit
tool (targeted replacements, not full-file rewrites)
Per-file linter checks run after fixes to verify they don’t introduce new issues
For PR reviews, fixes are committed and pushed from the worktree automatically — your working tree stays clean
Nice to have and low-confidence findings are never auto-fixed
PR review submission always uses the
pre-fix verdict
(e.g., “Request changes”) since the remote PR hasn’t been updated until the autofix push completes
Worktree Isolation
When reviewing a PR,
/review
creates a temporary git worktree (
.qwen/tmp/review-pr-<number>
) instead of switching your current branch. This means:
Your working tree, staged changes, and current branch are
never touched
Dependencies are installed in the worktree (
npm ci
, etc.) so linting and build/test work
Build and test commands run in isolation without polluting your local build cache
If anything goes wrong, your environment is unaffected — just delete the worktree
The worktree is automatically cleaned up after the review completes
If a review is interrupted (Ctrl+C, crash), the next
/review
of the same PR automatically cleans up the stale worktree before starting fresh
Review reports and cache are saved to the main project directory (not the worktree)
Cross-repo PR Review
You can review PRs from other repositories by passing the full URL:
/review
https://github.com/other-org/other-repo/pull/456
This runs in
lightweight mode
— no worktree, no linter, no build/test, no autofix. The review is based on the diff text only (fetched via GitHub API). PR comments can still be posted if you have write access.
Capability
Same-repo
Cross-repo
LLM review (Agents 1-6 + verify + iterative reverse audit)
✅
✅
Agent 7: Build & test
✅
❌ (no local codebase)
Deterministic analysis (linter/typecheck)
✅
❌
Cross-file impact analysis
✅
❌
Autofix
✅
❌
PR inline comments
✅
✅ (if you have write access)
Incremental review cache
✅
❌
PR Inline Comments
Use
--comment
to post findings directly on the PR:
/review
123
--comment
Or, after running
/review 123
, type
post comments
to publish findings without re-running the review.
What gets posted:
High-confidence Critical and Suggestion findings as inline comments on specific lines
For Approve/Request changes verdicts: a review summary with the verdict
For Comment verdict with all inline comments posted: no separate summary (inline comments are sufficient)
Model attribution footer on each comment (e.g.,
— qwen3-coder via Qwen Code /review
)
What stays terminal-only:
Nice to have findings (including linter warnings)
Low-confidence findings
Self-authored PRs:
GitHub does not allow you to submit
APPROVE
or
REQUEST_CHANGES
reviews on your own pull request — both fail with HTTP 422. When
/review
detects that the PR author matches the current authenticated user, it automatically downgrades the API event to
COMMENT
regardless of verdict, so the submission still succeeds. The terminal still shows the honest verdict (“Approve” / “Request changes” / “Comment”) — only the GitHub-side review event is neutralized. The actual findings still appear as inline comments on specific lines, so substantive feedback is unchanged.
Re-reviewing a PR with prior Qwen Code comments:
when
/review
runs on a PR that already has previous Qwen Code review comments, it classifies them before posting new ones. Only
same-line overlap
(an existing comment on the same
(path, line)
as a new finding) prompts you to confirm — that’s the case where you’d see a visual duplicate on the same code line. Comments from older commits, replied-to comments (treated as resolved), and comments that simply don’t overlap with any new finding are silently skipped, with a terminal log line so you know what was filtered.
CI / build status check before APPROVE:
if the verdict is “Approve”,
/review
queries the PR’s check-runs and commit statuses before submitting. If any check has failed (or all checks are still pending), the API event is automatically downgraded from
APPROVE
to
COMMENT
, with the review body explaining why. Rationale: the LLM review reads code statically and cannot see runtime test failures; approving while CI is red would be misleading. The inline findings are still posted unchanged. If you want to approve anyway (e.g., a known-flaky CI failure), submit the GitHub approval manually after verifying.
Follow-up Actions
After the review, context-aware tips appear as ghost text. Press Tab to accept:
State after review
Tip
What happens
Local review with unfixed findings
fix these issues
LLM interactively fixes each finding
PR review with findings
post comments
Posts PR inline comments (no re-review)
PR review, zero findings
post comments
Approves the PR on GitHub (LGTM)
Local review, all clear
commit
Commits your changes
Note:
fix these issues
is only available for local reviews. For PR reviews, use Autofix (Step 8) — the worktree is cleaned up after the review, so post-review interactive fixing is not possible.
Project Review Rules
You can customize review criteria per project.
/review
reads rules from these files (in order):
.qwen/review-rules.md
(Qwen Code native)
.github/copilot-instructions.md
(preferred) or
copilot-instructions.md
(fallback — only one is loaded, not both)
AGENTS.md
—
## Code Review
section
QWEN.md
—
## Code Review
section
Rules are injected into the LLM review agents (1-6) as additional criteria. For PR reviews, rules are read from the
base branch
to prevent a malicious PR from injecting bypass rules.
Example
.qwen/review-rules.md
:
# Review Rules
-
All API endpoints must validate authentication
-
Database queries must use parameterized statements
-
React components must not use inline styles
-
Error messages must not expose internal paths
Incremental Review
When reviewing a PR that was previously reviewed,
/review
only examines changes since the last review:
# First review — full review, cache created
/review
123
# PR updated with new commits — only new changes reviewed
/review
123
Cross-model review
If you switch models (via
/model
) and re-review the same PR,
/review
detects the model change and runs a full review instead of skipping:
# Review with model A
/review
123
# Switch model
/model
# Review again — full review with model B (not skipped)
/review
123
# → "Previous review used qwen3-coder. Running full review with gpt-4o for a second opinion."
Cache is stored in
.qwen/review-cache/
and tracks both the commit SHA and model ID. Make sure this directory is in your
.gitignore
(a broader rule like
.qwen/*
also works). If the cached commit was rebased away, it falls back to a full review.
Review Reports
For same-repo reviews, results are saved as a Markdown file in your project’s
.qwen/reviews/
directory (cross-repo lightweight reviews skip report persistence):
.qwen/reviews/2026-04-06-143022-pr-123.md
.qwen/reviews/2026-04-06-150510-local.md
Reports include: timestamp, diff stats, deterministic analysis results, all findings with verification status, and the verdict.
Cross-file Impact Analysis
When code changes modify exported functions, classes, or interfaces, the review agents automatically search for all callers and check compatibility:
Parameter count/type changes
Return type changes
Removed or renamed public methods
Breaking API changes
For large diffs (>10 modified symbols), analysis prioritizes functions with signature changes.
Token Efficiency
The review pipeline uses a bounded number of LLM calls regardless of how many findings are produced:
Stage
LLM calls
Notes
Deterministic analysis (Step 3)
0
Shell commands only
Review agents (Step 4)
9 (or 8)
Run in parallel; Agent 7 skipped in cross-repo mode
Batch verification (Step 5)
1
Single agent verifies all findings at once
Iterative reverse audit (Step 6)
1-3
Loops until “No issues found” or 3-round cap
Total
11-13 (10-12)
Same-repo: 11-13; cross-repo: 10-12 (no Agent 7)
Most PRs converge to the lower end of the range (1 reverse audit round); the cap prevents runaway cost on pathological cases.
What’s NOT Flagged
The review intentionally excludes:
Pre-existing issues in unchanged code (focus on the diff only)
Style/formatting/naming that matches your codebase conventions
Issues a linter or type checker would catch (handled by deterministic analysis)
Subjective “consider doing X” suggestions without a real problem
Minor refactoring that doesn’t fix a bug or risk
Missing documentation unless the logic is genuinely confusing
Issues already discussed in existing PR comments (avoids duplicating human feedback)
Design Philosophy
Silence is better than noise.
Every comment should be worth the reader’s time.
If unsure whether something is a problem → don’t report it
Linter/typecheck issues are handled by tools, not LLM guesses
Same pattern across N files → aggregated into one finding
PR comments are high-confidence only
Style/formatting issues matching codebase conventions are excluded
Last updated on
May 18, 2026
Commands
Followup Suggestions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/code-review/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Code Review
Code Review
Review code changes for correctness, security, performance, and code quality using
/review
.
Quick Start
# Review local uncommitted changes
/review
# Review a pull request (by number or URL)
/review
123
/review
https://github.com/org/repo/pull/123
# Review and post inline comments on the PR
/review
123
--comment
# Review a specific file
/review
src/utils/auth.ts
If there are no uncommitted changes,
/review
will let you know and stop — no agents are lau...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Code Review
Code Review
Review code changes for correctness, security, performance, and code quality using
/review
.
Quick Start
# Review local uncommitted changes
/review
# Review a pull request (by number or URL)
/review
123
/review
https://github.com/org/repo/pull/123
# Review and post inline comments on the PR
/review
123
--comment
# Review a specific file
/review
src/utils/auth.ts
If there are no uncommitted changes,
/review
will let you know and stop — no agents are launched.
How It Works
The
/review
command runs a multi-stage pipeline:
Step 1:  Determine scope (local diff / PR worktree / file)
Step 2:  Load project review rules
Step 3:  Run deterministic analysis (linter, typecheck)    [zero LLM cost]
Step 4:  9 parallel review agents                          [9 LLM calls]
|-- Agent 1: Correctness
|-- Agent 2: Security
|-- Agent 3: Code Quality
|-- Agent 4: Performance & Efficiency
|-- Agent 5: Test Coverage
|-- Agent 6: Undirected Audit (3 personas: 6a/6b/6c)
'-- Agent 7: Build & Test (runs shell commands)
Step 5:  Deduplicate --> Batch verify --> Aggregate         [1 LLM call]
Step 6:  Iterative reverse audit (1-3 rounds, gap finding) [1-3 LLM calls]
Step 7:  Present findings + verdict
Step 8:  Autofix (user-confirmed, optional)
Step 9:  Post PR inline comments (if requested)
Step 10: Save report + incremental cache
Step 11: Clean up (remove worktree + temp files)
Review Agents
Agent
Focus
Agent 1: Correctness
Logic errors, edge cases, null handling, race conditions, type safety
Agent 2: Security
Injection, XSS, SSRF, auth bypass, sensitive data exposure
Agent 3: Code Quality
Style consistency, naming, duplication, dead code
Agent 4: Performance & Efficiency
N+1 queries, memory leaks, unnecessary re-renders, bundle size
Agent 5: Test Coverage
Untested code paths in the diff, missing branch coverage, weak assertions
Agent 6: Undirected Audit
3 parallel personas (attacker / 3am-oncall / maintainer) — catches cross-dimensional issues
Agent 7: Build & Test
Runs build and test commands, reports failures
All agents run in parallel (Agent 6 launches 3 persona variants concurrently, totaling 9 parallel tasks for same-repo reviews). Findings from Agents 1-6 are verified in a
single batch verification pass
(one agent reviews all findings at once, keeping verification cost fixed regardless of finding count). After verification,
iterative reverse audit
runs 1-3 rounds of gap-finding — each round receives the cumulative finding list from prior rounds, so successive rounds focus on whatever’s left undiscovered. The loop stops as soon as a round returns “No issues found”, or after 3 rounds (hard cap). Reverse audit findings skip verification (the agent already has full context) and are included as high-confidence results.
Deterministic Analysis
Before the LLM agents run,
/review
automatically runs your project’s existing linters and type checkers:
Language
Tools detected
TypeScript/JavaScript
tsc --noEmit
,
npm run lint
,
eslint
Python
ruff
,
mypy
,
flake8
Rust
cargo clippy
Go
go vet
,
golangci-lint
Java
mvn compile
,
checkstyle
,
spotbugs
,
pmd
C/C++
clang-tidy
(if
compile_commands.json
available)
Other
Auto-discovered from CI config (
.github/workflows/*.yml
, etc.)
For projects that don’t match standard patterns (e.g., OpenJDK),
/review
reads CI configuration files to discover what lint/check commands the project uses. No user configuration needed.
Deterministic findings are tagged with
[linter]
or
[typecheck]
and skip LLM verification — they are ground truth.
Errors
→ Critical severity
Warnings
→ Nice to have (terminal only, not posted as PR comments)
If a tool is not installed or times out, it is skipped with an informational note.
Severity Levels
Severity
Meaning
Posted as PR comment?
Critical
Must fix before merging (bugs, security, data loss, build failures)
Yes (high-confidence only)
Suggestion
Recommended improvement
Yes (high-confidence only)
Nice to have
Optional optimization
No (terminal only)
Low-confidence findings appear in a separate “Needs Human Review” section in the terminal and are never posted as PR comments.
Autofix
After presenting findings,
/review
offers to auto-apply fixes for Critical and Suggestion findings that have clear solutions:
Found 3 issues with auto-fixable suggestions. Apply auto-fixes? (y/n)
Fixes are applied using the
edit
tool (targeted replacements, not full-file rewrites)
Per-file linter checks run after fixes to verify they don’t introduce new issues
For PR reviews, fixes are committed and pushed from the worktree automatically — your working tree stays clean
Nice to have and low-confidence findings are never auto-fixed
PR review submission always uses the
pre-fix verdict
(e.g., “Request changes”) since the remote PR hasn’t been updated until the autofix push completes
Worktree Isolation
When reviewing a PR,
/review
creates a temporary git worktree (
.qwen/tmp/review-pr-<number>
) instead of switching your current branch. This means:
Your working tree, staged changes, and current branch are
never touched
Dependencies are installed in the worktree (
npm ci
, etc.) so linting and build/test work
Build and test commands run in isolation without polluting your local build cache
If anything goes wrong, your environment is unaffected — just delete the worktree
The worktree is automatically cleaned up after the review completes
If a review is interrupted (Ctrl+C, crash), the next
/review
of the same PR automatically cleans up the stale worktree before starting fresh
Review reports and cache are saved to the main project directory (not the worktree)
Cross-repo PR Review
You can review PRs from other repositories by passing the full URL:
/review
https://github.com/other-org/other-repo/pull/456
This runs in
lightweight mode
— no worktree, no linter, no build/test, no autofix. The review is based on the diff text only (fetched via GitHub API). PR comments can still be posted if you have write access.
Capability
Same-repo
Cross-repo
LLM review (Agents 1-6 + verify + iterative reverse audit)
✅
✅
Agent 7: Build & test
✅
❌ (no local codebase)
Deterministic analysis (linter/typecheck)
✅
❌
Cross-file impact analysis
✅
❌
Autofix
✅
❌
PR inline comments
✅
✅ (if you have write access)
Incremental review cache
✅
❌
PR Inline Comments
Use
--comment
to post findings directly on the PR:
/review
123
--comment
Or, after running
/review 123
, type
post comments
to publish findings without re-running the review.
What gets posted:
High-confidence Critical and Suggestion findings as inline comments on specific lines
For Approve/Request changes verdicts: a review summary with the verdict
For Comment verdict with all inline comments posted: no separate summary (inline comments are sufficient)
Model attribution footer on each comment (e.g.,
— qwen3-coder via Qwen Code /review
)
What stays terminal-only:
Nice to have findings (including linter warnings)
Low-confidence findings
Self-authored PRs:
GitHub does not allow you to submit
APPROVE
or
REQUEST_CHANGES
reviews on your own pull request — both fail with HTTP 422. When
/review
detects that the PR author matches the current authenticated user, it automatically downgrades the API event to
COMMENT
regardless of verdict, so the submission still succeeds. The terminal still shows the honest verdict (“Approve” / “Request changes” / “Comment”) — only the GitHub-side review event is neutralized. The actual findings still appear as inline comments on specific lines, so substantive feedback is unchanged.
Re-reviewing a PR with prior Qwen Code comments:
when
/review
runs on a PR that already has previous Qwen Code review comments, it classifies them before posting new ones. Only
same-line overlap
(an existing comment on the same
(path, line)
as a new finding) prompts you to confirm — that’s the case where you’d see a visual duplicate on the same code line. Comments from older commits, replied-to comments (treated as resolved), and comments that simply don’t overlap with any new finding are silently skipped, with a terminal log line so you know what was filtered.
CI / build status check before APPROVE:
if the verdict is “Approve”,
/review
queries the PR’s check-runs and commit statuses before submitting. If any check has failed (or all checks are still pending), the API event is automatically downgraded from
APPROVE
to
COMMENT
, with the review body explaining why. Rationale: the LLM review reads code statically and cannot see runtime test failures; approving while CI is red would be misleading. The inline findings are still posted unchanged. If you want to approve anyway (e.g., a known-flaky CI failure), submit the GitHub approval manually after verifying.
Follow-up Actions
After the review, context-aware tips appear as ghost text. Press Tab to accept:
State after review
Tip
What happens
Local review with unfixed findings
fix these issues
LLM interactively fixes each finding
PR review with findings
post comments
Posts PR inline comments (no re-review)
PR review, zero findings
post comments
Approves the PR on GitHub (LGTM)
Local review, all clear
commit
Commits your changes
Note:
fix these issues
is only available for local reviews. For PR reviews, use Autofix (Step 8) — the worktree is cleaned up after the review, so post-review interactive fixing is not possible.
Project Review Rules
You can customize review criteria per project.
/review
reads rules from these files (in order):
.qwen/review-rules.md
(Qwen Code native)
.github/copilot-instructions.md
(preferred) or
copilot-instructions.md
(fallback — only one is loaded, not both)
AGENTS.md
—
## Code Review
section
QWEN.md
—
## Code Review
section
Rules are injected into the LLM review agents (1-6) as additional criteria. For PR reviews, rules are read from the
base branch
to prevent a malicious PR from injecting bypass rules.
Example
.qwen/review-rules.md
:
# Review Rules
-
All API endpoints must validate authentication
-
Database queries must use parameterized statements
-
React components must not use inline styles
-
Error messages must not expose internal paths
Incremental Review
When reviewing a PR that was previously reviewed,
/review
only examines changes since the last review:
# First review — full review, cache created
/review
123
# PR updated with new commits — only new changes reviewed
/review
123
Cross-model review
If you switch models (via
/model
) and re-review the same PR,
/review
detects the model change and runs a full review instead of skipping:
# Review with model A
/review
123
# Switch model
/model
# Review again — full review with model B (not skipped)
/review
123
# → "Previous review used qwen3-coder. Running full review with gpt-4o for a second opinion."
Cache is stored in
.qwen/review-cache/
and tracks both the commit SHA and model ID. Make sure this directory is in your
.gitignore
(a broader rule like
.qwen/*
also works). If the cached commit was rebased away, it falls back to a full review.
Review Reports
For same-repo reviews, results are saved as a Markdown file in your project’s
.qwen/reviews/
directory (cross-repo lightweight reviews skip report persistence):
.qwen/reviews/2026-04-06-143022-pr-123.md
.qwen/reviews/2026-04-06-150510-local.md
Reports include: timestamp, diff stats, deterministic analysis results, all findings with verification status, and the verdict.
Cross-file Impact Analysis
When code changes modify exported functions, classes, or interfaces, the review agents automatically search for all callers and check compatibility:
Parameter count/type changes
Return type changes
Removed or renamed public methods
Breaking API changes
For large diffs (>10 modified symbols), analysis prioritizes functions with signature changes.
Token Efficiency
The review pipeline uses a bounded number of LLM calls regardless of how many findings are produced:
Stage
LLM calls
Notes
Deterministic analysis (Step 3)
0
Shell commands only
Review agents (Step 4)
9 (or 8)
Run in parallel; Agent 7 skipped in cross-repo mode
Batch verification (Step 5)
1
Single agent verifies all findings at once
Iterative reverse audit (Step 6)
1-3
Loops until “No issues found” or 3-round cap
Total
11-13 (10-12)
Same-repo: 11-13; cross-repo: 10-12 (no Agent 7)
Most PRs converge to the lower end of the range (1 reverse audit round); the cap prevents runaway cost on pathological cases.
What’s NOT Flagged
The review intentionally excludes:
Pre-existing issues in unchanged code (focus on the diff only)
Style/formatting/naming that matches your codebase conventions
Issues a linter or type checker would catch (handled by deterministic analysis)
Subjective “consider doing X” suggestions without a real problem
Minor refactoring that doesn’t fix a bug or risk
Missing documentation unless the logic is genuinely confusing
Issues already discussed in existing PR comments (avoids duplicating human feedback)
Design Philosophy
Silence is better than noise.
Every comment should be worth the reader’s time.
If unsure whether something is a problem → don’t report it
Linter/typecheck issues are handled by tools, not LLM guesses
Same pattern across N files → aggregated into one finding
PR comments are high-confidence only
Style/formatting issues matching codebase conventions are excluded
Last updated on
May 18, 2026
Commands
Followup Suggestions</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/code-review/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Package Overview</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/development/npm/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/development/npm/</guid>
  <pubDate>Fri, 12 Jan 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
NPM
Package Overview
This monorepo contains two main packages:
@qwen-code/qwen-code
and
@qwen-code/qwen-code-core
.
@qwen-code/qwen-code
This is the main package for Qwen Code. It is responsible for the user interface, command parsing, and all other user-facing functionality.
When this package is published, it is bundled into a single executable file. This bundle includes all of the package’s dependencies, including
@qwen-code/qwen-code-core
. This means that whether ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
NPM
Package Overview
This monorepo contains two main packages:
@qwen-code/qwen-code
and
@qwen-code/qwen-code-core
.
@qwen-code/qwen-code
This is the main package for Qwen Code. It is responsible for the user interface, command parsing, and all other user-facing functionality.
When this package is published, it is bundled into a single executable file. This bundle includes all of the package’s dependencies, including
@qwen-code/qwen-code-core
. This means that whether a user installs the package with
npm install -g @qwen-code/qwen-code
or runs it directly with
npx @qwen-code/qwen-code
, they are using this single, self-contained executable.
@qwen-code/qwen-code-core
This package contains the core logic for the CLI. It is responsible for making API requests to configured providers, handling authentication, and managing the local cache.
This package is not bundled. When it is published, it is published as a standard Node.js package with its own dependencies. This allows it to be used as a standalone package in other projects, if needed. All transpiled js code in the
dist
folder is included in the package.
Release Process
This project follows a structured release process to ensure that all packages are versioned and published correctly. The process is designed to be as automated as possible.
How To Release
Releases are managed through the
release.yml
GitHub Actions workflow. To perform a manual release for a patch or hotfix:
Navigate to the
Actions
tab of the repository.
Select the
Release
workflow from the list.
Click the
Run workflow
dropdown button.
Fill in the required inputs:
Version
: The exact version to release (e.g.,
v0.2.1
).
Ref
: The branch or commit SHA to release from (defaults to
main
).
Dry Run
: Leave as
true
to test the workflow without publishing, or set to
false
to perform a live release.
Click
Run workflow
.
Release Types
The project supports multiple types of releases:
Stable Releases
Regular stable releases for production use.
Preview Releases
Weekly preview releases every Tuesday at 23:59 UTC for early access to upcoming features.
Nightly Releases
Daily nightly releases at midnight UTC for bleeding-edge development testing.
Automated Release Schedule
Nightly
: Every day at midnight UTC
Preview
: Every Tuesday at 23:59 UTC
Stable
: Manual releases triggered by maintainers
How to Use Different Release Types
To install the latest version of each type:
# Stable (default)
npm
install
-g
@qwen-code/qwen-code
# Preview
npm
install
-g
@qwen-code/qwen-code@preview
# Nightly
npm
install
-g
@qwen-code/qwen-code@nightly
Release Process Details
Every scheduled or manual release follows these steps:
Checks out the specified code (latest from
main
branch or specific commit).
Installs all dependencies.
Runs the full suite of
preflight
checks and integration tests.
If all tests succeed, it calculates the appropriate version number based on release type.
Builds and publishes the packages to npm with the appropriate dist-tag.
Creates a GitHub Release for the version.
Failure Handling
If any step in the release workflow fails, it will automatically create a new issue in the repository with the labels
bug
and a type-specific failure label (e.g.,
nightly-failure
,
preview-failure
). The issue will contain a link to the failed workflow run for easy debugging.
Release Validation
After pushing a new release smoke testing should be performed to ensure that the packages are working as expected. This can be done by installing the packages locally and running a set of tests to ensure that they are functioning correctly.
npx -y @qwen-code/qwen-code@latest --version
to validate the push worked as expected if you were not doing a rc or dev tag
npx -y @qwen-code/qwen-code@<release tag> --version
to validate the tag pushed appropriately
This is destructive locally
npm uninstall @qwen-code/qwen-code && npm uninstall -g @qwen-code/qwen-code && npm cache clean --force &&  npm install @qwen-code/qwen-code@<version>
Smoke testing a basic run through of exercising a few llm commands and tools is recommended to ensure that the packages are working as expected. We’ll codify this more in the future.
When to merge the version change, or not?
The above pattern for creating patch or hotfix releases from current or older commits leaves the repository in the following state:
The Tag (
vX.Y.Z-patch.1
): This tag correctly points to the original commit on main
that contains the stable code you intended to release. This is crucial. Anyone checking
out this tag gets the exact code that was published.
The Branch (
release-vX.Y.Z-patch.1
): This branch contains one new commit on top of the
tagged commit. That new commit only contains the version number change in package.json
(and other related files like package-lock.json).
This separation is good. It keeps your main branch history clean of release-specific
version bumps until you decide to merge them.
This is the critical decision, and it depends entirely on the nature of the release.
Merge Back for Stable Patches and Hotfixes
You almost always want to merge the
release-<tag>
branch back into
main
for any
stable patch or hotfix release.
Why? The primary reason is to update the version in main’s package.json. If you release
v1.2.1 from an older commit but never merge the version bump back, your main branch’s
package.json will still say “version”: “1.2.0”. The next developer who starts work for
the next feature release (v1.3.0) will be branching from a codebase that has an
incorrect, older version number. This leads to confusion and requires manual version
bumping later.
The Process: After the release-v1.2.1 branch is created and the package is successfully
published, you should open a pull request to merge release-v1.2.1 into main. This PR
will contain just one commit: “chore: bump version to v1.2.1”. It’s a clean, simple
integration that keeps your main branch in sync with the latest released version.
Do NOT Merge Back for Pre-Releases (RC, Beta, Dev)
You typically do not merge release branches for pre-releases back into
main
.
Why? Pre-release versions (e.g., v1.3.0-rc.1, v1.3.0-rc.2) are, by definition, not
stable and are temporary. You don’t want to pollute your main branch’s history with a
series of version bumps for release candidates. The package.json in main should reflect
the latest stable release version, not an RC.
The Process: The release-v1.3.0-rc.1 branch is created, the npm publish —tag rc happens,
and then… the branch has served its purpose. You can simply delete it. The code for
the RC is already on main (or a feature branch), so no functional code is lost. The
release branch was just a temporary vehicle for the version number.
Local Testing and Validation: Changes to the Packaging and Publishing Process
If you need to test the release process without actually publishing to NPM or creating a public GitHub release, you can trigger the workflow manually from the GitHub UI.
Go to the
Actions tab
of the repository.
Click on the “Run workflow” dropdown.
Leave the
dry_run
option checked (
true
).
Click the “Run workflow” button.
This will run the entire release process but will skip the
npm publish
and
gh release create
steps. You can inspect the workflow logs to ensure everything is working as expected.
It is crucial to test any changes to the packaging and publishing process locally before committing them. This ensures that the packages will be published correctly and that they will work as expected when installed by a user.
To validate your changes, you can perform a dry run of the publishing process. This will simulate the publishing process without actually publishing the packages to the npm registry.
npm_package_version
=
9.9.9
SANDBOX_IMAGE_REGISTRY
=
"registry"
SANDBOX_IMAGE_NAME
=
"thename"
npm
run
publish:npm
--dry-run
This command will do the following:
Build all the packages.
Run all the prepublish scripts.
Create the package tarballs that would be published to npm.
Print a summary of the packages that would be published.
You can then inspect the generated tarballs to ensure that they contain the correct files and that the
package.json
files have been updated correctly. The tarballs will be created in the root of each package’s directory (e.g.,
packages/cli/qwen-code-0.1.6.tgz
).
By performing a dry run, you can be confident that your changes to the packaging process are correct and that the packages will be published successfully.
Release Deep Dive
The main goal of the release process is to take the source code from the packages/ directory, build it, and assemble a
clean, self-contained package in a temporary
dist
directory at the root of the project. This
dist
directory is what
actually gets published to NPM.
Here are the key stages:
Stage 1: Pre-Release Sanity Checks and Versioning
What happens: Before any files are moved, the process ensures the project is in a good state. This involves running tests,
linting, and type-checking (npm run preflight). The version number in the root package.json and packages/cli/package.json
is updated to the new release version.
Why: This guarantees that only high-quality, working code is released. Versioning is the first step to signify a new
release.
Stage 2: Building the Source Code
What happens: The TypeScript source code in packages/core/src and packages/cli/src is compiled into JavaScript.
File movement:
packages/core/src/*
/
.ts -> compiled to -> packages/core/dist/
packages/cli/src/*
/
.ts -> compiled to -> packages/cli/dist/
Why: The TypeScript code written during development needs to be converted into plain JavaScript that can be run by
Node.js. The core package is built first as the cli package depends on it.
Stage 3: Bundling and Assembling the Final Publishable Package
This is the most critical stage where files are moved and transformed into their final state for publishing. The process uses modern bundling techniques to create the final package.
Bundle Creation:
What happens: The prepare-package.js script creates a clean distribution package in the
dist
directory.
Key transformations:
Copies README.md and LICENSE to dist/
Copies locales folder for internationalization
Creates a clean package.json for distribution with only necessary dependencies
Keeps distribution dependencies minimal (no bundled runtime deps)
Maintains optional dependencies for node-pty
The JavaScript Bundle is Created:
What happens: The built JavaScript from both packages/core/dist and packages/cli/dist are bundled into a single,
executable JavaScript file using esbuild.
File location: dist/cli.js
Why: This creates a single, optimized file that contains all the necessary application code. It simplifies the package
by removing the need for complex dependency resolution at install time.
Static and Supporting Files are Copied:
What happens: Essential files that are not part of the source code but are required for the package to work correctly
or be well-described are copied into the
dist
directory.
File movement:
README.md -> dist/README.md
LICENSE -> dist/LICENSE
locales/ -> dist/locales/
Vendor files -> dist/vendor/
Why:
The README.md and LICENSE are standard files that should be included in any NPM package.
Locales support internationalization features
Vendor files contain necessary runtime dependencies
Stage 4: Publishing to NPM
What happens: The npm publish command is run from inside the root
dist
directory.
Why: By running npm publish from within the
dist
directory, only the files we carefully assembled in Stage 3 are uploaded
to the NPM registry. This prevents any source code, test files, or development configurations from being accidentally
published, resulting in a clean and minimal package for users.
This process ensures that the final published artifact is a purpose-built, clean, and efficient representation of the
project, rather than a direct copy of the development workspace.
NPM Workspaces
This project uses
NPM Workspaces
to manage the packages within this monorepo. This simplifies development by allowing us to manage dependencies and run scripts across multiple packages from the root of the project.
How it Works
The root
package.json
file defines the workspaces for this project:
{
"workspaces"
: [
"packages/*"
]
}
This tells NPM that any folder inside the
packages
directory is a separate package that should be managed as part of the workspace.
Benefits of Workspaces
Simplified Dependency Management
: Running
npm install
from the root of the project will install all dependencies for all packages in the workspace and link them together. This means you don’t need to run
npm install
in each package’s directory.
Automatic Linking
: Packages within the workspace can depend on each other. When you run
npm install
, NPM will automatically create symlinks between the packages. This means that when you make changes to one package, the changes are immediately available to other packages that depend on it.
Simplified Script Execution
: You can run scripts in any package from the root of the project using the
--workspace
flag. For example, to run the
build
script in the
cli
package, you can run
npm run build --workspace @qwen-code/qwen-code
.
Last updated on
May 18, 2026
Example Proxy Script
Telemetry</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/npm/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
NPM
Package Overview
This monorepo contains two main packages:
@qwen-code/qwen-code
and
@qwen-code/qwen-code-core
.
@qwen-code/qwen-code
This is the main package for Qwen Code. It is responsible for the user interface, command parsing, and all other user-facing functionality.
When this package is published, it is bundled into a single executable file. This bundle includes all of the package’s dependencies, including
@qwen-code/qwen-code-core
. This means that whether ...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
NPM
Package Overview
This monorepo contains two main packages:
@qwen-code/qwen-code
and
@qwen-code/qwen-code-core
.
@qwen-code/qwen-code
This is the main package for Qwen Code. It is responsible for the user interface, command parsing, and all other user-facing functionality.
When this package is published, it is bundled into a single executable file. This bundle includes all of the package’s dependencies, including
@qwen-code/qwen-code-core
. This means that whether a user installs the package with
npm install -g @qwen-code/qwen-code
or runs it directly with
npx @qwen-code/qwen-code
, they are using this single, self-contained executable.
@qwen-code/qwen-code-core
This package contains the core logic for the CLI. It is responsible for making API requests to configured providers, handling authentication, and managing the local cache.
This package is not bundled. When it is published, it is published as a standard Node.js package with its own dependencies. This allows it to be used as a standalone package in other projects, if needed. All transpiled js code in the
dist
folder is included in the package.
Release Process
This project follows a structured release process to ensure that all packages are versioned and published correctly. The process is designed to be as automated as possible.
How To Release
Releases are managed through the
release.yml
GitHub Actions workflow. To perform a manual release for a patch or hotfix:
Navigate to the
Actions
tab of the repository.
Select the
Release
workflow from the list.
Click the
Run workflow
dropdown button.
Fill in the required inputs:
Version
: The exact version to release (e.g.,
v0.2.1
).
Ref
: The branch or commit SHA to release from (defaults to
main
).
Dry Run
: Leave as
true
to test the workflow without publishing, or set to
false
to perform a live release.
Click
Run workflow
.
Release Types
The project supports multiple types of releases:
Stable Releases
Regular stable releases for production use.
Preview Releases
Weekly preview releases every Tuesday at 23:59 UTC for early access to upcoming features.
Nightly Releases
Daily nightly releases at midnight UTC for bleeding-edge development testing.
Automated Release Schedule
Nightly
: Every day at midnight UTC
Preview
: Every Tuesday at 23:59 UTC
Stable
: Manual releases triggered by maintainers
How to Use Different Release Types
To install the latest version of each type:
# Stable (default)
npm
install
-g
@qwen-code/qwen-code
# Preview
npm
install
-g
@qwen-code/qwen-code@preview
# Nightly
npm
install
-g
@qwen-code/qwen-code@nightly
Release Process Details
Every scheduled or manual release follows these steps:
Checks out the specified code (latest from
main
branch or specific commit).
Installs all dependencies.
Runs the full suite of
preflight
checks and integration tests.
If all tests succeed, it calculates the appropriate version number based on release type.
Builds and publishes the packages to npm with the appropriate dist-tag.
Creates a GitHub Release for the version.
Failure Handling
If any step in the release workflow fails, it will automatically create a new issue in the repository with the labels
bug
and a type-specific failure label (e.g.,
nightly-failure
,
preview-failure
). The issue will contain a link to the failed workflow run for easy debugging.
Release Validation
After pushing a new release smoke testing should be performed to ensure that the packages are working as expected. This can be done by installing the packages locally and running a set of tests to ensure that they are functioning correctly.
npx -y @qwen-code/qwen-code@latest --version
to validate the push worked as expected if you were not doing a rc or dev tag
npx -y @qwen-code/qwen-code@<release tag> --version
to validate the tag pushed appropriately
This is destructive locally
npm uninstall @qwen-code/qwen-code && npm uninstall -g @qwen-code/qwen-code && npm cache clean --force &&  npm install @qwen-code/qwen-code@<version>
Smoke testing a basic run through of exercising a few llm commands and tools is recommended to ensure that the packages are working as expected. We’ll codify this more in the future.
When to merge the version change, or not?
The above pattern for creating patch or hotfix releases from current or older commits leaves the repository in the following state:
The Tag (
vX.Y.Z-patch.1
): This tag correctly points to the original commit on main
that contains the stable code you intended to release. This is crucial. Anyone checking
out this tag gets the exact code that was published.
The Branch (
release-vX.Y.Z-patch.1
): This branch contains one new commit on top of the
tagged commit. That new commit only contains the version number change in package.json
(and other related files like package-lock.json).
This separation is good. It keeps your main branch history clean of release-specific
version bumps until you decide to merge them.
This is the critical decision, and it depends entirely on the nature of the release.
Merge Back for Stable Patches and Hotfixes
You almost always want to merge the
release-<tag>
branch back into
main
for any
stable patch or hotfix release.
Why? The primary reason is to update the version in main’s package.json. If you release
v1.2.1 from an older commit but never merge the version bump back, your main branch’s
package.json will still say “version”: “1.2.0”. The next developer who starts work for
the next feature release (v1.3.0) will be branching from a codebase that has an
incorrect, older version number. This leads to confusion and requires manual version
bumping later.
The Process: After the release-v1.2.1 branch is created and the package is successfully
published, you should open a pull request to merge release-v1.2.1 into main. This PR
will contain just one commit: “chore: bump version to v1.2.1”. It’s a clean, simple
integration that keeps your main branch in sync with the latest released version.
Do NOT Merge Back for Pre-Releases (RC, Beta, Dev)
You typically do not merge release branches for pre-releases back into
main
.
Why? Pre-release versions (e.g., v1.3.0-rc.1, v1.3.0-rc.2) are, by definition, not
stable and are temporary. You don’t want to pollute your main branch’s history with a
series of version bumps for release candidates. The package.json in main should reflect
the latest stable release version, not an RC.
The Process: The release-v1.3.0-rc.1 branch is created, the npm publish —tag rc happens,
and then… the branch has served its purpose. You can simply delete it. The code for
the RC is already on main (or a feature branch), so no functional code is lost. The
release branch was just a temporary vehicle for the version number.
Local Testing and Validation: Changes to the Packaging and Publishing Process
If you need to test the release process without actually publishing to NPM or creating a public GitHub release, you can trigger the workflow manually from the GitHub UI.
Go to the
Actions tab
of the repository.
Click on the “Run workflow” dropdown.
Leave the
dry_run
option checked (
true
).
Click the “Run workflow” button.
This will run the entire release process but will skip the
npm publish
and
gh release create
steps. You can inspect the workflow logs to ensure everything is working as expected.
It is crucial to test any changes to the packaging and publishing process locally before committing them. This ensures that the packages will be published correctly and that they will work as expected when installed by a user.
To validate your changes, you can perform a dry run of the publishing process. This will simulate the publishing process without actually publishing the packages to the npm registry.
npm_package_version
=
9.9.9
SANDBOX_IMAGE_REGISTRY
=
"registry"
SANDBOX_IMAGE_NAME
=
"thename"
npm
run
publish:npm
--dry-run
This command will do the following:
Build all the packages.
Run all the prepublish scripts.
Create the package tarballs that would be published to npm.
Print a summary of the packages that would be published.
You can then inspect the generated tarballs to ensure that they contain the correct files and that the
package.json
files have been updated correctly. The tarballs will be created in the root of each package’s directory (e.g.,
packages/cli/qwen-code-0.1.6.tgz
).
By performing a dry run, you can be confident that your changes to the packaging process are correct and that the packages will be published successfully.
Release Deep Dive
The main goal of the release process is to take the source code from the packages/ directory, build it, and assemble a
clean, self-contained package in a temporary
dist
directory at the root of the project. This
dist
directory is what
actually gets published to NPM.
Here are the key stages:
Stage 1: Pre-Release Sanity Checks and Versioning
What happens: Before any files are moved, the process ensures the project is in a good state. This involves running tests,
linting, and type-checking (npm run preflight). The version number in the root package.json and packages/cli/package.json
is updated to the new release version.
Why: This guarantees that only high-quality, working code is released. Versioning is the first step to signify a new
release.
Stage 2: Building the Source Code
What happens: The TypeScript source code in packages/core/src and packages/cli/src is compiled into JavaScript.
File movement:
packages/core/src/*
/
.ts -> compiled to -> packages/core/dist/
packages/cli/src/*
/
.ts -> compiled to -> packages/cli/dist/
Why: The TypeScript code written during development needs to be converted into plain JavaScript that can be run by
Node.js. The core package is built first as the cli package depends on it.
Stage 3: Bundling and Assembling the Final Publishable Package
This is the most critical stage where files are moved and transformed into their final state for publishing. The process uses modern bundling techniques to create the final package.
Bundle Creation:
What happens: The prepare-package.js script creates a clean distribution package in the
dist
directory.
Key transformations:
Copies README.md and LICENSE to dist/
Copies locales folder for internationalization
Creates a clean package.json for distribution with only necessary dependencies
Keeps distribution dependencies minimal (no bundled runtime deps)
Maintains optional dependencies for node-pty
The JavaScript Bundle is Created:
What happens: The built JavaScript from both packages/core/dist and packages/cli/dist are bundled into a single,
executable JavaScript file using esbuild.
File location: dist/cli.js
Why: This creates a single, optimized file that contains all the necessary application code. It simplifies the package
by removing the need for complex dependency resolution at install time.
Static and Supporting Files are Copied:
What happens: Essential files that are not part of the source code but are required for the package to work correctly
or be well-described are copied into the
dist
directory.
File movement:
README.md -> dist/README.md
LICENSE -> dist/LICENSE
locales/ -> dist/locales/
Vendor files -> dist/vendor/
Why:
The README.md and LICENSE are standard files that should be included in any NPM package.
Locales support internationalization features
Vendor files contain necessary runtime dependencies
Stage 4: Publishing to NPM
What happens: The npm publish command is run from inside the root
dist
directory.
Why: By running npm publish from within the
dist
directory, only the files we carefully assembled in Stage 3 are uploaded
to the NPM registry. This prevents any source code, test files, or development configurations from being accidentally
published, resulting in a clean and minimal package for users.
This process ensures that the final published artifact is a purpose-built, clean, and efficient representation of the
project, rather than a direct copy of the development workspace.
NPM Workspaces
This project uses
NPM Workspaces
to manage the packages within this monorepo. This simplifies development by allowing us to manage dependencies and run scripts across multiple packages from the root of the project.
How it Works
The root
package.json
file defines the workspaces for this project:
{
"workspaces"
: [
"packages/*"
]
}
This tells NPM that any folder inside the
packages
directory is a separate package that should be managed as part of the workspace.
Benefits of Workspaces
Simplified Dependency Management
: Running
npm install
from the root of the project will install all dependencies for all packages in the workspace and link them together. This means you don’t need to run
npm install
in each package’s directory.
Automatic Linking
: Packages within the workspace can depend on each other. When you run
npm install
, NPM will automatically create symlinks between the packages. This means that when you make changes to one package, the changes are immediately available to other packages that depend on it.
Simplified Script Execution
: You can run scripts in any package from the root of the project using the
--workspace
flag. For example, to run the
build
script in the
cli
package, you can run
npm run build --workspace @qwen-code/qwen-code
.
Last updated on
May 18, 2026
Example Proxy Script
Telemetry</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/npm/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Integration Tests</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/developers/development/integration-tests/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/developers/development/integration-tests/</guid>
  <pubDate>Sat, 06 Jan 2024 00:00:00 +0000</pubDate>
  <category>Integrations</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Integration Tests
Integration Tests
This document provides information about the integration testing framework used in this project.
Overview
The integration tests are designed to validate the end-to-end functionality of Qwen Code. They execute the built binary in a controlled environment and verify that it behaves as expected when interacting with the file system.
These tests are located in the
integration-tests
directory and are run using a custom test runner.
Runni...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Integration Tests
Integration Tests
This document provides information about the integration testing framework used in this project.
Overview
The integration tests are designed to validate the end-to-end functionality of Qwen Code. They execute the built binary in a controlled environment and verify that it behaves as expected when interacting with the file system.
These tests are located in the
integration-tests
directory and are run using a custom test runner.
Running the tests
The integration tests are not run as part of the default
npm run test
command. They must be run explicitly using the
npm run test:integration:all
script.
The integration tests can also be run using the following shortcut:
npm
run
test:e2e
Running a specific set of tests
To run a subset of test files, you can use
npm run <integration test command> <file_name1> ....
where <integration test command> is either
test:e2e
or
test:integration*
and
<file_name>
is any of the
.test.js
files in the
integration-tests/
directory. For example, the following command runs
list_directory.test.js
and
write_file.test.js
:
npm
run
test:e2e
list_directory
write_file
Running a single test by name
To run a single test by its name, use the
--test-name-pattern
flag:
npm
run
test:e2e
--
--test-name-pattern
"reads a file"
Running all tests
To run the entire suite of integration tests, use the following command:
npm
run
test:integration:all
Sandbox matrix
The
all
command will run tests for
no sandboxing
,
docker
and
podman
.
Each individual type can be run using the following commands:
npm
run
test:integration:sandbox:none
npm
run
test:integration:sandbox:docker
npm
run
test:integration:sandbox:podman
Diagnostics
The integration test runner provides several options for diagnostics to help track down test failures.
Keeping test output
You can preserve the temporary files created during a test run for inspection. This is useful for debugging issues with file system operations.
To keep the test output set the
KEEP_OUTPUT
environment variable to
true
.
KEEP_OUTPUT
=
true
npm
run
test:integration:sandbox:none
When output is kept, the test runner will print the path to the unique directory for the test run.
Verbose output
For more detailed debugging, set the
VERBOSE
environment variable to
true
.
VERBOSE
=
true
npm
run
test:integration:sandbox:none
When using
VERBOSE=true
and
KEEP_OUTPUT=true
in the same command, the output is streamed to the console and also saved to a log file within the test’s temporary directory.
The verbose output is formatted to clearly identify the source of the logs:
--- TEST: <log dir>:<test-name> ---
... output from the qwen command ...
--- END TEST: <log dir>:<test-name> ---
Linting and formatting
To ensure code quality and consistency, the integration test files are linted as part of the main build process. You can also manually run the linter and auto-fixer.
Running the linter
To check for linting errors, run the following command:
npm
run
lint
You can include the
:fix
flag in the command to automatically fix any fixable linting errors:
npm
run
lint:fix
Directory structure
The integration tests create a unique directory for each test run inside the
.integration-tests
directory. Within this directory, a subdirectory is created for each test file, and within that, a subdirectory is created for each individual test case.
This structure makes it easy to locate the artifacts for a specific test run, file, or case.
.integration-tests/
└── <run-id>/
└── <test-file-name>.test.js/
└── <test-case-name>/
├── output.log
└── ...other test artifacts...
Continuous integration
To ensure the integration tests are always run, a GitHub Actions workflow is defined in
.github/workflows/e2e.yml
. This workflow automatically runs the integrations tests for pull requests against the
main
branch, or when a pull request is added to a merge queue.
The workflow runs the tests in different sandboxing environments to ensure Qwen Code is tested across each:
sandbox:none
: Runs the tests without any sandboxing.
sandbox:docker
: Runs the tests in a Docker container.
sandbox:podman
: Runs the tests in a Podman container.
Last updated on
May 18, 2026
Telemetry
Issue and PR Automation</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/integration-tests/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">Developer Guide
Development
Integration Tests
Integration Tests
This document provides information about the integration testing framework used in this project.
Overview
The integration tests are designed to validate the end-to-end functionality of Qwen Code. They execute the built binary in a controlled environment and verify that it behaves as expected when interacting with the file system.
These tests are located in the
integration-tests
directory and are run using a custom test runner.
Runni...</p><div style="font-size:16px;line-height:1.8;color:#333">Developer Guide
Development
Integration Tests
Integration Tests
This document provides information about the integration testing framework used in this project.
Overview
The integration tests are designed to validate the end-to-end functionality of Qwen Code. They execute the built binary in a controlled environment and verify that it behaves as expected when interacting with the file system.
These tests are located in the
integration-tests
directory and are run using a custom test runner.
Running the tests
The integration tests are not run as part of the default
npm run test
command. They must be run explicitly using the
npm run test:integration:all
script.
The integration tests can also be run using the following shortcut:
npm
run
test:e2e
Running a specific set of tests
To run a subset of test files, you can use
npm run <integration test command> <file_name1> ....
where <integration test command> is either
test:e2e
or
test:integration*
and
<file_name>
is any of the
.test.js
files in the
integration-tests/
directory. For example, the following command runs
list_directory.test.js
and
write_file.test.js
:
npm
run
test:e2e
list_directory
write_file
Running a single test by name
To run a single test by its name, use the
--test-name-pattern
flag:
npm
run
test:e2e
--
--test-name-pattern
"reads a file"
Running all tests
To run the entire suite of integration tests, use the following command:
npm
run
test:integration:all
Sandbox matrix
The
all
command will run tests for
no sandboxing
,
docker
and
podman
.
Each individual type can be run using the following commands:
npm
run
test:integration:sandbox:none
npm
run
test:integration:sandbox:docker
npm
run
test:integration:sandbox:podman
Diagnostics
The integration test runner provides several options for diagnostics to help track down test failures.
Keeping test output
You can preserve the temporary files created during a test run for inspection. This is useful for debugging issues with file system operations.
To keep the test output set the
KEEP_OUTPUT
environment variable to
true
.
KEEP_OUTPUT
=
true
npm
run
test:integration:sandbox:none
When output is kept, the test runner will print the path to the unique directory for the test run.
Verbose output
For more detailed debugging, set the
VERBOSE
environment variable to
true
.
VERBOSE
=
true
npm
run
test:integration:sandbox:none
When using
VERBOSE=true
and
KEEP_OUTPUT=true
in the same command, the output is streamed to the console and also saved to a log file within the test’s temporary directory.
The verbose output is formatted to clearly identify the source of the logs:
--- TEST: <log dir>:<test-name> ---
... output from the qwen command ...
--- END TEST: <log dir>:<test-name> ---
Linting and formatting
To ensure code quality and consistency, the integration test files are linted as part of the main build process. You can also manually run the linter and auto-fixer.
Running the linter
To check for linting errors, run the following command:
npm
run
lint
You can include the
:fix
flag in the command to automatically fix any fixable linting errors:
npm
run
lint:fix
Directory structure
The integration tests create a unique directory for each test run inside the
.integration-tests
directory. Within this directory, a subdirectory is created for each test file, and within that, a subdirectory is created for each individual test case.
This structure makes it easy to locate the artifacts for a specific test run, file, or case.
.integration-tests/
└── <run-id>/
└── <test-file-name>.test.js/
└── <test-case-name>/
├── output.log
└── ...other test artifacts...
Continuous integration
To ensure the integration tests are always run, a GitHub Actions workflow is defined in
.github/workflows/e2e.yml
. This workflow automatically runs the integrations tests for pull requests against the
main
branch, or when a pull request is added to a merge queue.
The workflow runs the tests in different sandboxing environments to ensure Qwen Code is tested across each:
sandbox:none
: Runs the tests without any sandboxing.
sandbox:docker
: Runs the tests in a Docker container.
sandbox:podman
: Runs the tests in a Podman container.
Last updated on
May 18, 2026
Telemetry
Issue and PR Automation</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/developers/development/integration-tests/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Themes</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/configuration/themes/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/configuration/themes/</guid>
  <pubDate>Fri, 05 Jan 2024 00:00:00 +0000</pubDate>
  <category>Configuration</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Themes
Themes
Qwen Code supports a variety of themes to customize its color scheme and appearance. You can change the theme to suit your preferences via the
/theme
command or
&quot;theme&quot;:
configuration setting.
Available Themes
Qwen Code comes with a selection of pre-defined themes, which you can list using the
/theme
command within the CLI:
Dark Themes:
ANSI
Atom One
Ayu
Default
Dracula
GitHub
Light Themes:
ANSI Light
Ayu Light
Default Light
GitHub Light
Google Code
Xcode
C...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Themes
Themes
Qwen Code supports a variety of themes to customize its color scheme and appearance. You can change the theme to suit your preferences via the
/theme
command or
"theme":
configuration setting.
Available Themes
Qwen Code comes with a selection of pre-defined themes, which you can list using the
/theme
command within the CLI:
Dark Themes:
ANSI
Atom One
Ayu
Default
Dracula
GitHub
Light Themes:
ANSI Light
Ayu Light
Default Light
GitHub Light
Google Code
Xcode
Changing Themes
Enter
/theme
into Qwen Code.
A dialog or selection prompt appears, listing the available themes.
Using the arrow keys, select a theme. Some interfaces might offer a live preview or highlight as you select.
Confirm your selection to apply the theme.
Note:
If a theme is defined in your
settings.json
file (either by name or by a file path), you must remove the
"theme"
setting from the file before you can change the theme using the
/theme
command.
Theme Persistence
Selected themes are saved in Qwen Code’s
configuration
so your preference is remembered across sessions.
Custom Color Themes
Qwen Code allows you to create your own custom color themes by specifying them in your
settings.json
file. This gives you full control over the color palette used in the CLI.
How to Define a Custom Theme
Add a
customThemes
block to your user, project, or system
settings.json
file. Each custom theme is defined as an object with a unique name and a set of color keys. For example:
{
"ui"
: {
"customThemes"
: {
"MyCustomTheme"
: {
"name"
:
"MyCustomTheme"
,
"type"
:
"custom"
,
"Background"
:
"#181818"
,
...
}
}
}
}
Color keys:
Background
Foreground
LightBlue
AccentBlue
AccentPurple
AccentCyan
AccentGreen
AccentYellow
AccentRed
Comment
Gray
DiffAdded
(optional, for added lines in diffs)
DiffRemoved
(optional, for removed lines in diffs)
DiffModified
(optional, for modified lines in diffs)
Required Properties:
name
(must match the key in the
customThemes
object and be a string)
type
(must be the string
"custom"
)
Background
Foreground
LightBlue
AccentBlue
AccentPurple
AccentCyan
AccentGreen
AccentYellow
AccentRed
Comment
Gray
You can use either hex codes (e.g.,
#FF0000
)
or
standard CSS color names (e.g.,
coral
,
teal
,
blue
) for any color value. See
CSS color names
for a full list of supported names.
You can define multiple custom themes by adding more entries to the
customThemes
object.
Loading Themes from a File
In addition to defining custom themes in
settings.json
, you can also load a theme directly from a JSON file by specifying the file path in your
settings.json
. This is useful for sharing themes or keeping them separate from your main configuration.
To load a theme from a file, set the
theme
property in your
settings.json
to the path of your theme file:
{
"ui"
: {
"theme"
:
"/path/to/your/theme.json"
}
}
The theme file must be a valid JSON file that follows the same structure as a custom theme defined in
settings.json
.
Example
my-theme.json
:
{
"name"
:
"My File Theme"
,
"type"
:
"custom"
,
"Background"
:
"#282A36"
,
"Foreground"
:
"#F8F8F2"
,
"LightBlue"
:
"#82AAFF"
,
"AccentBlue"
:
"#61AFEF"
,
"AccentPurple"
:
"#BD93F9"
,
"AccentCyan"
:
"#8BE9FD"
,
"AccentGreen"
:
"#50FA7B"
,
"AccentYellow"
:
"#F1FA8C"
,
"AccentRed"
:
"#FF5555"
,
"Comment"
:
"#6272A4"
,
"Gray"
:
"#ABB2BF"
,
"DiffAdded"
:
"#A6E3A1"
,
"DiffRemoved"
:
"#F38BA8"
,
"DiffModified"
:
"#89B4FA"
,
"GradientColors"
: [
"#4796E4"
,
"#847ACE"
,
"#C3677F"
]
}
Security Note:
For your safety, Gemini CLI will only load theme files that are located within your home directory. If you attempt to load a theme from outside your home directory, a warning will be displayed and the theme will not be loaded. This is to prevent loading potentially malicious theme files from untrusted sources.
Example Custom Theme
Using Your Custom Theme
Select your custom theme using the
/theme
command in Qwen Code. Your custom theme will appear in the theme selection dialog.
Or, set it as the default by adding
"theme": "MyCustomTheme"
to the
ui
object in your
settings.json
.
Custom themes can be set at the user, project, or system level, and follow the same
configuration precedence
as other settings.
Themes Preview
Dark Theme
Preview
Light Theme
Preview
ANSI
ANSI Light
Atom OneDark
Ayu Light
Ayu
Default Light
Default
GitHub Light
Dracula
Google Code
GitHub
Xcode
Last updated on
May 18, 2026
Trusted Folders
Model Providers</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/themes/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Configuration
Themes
Themes
Qwen Code supports a variety of themes to customize its color scheme and appearance. You can change the theme to suit your preferences via the
/theme
command or
&quot;theme&quot;:
configuration setting.
Available Themes
Qwen Code comes with a selection of pre-defined themes, which you can list using the
/theme
command within the CLI:
Dark Themes:
ANSI
Atom One
Ayu
Default
Dracula
GitHub
Light Themes:
ANSI Light
Ayu Light
Default Light
GitHub Light
Google Code
Xcode
C...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Configuration
Themes
Themes
Qwen Code supports a variety of themes to customize its color scheme and appearance. You can change the theme to suit your preferences via the
/theme
command or
"theme":
configuration setting.
Available Themes
Qwen Code comes with a selection of pre-defined themes, which you can list using the
/theme
command within the CLI:
Dark Themes:
ANSI
Atom One
Ayu
Default
Dracula
GitHub
Light Themes:
ANSI Light
Ayu Light
Default Light
GitHub Light
Google Code
Xcode
Changing Themes
Enter
/theme
into Qwen Code.
A dialog or selection prompt appears, listing the available themes.
Using the arrow keys, select a theme. Some interfaces might offer a live preview or highlight as you select.
Confirm your selection to apply the theme.
Note:
If a theme is defined in your
settings.json
file (either by name or by a file path), you must remove the
"theme"
setting from the file before you can change the theme using the
/theme
command.
Theme Persistence
Selected themes are saved in Qwen Code’s
configuration
so your preference is remembered across sessions.
Custom Color Themes
Qwen Code allows you to create your own custom color themes by specifying them in your
settings.json
file. This gives you full control over the color palette used in the CLI.
How to Define a Custom Theme
Add a
customThemes
block to your user, project, or system
settings.json
file. Each custom theme is defined as an object with a unique name and a set of color keys. For example:
{
"ui"
: {
"customThemes"
: {
"MyCustomTheme"
: {
"name"
:
"MyCustomTheme"
,
"type"
:
"custom"
,
"Background"
:
"#181818"
,
...
}
}
}
}
Color keys:
Background
Foreground
LightBlue
AccentBlue
AccentPurple
AccentCyan
AccentGreen
AccentYellow
AccentRed
Comment
Gray
DiffAdded
(optional, for added lines in diffs)
DiffRemoved
(optional, for removed lines in diffs)
DiffModified
(optional, for modified lines in diffs)
Required Properties:
name
(must match the key in the
customThemes
object and be a string)
type
(must be the string
"custom"
)
Background
Foreground
LightBlue
AccentBlue
AccentPurple
AccentCyan
AccentGreen
AccentYellow
AccentRed
Comment
Gray
You can use either hex codes (e.g.,
#FF0000
)
or
standard CSS color names (e.g.,
coral
,
teal
,
blue
) for any color value. See
CSS color names
for a full list of supported names.
You can define multiple custom themes by adding more entries to the
customThemes
object.
Loading Themes from a File
In addition to defining custom themes in
settings.json
, you can also load a theme directly from a JSON file by specifying the file path in your
settings.json
. This is useful for sharing themes or keeping them separate from your main configuration.
To load a theme from a file, set the
theme
property in your
settings.json
to the path of your theme file:
{
"ui"
: {
"theme"
:
"/path/to/your/theme.json"
}
}
The theme file must be a valid JSON file that follows the same structure as a custom theme defined in
settings.json
.
Example
my-theme.json
:
{
"name"
:
"My File Theme"
,
"type"
:
"custom"
,
"Background"
:
"#282A36"
,
"Foreground"
:
"#F8F8F2"
,
"LightBlue"
:
"#82AAFF"
,
"AccentBlue"
:
"#61AFEF"
,
"AccentPurple"
:
"#BD93F9"
,
"AccentCyan"
:
"#8BE9FD"
,
"AccentGreen"
:
"#50FA7B"
,
"AccentYellow"
:
"#F1FA8C"
,
"AccentRed"
:
"#FF5555"
,
"Comment"
:
"#6272A4"
,
"Gray"
:
"#ABB2BF"
,
"DiffAdded"
:
"#A6E3A1"
,
"DiffRemoved"
:
"#F38BA8"
,
"DiffModified"
:
"#89B4FA"
,
"GradientColors"
: [
"#4796E4"
,
"#847ACE"
,
"#C3677F"
]
}
Security Note:
For your safety, Gemini CLI will only load theme files that are located within your home directory. If you attempt to load a theme from outside your home directory, a warning will be displayed and the theme will not be loaded. This is to prevent loading potentially malicious theme files from untrusted sources.
Example Custom Theme
Using Your Custom Theme
Select your custom theme using the
/theme
command in Qwen Code. Your custom theme will appear in the theme selection dialog.
Or, set it as the default by adding
"theme": "MyCustomTheme"
to the
ui
object in your
settings.json
.
Custom themes can be set at the user, project, or system level, and follow the same
configuration precedence
as other settings.
Themes Preview
Dark Theme
Preview
Light Theme
Preview
ANSI
ANSI Light
Atom OneDark
Ayu Light
Ayu
Default Light
Default
GitHub Light
Dracula
Google Code
GitHub
Xcode
Last updated on
May 18, 2026
Trusted Folders
Model Providers</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/configuration/themes/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
<item>
  <title>Tool-Use Summaries</title>
  <link>https://QwenLM.github.io/qwen-code-docs/en/users/features/tool-use-summaries/</link>
  <guid isPermaLink="false">https://QwenLM.github.io/qwen-code-docs/en/users/features/tool-use-summaries/</guid>
  <pubDate>Fri, 05 Jan 2024 00:00:00 +0000</pubDate>
  <category>Documentation</category>
  <description><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Tool-Use Summaries
Tool-Use Summaries
Qwen Code can generate a short, git-commit-subject-style label after each tool batch completes, summarizing what the batch accomplished. The label appears inline in the transcript and replaces the generic
Tool × N
header in compact mode.
This is a UX aid for parallel tool calls: when the model fans out into several
Read
+
Grep
+
Bash
calls at once, the summary tells you the intent at a glance instead of forcing you to scan the tool list.
...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Tool-Use Summaries
Tool-Use Summaries
Qwen Code can generate a short, git-commit-subject-style label after each tool batch completes, summarizing what the batch accomplished. The label appears inline in the transcript and replaces the generic
Tool × N
header in compact mode.
This is a UX aid for parallel tool calls: when the model fans out into several
Read
+
Grep
+
Bash
calls at once, the summary tells you the intent at a glance instead of forcing you to scan the tool list.
The feature is enabled by default and runs silently in the background. It requires a configured
fast model
.
What You See
Full mode (default)
The summary appears as a dim badge line directly below the tool group:
╭──────────────────────────────────────────────╮
│ ✓  ReadFile a.txt                            │
│ ✓  ReadFile b.txt                            │
│ ✓  ReadFile c.txt                            │
│ ✓  ReadFile d.txt                            │
╰──────────────────────────────────────────────╯
● Read 4 text files
Compact mode (
Ctrl+O
or
ui.compactMode: true
)
The label replaces the generic
Tool × N
header in the compact one-liner:
╭──────────────────────────────────────────────╮
│✓  Read txt files  · 4 tools                  │
│Press Ctrl+O to show full tool output         │
╰──────────────────────────────────────────────╯
The individual tool calls are still a keystroke away (
Ctrl+O
to toggle to full mode).
How It Works
After a tool batch finalizes, Qwen Code fires a fire-and-forget call to the configured fast model with:
The tool names, truncated arguments, and truncated results (each capped at 300 characters).
The assistant’s most recent text output (first 200 characters) as an intent prefix.
A system prompt instructing the model to return a past-tense, 30-character label in git-commit-subject style.
The call runs in parallel with the next turn’s API streaming, so its ~1s latency is hidden behind the main model’s response. When the label resolves, it is appended to the transcript as a
tool_use_summary
entry.
Example labels:
Searched in auth/
,
Fixed NPE in UserService
,
Created signup endpoint
,
Read config.json
,
Ran failing tests
.
When It Appears
The summary is generated when
all
of the following are true:
experimental.emitToolUseSummaries
is
true
(default).
A
fastModel
is configured (via settings or
/model --fast
).
At least one tool completed in the batch.
The turn was not aborted before tool completion.
The fast model returned a non-empty, non-error response.
Subagent tool calls do not trigger summary generation — only the main session’s tool batches do.
When It Doesn’t Appear
The summary is silently skipped (no error, no UI change) when:
No fast model is configured.
The fast model call fails, times out, or returns empty.
The model returned an obvious error-message-like string (e.g.,
Error: ...
,
I cannot ...
) — filtered out by the client so the UI does not show misleading labels.
The turn was aborted (
Ctrl+C
) before the model finished.
In all these cases, the tool group renders as it always has.
Fast Model
The label is generated using the
fast model
— the same model you configure for prompt suggestions and speculative execution. Configure it via:
Via command
/model --fast qwen3-coder-flash
Via
settings.json
{
"fastModel"
:
"qwen3-coder-flash"
}
When no fast model is configured, summary generation is skipped entirely — the feature has no effect until you set one up.
Configuration
These settings can be configured in
settings.json
:
Setting
Type
Default
Description
experimental.emitToolUseSummaries
boolean
true
Master switch for summary generation. Turn off to disable the extra fast-model call.
fastModel
string
""
Fast model used for summary generation (shared with prompt suggestions). Required; no-op if empty.
Environment override
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES
overrides the
experimental.emitToolUseSummaries
setting for the current session:
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
or
=false
— force off.
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=1
or
=true
— force on.
Unset — use the
experimental.emitToolUseSummaries
setting.
Example
{
"fastModel"
:
"qwen3-coder-flash"
,
"experimental"
: {
"emitToolUseSummaries"
:
true
}
}
Scope & lifecycle
Three points that tend to trip up a first read of this feature:
One generation per batch, shared by both display modes.
The fast-model call happens exactly once in
handleCompletedTools
when a tool batch finalizes. Toggling
Ctrl+O
afterwards does
not
trigger a new call — both modes read from the same
tool_use_summary
history entry that was captured the first time. You can flip compact mode on and off freely without extra cost.
No backfill on toggle or on session resume.
A
tool_group
that completed before the feature was enabled (or before you flipped the setting on, or in a resumed session —
ChatRecordingService
does not persist summary entries) will never get a label. There is no “sweep existing history” pass. If you turn this setting on mid-session, only
future
batches will show a label; older groups keep the default rendering with no indicator that a label is missing.
Main-agent batches only.
The trigger lives in the main session’s turn loop (
useGeminiStream
), so:
✅ Shell, MCP, file operations, and the
Task
/ subagent tool
call itself
(as it appears in the main batch) are summarized.
❌ A subagent’s
internal
tool batches (run through
packages/core/src/agents/runtime/
) are not summarized.
An outer batch that
contains
a
Task
tool will still be labeled, but the fast model sees only the subagent tool call and its aggregated output — not the individual tool calls inside the subagent. Expect labels like
Ran research-agent
or
Delegated file search
rather than
Searched 14 files
. This is intentional — summarizing subagent internals would multiply the fast-model cost and surface noise that never shows up in the primary UI.
Recommended pairing: enable compact mode
For batches of 3+ parallel tool calls, pairing this feature with
ui.compactMode: true
produces the cleanest transcript. The compact view folds the whole batch into a single labeled row (
✓  Read txt files  · 4 tools
) instead of showing every tool line plus the trailing summary. Details remain one keystroke away via
Ctrl+O
.
{
"fastModel"
:
"qwen3-coder-flash"
,
"ui"
: {
"compactMode"
:
true
},
"experimental"
: {
"emitToolUseSummaries"
:
true
}
}
In full mode (the default), the summary renders as a trailing
● <label>
line below the tool group — useful for large or heterogeneous batches, but for small same-type batches (e.g.
Read × 3
) the label can read as a restatement of the visible tool lines. If that matches your usual workflow, either turn compact mode on as above, or turn the summary off entirely via
experimental.emitToolUseSummaries: false
.
Monitoring
Summary model usage appears in
/stats
output under the fast-model token totals, with the
prompt_id
tool_use_summary_generation
so it can be distinguished from prompt suggestions and other background tasks.
Data flow & privacy
The summary call sends each successful tool’s name, truncated
args
, and truncated result (each field capped at 300 characters) to the
fast model
, plus the first 200 characters of the assistant’s most recent text as an intent prefix.
If your fast model is configured for the same provider/auth as your main session model, the data flows along the same boundary your main session already uses — no change in trust scope. If you have configured a fast model from a
different provider
, tool inputs and outputs (potentially including file contents read by
read_file
, command output from shell calls, or values surfaced through MCP tools) will be sent to that other provider as part of the summarization prompt. That is a strictly larger data-sharing scope than the main session alone.
If this matters for your workflow, you have two clean options:
Configure
fastModel
to a model under the same provider as your main session, so the summary call doesn’t cross any new auth/data boundary.
Disable the feature entirely with
experimental.emitToolUseSummaries: false
(or
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
).
The 300-character per-field cap limits exposure but does not eliminate it — secrets discovered in tool output during the cap window can still be sent. Treat the fast model’s data boundary the same way you treat the main model’s.
Cost
One fast-model call per qualifying tool batch. Input is a small fixed system prompt plus the truncated tool inputs/outputs (each capped at 300 characters per field). Output is a single short line (capped at 100 characters, typically 20 tokens or fewer). On a typical fast model this is roughly $0.001 per batch.
If you do not want the extra cost, turn the feature off via
experimental.emitToolUseSummaries: false
or
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
.
Related
Compact Mode
— toggle with
Ctrl+O
; the summary replaces the generic tool-group header when compact mode is on.
Followup Suggestions
— another fast-model-driven UX enhancement that shares the same
fastModel
setting.
Last updated on
May 18, 2026
Followup Suggestions
SubAgents</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/tool-use-summaries/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></description>
  <content:encoded><![CDATA[<p style="color:#666;font-size:14px;margin-bottom:16px">User Guide
Features
Tool-Use Summaries
Tool-Use Summaries
Qwen Code can generate a short, git-commit-subject-style label after each tool batch completes, summarizing what the batch accomplished. The label appears inline in the transcript and replaces the generic
Tool × N
header in compact mode.
This is a UX aid for parallel tool calls: when the model fans out into several
Read
+
Grep
+
Bash
calls at once, the summary tells you the intent at a glance instead of forcing you to scan the tool list.
...</p><div style="font-size:16px;line-height:1.8;color:#333">User Guide
Features
Tool-Use Summaries
Tool-Use Summaries
Qwen Code can generate a short, git-commit-subject-style label after each tool batch completes, summarizing what the batch accomplished. The label appears inline in the transcript and replaces the generic
Tool × N
header in compact mode.
This is a UX aid for parallel tool calls: when the model fans out into several
Read
+
Grep
+
Bash
calls at once, the summary tells you the intent at a glance instead of forcing you to scan the tool list.
The feature is enabled by default and runs silently in the background. It requires a configured
fast model
.
What You See
Full mode (default)
The summary appears as a dim badge line directly below the tool group:
╭──────────────────────────────────────────────╮
│ ✓  ReadFile a.txt                            │
│ ✓  ReadFile b.txt                            │
│ ✓  ReadFile c.txt                            │
│ ✓  ReadFile d.txt                            │
╰──────────────────────────────────────────────╯
● Read 4 text files
Compact mode (
Ctrl+O
or
ui.compactMode: true
)
The label replaces the generic
Tool × N
header in the compact one-liner:
╭──────────────────────────────────────────────╮
│✓  Read txt files  · 4 tools                  │
│Press Ctrl+O to show full tool output         │
╰──────────────────────────────────────────────╯
The individual tool calls are still a keystroke away (
Ctrl+O
to toggle to full mode).
How It Works
After a tool batch finalizes, Qwen Code fires a fire-and-forget call to the configured fast model with:
The tool names, truncated arguments, and truncated results (each capped at 300 characters).
The assistant’s most recent text output (first 200 characters) as an intent prefix.
A system prompt instructing the model to return a past-tense, 30-character label in git-commit-subject style.
The call runs in parallel with the next turn’s API streaming, so its ~1s latency is hidden behind the main model’s response. When the label resolves, it is appended to the transcript as a
tool_use_summary
entry.
Example labels:
Searched in auth/
,
Fixed NPE in UserService
,
Created signup endpoint
,
Read config.json
,
Ran failing tests
.
When It Appears
The summary is generated when
all
of the following are true:
experimental.emitToolUseSummaries
is
true
(default).
A
fastModel
is configured (via settings or
/model --fast
).
At least one tool completed in the batch.
The turn was not aborted before tool completion.
The fast model returned a non-empty, non-error response.
Subagent tool calls do not trigger summary generation — only the main session’s tool batches do.
When It Doesn’t Appear
The summary is silently skipped (no error, no UI change) when:
No fast model is configured.
The fast model call fails, times out, or returns empty.
The model returned an obvious error-message-like string (e.g.,
Error: ...
,
I cannot ...
) — filtered out by the client so the UI does not show misleading labels.
The turn was aborted (
Ctrl+C
) before the model finished.
In all these cases, the tool group renders as it always has.
Fast Model
The label is generated using the
fast model
— the same model you configure for prompt suggestions and speculative execution. Configure it via:
Via command
/model --fast qwen3-coder-flash
Via
settings.json
{
"fastModel"
:
"qwen3-coder-flash"
}
When no fast model is configured, summary generation is skipped entirely — the feature has no effect until you set one up.
Configuration
These settings can be configured in
settings.json
:
Setting
Type
Default
Description
experimental.emitToolUseSummaries
boolean
true
Master switch for summary generation. Turn off to disable the extra fast-model call.
fastModel
string
""
Fast model used for summary generation (shared with prompt suggestions). Required; no-op if empty.
Environment override
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES
overrides the
experimental.emitToolUseSummaries
setting for the current session:
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
or
=false
— force off.
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=1
or
=true
— force on.
Unset — use the
experimental.emitToolUseSummaries
setting.
Example
{
"fastModel"
:
"qwen3-coder-flash"
,
"experimental"
: {
"emitToolUseSummaries"
:
true
}
}
Scope & lifecycle
Three points that tend to trip up a first read of this feature:
One generation per batch, shared by both display modes.
The fast-model call happens exactly once in
handleCompletedTools
when a tool batch finalizes. Toggling
Ctrl+O
afterwards does
not
trigger a new call — both modes read from the same
tool_use_summary
history entry that was captured the first time. You can flip compact mode on and off freely without extra cost.
No backfill on toggle or on session resume.
A
tool_group
that completed before the feature was enabled (or before you flipped the setting on, or in a resumed session —
ChatRecordingService
does not persist summary entries) will never get a label. There is no “sweep existing history” pass. If you turn this setting on mid-session, only
future
batches will show a label; older groups keep the default rendering with no indicator that a label is missing.
Main-agent batches only.
The trigger lives in the main session’s turn loop (
useGeminiStream
), so:
✅ Shell, MCP, file operations, and the
Task
/ subagent tool
call itself
(as it appears in the main batch) are summarized.
❌ A subagent’s
internal
tool batches (run through
packages/core/src/agents/runtime/
) are not summarized.
An outer batch that
contains
a
Task
tool will still be labeled, but the fast model sees only the subagent tool call and its aggregated output — not the individual tool calls inside the subagent. Expect labels like
Ran research-agent
or
Delegated file search
rather than
Searched 14 files
. This is intentional — summarizing subagent internals would multiply the fast-model cost and surface noise that never shows up in the primary UI.
Recommended pairing: enable compact mode
For batches of 3+ parallel tool calls, pairing this feature with
ui.compactMode: true
produces the cleanest transcript. The compact view folds the whole batch into a single labeled row (
✓  Read txt files  · 4 tools
) instead of showing every tool line plus the trailing summary. Details remain one keystroke away via
Ctrl+O
.
{
"fastModel"
:
"qwen3-coder-flash"
,
"ui"
: {
"compactMode"
:
true
},
"experimental"
: {
"emitToolUseSummaries"
:
true
}
}
In full mode (the default), the summary renders as a trailing
● <label>
line below the tool group — useful for large or heterogeneous batches, but for small same-type batches (e.g.
Read × 3
) the label can read as a restatement of the visible tool lines. If that matches your usual workflow, either turn compact mode on as above, or turn the summary off entirely via
experimental.emitToolUseSummaries: false
.
Monitoring
Summary model usage appears in
/stats
output under the fast-model token totals, with the
prompt_id
tool_use_summary_generation
so it can be distinguished from prompt suggestions and other background tasks.
Data flow & privacy
The summary call sends each successful tool’s name, truncated
args
, and truncated result (each field capped at 300 characters) to the
fast model
, plus the first 200 characters of the assistant’s most recent text as an intent prefix.
If your fast model is configured for the same provider/auth as your main session model, the data flows along the same boundary your main session already uses — no change in trust scope. If you have configured a fast model from a
different provider
, tool inputs and outputs (potentially including file contents read by
read_file
, command output from shell calls, or values surfaced through MCP tools) will be sent to that other provider as part of the summarization prompt. That is a strictly larger data-sharing scope than the main session alone.
If this matters for your workflow, you have two clean options:
Configure
fastModel
to a model under the same provider as your main session, so the summary call doesn’t cross any new auth/data boundary.
Disable the feature entirely with
experimental.emitToolUseSummaries: false
(or
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
).
The 300-character per-field cap limits exposure but does not eliminate it — secrets discovered in tool output during the cap window can still be sent. Treat the fast model’s data boundary the same way you treat the main model’s.
Cost
One fast-model call per qualifying tool batch. Input is a small fixed system prompt plus the truncated tool inputs/outputs (each capped at 300 characters per field). Output is a single short line (capped at 100 characters, typically 20 tokens or fewer). On a typical fast model this is roughly $0.001 per batch.
If you do not want the extra cost, turn the feature off via
experimental.emitToolUseSummaries: false
or
QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0
.
Related
Compact Mode
— toggle with
Ctrl+O
; the summary replaces the generic tool-group header when compact mode is on.
Followup Suggestions
— another fast-model-driven UX enhancement that shares the same
fastModel
setting.
Last updated on
May 18, 2026
Followup Suggestions
SubAgents</div><hr style="margin:24px 0;border:none;border-top:1px solid #eee"/><p style="margin:12px 0 0"><a href="https://QwenLM.github.io/qwen-code-docs/en/users/features/tool-use-summaries/" style="color:#1890ff;text-decoration:none;font-size:14px">View Original &rarr;</a></p>]]></content:encoded>
</item>
</channel>
</rss>
