@mmmbuto/qwen-code-termux 0.14.3-termux → 0.15.5-termux
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.
- package/README.md +1 -1
- package/bundled/batch/SKILL.md +304 -0
- package/bundled/loop/SKILL.md +1 -0
- package/bundled/qc-helper/SKILL.md +1 -0
- package/bundled/qc-helper/docs/configuration/auth.md +13 -6
- package/bundled/qc-helper/docs/configuration/settings.md +158 -109
- package/bundled/qc-helper/docs/features/_meta.ts +4 -0
- package/bundled/qc-helper/docs/features/arena.md +3 -2
- package/bundled/qc-helper/docs/features/commands.md +67 -11
- package/bundled/qc-helper/docs/features/dual-output.md +593 -0
- package/bundled/qc-helper/docs/features/headless.md +61 -0
- package/bundled/qc-helper/docs/features/hooks.md +408 -120
- package/bundled/qc-helper/docs/features/mcp.md +100 -14
- package/bundled/qc-helper/docs/features/memory.md +168 -0
- package/bundled/qc-helper/docs/features/sandbox.md +9 -1
- package/bundled/qc-helper/docs/features/status-line.md +36 -10
- package/bundled/qc-helper/docs/features/sub-agents.md +126 -7
- package/bundled/qc-helper/docs/features/tips.md +54 -0
- package/bundled/qc-helper/docs/features/tool-use-summaries.md +178 -0
- package/bundled/qc-helper/docs/overview.md +4 -4
- package/bundled/qc-helper/docs/quickstart.md +15 -9
- package/bundled/qc-helper/docs/support/tos-privacy.md +1 -1
- package/bundled/qc-helper/docs/support/troubleshooting.md +9 -3
- package/bundled/review/SKILL.md +3 -2
- package/cli.js +241845 -206771
- package/locales/ca.js +2143 -0
- package/locales/de.js +82 -8
- package/locales/en.js +91 -8
- package/locales/fr.js +2099 -0
- package/locales/ja.js +81 -8
- package/locales/pt.js +83 -8
- package/locales/ru.js +81 -8
- package/locales/zh-TW.js +1678 -0
- package/locales/zh.js +88 -8
- package/package.json +2 -2
- package/bundled/qc-helper/docs/configuration/memory.md +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Qwen Code Hooks
|
|
1
|
+
# Qwen Code Hooks
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
@@ -28,46 +28,204 @@ Hooks are user-defined scripts or programs that are automatically executed by Qw
|
|
|
28
28
|
- Integrate with external systems and services
|
|
29
29
|
- Modify tool inputs or responses programmatically
|
|
30
30
|
|
|
31
|
-
## Hook
|
|
31
|
+
## Hook Types
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Qwen Code supports three hook executor types:
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
| Type | Description |
|
|
36
|
+
| :--------- | :--------------------------------------------------------------------------------------------- |
|
|
37
|
+
| `command` | Execute a shell command. Receives JSON via `stdin`, returns results via `stdout`. |
|
|
38
|
+
| `http` | Send JSON as a `POST` request body to a specified URL. Returns results via HTTP response body. |
|
|
39
|
+
| `function` | Directly call a registered JavaScript function (session-level hooks only). |
|
|
40
|
+
|
|
41
|
+
### Command Hooks
|
|
42
|
+
|
|
43
|
+
Command hooks execute commands via child processes. Input JSON is passed through stdin, and output is returned via stdout.
|
|
44
|
+
|
|
45
|
+
**Configuration:**
|
|
46
|
+
|
|
47
|
+
| Field | Type | Required | Description |
|
|
48
|
+
| :-------------- | :----------------------- | :------- | :------------------------------------------ |
|
|
49
|
+
| `type` | `"command"` | Yes | Hook type |
|
|
50
|
+
| `command` | `string` | Yes | Command to execute |
|
|
51
|
+
| `name` | `string` | No | Hook name (for logging) |
|
|
52
|
+
| `description` | `string` | No | Hook description |
|
|
53
|
+
| `timeout` | `number` | No | Timeout in milliseconds, default 60000 |
|
|
54
|
+
| `async` | `boolean` | No | Whether to run asynchronously in background |
|
|
55
|
+
| `env` | `Record<string, string>` | No | Environment variables |
|
|
56
|
+
| `shell` | `"bash" \| "powershell"` | No | Shell to use |
|
|
57
|
+
| `statusMessage` | `string` | No | Status message displayed during execution |
|
|
58
|
+
|
|
59
|
+
**Example:**
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"hooks": {
|
|
64
|
+
"PreToolUse": [
|
|
65
|
+
{
|
|
66
|
+
"matcher": "WriteFile",
|
|
67
|
+
"hooks": [
|
|
68
|
+
{
|
|
69
|
+
"type": "command",
|
|
70
|
+
"command": "$QWEN_PROJECT_DIR/.qwen/hooks/security-check.sh",
|
|
71
|
+
"name": "security-check",
|
|
72
|
+
"timeout": 10000
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### HTTP Hooks
|
|
82
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
**Configuration:**
|
|
86
|
+
|
|
87
|
+
| Field | Type | Required | Description |
|
|
88
|
+
| :--------------- | :----------------------- | :------- | :-------------------------------------------------------- |
|
|
89
|
+
| `type` | `"http"` | Yes | Hook type |
|
|
90
|
+
| `url` | `string` | Yes | Target URL |
|
|
91
|
+
| `headers` | `Record<string, string>` | No | Request headers (supports env var interpolation) |
|
|
92
|
+
| `allowedEnvVars` | `string[]` | No | Whitelist of environment variables allowed in URL/headers |
|
|
93
|
+
| `timeout` | `number` | No | Timeout in seconds, default 600 |
|
|
94
|
+
| `name` | `string` | No | Hook name (for logging) |
|
|
95
|
+
| `statusMessage` | `string` | No | Status message displayed during execution |
|
|
96
|
+
| `once` | `boolean` | No | Execute only once per event per session (HTTP hooks only) |
|
|
97
|
+
|
|
98
|
+
**Security Features:**
|
|
99
|
+
|
|
100
|
+
- **URL Whitelist**: Configure allowed URL patterns via `allowedUrls`
|
|
101
|
+
- **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)
|
|
102
|
+
- **DNS Validation**: Validates domain resolution before requests to prevent DNS rebinding attacks
|
|
103
|
+
- **Environment Variable Interpolation**: `${VAR}` syntax, only allows variables in `allowedEnvVars` whitelist
|
|
104
|
+
|
|
105
|
+
**Example:**
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"hooks": {
|
|
110
|
+
"PreToolUse": [
|
|
111
|
+
{
|
|
112
|
+
"matcher": "*",
|
|
113
|
+
"hooks": [
|
|
114
|
+
{
|
|
115
|
+
"type": "http",
|
|
116
|
+
"url": "http://127.0.0.1:8080/hooks/pre-tool-use",
|
|
117
|
+
"headers": {
|
|
118
|
+
"Authorization": "Bearer ${HOOK_API_KEY}"
|
|
119
|
+
},
|
|
120
|
+
"allowedEnvVars": ["HOOK_API_KEY"],
|
|
121
|
+
"timeout": 10,
|
|
122
|
+
"name": "remote-security-check"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Function Hooks
|
|
132
|
+
|
|
133
|
+
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.
|
|
134
|
+
|
|
135
|
+
**Note**: For most use cases, use **command hooks** or **HTTP hooks** instead, which can be configured in settings files.
|
|
40
136
|
|
|
41
137
|
## Hook Events
|
|
42
138
|
|
|
43
|
-
Hooks fire at specific points during a Qwen Code session.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
|
139
|
+
Hooks fire at specific points during a Qwen Code session. Different events support different matchers to filter trigger conditions.
|
|
140
|
+
|
|
141
|
+
| Event | Triggered When | Matcher Target |
|
|
142
|
+
| :------------------- | :---------------------------------------- | :-------------------------------------------------------- |
|
|
143
|
+
| `PreToolUse` | Before tool execution | Tool name (`WriteFile`, `ReadFile`, `Bash`, etc.) |
|
|
144
|
+
| `PostToolUse` | After successful tool execution | Tool name |
|
|
145
|
+
| `PostToolUseFailure` | After tool execution fails | Tool name |
|
|
146
|
+
| `UserPromptSubmit` | After user submits prompt | None (always fires) |
|
|
147
|
+
| `SessionStart` | When session starts or resumes | Source (`startup`, `resume`, `clear`, `compact`) |
|
|
148
|
+
| `SessionEnd` | When session ends | Reason (`clear`, `logout`, `prompt_input_exit`, etc.) |
|
|
149
|
+
| `Stop` | When Claude prepares to conclude response | None (always fires) |
|
|
150
|
+
| `SubagentStart` | When subagent starts | Agent type (`Bash`, `Explorer`, `Plan`, etc.) |
|
|
151
|
+
| `SubagentStop` | When subagent stops | Agent type |
|
|
152
|
+
| `PreCompact` | Before conversation compaction | Trigger (`manual`, `auto`) |
|
|
153
|
+
| `Notification` | When notifications are sent | Type (`permission_prompt`, `idle_prompt`, `auth_success`) |
|
|
154
|
+
| `PermissionRequest` | When permission dialog is shown | Tool name |
|
|
155
|
+
|
|
156
|
+
### Matcher Patterns
|
|
157
|
+
|
|
158
|
+
`matcher` is a regular expression used to filter trigger conditions.
|
|
159
|
+
|
|
160
|
+
| Event Type | Events | Matcher Support | Matcher Target |
|
|
161
|
+
| :------------------ | :--------------------------------------------------------------------- | :-------------- | :------------------------------------------------------- |
|
|
162
|
+
| Tool Events | `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | ✅ Regex | Tool name: `WriteFile`, `ReadFile`, `Bash`, etc. |
|
|
163
|
+
| Subagent Events | `SubagentStart`, `SubagentStop` | ✅ Regex | Agent type: `Bash`, `Explorer`, etc. |
|
|
164
|
+
| Session Events | `SessionStart` | ✅ Regex | Source: `startup`, `resume`, `clear`, `compact` |
|
|
165
|
+
| Session Events | `SessionEnd` | ✅ Regex | Reason: `clear`, `logout`, `prompt_input_exit`, etc. |
|
|
166
|
+
| Notification Events | `Notification` | ✅ Exact match | Type: `permission_prompt`, `idle_prompt`, `auth_success` |
|
|
167
|
+
| Compact Events | `PreCompact` | ✅ Exact match | Trigger: `manual`, `auto` |
|
|
168
|
+
| Prompt Events | `UserPromptSubmit` | ❌ No | N/A |
|
|
169
|
+
| Stop Events | `Stop` | ❌ No | N/A |
|
|
170
|
+
|
|
171
|
+
**Matcher Syntax:**
|
|
172
|
+
|
|
173
|
+
- Empty string `""` or `"*"` matches all events of that type
|
|
174
|
+
- Standard regex syntax supported (e.g., `^Bash$`, `Read.*`, `(WriteFile|Edit)`)
|
|
175
|
+
|
|
176
|
+
**Examples:**
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"hooks": {
|
|
181
|
+
"PreToolUse": [
|
|
182
|
+
{
|
|
183
|
+
"matcher": "^Bash$",
|
|
184
|
+
"hooks": [
|
|
185
|
+
{
|
|
186
|
+
"type": "command",
|
|
187
|
+
"command": "echo 'bash check' >> /tmp/hooks.log"
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"matcher": "Write.*",
|
|
193
|
+
"hooks": [
|
|
194
|
+
{
|
|
195
|
+
"type": "command",
|
|
196
|
+
"command": "echo 'write check' >> /tmp/hooks.log"
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"matcher": "*",
|
|
202
|
+
"hooks": [
|
|
203
|
+
{ "type": "command", "command": "echo 'all tools' >> /tmp/hooks.log" }
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
"SubagentStart": [
|
|
208
|
+
{
|
|
209
|
+
"matcher": "^(Bash|Explorer)$",
|
|
210
|
+
"hooks": [
|
|
211
|
+
{
|
|
212
|
+
"type": "command",
|
|
213
|
+
"command": "echo 'subagent check' >> /tmp/hooks.log"
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
65
221
|
|
|
66
222
|
## Input/Output Rules
|
|
67
223
|
|
|
68
224
|
### Hook Input Structure
|
|
69
225
|
|
|
70
|
-
All hooks receive standardized input in JSON format through stdin
|
|
226
|
+
All hooks receive standardized input in JSON format through stdin (command) or POST body (http).
|
|
227
|
+
|
|
228
|
+
**Common Fields:**
|
|
71
229
|
|
|
72
230
|
```json
|
|
73
231
|
{
|
|
@@ -79,7 +237,39 @@ All hooks receive standardized input in JSON format through stdin. Common fields
|
|
|
79
237
|
}
|
|
80
238
|
```
|
|
81
239
|
|
|
82
|
-
Event-specific fields are added based on the hook type.
|
|
240
|
+
Event-specific fields are added based on the hook type. When running in a subagent, `agent_id` and `agent_type` are additionally included.
|
|
241
|
+
|
|
242
|
+
### Hook Output Structure
|
|
243
|
+
|
|
244
|
+
Hook output is returned via `stdout` (command) or HTTP response body (http) as JSON.
|
|
245
|
+
|
|
246
|
+
**Exit Code Behavior (Command Hooks):**
|
|
247
|
+
|
|
248
|
+
| Exit Code | Behavior |
|
|
249
|
+
| :-------- | :------------------------------------------------------------------------------------ |
|
|
250
|
+
| `0` | Success. Parse JSON in `stdout` to control behavior. |
|
|
251
|
+
| `2` | **Blocking error**. Ignores `stdout`, passes `stderr` as error feedback to the model. |
|
|
252
|
+
| Other | Non-blocking error. `stderr` only shown in debug mode, execution continues. |
|
|
253
|
+
|
|
254
|
+
**Output Structure:**
|
|
255
|
+
|
|
256
|
+
Hook output supports three categories of fields:
|
|
257
|
+
|
|
258
|
+
1. **Common Fields**: `continue`, `stopReason`, `suppressOutput`, `systemMessage`
|
|
259
|
+
2. **Top-level Decision**: `decision`, `reason` (used by some events)
|
|
260
|
+
3. **Event-specific Control**: `hookSpecificOutput` (must include `hookEventName`)
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"continue": true,
|
|
265
|
+
"decision": "allow",
|
|
266
|
+
"reason": "Operation approved",
|
|
267
|
+
"hookSpecificOutput": {
|
|
268
|
+
"hookEventName": "PreToolUse",
|
|
269
|
+
"additionalContext": "Additional context information"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
83
273
|
|
|
84
274
|
### Individual Hook Event Details
|
|
85
275
|
|
|
@@ -113,11 +303,8 @@ Event-specific fields are added based on the hook type. Below are the event-spec
|
|
|
113
303
|
{
|
|
114
304
|
"hookSpecificOutput": {
|
|
115
305
|
"hookEventName": "PreToolUse",
|
|
116
|
-
"permissionDecision": "
|
|
117
|
-
"permissionDecisionReason": "
|
|
118
|
-
"updatedInput": {
|
|
119
|
-
"field_to_modify": "new value"
|
|
120
|
-
},
|
|
306
|
+
"permissionDecision": "deny",
|
|
307
|
+
"permissionDecisionReason": "Security policy blocks database writes",
|
|
121
308
|
"additionalContext": "Current environment: production. Proceed with caution."
|
|
122
309
|
}
|
|
123
310
|
}
|
|
@@ -299,6 +486,60 @@ Event-specific fields are added based on the hook type. Below are the event-spec
|
|
|
299
486
|
}
|
|
300
487
|
```
|
|
301
488
|
|
|
489
|
+
#### StopFailure
|
|
490
|
+
|
|
491
|
+
**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.
|
|
492
|
+
|
|
493
|
+
**Event-specific fields**:
|
|
494
|
+
|
|
495
|
+
```json
|
|
496
|
+
{
|
|
497
|
+
"error": "rate_limit | authentication_failed | billing_error | invalid_request | server_error | max_output_tokens | unknown",
|
|
498
|
+
"error_details": "detailed error message (optional)",
|
|
499
|
+
"last_assistant_message": "the last message from the assistant before the error (optional)"
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**Matcher**: Matches against the `error` field. For example, `"matcher": "rate_limit"` will only trigger for rate limit errors.
|
|
504
|
+
|
|
505
|
+
**Output Options**:
|
|
506
|
+
|
|
507
|
+
- **None** - StopFailure is fire-and-forget. All hook output and exit codes are ignored.
|
|
508
|
+
|
|
509
|
+
**Exit Code Handling**:
|
|
510
|
+
|
|
511
|
+
| Exit Code | Behavior |
|
|
512
|
+
| --------- | ------------------------- |
|
|
513
|
+
| Any | Ignored (fire-and-forget) |
|
|
514
|
+
|
|
515
|
+
**Example Configuration**:
|
|
516
|
+
|
|
517
|
+
```json
|
|
518
|
+
{
|
|
519
|
+
"hooks": {
|
|
520
|
+
"StopFailure": [
|
|
521
|
+
{
|
|
522
|
+
"matcher": "rate_limit",
|
|
523
|
+
"hooks": [
|
|
524
|
+
{
|
|
525
|
+
"type": "command",
|
|
526
|
+
"command": "/path/to/rate-limit-alert.sh",
|
|
527
|
+
"name": "rate-limit-alerter"
|
|
528
|
+
}
|
|
529
|
+
]
|
|
530
|
+
}
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Use Cases**:
|
|
537
|
+
|
|
538
|
+
- Rate limit monitoring and alerting
|
|
539
|
+
- Authentication failure logging
|
|
540
|
+
- Billing error notifications
|
|
541
|
+
- Error statistics collection
|
|
542
|
+
|
|
302
543
|
#### SubagentStart
|
|
303
544
|
|
|
304
545
|
**Purpose**: Executed when a subagent (like the Task tool) is started to set up context or permissions.
|
|
@@ -387,6 +628,63 @@ Event-specific fields are added based on the hook type. Below are the event-spec
|
|
|
387
628
|
}
|
|
388
629
|
```
|
|
389
630
|
|
|
631
|
+
#### PostCompact
|
|
632
|
+
|
|
633
|
+
**Purpose**: Executed after conversation compaction completes to archive summaries or track usage.
|
|
634
|
+
|
|
635
|
+
**Event-specific fields**:
|
|
636
|
+
|
|
637
|
+
```json
|
|
638
|
+
{
|
|
639
|
+
"trigger": "manual | auto",
|
|
640
|
+
"compact_summary": "the summary generated by the compaction process"
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**Matcher**: Matches against the `trigger` field. For example, `"matcher": "manual"` will only trigger for manual compaction via `/compact` command.
|
|
645
|
+
|
|
646
|
+
**Output Options**:
|
|
647
|
+
|
|
648
|
+
- `hookSpecificOutput.additionalContext`: additional context (for logging only)
|
|
649
|
+
- Standard hook output fields (for logging only)
|
|
650
|
+
|
|
651
|
+
**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.
|
|
652
|
+
|
|
653
|
+
**Exit Code Handling**:
|
|
654
|
+
|
|
655
|
+
| Exit Code | Behavior |
|
|
656
|
+
| --------- | --------------------------------------------------------- |
|
|
657
|
+
| 0 | Success - stdout shown to user in verbose mode |
|
|
658
|
+
| Other | Non-blocking error - stderr shown to user in verbose mode |
|
|
659
|
+
|
|
660
|
+
**Example Configuration**:
|
|
661
|
+
|
|
662
|
+
```json
|
|
663
|
+
{
|
|
664
|
+
"hooks": {
|
|
665
|
+
"PostCompact": [
|
|
666
|
+
{
|
|
667
|
+
"matcher": "manual",
|
|
668
|
+
"hooks": [
|
|
669
|
+
{
|
|
670
|
+
"type": "command",
|
|
671
|
+
"command": "/path/to/save-compact-summary.sh",
|
|
672
|
+
"name": "save-summary"
|
|
673
|
+
}
|
|
674
|
+
]
|
|
675
|
+
}
|
|
676
|
+
]
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
**Use Cases**:
|
|
682
|
+
|
|
683
|
+
- Summary archiving to files or databases
|
|
684
|
+
- Usage statistics tracking
|
|
685
|
+
- Context change monitoring
|
|
686
|
+
- Audit logging for compaction operations
|
|
687
|
+
|
|
390
688
|
#### Notification
|
|
391
689
|
|
|
392
690
|
**Purpose**: Executed when notifications are sent to customize or intercept them.
|
|
@@ -465,12 +763,12 @@ Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` o
|
|
|
465
763
|
"hooks": {
|
|
466
764
|
"PreToolUse": [
|
|
467
765
|
{
|
|
468
|
-
"matcher": "^
|
|
469
|
-
"sequential": false,
|
|
766
|
+
"matcher": "^Bash$",
|
|
767
|
+
"sequential": false,
|
|
470
768
|
"hooks": [
|
|
471
769
|
{
|
|
472
770
|
"type": "command",
|
|
473
|
-
"command": "/path/to/
|
|
771
|
+
"command": "/path/to/security-check.sh",
|
|
474
772
|
"name": "security-check",
|
|
475
773
|
"description": "Run security checks before tool execution",
|
|
476
774
|
"timeout": 30000
|
|
@@ -493,69 +791,58 @@ Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` o
|
|
|
493
791
|
}
|
|
494
792
|
```
|
|
495
793
|
|
|
496
|
-
|
|
794
|
+
## Hook Execution
|
|
497
795
|
|
|
498
|
-
|
|
796
|
+
### Parallel vs Sequential Execution
|
|
499
797
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
| Subagent Events | `SubagentStart`, `SubagentStop` | ✅ Yes (regex) | Agent type: `Bash`, `Explorer`, etc. |
|
|
504
|
-
| Session Events | `SessionStart` | ✅ Yes (regex) | Source: `startup`, `resume`, `clear`, `compact` |
|
|
505
|
-
| Session Events | `SessionEnd` | ✅ Yes (regex) | Reason: `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
|
|
506
|
-
| Notification Events | `Notification` | ✅ Yes (exact) | Type: `permission_prompt`, `idle_prompt`, `auth_success` |
|
|
507
|
-
| Compact Events | `PreCompact` | ✅ Yes (exact) | Trigger: `manual`, `auto` |
|
|
508
|
-
| Prompt Events | `UserPromptSubmit` | ❌ No | N/A |
|
|
509
|
-
| Stop Events | `Stop` | ❌ No | N/A |
|
|
798
|
+
- By default, hooks execute in parallel for better performance
|
|
799
|
+
- Use `sequential: true` in hook definition to enforce order-dependent execution
|
|
800
|
+
- Sequential hooks can modify input for subsequent hooks in the chain
|
|
510
801
|
|
|
511
|
-
|
|
802
|
+
### Async Hooks
|
|
512
803
|
|
|
513
|
-
|
|
514
|
-
- Empty string `""` or `"*"` matches all events of that type
|
|
515
|
-
- Standard regex syntax supported (e.g., `^bash$`, `read.*`, `(bash|run_shell_command)`)
|
|
804
|
+
Only `command` type supports asynchronous execution. Setting `"async": true` runs the hook in the background without blocking the main flow.
|
|
516
805
|
|
|
517
|
-
**
|
|
806
|
+
**Features:**
|
|
807
|
+
|
|
808
|
+
- Cannot return decision control (operation has already occurred)
|
|
809
|
+
- Results are injected in the next conversation turn via `systemMessage` or `additionalContext`
|
|
810
|
+
- Suitable for auditing, logging, background testing, etc.
|
|
811
|
+
|
|
812
|
+
**Example:**
|
|
518
813
|
|
|
519
814
|
```json
|
|
520
815
|
{
|
|
521
816
|
"hooks": {
|
|
522
|
-
"
|
|
523
|
-
{
|
|
524
|
-
"matcher": "^bash$", // Only match bash tool
|
|
525
|
-
"hooks": [...]
|
|
526
|
-
},
|
|
527
|
-
{
|
|
528
|
-
"matcher": "read.*", // Match read_file, read_multiple_files, etc.
|
|
529
|
-
"hooks": [...]
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
"matcher": "", // Match all tools (same as "*" or omitting matcher)
|
|
533
|
-
"hooks": [...]
|
|
534
|
-
}
|
|
535
|
-
],
|
|
536
|
-
"SubagentStart": [
|
|
537
|
-
{
|
|
538
|
-
"matcher": "^(Bash|Explorer)$", // Only match Bash and Explorer agents
|
|
539
|
-
"hooks": [...]
|
|
540
|
-
}
|
|
541
|
-
],
|
|
542
|
-
"SessionStart": [
|
|
817
|
+
"PostToolUse": [
|
|
543
818
|
{
|
|
544
|
-
"matcher": "
|
|
545
|
-
"hooks": [
|
|
819
|
+
"matcher": "WriteFile|Edit",
|
|
820
|
+
"hooks": [
|
|
821
|
+
{
|
|
822
|
+
"type": "command",
|
|
823
|
+
"command": "$QWEN_PROJECT_DIR/.qwen/hooks/run-tests-async.sh",
|
|
824
|
+
"async": true,
|
|
825
|
+
"timeout": 300000
|
|
826
|
+
}
|
|
827
|
+
]
|
|
546
828
|
}
|
|
547
829
|
]
|
|
548
830
|
}
|
|
549
831
|
}
|
|
550
832
|
```
|
|
551
833
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
834
|
+
```bash
|
|
835
|
+
#!/bin/bash
|
|
836
|
+
INPUT=$(cat)
|
|
837
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
838
|
+
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then exit 0; fi
|
|
839
|
+
RESULT=$(npm test 2>&1)
|
|
840
|
+
if [ $? -eq 0 ]; then
|
|
841
|
+
echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}"
|
|
842
|
+
else
|
|
843
|
+
echo "{\"systemMessage\": \"Tests failed: $RESULT\"}"
|
|
844
|
+
fi
|
|
845
|
+
```
|
|
559
846
|
|
|
560
847
|
### Security Model
|
|
561
848
|
|
|
@@ -563,32 +850,6 @@ Matchers allow filtering hooks based on context. Not all hook events support mat
|
|
|
563
850
|
- Project-level hooks require trusted folder status
|
|
564
851
|
- Timeouts prevent hanging hooks (default: 60 seconds)
|
|
565
852
|
|
|
566
|
-
### Exit Codes
|
|
567
|
-
|
|
568
|
-
Hook scripts communicate their result through exit codes:
|
|
569
|
-
|
|
570
|
-
| Exit Code | Meaning | Behavior |
|
|
571
|
-
| --------- | ------------------ | ----------------------------------------------- |
|
|
572
|
-
| `0` | Success | stdout/stderr not shown |
|
|
573
|
-
| `2` | Blocking error | Show stderr to model and block tool call |
|
|
574
|
-
| Other | Non-blocking error | Show stderr to user only but continue tool call |
|
|
575
|
-
|
|
576
|
-
**Examples**:
|
|
577
|
-
|
|
578
|
-
```bash
|
|
579
|
-
#!/bin/bash
|
|
580
|
-
|
|
581
|
-
# Success (exit 0 is default, can be omitted)
|
|
582
|
-
echo '{"decision": "allow"}'
|
|
583
|
-
exit 0
|
|
584
|
-
|
|
585
|
-
# Blocking error - prevents operation
|
|
586
|
-
echo "Dangerous operation blocked by security policy" >&2
|
|
587
|
-
exit 2
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
> **Note**: If no exit code is specified, the script defaults to `0` (success).
|
|
591
|
-
|
|
592
853
|
## Best Practices
|
|
593
854
|
|
|
594
855
|
### Example 1: Security Validation Hook
|
|
@@ -610,24 +871,20 @@ TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
|
|
|
610
871
|
# Check for potentially dangerous operations
|
|
611
872
|
if echo "$TOOL_INPUT" | grep -qiE "(rm.*-rf|mv.*\/|chmod.*777)"; then
|
|
612
873
|
echo '{
|
|
613
|
-
"decision": "deny",
|
|
614
|
-
"reason": "Potentially dangerous operation detected",
|
|
615
874
|
"hookSpecificOutput": {
|
|
616
875
|
"hookEventName": "PreToolUse",
|
|
617
876
|
"permissionDecision": "deny",
|
|
618
|
-
"permissionDecisionReason": "
|
|
877
|
+
"permissionDecisionReason": "Security policy blocks dangerous command"
|
|
619
878
|
}
|
|
620
879
|
}'
|
|
621
880
|
exit 2 # Blocking error
|
|
622
881
|
fi
|
|
623
882
|
|
|
624
|
-
#
|
|
883
|
+
# Log the operation
|
|
625
884
|
echo "INFO: Tool $TOOL_NAME executed safely at $(date)" >> /var/log/qwen-security.log
|
|
626
885
|
|
|
627
886
|
# Allow with additional context
|
|
628
887
|
echo '{
|
|
629
|
-
"decision": "allow",
|
|
630
|
-
"reason": "Operation approved by security checker",
|
|
631
888
|
"hookSpecificOutput": {
|
|
632
889
|
"hookEventName": "PreToolUse",
|
|
633
890
|
"permissionDecision": "allow",
|
|
@@ -660,7 +917,36 @@ Configure in `.qwen/settings.json`:
|
|
|
660
917
|
}
|
|
661
918
|
```
|
|
662
919
|
|
|
663
|
-
### Example 2:
|
|
920
|
+
### Example 2: HTTP Audit Hook
|
|
921
|
+
|
|
922
|
+
A PostToolUse HTTP hook that sends all tool execution records to a remote audit service:
|
|
923
|
+
|
|
924
|
+
```json
|
|
925
|
+
{
|
|
926
|
+
"hooks": {
|
|
927
|
+
"PostToolUse": [
|
|
928
|
+
{
|
|
929
|
+
"matcher": "*",
|
|
930
|
+
"hooks": [
|
|
931
|
+
{
|
|
932
|
+
"type": "http",
|
|
933
|
+
"url": "https://audit.example.com/api/tool-execution",
|
|
934
|
+
"headers": {
|
|
935
|
+
"Authorization": "Bearer ${AUDIT_API_TOKEN}",
|
|
936
|
+
"Content-Type": "application/json"
|
|
937
|
+
},
|
|
938
|
+
"allowedEnvVars": ["AUDIT_API_TOKEN"],
|
|
939
|
+
"timeout": 10,
|
|
940
|
+
"name": "audit-logger"
|
|
941
|
+
}
|
|
942
|
+
]
|
|
943
|
+
}
|
|
944
|
+
]
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
### Example 3: User Prompt Validation Hook
|
|
664
950
|
|
|
665
951
|
A UserPromptSubmit hook that validates user prompts for sensitive information and provides context for long prompts:
|
|
666
952
|
|
|
@@ -718,3 +1004,5 @@ exit(0)
|
|
|
718
1004
|
- Verify hook script permissions and executability
|
|
719
1005
|
- Ensure proper JSON formatting in hook outputs
|
|
720
1006
|
- Use specific matcher patterns to avoid unintended hook execution
|
|
1007
|
+
- Use `--debug` mode to see detailed hook matching and execution information
|
|
1008
|
+
- Temporarily disable all hooks: add `"disableAllHooks": true` in settings
|