claude-mem 3.3.7 → 3.3.9

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 (96) hide show
  1. package/README.md +183 -46
  2. package/dist/bin/cli.d.ts +2 -0
  3. package/dist/bin/cli.js +179 -0
  4. package/dist/commands/compress.d.ts +2 -0
  5. package/dist/commands/compress.js +27 -0
  6. package/dist/commands/hooks.d.ts +19 -0
  7. package/dist/commands/hooks.js +131 -0
  8. package/dist/commands/install.d.ts +2 -0
  9. package/dist/commands/install.js +836 -0
  10. package/dist/commands/load-context.d.ts +2 -0
  11. package/dist/commands/load-context.js +151 -0
  12. package/dist/commands/logs.d.ts +2 -0
  13. package/dist/commands/logs.js +76 -0
  14. package/dist/commands/migrate-to-jsonl.d.ts +5 -0
  15. package/dist/commands/migrate-to-jsonl.js +99 -0
  16. package/dist/commands/restore.d.ts +1 -0
  17. package/dist/commands/restore.js +20 -0
  18. package/dist/commands/status.d.ts +1 -0
  19. package/dist/commands/status.js +136 -0
  20. package/dist/commands/trash-empty.d.ts +3 -0
  21. package/dist/commands/trash-empty.js +56 -0
  22. package/dist/commands/trash-view.d.ts +1 -0
  23. package/dist/commands/trash-view.js +101 -0
  24. package/dist/commands/trash.d.ts +6 -0
  25. package/dist/commands/trash.js +49 -0
  26. package/dist/commands/uninstall.d.ts +2 -0
  27. package/dist/commands/uninstall.js +107 -0
  28. package/dist/constants.d.ts +271 -0
  29. package/dist/constants.js +199 -0
  30. package/dist/core/compression/TranscriptCompressor.d.ts +79 -0
  31. package/dist/core/compression/TranscriptCompressor.js +585 -0
  32. package/dist/core/orchestration/PromptOrchestrator.d.ts +165 -0
  33. package/dist/core/orchestration/PromptOrchestrator.js +182 -0
  34. package/dist/lib/time-utils.d.ts +5 -0
  35. package/dist/lib/time-utils.js +70 -0
  36. package/dist/prompts/constants.d.ts +126 -0
  37. package/dist/prompts/constants.js +161 -0
  38. package/dist/prompts/index.d.ts +10 -0
  39. package/dist/prompts/index.js +11 -0
  40. package/dist/prompts/templates/analysis/AnalysisTemplates.d.ts +13 -0
  41. package/dist/prompts/templates/analysis/AnalysisTemplates.js +94 -0
  42. package/dist/prompts/templates/context/ContextTemplates.d.ts +119 -0
  43. package/dist/prompts/templates/context/ContextTemplates.js +399 -0
  44. package/dist/prompts/templates/hooks/HookTemplates.d.ts +175 -0
  45. package/dist/prompts/templates/hooks/HookTemplates.js +394 -0
  46. package/dist/prompts/templates/hooks/HookTemplates.test.d.ts +7 -0
  47. package/dist/prompts/templates/hooks/HookTemplates.test.js +127 -0
  48. package/dist/shared/config.d.ts +4 -0
  49. package/dist/shared/config.js +41 -0
  50. package/dist/shared/error-handler.d.ts +22 -0
  51. package/dist/shared/error-handler.js +142 -0
  52. package/dist/shared/logger.d.ts +19 -0
  53. package/dist/shared/logger.js +51 -0
  54. package/dist/shared/paths.d.ts +28 -0
  55. package/dist/shared/paths.js +100 -0
  56. package/dist/shared/settings.d.ts +41 -0
  57. package/dist/shared/settings.js +81 -0
  58. package/dist/shared/types.d.ts +145 -0
  59. package/dist/shared/types.js +78 -0
  60. package/docs/STATUS.md +155 -0
  61. package/docs/chroma-backend-migration.md +161 -0
  62. package/docs/landing-page-outline.md +287 -0
  63. package/docs/multi-platform-builds.md +96 -0
  64. package/docs/plans/cloud-service-plan.md +274 -0
  65. package/docs/plans/fix-response-format-issue.md +61 -0
  66. package/docs/plans/restructure-session-hook-output.md +102 -0
  67. package/docs/plans/session-start-hook-investigation.md +45 -0
  68. package/docs/plans/src-reorganization-plan.md +181 -0
  69. package/docs/plans/terminal-effects-decision.md +22 -0
  70. package/docs/plans/terminal-effects-integration.md +82 -0
  71. package/docs/plans/trash-bin-feature-plan.md +240 -0
  72. package/docs/plans/trash-bin-minimal-plan.md +102 -0
  73. package/docs/reference/bun-single-executable.md +584 -0
  74. package/docs/reference/cc-output-styles.md +99 -0
  75. package/docs/reference/chroma-mcp-project-memory-example.md +80 -0
  76. package/docs/reference/chroma-mcp-team-example.md +92 -0
  77. package/docs/reference/claude-code/cc-hooks.md +787 -0
  78. package/docs/reference/claude-code/cc-status-line.md +202 -0
  79. package/docs/reference/claude-code/hook-configuration.md +173 -0
  80. package/docs/reference/claude-code/hook-responses.md +127 -0
  81. package/docs/reference/claude-code/hooks.md +175 -0
  82. package/docs/reference/claude-code/mcp-configuration.md +133 -0
  83. package/docs/reference/claude-code/session-start-hook.md +82 -0
  84. package/docs/reference/load-context-format-example.md +33 -0
  85. package/docs/reference/mcp-sdk/mcp-typescript-sdk-readme.md +1323 -0
  86. package/docs/reference/mcp-sdk/server-implementation.md +286 -0
  87. package/docs/reference/mcp-sdk/stdio-transport.md +345 -0
  88. package/docs/todos/fix-response-format-tasks.md +43 -0
  89. package/docs/todos/implementation-todos.md +280 -0
  90. package/docs/todos/restructure-hook-output-tasks.md +103 -0
  91. package/docs/todos/session-start-hook-fix.md +26 -0
  92. package/docs/todos/terminal-effects-tasks.md +42 -0
  93. package/docs/todos/trash-bin-implementation-todos.md +348 -0
  94. package/docs/todos/trash-bin-minimal-todos.md +27 -0
  95. package/package.json +56 -6
  96. package/claude-mem +0 -0
@@ -0,0 +1,787 @@
1
+ # Hooks reference
2
+
3
+ > This page provides reference documentation for implementing hooks in Claude Code.
4
+
5
+ <Tip>
6
+ For a quickstart guide with examples, see [Get started with Claude Code hooks](/en/docs/claude-code/hooks-guide).
7
+ </Tip>
8
+
9
+ ## Configuration
10
+
11
+ Claude Code hooks are configured in your [settings files](/en/docs/claude-code/settings):
12
+
13
+ * `~/.claude/settings.json` - User settings
14
+ * `.claude/settings.json` - Project settings
15
+ * `.claude/settings.local.json` - Local project settings (not committed)
16
+ * Enterprise managed policy settings
17
+
18
+ ### Structure
19
+
20
+ Hooks are organized by matchers, where each matcher can have multiple hooks:
21
+
22
+ ```json
23
+ {
24
+ "hooks": {
25
+ "EventName": [
26
+ {
27
+ "matcher": "ToolPattern",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "your-command-here"
32
+ }
33
+ ]
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ ```
39
+
40
+ * **matcher**: Pattern to match tool names, case-sensitive (only applicable for
41
+ `PreToolUse` and `PostToolUse`)
42
+ * Simple strings match exactly: `Write` matches only the Write tool
43
+ * Supports regex: `Edit|Write` or `Notebook.*`
44
+ * Use `*` to match all tools. You can also use empty string (`""`) or leave
45
+ `matcher` blank.
46
+ * **hooks**: Array of commands to execute when the pattern matches
47
+ * `type`: Currently only `"command"` is supported
48
+ * `command`: The bash command to execute (can use `$CLAUDE_PROJECT_DIR`
49
+ environment variable)
50
+ * `timeout`: (Optional) How long a command should run, in seconds, before
51
+ canceling that specific command.
52
+
53
+ For events like `UserPromptSubmit`, `Notification`, `Stop`, and `SubagentStop`
54
+ that don't use matchers, you can omit the matcher field:
55
+
56
+ ```json
57
+ {
58
+ "hooks": {
59
+ "UserPromptSubmit": [
60
+ {
61
+ "hooks": [
62
+ {
63
+ "type": "command",
64
+ "command": "/path/to/prompt-validator.py"
65
+ }
66
+ ]
67
+ }
68
+ ]
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### Project-Specific Hook Scripts
74
+
75
+ You can use the environment variable `CLAUDE_PROJECT_DIR` (only available when
76
+ Claude Code spawns the hook command) to reference scripts stored in your project,
77
+ ensuring they work regardless of Claude's current directory:
78
+
79
+ ```json
80
+ {
81
+ "hooks": {
82
+ "PostToolUse": [
83
+ {
84
+ "matcher": "Write|Edit",
85
+ "hooks": [
86
+ {
87
+ "type": "command",
88
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-style.sh"
89
+ }
90
+ ]
91
+ }
92
+ ]
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## Hook Events
98
+
99
+ ### PreToolUse
100
+
101
+ Runs after Claude creates tool parameters and before processing the tool call.
102
+
103
+ **Common matchers:**
104
+
105
+ * `Task` - Subagent tasks (see [subagents documentation](/en/docs/claude-code/sub-agents))
106
+ * `Bash` - Shell commands
107
+ * `Glob` - File pattern matching
108
+ * `Grep` - Content search
109
+ * `Read` - File reading
110
+ * `Edit`, `MultiEdit` - File editing
111
+ * `Write` - File writing
112
+ * `WebFetch`, `WebSearch` - Web operations
113
+
114
+ ### PostToolUse
115
+
116
+ Runs immediately after a tool completes successfully.
117
+
118
+ Recognizes the same matcher values as PreToolUse.
119
+
120
+ ### Notification
121
+
122
+ Runs when Claude Code sends notifications. Notifications are sent when:
123
+
124
+ 1. Claude needs your permission to use a tool. Example: "Claude needs your
125
+ permission to use Bash"
126
+ 2. The prompt input has been idle for at least 60 seconds. "Claude is waiting
127
+ for your input"
128
+
129
+ ### UserPromptSubmit
130
+
131
+ Runs when the user submits a prompt, before Claude processes it. This allows you
132
+ to add additional context based on the prompt/conversation, validate prompts, or
133
+ block certain types of prompts.
134
+
135
+ ### Stop
136
+
137
+ Runs when the main Claude Code agent has finished responding. Does not run if
138
+ the stoppage occurred due to a user interrupt.
139
+
140
+ ### SubagentStop
141
+
142
+ Runs when a Claude Code subagent (Task tool call) has finished responding.
143
+
144
+ ### SessionEnd
145
+
146
+ Runs when a Claude Code session ends. Useful for cleanup tasks, logging session
147
+ statistics, or saving session state.
148
+
149
+ The `reason` field in the hook input will be one of:
150
+
151
+ * `clear` - Session cleared with /clear command
152
+ * `logout` - User logged out
153
+ * `prompt_input_exit` - User exited while prompt input was visible
154
+ * `other` - Other exit reasons
155
+
156
+ ### PreCompact
157
+
158
+ Runs before Claude Code is about to run a compact operation.
159
+
160
+ **Matchers:**
161
+
162
+ * `manual` - Invoked from `/compact`
163
+ * `auto` - Invoked from auto-compact (due to full context window)
164
+
165
+ ### SessionStart
166
+
167
+ Runs when Claude Code starts a new session or resumes an existing session (which
168
+ currently does start a new session under the hood). Useful for loading in
169
+ development context like existing issues or recent changes to your codebase.
170
+
171
+ **Matchers:**
172
+
173
+ * `startup` - Invoked from startup
174
+ * `resume` - Invoked from `--resume`, `--continue`, or `/resume`
175
+ * `clear` - Invoked from `/clear`
176
+
177
+ ## Hook Input
178
+
179
+ Hooks receive JSON data via stdin containing session information and
180
+ event-specific data:
181
+
182
+ ```typescript
183
+ {
184
+ // Common fields
185
+ session_id: string
186
+ transcript_path: string // Path to conversation JSON
187
+ cwd: string // The current working directory when the hook is invoked
188
+
189
+ // Event-specific fields
190
+ hook_event_name: string
191
+ ...
192
+ }
193
+ ```
194
+
195
+ ### PreToolUse Input
196
+
197
+ The exact schema for `tool_input` depends on the tool.
198
+
199
+ ```json
200
+ {
201
+ "session_id": "abc123",
202
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
203
+ "cwd": "/Users/...",
204
+ "hook_event_name": "PreToolUse",
205
+ "tool_name": "Write",
206
+ "tool_input": {
207
+ "file_path": "/path/to/file.txt",
208
+ "content": "file content"
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### PostToolUse Input
214
+
215
+ The exact schema for `tool_input` and `tool_response` depends on the tool.
216
+
217
+ ```json
218
+ {
219
+ "session_id": "abc123",
220
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
221
+ "cwd": "/Users/...",
222
+ "hook_event_name": "PostToolUse",
223
+ "tool_name": "Write",
224
+ "tool_input": {
225
+ "file_path": "/path/to/file.txt",
226
+ "content": "file content"
227
+ },
228
+ "tool_response": {
229
+ "filePath": "/path/to/file.txt",
230
+ "success": true
231
+ }
232
+ }
233
+ ```
234
+
235
+ ### Notification Input
236
+
237
+ ```json
238
+ {
239
+ "session_id": "abc123",
240
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
241
+ "cwd": "/Users/...",
242
+ "hook_event_name": "Notification",
243
+ "message": "Task completed successfully"
244
+ }
245
+ ```
246
+
247
+ ### UserPromptSubmit Input
248
+
249
+ ```json
250
+ {
251
+ "session_id": "abc123",
252
+ "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
253
+ "cwd": "/Users/...",
254
+ "hook_event_name": "UserPromptSubmit",
255
+ "prompt": "Write a function to calculate the factorial of a number"
256
+ }
257
+ ```
258
+
259
+ ### Stop and SubagentStop Input
260
+
261
+ `stop_hook_active` is true when Claude Code is already continuing as a result of
262
+ a stop hook. Check this value or process the transcript to prevent Claude Code
263
+ from running indefinitely.
264
+
265
+ ```json
266
+ {
267
+ "session_id": "abc123",
268
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
269
+ "hook_event_name": "Stop",
270
+ "stop_hook_active": true
271
+ }
272
+ ```
273
+
274
+ ### PreCompact Input
275
+
276
+ For `manual`, `custom_instructions` comes from what the user passes into
277
+ `/compact`. For `auto`, `custom_instructions` is empty.
278
+
279
+ ```json
280
+ {
281
+ "session_id": "abc123",
282
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
283
+ "hook_event_name": "PreCompact",
284
+ "trigger": "manual",
285
+ "custom_instructions": ""
286
+ }
287
+ ```
288
+
289
+ ### SessionStart Input
290
+
291
+ ```json
292
+ {
293
+ "session_id": "abc123",
294
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
295
+ "hook_event_name": "SessionStart",
296
+ "source": "startup"
297
+ }
298
+ ```
299
+
300
+ ### SessionEnd Input
301
+
302
+ ```json
303
+ {
304
+ "session_id": "abc123",
305
+ "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
306
+ "cwd": "/Users/...",
307
+ "hook_event_name": "SessionEnd",
308
+ "reason": "exit"
309
+ }
310
+ ```
311
+
312
+ ## Hook Output
313
+
314
+ There are two ways for hooks to return output back to Claude Code. The output
315
+ communicates whether to block and any feedback that should be shown to Claude
316
+ and the user.
317
+
318
+ ### Simple: Exit Code
319
+
320
+ Hooks communicate status through exit codes, stdout, and stderr:
321
+
322
+ * **Exit code 0**: Success. `stdout` is shown to the user in transcript mode
323
+ (CTRL-R), except for `UserPromptSubmit` and `SessionStart`, where stdout is
324
+ added to the context.
325
+ * **Exit code 2**: Blocking error. `stderr` is fed back to Claude to process
326
+ automatically. See per-hook-event behavior below.
327
+ * **Other exit codes**: Non-blocking error. `stderr` is shown to the user and
328
+ execution continues.
329
+
330
+ <Warning>
331
+ Reminder: Claude Code does not see stdout if the exit code is 0, except for
332
+ the `UserPromptSubmit` hook where stdout is injected as context.
333
+ </Warning>
334
+
335
+ #### Exit Code 2 Behavior
336
+
337
+ | Hook Event | Behavior |
338
+ | ------------------ | ------------------------------------------------------------------ |
339
+ | `PreToolUse` | Blocks the tool call, shows stderr to Claude |
340
+ | `PostToolUse` | Shows stderr to Claude (tool already ran) |
341
+ | `Notification` | N/A, shows stderr to user only |
342
+ | `UserPromptSubmit` | Blocks prompt processing, erases prompt, shows stderr to user only |
343
+ | `Stop` | Blocks stoppage, shows stderr to Claude |
344
+ | `SubagentStop` | Blocks stoppage, shows stderr to Claude subagent |
345
+ | `PreCompact` | N/A, shows stderr to user only |
346
+ | `SessionStart` | N/A, shows stderr to user only |
347
+ | `SessionEnd` | N/A, shows stderr to user only |
348
+
349
+ ### Advanced: JSON Output
350
+
351
+ Hooks can return structured JSON in `stdout` for more sophisticated control:
352
+
353
+ #### Common JSON Fields
354
+
355
+ All hook types can include these optional fields:
356
+
357
+ ```json
358
+ {
359
+ "continue": true, // Whether Claude should continue after hook execution (default: true)
360
+ "stopReason": "string", // Message shown when continue is false
361
+
362
+ "suppressOutput": true, // Hide stdout from transcript mode (default: false)
363
+ "systemMessage": "string" // Optional warning message shown to the user
364
+ }
365
+ ```
366
+
367
+ If `continue` is false, Claude stops processing after the hooks run.
368
+
369
+ * For `PreToolUse`, this is different from `"permissionDecision": "deny"`, which
370
+ only blocks a specific tool call and provides automatic feedback to Claude.
371
+ * For `PostToolUse`, this is different from `"decision": "block"`, which
372
+ provides automated feedback to Claude.
373
+ * For `UserPromptSubmit`, this prevents the prompt from being processed.
374
+ * For `Stop` and `SubagentStop`, this takes precedence over any
375
+ `"decision": "block"` output.
376
+ * In all cases, `"continue" = false` takes precedence over any
377
+ `"decision": "block"` output.
378
+
379
+ `stopReason` accompanies `continue` with a reason shown to the user, not shown
380
+ to Claude.
381
+
382
+ #### `PreToolUse` Decision Control
383
+
384
+ `PreToolUse` hooks can control whether a tool call proceeds.
385
+
386
+ * `"allow"` bypasses the permission system. `permissionDecisionReason` is shown
387
+ to the user but not to Claude.
388
+ * `"deny"` prevents the tool call from executing. `permissionDecisionReason` is
389
+ shown to Claude.
390
+ * `"ask"` asks the user to confirm the tool call in the UI.
391
+ `permissionDecisionReason` is shown to the user but not to Claude.
392
+
393
+ ```json
394
+ {
395
+ "hookSpecificOutput": {
396
+ "hookEventName": "PreToolUse",
397
+ "permissionDecision": "allow" | "deny" | "ask",
398
+ "permissionDecisionReason": "My reason here"
399
+ }
400
+ }
401
+ ```
402
+
403
+ <Note>
404
+ The `decision` and `reason` fields are deprecated for PreToolUse hooks.
405
+ Use `hookSpecificOutput.permissionDecision` and
406
+ `hookSpecificOutput.permissionDecisionReason` instead. The deprecated fields
407
+ `"approve"` and `"block"` map to `"allow"` and `"deny"` respectively.
408
+ </Note>
409
+
410
+ #### `PostToolUse` Decision Control
411
+
412
+ `PostToolUse` hooks can provide feedback to Claude after tool execution.
413
+
414
+ * `"block"` automatically prompts Claude with `reason`.
415
+ * `undefined` does nothing. `reason` is ignored.
416
+ * `"hookSpecificOutput.additionalContext"` adds context for Claude to consider.
417
+
418
+ ```json
419
+ {
420
+ "decision": "block" | undefined,
421
+ "reason": "Explanation for decision",
422
+ "hookSpecificOutput": {
423
+ "hookEventName": "PostToolUse",
424
+ "additionalContext": "Additional information for Claude"
425
+ }
426
+ }
427
+ ```
428
+
429
+ #### `UserPromptSubmit` Decision Control
430
+
431
+ `UserPromptSubmit` hooks can control whether a user prompt is processed.
432
+
433
+ * `"block"` prevents the prompt from being processed. The submitted prompt is
434
+ erased from context. `"reason"` is shown to the user but not added to context.
435
+ * `undefined` allows the prompt to proceed normally. `"reason"` is ignored.
436
+ * `"hookSpecificOutput.additionalContext"` adds the string to the context if not
437
+ blocked.
438
+
439
+ ```json
440
+ {
441
+ "decision": "block" | undefined,
442
+ "reason": "Explanation for decision",
443
+ "hookSpecificOutput": {
444
+ "hookEventName": "UserPromptSubmit",
445
+ "additionalContext": "My additional context here"
446
+ }
447
+ }
448
+ ```
449
+
450
+ #### `Stop`/`SubagentStop` Decision Control
451
+
452
+ `Stop` and `SubagentStop` hooks can control whether Claude must continue.
453
+
454
+ * `"block"` prevents Claude from stopping. You must populate `reason` for Claude
455
+ to know how to proceed.
456
+ * `undefined` allows Claude to stop. `reason` is ignored.
457
+
458
+ ```json
459
+ {
460
+ "decision": "block" | undefined,
461
+ "reason": "Must be provided when Claude is blocked from stopping"
462
+ }
463
+ ```
464
+
465
+ #### `SessionStart` Decision Control
466
+
467
+ `SessionStart` hooks allow you to load in context at the start of a session.
468
+
469
+ * `"hookSpecificOutput.additionalContext"` adds the string to the context.
470
+ * Multiple hooks' `additionalContext` values are concatenated.
471
+
472
+ ```json
473
+ {
474
+ "hookSpecificOutput": {
475
+ "hookEventName": "SessionStart",
476
+ "additionalContext": "My additional context here"
477
+ }
478
+ }
479
+ ```
480
+
481
+ #### `SessionEnd` Decision Control
482
+
483
+ `SessionEnd` hooks run when a session ends. They cannot block session termination
484
+ but can perform cleanup tasks.
485
+
486
+ #### Exit Code Example: Bash Command Validation
487
+
488
+ ```python
489
+ #!/usr/bin/env python3
490
+ import json
491
+ import re
492
+ import sys
493
+
494
+ # Define validation rules as a list of (regex pattern, message) tuples
495
+ VALIDATION_RULES = [
496
+ (
497
+ r"\bgrep\b(?!.*\|)",
498
+ "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
499
+ ),
500
+ (
501
+ r"\bfind\s+\S+\s+-name\b",
502
+ "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",
503
+ ),
504
+ ]
505
+
506
+
507
+ def validate_command(command: str) -> list[str]:
508
+ issues = []
509
+ for pattern, message in VALIDATION_RULES:
510
+ if re.search(pattern, command):
511
+ issues.append(message)
512
+ return issues
513
+
514
+
515
+ try:
516
+ input_data = json.load(sys.stdin)
517
+ except json.JSONDecodeError as e:
518
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
519
+ sys.exit(1)
520
+
521
+ tool_name = input_data.get("tool_name", "")
522
+ tool_input = input_data.get("tool_input", {})
523
+ command = tool_input.get("command", "")
524
+
525
+ if tool_name != "Bash" or not command:
526
+ sys.exit(1)
527
+
528
+ # Validate the command
529
+ issues = validate_command(command)
530
+
531
+ if issues:
532
+ for message in issues:
533
+ print(f"• {message}", file=sys.stderr)
534
+ # Exit code 2 blocks tool call and shows stderr to Claude
535
+ sys.exit(2)
536
+ ```
537
+
538
+ #### JSON Output Example: UserPromptSubmit to Add Context and Validation
539
+
540
+ <Note>
541
+ For `UserPromptSubmit` hooks, you can inject context using either method:
542
+
543
+ * Exit code 0 with stdout: Claude sees the context (special case for `UserPromptSubmit`)
544
+ * JSON output: Provides more control over the behavior
545
+ </Note>
546
+
547
+ ```python
548
+ #!/usr/bin/env python3
549
+ import json
550
+ import sys
551
+ import re
552
+ import datetime
553
+
554
+ # Load input from stdin
555
+ try:
556
+ input_data = json.load(sys.stdin)
557
+ except json.JSONDecodeError as e:
558
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
559
+ sys.exit(1)
560
+
561
+ prompt = input_data.get("prompt", "")
562
+
563
+ # Check for sensitive patterns
564
+ sensitive_patterns = [
565
+ (r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),
566
+ ]
567
+
568
+ for pattern, message in sensitive_patterns:
569
+ if re.search(pattern, prompt):
570
+ # Use JSON output to block with a specific reason
571
+ output = {
572
+ "decision": "block",
573
+ "reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
574
+ }
575
+ print(json.dumps(output))
576
+ sys.exit(0)
577
+
578
+ # Add current time to context
579
+ context = f"Current time: {datetime.datetime.now()}"
580
+ print(context)
581
+
582
+ """
583
+ The following is also equivalent:
584
+ print(json.dumps({
585
+ "hookSpecificOutput": {
586
+ "hookEventName": "UserPromptSubmit",
587
+ "additionalContext": context,
588
+ },
589
+ }))
590
+ """
591
+
592
+ # Allow the prompt to proceed with the additional context
593
+ sys.exit(0)
594
+ ```
595
+
596
+ #### JSON Output Example: PreToolUse with Approval
597
+
598
+ ```python
599
+ #!/usr/bin/env python3
600
+ import json
601
+ import sys
602
+
603
+ # Load input from stdin
604
+ try:
605
+ input_data = json.load(sys.stdin)
606
+ except json.JSONDecodeError as e:
607
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
608
+ sys.exit(1)
609
+
610
+ tool_name = input_data.get("tool_name", "")
611
+ tool_input = input_data.get("tool_input", {})
612
+
613
+ # Example: Auto-approve file reads for documentation files
614
+ if tool_name == "Read":
615
+ file_path = tool_input.get("file_path", "")
616
+ if file_path.endswith((".md", ".mdx", ".txt", ".json")):
617
+ # Use JSON output to auto-approve the tool call
618
+ output = {
619
+ "decision": "approve",
620
+ "reason": "Documentation file auto-approved",
621
+ "suppressOutput": True # Don't show in transcript mode
622
+ }
623
+ print(json.dumps(output))
624
+ sys.exit(0)
625
+
626
+ # For other cases, let the normal permission flow proceed
627
+ sys.exit(0)
628
+ ```
629
+
630
+ ## Working with MCP Tools
631
+
632
+ Claude Code hooks work seamlessly with
633
+ [Model Context Protocol (MCP) tools](/en/docs/claude-code/mcp). When MCP servers
634
+ provide tools, they appear with a special naming pattern that you can match in
635
+ your hooks.
636
+
637
+ ### MCP Tool Naming
638
+
639
+ MCP tools follow the pattern `mcp__<server>__<tool>`, for example:
640
+
641
+ * `mcp__memory__create_entities` - Memory server's create entities tool
642
+ * `mcp__filesystem__read_file` - Filesystem server's read file tool
643
+ * `mcp__github__search_repositories` - GitHub server's search tool
644
+
645
+ ### Configuring Hooks for MCP Tools
646
+
647
+ You can target specific MCP tools or entire MCP servers:
648
+
649
+ ```json
650
+ {
651
+ "hooks": {
652
+ "PreToolUse": [
653
+ {
654
+ "matcher": "mcp__memory__.*",
655
+ "hooks": [
656
+ {
657
+ "type": "command",
658
+ "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
659
+ }
660
+ ]
661
+ },
662
+ {
663
+ "matcher": "mcp__.*__write.*",
664
+ "hooks": [
665
+ {
666
+ "type": "command",
667
+ "command": "/home/user/scripts/validate-mcp-write.py"
668
+ }
669
+ ]
670
+ }
671
+ ]
672
+ }
673
+ }
674
+ ```
675
+
676
+ ## Examples
677
+
678
+ <Tip>
679
+ For practical examples including code formatting, notifications, and file protection, see [More Examples](/en/docs/claude-code/hooks-guide#more-examples) in the get started guide.
680
+ </Tip>
681
+
682
+ ## Security Considerations
683
+
684
+ ### Disclaimer
685
+
686
+ **USE AT YOUR OWN RISK**: Claude Code hooks execute arbitrary shell commands on
687
+ your system automatically. By using hooks, you acknowledge that:
688
+
689
+ * You are solely responsible for the commands you configure
690
+ * Hooks can modify, delete, or access any files your user account can access
691
+ * Malicious or poorly written hooks can cause data loss or system damage
692
+ * Anthropic provides no warranty and assumes no liability for any damages
693
+ resulting from hook usage
694
+ * You should thoroughly test hooks in a safe environment before production use
695
+
696
+ Always review and understand any hook commands before adding them to your
697
+ configuration.
698
+
699
+ ### Security Best Practices
700
+
701
+ Here are some key practices for writing more secure hooks:
702
+
703
+ 1. **Validate and sanitize inputs** - Never trust input data blindly
704
+ 2. **Always quote shell variables** - Use `"$VAR"` not `$VAR`
705
+ 3. **Block path traversal** - Check for `..` in file paths
706
+ 4. **Use absolute paths** - Specify full paths for scripts (use
707
+ `$CLAUDE_PROJECT_DIR` for the project path)
708
+ 5. **Skip sensitive files** - Avoid `.env`, `.git/`, keys, etc.
709
+
710
+ ### Configuration Safety
711
+
712
+ Direct edits to hooks in settings files don't take effect immediately. Claude
713
+ Code:
714
+
715
+ 1. Captures a snapshot of hooks at startup
716
+ 2. Uses this snapshot throughout the session
717
+ 3. Warns if hooks are modified externally
718
+ 4. Requires review in `/hooks` menu for changes to apply
719
+
720
+ This prevents malicious hook modifications from affecting your current session.
721
+
722
+ ## Hook Execution Details
723
+
724
+ * **Timeout**: 60-second execution limit by default, configurable per command.
725
+ * A timeout for an individual command does not affect the other commands.
726
+ * **Parallelization**: All matching hooks run in parallel
727
+ * **Deduplication**: Multiple identical hook commands are deduplicated automatically
728
+ * **Environment**: Runs in current directory with Claude Code's environment
729
+ * The `CLAUDE_PROJECT_DIR` environment variable is available and contains the
730
+ absolute path to the project root directory (where Claude Code was started)
731
+ * **Input**: JSON via stdin
732
+ * **Output**:
733
+ * PreToolUse/PostToolUse/Stop/SubagentStop: Progress shown in transcript (Ctrl-R)
734
+ * Notification/SessionEnd: Logged to debug only (`--debug`)
735
+ * UserPromptSubmit/SessionStart: stdout added as context for Claude
736
+
737
+ ## Debugging
738
+
739
+ ### Basic Troubleshooting
740
+
741
+ If your hooks aren't working:
742
+
743
+ 1. **Check configuration** - Run `/hooks` to see if your hook is registered
744
+ 2. **Verify syntax** - Ensure your JSON settings are valid
745
+ 3. **Test commands** - Run hook commands manually first
746
+ 4. **Check permissions** - Make sure scripts are executable
747
+ 5. **Review logs** - Use `claude --debug` to see hook execution details
748
+
749
+ Common issues:
750
+
751
+ * **Quotes not escaped** - Use `\"` inside JSON strings
752
+ * **Wrong matcher** - Check tool names match exactly (case-sensitive)
753
+ * **Command not found** - Use full paths for scripts
754
+
755
+ ### Advanced Debugging
756
+
757
+ For complex hook issues:
758
+
759
+ 1. **Inspect hook execution** - Use `claude --debug` to see detailed hook
760
+ execution
761
+ 2. **Validate JSON schemas** - Test hook input/output with external tools
762
+ 3. **Check environment variables** - Verify Claude Code's environment is correct
763
+ 4. **Test edge cases** - Try hooks with unusual file paths or inputs
764
+ 5. **Monitor system resources** - Check for resource exhaustion during hook
765
+ execution
766
+ 6. **Use structured logging** - Implement logging in your hook scripts
767
+
768
+ ### Debug Output Example
769
+
770
+ Use `claude --debug` to see hook execution details:
771
+
772
+ ```
773
+ [DEBUG] Executing hooks for PostToolUse:Write
774
+ [DEBUG] Getting matching hook commands for PostToolUse with query: Write
775
+ [DEBUG] Found 1 hook matchers in settings
776
+ [DEBUG] Matched 1 hooks for query "Write"
777
+ [DEBUG] Found 1 hook commands to execute
778
+ [DEBUG] Executing hook command: <Your command> with timeout 60000ms
779
+ [DEBUG] Hook command completed with status 0: <Your stdout>
780
+ ```
781
+
782
+ Progress messages appear in transcript mode (Ctrl-R) showing:
783
+
784
+ * Which hook is running
785
+ * Command being executed
786
+ * Success/failure status
787
+ * Output or error messages