@qwen-code/qwen-code 0.13.2 → 0.14.0-nightly.20260402.a5f17ee39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: loop
3
+ description: Create a recurring loop that runs a prompt on a schedule. Usage - /loop 5m check the build, /loop check the PR every 30m, /loop run tests (defaults to 10m). /loop list to show jobs, /loop clear to cancel all.
4
+ allowedTools:
5
+ - cron_create
6
+ - cron_list
7
+ - cron_delete
8
+ ---
9
+
10
+ # /loop — schedule a recurring prompt
11
+
12
+ ## Subcommands
13
+
14
+ If the input (after stripping the `/loop` prefix) is exactly one of these keywords, run the subcommand instead of scheduling:
15
+
16
+ - **`list`** — call CronList and display the results. Done.
17
+ - **`clear`** — call CronList, then call CronDelete for every job returned. Confirm how many were cancelled. Done.
18
+
19
+ Otherwise, parse the input below into `[interval] <prompt…>` and schedule it with CronCreate.
20
+
21
+ ## Parsing (in priority order)
22
+
23
+ 1. **Leading token**: if the first whitespace-delimited token matches `^\d+[smhd]$` (e.g. `5m`, `2h`), that's the interval; the rest is the prompt.
24
+ 2. **Trailing "every" clause**: otherwise, if the input ends with `every <N><unit>` or `every <N> <unit-word>` (e.g. `every 20m`, `every 5 minutes`, `every 2 hours`), extract that as the interval and strip it from the prompt. Only match when what follows "every" is a time expression — `check every PR` has no interval.
25
+ 3. **Default**: otherwise, interval is `10m` and the entire input is the prompt.
26
+
27
+ If the resulting prompt is empty, show usage `/loop [interval] <prompt>` and stop — do not call CronCreate.
28
+
29
+ Examples:
30
+
31
+ - `5m /babysit-prs` → interval `5m`, prompt `/babysit-prs` (rule 1)
32
+ - `check the deploy every 20m` → interval `20m`, prompt `check the deploy` (rule 2)
33
+ - `run tests every 5 minutes` → interval `5m`, prompt `run tests` (rule 2)
34
+ - `check the deploy` → interval `10m`, prompt `check the deploy` (rule 3)
35
+ - `check every PR` → interval `10m`, prompt `check every PR` (rule 3 — "every" not followed by time)
36
+ - `5m` → empty prompt → show usage
37
+
38
+ ## Interval → cron
39
+
40
+ Supported suffixes: `s` (seconds, rounded up to nearest minute, min 1), `m` (minutes), `h` (hours), `d` (days). Convert:
41
+
42
+ | Interval pattern | Cron expression | Notes |
43
+ | ----------------- | ---------------------- | ----------------------------------------- |
44
+ | `Nm` where N ≤ 59 | `*/N * * * *` | every N minutes |
45
+ | `Nm` where N ≥ 60 | `0 */H * * *` | round to hours (H = N/60, must divide 24) |
46
+ | `Nh` where N ≤ 23 | `0 */N * * *` | every N hours |
47
+ | `Nd` | `0 0 */N * *` | every N days at midnight local |
48
+ | `Ns` | treat as `ceil(N/60)m` | cron minimum granularity is 1 minute |
49
+
50
+ **If the interval doesn't cleanly divide its unit** (e.g. `7m` → `*/7 * * * *` gives uneven gaps at :56→:00; `90m` → 1.5h which cron can't express), pick the nearest clean interval and tell the user what you rounded to before scheduling.
51
+
52
+ ## Action
53
+
54
+ 1. Call CronCreate with:
55
+ - `cron`: the expression from the table above
56
+ - `prompt`: the parsed prompt from above, verbatim (slash commands are passed through unchanged)
57
+ - `recurring`: `true`
58
+ 2. Briefly confirm: what's scheduled, the cron expression, the human-readable cadence, that recurring tasks auto-expire after 3 days, and that they can cancel sooner with CronDelete (include the job ID).
59
+ 3. **Then immediately execute the parsed prompt now** — don't wait for the first cron fire. If it's a slash command, invoke it via the Skill tool; otherwise act on it directly.
60
+
61
+ ## Input
@@ -1,11 +1,12 @@
1
1
  # Extension Releasing
2
2
 
3
- There are two primary ways of releasing extensions to users:
3
+ There are three primary ways of releasing extensions to users:
4
4
 
5
5
  - [Git repository](#releasing-through-a-git-repository)
6
6
  - [Github Releases](#releasing-through-github-releases)
7
+ - [npm Registry](#releasing-through-npm-registry)
7
8
 
8
- 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.
9
+ 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.
9
10
 
10
11
  ## Releasing through a git repository
11
12
 
@@ -119,3 +120,85 @@ jobs:
119
120
  release/linux.arm64.my-tool.tar.gz
120
121
  release/win32.arm64.my-tool.zip
121
122
  ```
123
+
124
+ ## Releasing through npm registry
125
+
126
+ You can publish Qwen Code extensions as scoped npm packages (e.g. `@your-org/my-extension`). This is a good fit when:
127
+
128
+ - Your team already uses npm for package distribution
129
+ - You need private registry support with existing auth infrastructure
130
+ - You want version resolution and access control handled by npm
131
+
132
+ ### Package requirements
133
+
134
+ 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.
135
+
136
+ A minimal package structure looks like:
137
+
138
+ ```
139
+ my-extension/
140
+ ├── package.json
141
+ ├── qwen-extension.json
142
+ ├── QWEN.md # optional context file
143
+ ├── commands/ # optional custom commands
144
+ ├── skills/ # optional custom skills
145
+ └── agents/ # optional custom subagents
146
+ ```
147
+
148
+ Make sure `qwen-extension.json` is included in your published package (i.e. not excluded by `.npmignore` or the `files` field in `package.json`).
149
+
150
+ ### Publishing
151
+
152
+ Use standard npm publishing tools:
153
+
154
+ ```bash
155
+ # Publish to the default registry
156
+ npm publish
157
+
158
+ # Publish to a private/custom registry
159
+ npm publish --registry https://your-registry.com
160
+ ```
161
+
162
+ ### Installation
163
+
164
+ Users install your extension using the scoped package name:
165
+
166
+ ```bash
167
+ # Install latest version
168
+ qwen extensions install @your-org/my-extension
169
+
170
+ # Install a specific version
171
+ qwen extensions install @your-org/my-extension@1.2.0
172
+
173
+ # Install from a custom registry
174
+ qwen extensions install @your-org/my-extension --registry https://your-registry.com
175
+ ```
176
+
177
+ ### Update behavior
178
+
179
+ - Extensions installed without a version pin (e.g. `@scope/pkg`) track the `latest` dist-tag.
180
+ - Extensions installed with a dist-tag (e.g. `@scope/pkg@beta`) track that specific tag.
181
+ - 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.
182
+
183
+ ### Authentication for private registries
184
+
185
+ Qwen Code reads npm auth credentials automatically:
186
+
187
+ 1. **`NPM_TOKEN` environment variable** — highest priority
188
+ 2. **`.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`)
189
+
190
+ `.npmrc` files are read from the current directory and the user's home directory.
191
+
192
+ ### Managing release channels
193
+
194
+ You can use npm dist-tags to manage release channels:
195
+
196
+ ```bash
197
+ # Publish a beta release
198
+ npm publish --tag beta
199
+
200
+ # Users install beta channel
201
+ qwen extensions install @your-org/my-extension@beta
202
+ ```
203
+
204
+ This works similarly to git branch-based release channels but uses npm's native dist-tag mechanism.
@@ -12,11 +12,11 @@ We offer a suite of extension management tools using both `qwen extensions` CLI
12
12
 
13
13
  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.
14
14
 
15
- | Command | Description |
16
- | ------------------------------------- | ----------------------------------------------------------------- |
17
- | `/extensions` or `/extensions manage` | Manage all installed extensions |
18
- | `/extensions install <source>` | Install an extension from a git URL, local path, or marketplace |
19
- | `/extensions explore [source]` | Open extensions source page(Gemini or ClaudeCode) in your browser |
15
+ | Command | Description |
16
+ | ------------------------------------- | ---------------------------------------------------------------------------- |
17
+ | `/extensions` or `/extensions manage` | Manage all installed extensions |
18
+ | `/extensions install <source>` | Install an extension from a git URL, local path, npm package, or marketplace |
19
+ | `/extensions explore [source]` | Open extensions source page(Gemini or ClaudeCode) in your browser |
20
20
 
21
21
  ### CLI Extension Management
22
22
 
@@ -89,6 +89,34 @@ Gemini extensions are automatically converted to Qwen Code format during install
89
89
  - TOML command files are automatically migrated to Markdown format
90
90
  - MCP servers, context files, and settings are preserved
91
91
 
92
+ #### From npm Registry
93
+
94
+ 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.
95
+
96
+ ```bash
97
+ # Install the latest version
98
+ qwen extensions install @scope/my-extension
99
+
100
+ # Install a specific version
101
+ qwen extensions install @scope/my-extension@1.2.0
102
+
103
+ # Install from a custom registry
104
+ qwen extensions install @scope/my-extension --registry https://your-registry.com
105
+ ```
106
+
107
+ Only scoped packages (`@scope/package-name`) are supported to avoid ambiguity with the `owner/repo` GitHub shorthand format.
108
+
109
+ **Registry resolution** follows this priority:
110
+
111
+ 1. `--registry` CLI flag (explicit override)
112
+ 2. Scoped registry from `.npmrc` (e.g. `@scope:registry=https://...`)
113
+ 3. Default registry from `.npmrc`
114
+ 4. Fallback: `https://registry.npmjs.org/`
115
+
116
+ **Authentication** is handled automatically via the `NPM_TOKEN` environment variable or registry-specific `_authToken` entries in your `.npmrc` file.
117
+
118
+ > **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](./extension-releasing.md#releasing-through-npm-registry) for packaging details.
119
+
92
120
  #### From Git Repository
93
121
 
94
122
  ```bash
@@ -127,7 +155,7 @@ This is useful if you have an extension disabled at the top-level and only enabl
127
155
 
128
156
  ### Updating an extension
129
157
 
130
- 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`.
158
+ 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.
131
159
 
132
160
  You can update all extensions with:
133
161
 
@@ -156,6 +184,12 @@ The `qwen-extension.json` file contains the configuration for the extension. The
156
184
  "command": "node my-server.js"
157
185
  }
158
186
  },
187
+ "channels": {
188
+ "my-platform": {
189
+ "entry": "dist/index.js",
190
+ "displayName": "My Platform Channel"
191
+ }
192
+ },
159
193
  "contextFileName": "QWEN.md",
160
194
  "commands": "commands",
161
195
  "skills": "skills",
@@ -175,6 +209,7 @@ The `qwen-extension.json` file contains the configuration for the extension. The
175
209
  - `version`: The version of the extension.
176
210
  - `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](./cli/configuration.md). 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.
177
211
  - Note that all MCP server configuration options are supported except for `trust`.
212
+ - `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](../features/channels/plugins) for a full guide.
178
213
  - `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.
179
214
  - `commands`: The directory containing custom commands (default: `commands`). Commands are `.md` files that define prompts.
180
215
  - `skills`: The directory containing custom skills (default: `skills`). Skills are discovered automatically and become available via the `/skills` command.
@@ -13,5 +13,7 @@ export default {
13
13
  'token-caching': 'Token Caching',
14
14
  sandbox: 'Sandboxing',
15
15
  language: 'i18n',
16
+ channels: 'Channels',
16
17
  hooks: 'Hooks',
18
+ 'scheduled-tasks': 'Scheduled Tasks',
17
19
  };
@@ -0,0 +1,7 @@
1
+ export default {
2
+ overview: 'Overview',
3
+ telegram: 'Telegram',
4
+ weixin: 'WeChat',
5
+ dingtalk: 'DingTalk',
6
+ plugins: 'Plugins',
7
+ };
@@ -0,0 +1,134 @@
1
+ # DingTalk (Dingtalk)
2
+
3
+ This guide covers setting up a Qwen Code channel on DingTalk (钉钉).
4
+
5
+ ## Prerequisites
6
+
7
+ - A DingTalk organization account
8
+ - A DingTalk bot application with AppKey and AppSecret (see below)
9
+
10
+ ## Creating a Bot
11
+
12
+ 1. Go to the [DingTalk Developer Portal](https://open-dev.dingtalk.com)
13
+ 2. Create a new application (or use an existing one)
14
+ 3. Under the application, enable the **Robot** capability
15
+ 4. In Robot settings, enable **Stream Mode** (机器人协议 → Stream 模式)
16
+ 5. Note the **AppKey** (Client ID) and **AppSecret** (Client Secret) from the application credentials page
17
+
18
+ ### Stream Mode
19
+
20
+ 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.
21
+
22
+ ## Configuration
23
+
24
+ Add the channel to `~/.qwen/settings.json`:
25
+
26
+ ```json
27
+ {
28
+ "channels": {
29
+ "my-dingtalk": {
30
+ "type": "dingtalk",
31
+ "clientId": "$DINGTALK_CLIENT_ID",
32
+ "clientSecret": "$DINGTALK_CLIENT_SECRET",
33
+ "senderPolicy": "open",
34
+ "sessionScope": "user",
35
+ "cwd": "/path/to/your/project",
36
+ "instructions": "You are a concise coding assistant responding via DingTalk.",
37
+ "groupPolicy": "open",
38
+ "groups": {
39
+ "*": { "requireMention": true }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Set the credentials as environment variables:
47
+
48
+ ```bash
49
+ export DINGTALK_CLIENT_ID=<your-app-key>
50
+ export DINGTALK_CLIENT_SECRET=<your-app-secret>
51
+ ```
52
+
53
+ Or define them in the `env` section of `settings.json`:
54
+
55
+ ```json
56
+ {
57
+ "env": {
58
+ "DINGTALK_CLIENT_ID": "your-app-key",
59
+ "DINGTALK_CLIENT_SECRET": "your-app-secret"
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Running
65
+
66
+ ```bash
67
+ # Start only the DingTalk channel
68
+ qwen channel start my-dingtalk
69
+
70
+ # Or start all configured channels together
71
+ qwen channel start
72
+ ```
73
+
74
+ Open DingTalk and send a message to the bot. You should see a 👀 emoji reaction appear while the agent processes, followed by the response.
75
+
76
+ ## Group Chats
77
+
78
+ DingTalk bots work in both DM and group conversations. To enable group support:
79
+
80
+ 1. Set `groupPolicy` to `"allowlist"` or `"open"` in your channel config
81
+ 2. Add the bot to a DingTalk group
82
+ 3. @mention the bot in the group to trigger a response
83
+
84
+ 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](./overview#group-chats) for full details.
85
+
86
+ ### Finding a Group's Conversation ID
87
+
88
+ 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.
89
+
90
+ ## Images and Files
91
+
92
+ You can send photos and documents to the bot, not just text.
93
+
94
+ **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).
95
+
96
+ **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.
97
+
98
+ ## Key Differences from Telegram
99
+
100
+ - **Authentication:** AppKey + AppSecret instead of a static bot token. The SDK manages access token refresh automatically.
101
+ - **Connection:** WebSocket stream instead of polling — no public IP or webhook URL needed.
102
+ - **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.
103
+ - **Working indicator:** A 👀 emoji reaction is added to the user's message while processing, then removed when the response is sent.
104
+ - **Media download:** Two-step process — a `downloadCode` from the message is exchanged for a temporary download URL via DingTalk's API.
105
+ - **Groups:** DingTalk uses `isInAtList` for @mention detection instead of parsing message entities.
106
+
107
+ ## Tips
108
+
109
+ - **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.
110
+ - **Restrict access** — In an organization context, `senderPolicy: "open"` may be acceptable. For tighter control, use `"allowlist"` or `"pairing"`. See [DM Pairing](./overview#dm-pairing) for details.
111
+ - **Referenced messages** — Quoting (replying to) a user message includes the quoted text as context for the agent. Quoting bot responses is not yet supported.
112
+
113
+ ## Troubleshooting
114
+
115
+ ### Bot doesn't connect
116
+
117
+ - Verify your AppKey and AppSecret are correct
118
+ - Check that the environment variables are set before running `qwen channel start`
119
+ - Make sure **Stream Mode** is enabled in the bot's settings on the DingTalk Developer Portal
120
+ - Check the terminal output for connection errors
121
+
122
+ ### Bot doesn't respond in groups
123
+
124
+ - Check that `groupPolicy` is set to `"allowlist"` or `"open"` (default is `"disabled"`)
125
+ - Make sure you @mention the bot in the group message
126
+ - Verify the bot has been added to the group
127
+
128
+ ### "No sessionWebhook in message"
129
+
130
+ 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.
131
+
132
+ ### "Sorry, something went wrong processing your message"
133
+
134
+ This usually means the agent encountered an error. Check the terminal output for details.