@qwen-code/qwen-code 0.14.5 → 0.15.0-nightly.20260423.d40fe7cdb
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 +83 -19
- package/bundled/batch/SKILL.md +303 -0
- package/bundled/qc-helper/docs/configuration/settings.md +131 -84
- package/bundled/qc-helper/docs/features/_meta.ts +2 -0
- package/bundled/qc-helper/docs/features/arena.md +3 -2
- package/bundled/qc-helper/docs/features/commands.md +66 -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 +297 -122
- 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/status-line.md +36 -10
- package/bundled/qc-helper/docs/overview.md +4 -4
- package/bundled/qc-helper/docs/quickstart.md +14 -9
- package/bundled/qc-helper/docs/support/troubleshooting.md +9 -3
- package/cli.js +91695 -71445
- package/locales/de.js +51 -0
- package/locales/en.js +54 -0
- package/locales/fr.js +2 -0
- package/locales/ja.js +51 -0
- package/locales/pt.js +52 -0
- package/locales/ru.js +50 -0
- package/locales/zh.js +53 -0
- 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,48 +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
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
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
|
+
```
|
|
67
221
|
|
|
68
222
|
## Input/Output Rules
|
|
69
223
|
|
|
70
224
|
### Hook Input Structure
|
|
71
225
|
|
|
72
|
-
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:**
|
|
73
229
|
|
|
74
230
|
```json
|
|
75
231
|
{
|
|
@@ -81,7 +237,39 @@ All hooks receive standardized input in JSON format through stdin. Common fields
|
|
|
81
237
|
}
|
|
82
238
|
```
|
|
83
239
|
|
|
84
|
-
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
|
+
```
|
|
85
273
|
|
|
86
274
|
### Individual Hook Event Details
|
|
87
275
|
|
|
@@ -115,11 +303,8 @@ Event-specific fields are added based on the hook type. Below are the event-spec
|
|
|
115
303
|
{
|
|
116
304
|
"hookSpecificOutput": {
|
|
117
305
|
"hookEventName": "PreToolUse",
|
|
118
|
-
"permissionDecision": "
|
|
119
|
-
"permissionDecisionReason": "
|
|
120
|
-
"updatedInput": {
|
|
121
|
-
"field_to_modify": "new value"
|
|
122
|
-
},
|
|
306
|
+
"permissionDecision": "deny",
|
|
307
|
+
"permissionDecisionReason": "Security policy blocks database writes",
|
|
123
308
|
"additionalContext": "Current environment: production. Proceed with caution."
|
|
124
309
|
}
|
|
125
310
|
}
|
|
@@ -578,12 +763,12 @@ Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` o
|
|
|
578
763
|
"hooks": {
|
|
579
764
|
"PreToolUse": [
|
|
580
765
|
{
|
|
581
|
-
"matcher": "^
|
|
582
|
-
"sequential": false,
|
|
766
|
+
"matcher": "^Bash$",
|
|
767
|
+
"sequential": false,
|
|
583
768
|
"hooks": [
|
|
584
769
|
{
|
|
585
770
|
"type": "command",
|
|
586
|
-
"command": "/path/to/
|
|
771
|
+
"command": "/path/to/security-check.sh",
|
|
587
772
|
"name": "security-check",
|
|
588
773
|
"description": "Run security checks before tool execution",
|
|
589
774
|
"timeout": 30000
|
|
@@ -606,69 +791,58 @@ Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` o
|
|
|
606
791
|
}
|
|
607
792
|
```
|
|
608
793
|
|
|
609
|
-
|
|
794
|
+
## Hook Execution
|
|
610
795
|
|
|
611
|
-
|
|
796
|
+
### Parallel vs Sequential Execution
|
|
797
|
+
|
|
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
|
|
612
801
|
|
|
613
|
-
|
|
614
|
-
| ------------------- | ---------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------- |
|
|
615
|
-
| Tool Events | `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | ✅ Yes (regex) | Tool name: `bash`, `read_file`, `write_file`, `edit`, `glob`, `grep_search`, etc. |
|
|
616
|
-
| Subagent Events | `SubagentStart`, `SubagentStop` | ✅ Yes (regex) | Agent type: `Bash`, `Explorer`, etc. |
|
|
617
|
-
| Session Events | `SessionStart` | ✅ Yes (regex) | Source: `startup`, `resume`, `clear`, `compact` |
|
|
618
|
-
| Session Events | `SessionEnd` | ✅ Yes (regex) | Reason: `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
|
|
619
|
-
| Notification Events | `Notification` | ✅ Yes (exact) | Type: `permission_prompt`, `idle_prompt`, `auth_success` |
|
|
620
|
-
| Compact Events | `PreCompact` | ✅ Yes (exact) | Trigger: `manual`, `auto` |
|
|
621
|
-
| Prompt Events | `UserPromptSubmit` | ❌ No | N/A |
|
|
622
|
-
| Stop Events | `Stop` | ❌ No | N/A |
|
|
802
|
+
### Async Hooks
|
|
623
803
|
|
|
624
|
-
|
|
804
|
+
Only `command` type supports asynchronous execution. Setting `"async": true` runs the hook in the background without blocking the main flow.
|
|
625
805
|
|
|
626
|
-
|
|
627
|
-
- Empty string `""` or `"*"` matches all events of that type
|
|
628
|
-
- Standard regex syntax supported (e.g., `^bash$`, `read.*`, `(bash|run_shell_command)`)
|
|
806
|
+
**Features:**
|
|
629
807
|
|
|
630
|
-
|
|
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:**
|
|
631
813
|
|
|
632
814
|
```json
|
|
633
815
|
{
|
|
634
816
|
"hooks": {
|
|
635
|
-
"
|
|
636
|
-
{
|
|
637
|
-
"matcher": "^bash$", // Only match bash tool
|
|
638
|
-
"hooks": [...]
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
"matcher": "read.*", // Match read_file, read_multiple_files, etc.
|
|
642
|
-
"hooks": [...]
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
"matcher": "", // Match all tools (same as "*" or omitting matcher)
|
|
646
|
-
"hooks": [...]
|
|
647
|
-
}
|
|
648
|
-
],
|
|
649
|
-
"SubagentStart": [
|
|
817
|
+
"PostToolUse": [
|
|
650
818
|
{
|
|
651
|
-
"matcher": "
|
|
652
|
-
"hooks": [
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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
|
+
]
|
|
659
828
|
}
|
|
660
829
|
]
|
|
661
830
|
}
|
|
662
831
|
}
|
|
663
832
|
```
|
|
664
833
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
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
|
+
```
|
|
672
846
|
|
|
673
847
|
### Security Model
|
|
674
848
|
|
|
@@ -676,32 +850,6 @@ Matchers allow filtering hooks based on context. Not all hook events support mat
|
|
|
676
850
|
- Project-level hooks require trusted folder status
|
|
677
851
|
- Timeouts prevent hanging hooks (default: 60 seconds)
|
|
678
852
|
|
|
679
|
-
### Exit Codes
|
|
680
|
-
|
|
681
|
-
Hook scripts communicate their result through exit codes:
|
|
682
|
-
|
|
683
|
-
| Exit Code | Meaning | Behavior |
|
|
684
|
-
| --------- | ------------------ | ----------------------------------------------- |
|
|
685
|
-
| `0` | Success | stdout/stderr not shown |
|
|
686
|
-
| `2` | Blocking error | Show stderr to model and block tool call |
|
|
687
|
-
| Other | Non-blocking error | Show stderr to user only but continue tool call |
|
|
688
|
-
|
|
689
|
-
**Examples**:
|
|
690
|
-
|
|
691
|
-
```bash
|
|
692
|
-
#!/bin/bash
|
|
693
|
-
|
|
694
|
-
# Success (exit 0 is default, can be omitted)
|
|
695
|
-
echo '{"decision": "allow"}'
|
|
696
|
-
exit 0
|
|
697
|
-
|
|
698
|
-
# Blocking error - prevents operation
|
|
699
|
-
echo "Dangerous operation blocked by security policy" >&2
|
|
700
|
-
exit 2
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
> **Note**: If no exit code is specified, the script defaults to `0` (success).
|
|
704
|
-
|
|
705
853
|
## Best Practices
|
|
706
854
|
|
|
707
855
|
### Example 1: Security Validation Hook
|
|
@@ -723,24 +871,20 @@ TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
|
|
|
723
871
|
# Check for potentially dangerous operations
|
|
724
872
|
if echo "$TOOL_INPUT" | grep -qiE "(rm.*-rf|mv.*\/|chmod.*777)"; then
|
|
725
873
|
echo '{
|
|
726
|
-
"decision": "deny",
|
|
727
|
-
"reason": "Potentially dangerous operation detected",
|
|
728
874
|
"hookSpecificOutput": {
|
|
729
875
|
"hookEventName": "PreToolUse",
|
|
730
876
|
"permissionDecision": "deny",
|
|
731
|
-
"permissionDecisionReason": "
|
|
877
|
+
"permissionDecisionReason": "Security policy blocks dangerous command"
|
|
732
878
|
}
|
|
733
879
|
}'
|
|
734
880
|
exit 2 # Blocking error
|
|
735
881
|
fi
|
|
736
882
|
|
|
737
|
-
#
|
|
883
|
+
# Log the operation
|
|
738
884
|
echo "INFO: Tool $TOOL_NAME executed safely at $(date)" >> /var/log/qwen-security.log
|
|
739
885
|
|
|
740
886
|
# Allow with additional context
|
|
741
887
|
echo '{
|
|
742
|
-
"decision": "allow",
|
|
743
|
-
"reason": "Operation approved by security checker",
|
|
744
888
|
"hookSpecificOutput": {
|
|
745
889
|
"hookEventName": "PreToolUse",
|
|
746
890
|
"permissionDecision": "allow",
|
|
@@ -773,7 +917,36 @@ Configure in `.qwen/settings.json`:
|
|
|
773
917
|
}
|
|
774
918
|
```
|
|
775
919
|
|
|
776
|
-
### 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
|
|
777
950
|
|
|
778
951
|
A UserPromptSubmit hook that validates user prompts for sensitive information and provides context for long prompts:
|
|
779
952
|
|
|
@@ -831,3 +1004,5 @@ exit(0)
|
|
|
831
1004
|
- Verify hook script permissions and executability
|
|
832
1005
|
- Ensure proper JSON formatting in hook outputs
|
|
833
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
|