@qwen-code/qwen-code 0.14.0-preview.1 → 0.14.0-preview.2

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.
Files changed (59) hide show
  1. package/README.md +19 -9
  2. package/bundled/qc-helper/SKILL.md +151 -0
  3. package/bundled/qc-helper/docs/_meta.ts +30 -0
  4. package/bundled/qc-helper/docs/common-workflow.md +571 -0
  5. package/bundled/qc-helper/docs/configuration/_meta.ts +10 -0
  6. package/bundled/qc-helper/docs/configuration/auth.md +366 -0
  7. package/bundled/qc-helper/docs/configuration/memory.md +0 -0
  8. package/bundled/qc-helper/docs/configuration/model-providers.md +542 -0
  9. package/bundled/qc-helper/docs/configuration/qwen-ignore.md +55 -0
  10. package/bundled/qc-helper/docs/configuration/settings.md +655 -0
  11. package/bundled/qc-helper/docs/configuration/themes.md +160 -0
  12. package/bundled/qc-helper/docs/configuration/trusted-folders.md +61 -0
  13. package/bundled/qc-helper/docs/extension/_meta.ts +9 -0
  14. package/bundled/qc-helper/docs/extension/extension-releasing.md +121 -0
  15. package/bundled/qc-helper/docs/extension/getting-started-extensions.md +299 -0
  16. package/bundled/qc-helper/docs/extension/introduction.md +310 -0
  17. package/bundled/qc-helper/docs/features/_meta.ts +18 -0
  18. package/bundled/qc-helper/docs/features/approval-mode.md +263 -0
  19. package/bundled/qc-helper/docs/features/arena.md +218 -0
  20. package/bundled/qc-helper/docs/features/channels/_meta.ts +7 -0
  21. package/bundled/qc-helper/docs/features/channels/dingtalk.md +134 -0
  22. package/bundled/qc-helper/docs/features/channels/overview.md +336 -0
  23. package/bundled/qc-helper/docs/features/channels/plugins.md +87 -0
  24. package/bundled/qc-helper/docs/features/channels/telegram.md +120 -0
  25. package/bundled/qc-helper/docs/features/channels/weixin.md +106 -0
  26. package/bundled/qc-helper/docs/features/checkpointing.md +77 -0
  27. package/bundled/qc-helper/docs/features/commands.md +312 -0
  28. package/bundled/qc-helper/docs/features/headless.md +318 -0
  29. package/bundled/qc-helper/docs/features/hooks.md +715 -0
  30. package/bundled/qc-helper/docs/features/language.md +139 -0
  31. package/bundled/qc-helper/docs/features/lsp.md +417 -0
  32. package/bundled/qc-helper/docs/features/mcp.md +281 -0
  33. package/bundled/qc-helper/docs/features/sandbox.md +241 -0
  34. package/bundled/qc-helper/docs/features/skills.md +289 -0
  35. package/bundled/qc-helper/docs/features/sub-agents.md +511 -0
  36. package/bundled/qc-helper/docs/features/token-caching.md +29 -0
  37. package/bundled/qc-helper/docs/ide-integration/_meta.ts +4 -0
  38. package/bundled/qc-helper/docs/ide-integration/ide-companion-spec.md +182 -0
  39. package/bundled/qc-helper/docs/ide-integration/ide-integration.md +144 -0
  40. package/bundled/qc-helper/docs/integration-github-action.md +241 -0
  41. package/bundled/qc-helper/docs/integration-jetbrains.md +81 -0
  42. package/bundled/qc-helper/docs/integration-vscode.md +39 -0
  43. package/bundled/qc-helper/docs/integration-zed.md +72 -0
  44. package/bundled/qc-helper/docs/overview.md +64 -0
  45. package/bundled/qc-helper/docs/quickstart.md +273 -0
  46. package/bundled/qc-helper/docs/reference/_meta.ts +3 -0
  47. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +72 -0
  48. package/bundled/qc-helper/docs/support/Uninstall.md +42 -0
  49. package/bundled/qc-helper/docs/support/_meta.ts +6 -0
  50. package/bundled/qc-helper/docs/support/tos-privacy.md +112 -0
  51. package/bundled/qc-helper/docs/support/troubleshooting.md +123 -0
  52. package/cli.js +4530 -3605
  53. package/locales/de.js +2 -2
  54. package/locales/en.js +2 -2
  55. package/locales/ja.js +2 -2
  56. package/locales/pt.js +2 -2
  57. package/locales/ru.js +2 -2
  58. package/locales/zh.js +1 -1
  59. package/package.json +2 -2
@@ -0,0 +1,715 @@
1
+ # Qwen Code Hooks Documentation
2
+
3
+ ## Overview
4
+
5
+ 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.
6
+
7
+ > **⚠️ EXPERIMENTAL FEATURE**
8
+ >
9
+ > Hooks are currently in an experimental stage. To enable hooks, start Qwen Code with the `--experimental-hooks` flag:
10
+ >
11
+ > ```bash
12
+ > qwen --experimental-hooks
13
+ > ```
14
+
15
+ ## What are Hooks?
16
+
17
+ 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:
18
+
19
+ - Monitor and audit tool usage
20
+ - Enforce security policies
21
+ - Inject additional context into conversations
22
+ - Customize application behavior based on events
23
+ - Integrate with external systems and services
24
+ - Modify tool inputs or responses programmatically
25
+
26
+ ## Hook Architecture
27
+
28
+ The Qwen Code hook system consists of several key components:
29
+
30
+ 1. **Hook Registry**: Stores and manages all configured hooks
31
+ 2. **Hook Planner**: Determines which hooks should run for each event
32
+ 3. **Hook Runner**: Executes individual hooks with proper context
33
+ 4. **Hook Aggregator**: Combines results from multiple hooks
34
+ 5. **Hook Event Handler**: Coordinates the firing of hooks for events
35
+
36
+ ## Hook Events
37
+
38
+ Hooks fire at specific points during a Qwen Code session. When an event fires and a matcher matches, Qwen Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. Your handler can inspect the input, take action, and optionally return a decision. Some events fire once per session, while others fire repeatedly inside the agentic loop.
39
+
40
+ <div align="center">
41
+ <img src="https://img.alicdn.com/imgextra/i4/O1CN01sYWUTh1RDJl7Lz2ne_!!6000000002077-2-tps-812-1212.png" alt="Hook Lifecycle Diagram" width="400"/>
42
+ </div>
43
+
44
+ The following table lists all available hook events in Qwen Code:
45
+
46
+ | Event Name | Description | Use Case |
47
+ | -------------------- | ------------------------------------------- | ----------------------------------------------- |
48
+ | `PreToolUse` | Fired before tool execution | Permission checking, input validation, logging |
49
+ | `PostToolUse` | Fired after successful tool execution | Logging, output processing, monitoring |
50
+ | `PostToolUseFailure` | Fired when tool execution fails | Error handling, alerting, remediation |
51
+ | `Notification` | Fired when notifications are sent | Notification customization, logging |
52
+ | `UserPromptSubmit` | Fired when user submits a prompt | Input processing, validation, context injection |
53
+ | `SessionStart` | Fired when a new session starts | Initialization, context setup |
54
+ | `Stop` | Fired before Qwen concludes its response | Finalization, cleanup |
55
+ | `SubagentStart` | Fired when a subagent starts | Subagent initialization |
56
+ | `SubagentStop` | Fired when a subagent stops | Subagent finalization |
57
+ | `PreCompact` | Fired before conversation compaction | Pre-compaction processing |
58
+ | `SessionEnd` | Fired when a session ends | Cleanup, reporting |
59
+ | `PermissionRequest` | Fired when permission dialogs are displayed | Permission automation, policy enforcement |
60
+
61
+ ## Input/Output Rules
62
+
63
+ ### Hook Input Structure
64
+
65
+ All hooks receive standardized input in JSON format through stdin. Common fields included in every hook event:
66
+
67
+ ```json
68
+ {
69
+ "session_id": "string",
70
+ "transcript_path": "string",
71
+ "cwd": "string",
72
+ "hook_event_name": "string",
73
+ "timestamp": "string"
74
+ }
75
+ ```
76
+
77
+ Event-specific fields are added based on the hook type. Below are the event-specific fields for each hook event:
78
+
79
+ ### Individual Hook Event Details
80
+
81
+ #### PreToolUse
82
+
83
+ **Purpose**: Executed before a tool is used to allow for permission checks, input validation, or context injection.
84
+
85
+ **Event-specific fields**:
86
+
87
+ ```json
88
+ {
89
+ "permission_mode": "default | plan | auto_edit | yolo",
90
+ "tool_name": "name of the tool being executed",
91
+ "tool_input": "object containing the tool's input parameters",
92
+ "tool_use_id": "unique identifier for this tool use instance"
93
+ }
94
+ ```
95
+
96
+ **Output Options**:
97
+
98
+ - `hookSpecificOutput.permissionDecision`: "allow", "deny", or "ask" (REQUIRED)
99
+ - `hookSpecificOutput.permissionDecisionReason`: explanation for the decision (REQUIRED)
100
+ - `hookSpecificOutput.updatedInput`: modified tool input parameters to use instead of original
101
+ - `hookSpecificOutput.additionalContext`: additional context information
102
+
103
+ **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`.
104
+
105
+ **Example Output**:
106
+
107
+ ```json
108
+ {
109
+ "hookSpecificOutput": {
110
+ "hookEventName": "PreToolUse",
111
+ "permissionDecision": "allow",
112
+ "permissionDecisionReason": "My reason here",
113
+ "updatedInput": {
114
+ "field_to_modify": "new value"
115
+ },
116
+ "additionalContext": "Current environment: production. Proceed with caution."
117
+ }
118
+ }
119
+ ```
120
+
121
+ #### PostToolUse
122
+
123
+ **Purpose**: Executed after a tool completes successfully to process results, log outcomes, or inject additional context.
124
+
125
+ **Event-specific fields**:
126
+
127
+ ```json
128
+ {
129
+ "permission_mode": "default | plan | auto_edit | yolo",
130
+ "tool_name": "name of the tool that was executed",
131
+ "tool_input": "object containing the tool's input parameters",
132
+ "tool_response": "object containing the tool's response",
133
+ "tool_use_id": "unique identifier for this tool use instance"
134
+ }
135
+ ```
136
+
137
+ **Output Options**:
138
+
139
+ - `decision`: "allow", "deny", "block" (defaults to "allow" if not specified)
140
+ - `reason`: reason for the decision
141
+ - `hookSpecificOutput.additionalContext`: additional information to be included
142
+
143
+ **Example Output**:
144
+
145
+ ```json
146
+ {
147
+ "decision": "allow",
148
+ "reason": "Tool executed successfully",
149
+ "hookSpecificOutput": {
150
+ "additionalContext": "File modification recorded in audit log"
151
+ }
152
+ }
153
+ ```
154
+
155
+ #### PostToolUseFailure
156
+
157
+ **Purpose**: Executed when a tool execution fails to handle errors, send alerts, or record failures.
158
+
159
+ **Event-specific fields**:
160
+
161
+ ```json
162
+ {
163
+ "permission_mode": "default | plan | auto_edit | yolo",
164
+ "tool_use_id": "unique identifier for the tool use",
165
+ "tool_name": "name of the tool that failed",
166
+ "tool_input": "object containing the tool's input parameters",
167
+ "error": "error message describing the failure",
168
+ "is_interrupt": "boolean indicating if failure was due to user interruption (optional)"
169
+ }
170
+ ```
171
+
172
+ **Output Options**:
173
+
174
+ - `hookSpecificOutput.additionalContext`: error handling information
175
+ - Standard hook output fields
176
+
177
+ **Example Output**:
178
+
179
+ ```json
180
+ {
181
+ "hookSpecificOutput": {
182
+ "additionalContext": "Error: File not found. Failure logged in monitoring system."
183
+ }
184
+ }
185
+ ```
186
+
187
+ #### UserPromptSubmit
188
+
189
+ **Purpose**: Executed when the user submits a prompt to modify, validate, or enrich the input.
190
+
191
+ **Event-specific fields**:
192
+
193
+ ```json
194
+ {
195
+ "prompt": "the user's submitted prompt text"
196
+ }
197
+ ```
198
+
199
+ **Output Options**:
200
+
201
+ - `decision`: "allow", "deny", "block", or "ask"
202
+ - `reason`: human-readable explanation for the decision
203
+ - `hookSpecificOutput.additionalContext`: additional context to append to the prompt (optional)
204
+
205
+ **Note**: Since UserPromptSubmitOutput extends HookOutput, all standard fields are available but only additionalContext in hookSpecificOutput is specifically defined for this event.
206
+
207
+ **Example Output**:
208
+
209
+ ```json
210
+ {
211
+ "decision": "allow",
212
+ "reason": "Prompt reviewed and approved",
213
+ "hookSpecificOutput": {
214
+ "additionalContext": "Remember to follow company coding standards."
215
+ }
216
+ }
217
+ ```
218
+
219
+ #### SessionStart
220
+
221
+ **Purpose**: Executed when a new session starts to perform initialization tasks.
222
+
223
+ **Event-specific fields**:
224
+
225
+ ```json
226
+ {
227
+ "permission_mode": "default | plan | auto_edit | yolo",
228
+ "source": "startup | resume | clear | compact",
229
+ "model": "the model being used",
230
+ "agent_type": "the type of agent if applicable (optional)"
231
+ }
232
+ ```
233
+
234
+ **Output Options**:
235
+
236
+ - `hookSpecificOutput.additionalContext`: context to be available in the session
237
+ - Standard hook output fields
238
+
239
+ **Example Output**:
240
+
241
+ ```json
242
+ {
243
+ "hookSpecificOutput": {
244
+ "additionalContext": "Session started with security policies enabled."
245
+ }
246
+ }
247
+ ```
248
+
249
+ #### SessionEnd
250
+
251
+ **Purpose**: Executed when a session ends to perform cleanup tasks.
252
+
253
+ **Event-specific fields**:
254
+
255
+ ```json
256
+ {
257
+ "reason": "clear | logout | prompt_input_exit | bypass_permissions_disabled | other"
258
+ }
259
+ ```
260
+
261
+ **Output Options**:
262
+
263
+ - Standard hook output fields (typically not used for blocking)
264
+
265
+ #### Stop
266
+
267
+ **Purpose**: Executed before Qwen concludes its response to provide final feedback or summaries.
268
+
269
+ **Event-specific fields**:
270
+
271
+ ```json
272
+ {
273
+ "stop_hook_active": "boolean indicating if stop hook is active",
274
+ "last_assistant_message": "the last message from the assistant"
275
+ }
276
+ ```
277
+
278
+ **Output Options**:
279
+
280
+ - `decision`: "allow", "deny", "block", or "ask"
281
+ - `reason`: human-readable explanation for the decision
282
+ - `stopReason`: feedback to include in the stop response
283
+ - `continue`: set to false to stop execution
284
+ - `hookSpecificOutput.additionalContext`: additional context information
285
+
286
+ **Note**: Since StopOutput extends HookOutput, all standard fields are available but the stopReason field is particularly relevant for this event.
287
+
288
+ **Example Output**:
289
+
290
+ ```json
291
+ {
292
+ "decision": "block",
293
+ "reason": "Must be provided when Qwen Code is blocked from stopping"
294
+ }
295
+ ```
296
+
297
+ #### SubagentStart
298
+
299
+ **Purpose**: Executed when a subagent (like the Task tool) is started to set up context or permissions.
300
+
301
+ **Event-specific fields**:
302
+
303
+ ```json
304
+ {
305
+ "permission_mode": "default | plan | auto_edit | yolo",
306
+ "agent_id": "identifier for the subagent",
307
+ "agent_type": "type of agent (Bash, Explorer, Plan, Custom, etc.)"
308
+ }
309
+ ```
310
+
311
+ **Output Options**:
312
+
313
+ - `hookSpecificOutput.additionalContext`: initial context for the subagent
314
+ - Standard hook output fields
315
+
316
+ **Example Output**:
317
+
318
+ ```json
319
+ {
320
+ "hookSpecificOutput": {
321
+ "additionalContext": "Subagent initialized with restricted permissions."
322
+ }
323
+ }
324
+ ```
325
+
326
+ #### SubagentStop
327
+
328
+ **Purpose**: Executed when a subagent finishes to perform finalization tasks.
329
+
330
+ **Event-specific fields**:
331
+
332
+ ```json
333
+ {
334
+ "permission_mode": "default | plan | auto_edit | yolo",
335
+ "stop_hook_active": "boolean indicating if stop hook is active",
336
+ "agent_id": "identifier for the subagent",
337
+ "agent_type": "type of agent",
338
+ "agent_transcript_path": "path to the subagent's transcript",
339
+ "last_assistant_message": "the last message from the subagent"
340
+ }
341
+ ```
342
+
343
+ **Output Options**:
344
+
345
+ - `decision`: "allow", "deny", "block", or "ask"
346
+ - `reason`: human-readable explanation for the decision
347
+
348
+ **Example Output**:
349
+
350
+ ```json
351
+ {
352
+ "decision": "block",
353
+ "reason": "Must be provided when Qwen Code is blocked from stopping"
354
+ }
355
+ ```
356
+
357
+ #### PreCompact
358
+
359
+ **Purpose**: Executed before conversation compaction to prepare or log the compaction.
360
+
361
+ **Event-specific fields**:
362
+
363
+ ```json
364
+ {
365
+ "trigger": "manual | auto",
366
+ "custom_instructions": "custom instructions currently set"
367
+ }
368
+ ```
369
+
370
+ **Output Options**:
371
+
372
+ - `hookSpecificOutput.additionalContext`: context to include before compaction
373
+ - Standard hook output fields
374
+
375
+ **Example Output**:
376
+
377
+ ```json
378
+ {
379
+ "hookSpecificOutput": {
380
+ "additionalContext": "Compacting conversation to maintain optimal context window."
381
+ }
382
+ }
383
+ ```
384
+
385
+ #### Notification
386
+
387
+ **Purpose**: Executed when notifications are sent to customize or intercept them.
388
+
389
+ **Event-specific fields**:
390
+
391
+ ```json
392
+ {
393
+ "message": "notification message content",
394
+ "title": "notification title (optional)",
395
+ "notification_type": "permission_prompt | idle_prompt | auth_success"
396
+ }
397
+ ```
398
+
399
+ > **Note**: `elicitation_dialog` type is defined but not currently implemented.
400
+
401
+ **Output Options**:
402
+
403
+ - `hookSpecificOutput.additionalContext`: additional information to include
404
+ - Standard hook output fields
405
+
406
+ **Example Output**:
407
+
408
+ ```json
409
+ {
410
+ "hookSpecificOutput": {
411
+ "additionalContext": "Notification processed by monitoring system."
412
+ }
413
+ }
414
+ ```
415
+
416
+ #### PermissionRequest
417
+
418
+ **Purpose**: Executed when permission dialogs are displayed to automate decisions or update permissions.
419
+
420
+ **Event-specific fields**:
421
+
422
+ ```json
423
+ {
424
+ "permission_mode": "default | plan | auto_edit | yolo",
425
+ "tool_name": "name of the tool requesting permission",
426
+ "tool_input": "object containing the tool's input parameters",
427
+ "permission_suggestions": "array of suggested permissions (optional)"
428
+ }
429
+ ```
430
+
431
+ **Output Options**:
432
+
433
+ - `hookSpecificOutput.decision`: structured object with permission decision details:
434
+ - `behavior`: "allow" or "deny"
435
+ - `updatedInput`: modified tool input (optional)
436
+ - `updatedPermissions`: modified permissions (optional)
437
+ - `message`: message to show to user (optional)
438
+ - `interrupt`: whether to interrupt the workflow (optional)
439
+
440
+ **Example Output**:
441
+
442
+ ```json
443
+ {
444
+ "hookSpecificOutput": {
445
+ "decision": {
446
+ "behavior": "allow",
447
+ "message": "Permission granted based on security policy",
448
+ "interrupt": false
449
+ }
450
+ }
451
+ }
452
+ ```
453
+
454
+ ## Hook Configuration
455
+
456
+ Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` or user configuration files:
457
+
458
+ ```json
459
+ {
460
+ "hooks": {
461
+ "PreToolUse": [
462
+ {
463
+ "matcher": "^bash$", // Regex to match tool names
464
+ "sequential": false, // Whether to run hooks sequentially
465
+ "hooks": [
466
+ {
467
+ "type": "command",
468
+ "command": "/path/to/script.sh",
469
+ "name": "security-check",
470
+ "description": "Run security checks before tool execution",
471
+ "timeout": 30000
472
+ }
473
+ ]
474
+ }
475
+ ],
476
+ "SessionStart": [
477
+ {
478
+ "hooks": [
479
+ {
480
+ "type": "command",
481
+ "command": "echo 'Session started'",
482
+ "name": "session-init"
483
+ }
484
+ ]
485
+ }
486
+ ]
487
+ }
488
+ }
489
+ ```
490
+
491
+ ### Matcher Patterns
492
+
493
+ Matchers allow filtering hooks based on context. Not all hook events support matchers:
494
+
495
+ | Event Type | Events | Matcher Support | Matcher Target (Values) |
496
+ | ------------------- | ---------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------- |
497
+ | Tool Events | `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | ✅ Yes (regex) | Tool name: `bash`, `read_file`, `write_file`, `edit`, `glob`, `grep_search`, etc. |
498
+ | Subagent Events | `SubagentStart`, `SubagentStop` | ✅ Yes (regex) | Agent type: `Bash`, `Explorer`, etc. |
499
+ | Session Events | `SessionStart` | ✅ Yes (regex) | Source: `startup`, `resume`, `clear`, `compact` |
500
+ | Session Events | `SessionEnd` | ✅ Yes (regex) | Reason: `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
501
+ | Notification Events | `Notification` | ✅ Yes (exact) | Type: `permission_prompt`, `idle_prompt`, `auth_success` |
502
+ | Compact Events | `PreCompact` | ✅ Yes (exact) | Trigger: `manual`, `auto` |
503
+ | Prompt Events | `UserPromptSubmit` | ❌ No | N/A |
504
+ | Stop Events | `Stop` | ❌ No | N/A |
505
+
506
+ **Matcher Syntax**:
507
+
508
+ - Regex pattern matched against the target field
509
+ - Empty string `""` or `"*"` matches all events of that type
510
+ - Standard regex syntax supported (e.g., `^bash$`, `read.*`, `(bash|run_shell_command)`)
511
+
512
+ **Examples**:
513
+
514
+ ```json
515
+ {
516
+ "hooks": {
517
+ "PreToolUse": [
518
+ {
519
+ "matcher": "^bash$", // Only match bash tool
520
+ "hooks": [...]
521
+ },
522
+ {
523
+ "matcher": "read.*", // Match read_file, read_multiple_files, etc.
524
+ "hooks": [...]
525
+ },
526
+ {
527
+ "matcher": "", // Match all tools (same as "*" or omitting matcher)
528
+ "hooks": [...]
529
+ }
530
+ ],
531
+ "SubagentStart": [
532
+ {
533
+ "matcher": "^(Bash|Explorer)$", // Only match Bash and Explorer agents
534
+ "hooks": [...]
535
+ }
536
+ ],
537
+ "SessionStart": [
538
+ {
539
+ "matcher": "^(startup|resume)$", // Only match startup and resume sources
540
+ "hooks": [...]
541
+ }
542
+ ]
543
+ }
544
+ }
545
+ ```
546
+
547
+ ## Hook Execution
548
+
549
+ ### Parallel vs Sequential Execution
550
+
551
+ - By default, hooks execute in parallel for better performance
552
+ - Use `sequential: true` in hook definition to enforce order-dependent execution
553
+ - Sequential hooks can modify input for subsequent hooks in the chain
554
+
555
+ ### Security Model
556
+
557
+ - Hooks run in the user's environment with user privileges
558
+ - Project-level hooks require trusted folder status
559
+ - Timeouts prevent hanging hooks (default: 60 seconds)
560
+
561
+ ### Exit Codes
562
+
563
+ Hook scripts communicate their result through exit codes:
564
+
565
+ | Exit Code | Meaning | Behavior |
566
+ | --------- | ------------------ | ----------------------------------------------- |
567
+ | `0` | Success | stdout/stderr not shown |
568
+ | `2` | Blocking error | Show stderr to model and block tool call |
569
+ | Other | Non-blocking error | Show stderr to user only but continue tool call |
570
+
571
+ **Examples**:
572
+
573
+ ```bash
574
+ #!/bin/bash
575
+
576
+ # Success (exit 0 is default, can be omitted)
577
+ echo '{"decision": "allow"}'
578
+ exit 0
579
+
580
+ # Blocking error - prevents operation
581
+ echo "Dangerous operation blocked by security policy" >&2
582
+ exit 2
583
+ ```
584
+
585
+ > **Note**: If no exit code is specified, the script defaults to `0` (success).
586
+
587
+ ## Best Practices
588
+
589
+ ### Example 1: Security Validation Hook
590
+
591
+ A PreToolUse hook that logs and potentially blocks dangerous commands:
592
+
593
+ **security_check.sh**
594
+
595
+ ```bash
596
+ #!/bin/bash
597
+
598
+ # Read input from stdin
599
+ INPUT=$(cat)
600
+
601
+ # Parse the input to extract tool info
602
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
603
+ TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
604
+
605
+ # Check for potentially dangerous operations
606
+ if echo "$TOOL_INPUT" | grep -qiE "(rm.*-rf|mv.*\/|chmod.*777)"; then
607
+ echo '{
608
+ "decision": "deny",
609
+ "reason": "Potentially dangerous operation detected",
610
+ "hookSpecificOutput": {
611
+ "hookEventName": "PreToolUse",
612
+ "permissionDecision": "deny",
613
+ "permissionDecisionReason": "Dangerous command blocked by security policy"
614
+ }
615
+ }'
616
+ exit 2 # Blocking error
617
+ fi
618
+
619
+ # Allow the operation with a log
620
+ echo "INFO: Tool $TOOL_NAME executed safely at $(date)" >> /var/log/qwen-security.log
621
+
622
+ # Allow with additional context
623
+ echo '{
624
+ "decision": "allow",
625
+ "reason": "Operation approved by security checker",
626
+ "hookSpecificOutput": {
627
+ "hookEventName": "PreToolUse",
628
+ "permissionDecision": "allow",
629
+ "permissionDecisionReason": "Security check passed",
630
+ "additionalContext": "Command approved by security policy"
631
+ }
632
+ }'
633
+ exit 0
634
+ ```
635
+
636
+ Configure in `.qwen/settings.json`:
637
+
638
+ ```json
639
+ {
640
+ "hooks": {
641
+ "PreToolUse": [
642
+ {
643
+ "hooks": [
644
+ {
645
+ "type": "command",
646
+ "command": "${SECURITY_CHECK_SCRIPT}",
647
+ "name": "security-checker",
648
+ "description": "Security validation for bash commands",
649
+ "timeout": 10000
650
+ }
651
+ ]
652
+ }
653
+ ]
654
+ }
655
+ }
656
+ ```
657
+
658
+ ### Example 2: User Prompt Validation Hook
659
+
660
+ A UserPromptSubmit hook that validates user prompts for sensitive information and provides context for long prompts:
661
+
662
+ **prompt_validator.py**
663
+
664
+ ```python
665
+ import json
666
+ import sys
667
+ import re
668
+
669
+ # Load input from stdin
670
+ try:
671
+ input_data = json.load(sys.stdin)
672
+ except json.JSONDecodeError as e:
673
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
674
+ exit(1)
675
+
676
+ user_prompt = input_data.get("prompt", "")
677
+
678
+ # Sensitive words list
679
+ sensitive_words = ["password", "secret", "token", "api_key"]
680
+
681
+ # Check for sensitive information
682
+ for word in sensitive_words:
683
+ if re.search(rf"\b{word}\b", user_prompt.lower()):
684
+ # Block prompts containing sensitive information
685
+ output = {
686
+ "decision": "block",
687
+ "reason": f"Prompt contains sensitive information '{word}'. Please remove sensitive content and resubmit.",
688
+ "hookSpecificOutput": {
689
+ "hookEventName": "UserPromptSubmit"
690
+ }
691
+ }
692
+ print(json.dumps(output))
693
+ exit(0)
694
+
695
+ # Check prompt length and add warning context if too long
696
+ if len(user_prompt) > 1000:
697
+ output = {
698
+ "hookSpecificOutput": {
699
+ "hookEventName": "UserPromptSubmit",
700
+ "additionalContext": "Note: User submitted a long prompt. Please read carefully and ensure all requirements are understood."
701
+ }
702
+ }
703
+ print(json.dumps(output))
704
+ exit(0)
705
+
706
+ # No processing needed for normal cases
707
+ exit(0)
708
+ ```
709
+
710
+ ## Troubleshooting
711
+
712
+ - Check application logs for hook execution details
713
+ - Verify hook script permissions and executability
714
+ - Ensure proper JSON formatting in hook outputs
715
+ - Use specific matcher patterns to avoid unintended hook execution