claude_hooks 1.0.2 → 1.1.0
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.
- checksums.yaml +4 -4
- data/.claude/auto-fix.md +7 -0
- data/CHANGELOG.md +50 -0
- data/README.md +7 -4
- data/docs/1.1.0_UPGRADE_GUIDE.md +269 -0
- data/docs/API/COMMON.md +1 -0
- data/docs/API/NOTIFICATION.md +1 -0
- data/docs/API/PERMISSION_REQUEST.md +196 -0
- data/docs/API/POST_TOOL_USE.md +1 -0
- data/docs/API/PRE_TOOL_USE.md +4 -0
- data/docs/external/claude-hooks-reference.md +1294 -18
- data/lib/claude_hooks/base.rb +5 -1
- data/lib/claude_hooks/notification.rb +5 -1
- data/lib/claude_hooks/output/base.rb +2 -0
- data/lib/claude_hooks/output/permission_request.rb +95 -0
- data/lib/claude_hooks/output/pre_tool_use.rb +18 -0
- data/lib/claude_hooks/permission_request.rb +56 -0
- data/lib/claude_hooks/post_tool_use.rb +5 -1
- data/lib/claude_hooks/pre_tool_use.rb +16 -1
- data/lib/claude_hooks/version.rb +1 -1
- data/lib/claude_hooks.rb +2 -0
- metadata +6 -1
|
@@ -1,34 +1,1310 @@
|
|
|
1
|
-
|
|
1
|
+
[Skip to main content](https://code.claude.com/docs/en/hooks#content-area)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[Claude Code Docs home page](https://code.claude.com/docs)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
English
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Search...
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-

|
|
11
|
+
Ctrl K
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
Search...
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
Navigation
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
Reference
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
Hooks reference
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
[Getting started](https://code.claude.com/docs/en/overview) [Build with Claude Code](https://code.claude.com/docs/en/sub-agents) [Deployment](https://code.claude.com/docs/en/third-party-integrations) [Administration](https://code.claude.com/docs/en/setup) [Configuration](https://code.claude.com/docs/en/settings) [Reference](https://code.claude.com/docs/en/cli-reference) [Resources](https://code.claude.com/docs/en/legal-and-compliance)
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
On this page
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
- [Configuration](https://code.claude.com/docs/en/hooks#configuration)
|
|
26
|
+
- [Structure](https://code.claude.com/docs/en/hooks#structure)
|
|
27
|
+
- [Project-Specific Hook Scripts](https://code.claude.com/docs/en/hooks#project-specific-hook-scripts)
|
|
28
|
+
- [Plugin hooks](https://code.claude.com/docs/en/hooks#plugin-hooks)
|
|
29
|
+
- [Prompt-Based Hooks](https://code.claude.com/docs/en/hooks#prompt-based-hooks)
|
|
30
|
+
- [How prompt-based hooks work](https://code.claude.com/docs/en/hooks#how-prompt-based-hooks-work)
|
|
31
|
+
- [Configuration](https://code.claude.com/docs/en/hooks#configuration-2)
|
|
32
|
+
- [Response schema](https://code.claude.com/docs/en/hooks#response-schema)
|
|
33
|
+
- [Supported hook events](https://code.claude.com/docs/en/hooks#supported-hook-events)
|
|
34
|
+
- [Example: Intelligent Stop hook](https://code.claude.com/docs/en/hooks#example%3A-intelligent-stop-hook)
|
|
35
|
+
- [Example: SubagentStop with custom logic](https://code.claude.com/docs/en/hooks#example%3A-subagentstop-with-custom-logic)
|
|
36
|
+
- [Comparison with bash command hooks](https://code.claude.com/docs/en/hooks#comparison-with-bash-command-hooks)
|
|
37
|
+
- [Best practices](https://code.claude.com/docs/en/hooks#best-practices)
|
|
38
|
+
- [Hook Events](https://code.claude.com/docs/en/hooks#hook-events)
|
|
39
|
+
- [PreToolUse](https://code.claude.com/docs/en/hooks#pretooluse)
|
|
40
|
+
- [PermissionRequest](https://code.claude.com/docs/en/hooks#permissionrequest)
|
|
41
|
+
- [PostToolUse](https://code.claude.com/docs/en/hooks#posttooluse)
|
|
42
|
+
- [Notification](https://code.claude.com/docs/en/hooks#notification)
|
|
43
|
+
- [UserPromptSubmit](https://code.claude.com/docs/en/hooks#userpromptsubmit)
|
|
44
|
+
- [Stop](https://code.claude.com/docs/en/hooks#stop)
|
|
45
|
+
- [SubagentStop](https://code.claude.com/docs/en/hooks#subagentstop)
|
|
46
|
+
- [PreCompact](https://code.claude.com/docs/en/hooks#precompact)
|
|
47
|
+
- [SessionStart](https://code.claude.com/docs/en/hooks#sessionstart)
|
|
48
|
+
- [Persisting environment variables](https://code.claude.com/docs/en/hooks#persisting-environment-variables)
|
|
49
|
+
- [SessionEnd](https://code.claude.com/docs/en/hooks#sessionend)
|
|
50
|
+
- [Hook Input](https://code.claude.com/docs/en/hooks#hook-input)
|
|
51
|
+
- [PreToolUse Input](https://code.claude.com/docs/en/hooks#pretooluse-input)
|
|
52
|
+
- [PostToolUse Input](https://code.claude.com/docs/en/hooks#posttooluse-input)
|
|
53
|
+
- [Notification Input](https://code.claude.com/docs/en/hooks#notification-input)
|
|
54
|
+
- [UserPromptSubmit Input](https://code.claude.com/docs/en/hooks#userpromptsubmit-input)
|
|
55
|
+
- [Stop and SubagentStop Input](https://code.claude.com/docs/en/hooks#stop-and-subagentstop-input)
|
|
56
|
+
- [PreCompact Input](https://code.claude.com/docs/en/hooks#precompact-input)
|
|
57
|
+
- [SessionStart Input](https://code.claude.com/docs/en/hooks#sessionstart-input)
|
|
58
|
+
- [SessionEnd Input](https://code.claude.com/docs/en/hooks#sessionend-input)
|
|
59
|
+
- [Hook Output](https://code.claude.com/docs/en/hooks#hook-output)
|
|
60
|
+
- [Simple: Exit Code](https://code.claude.com/docs/en/hooks#simple%3A-exit-code)
|
|
61
|
+
- [Exit Code 2 Behavior](https://code.claude.com/docs/en/hooks#exit-code-2-behavior)
|
|
62
|
+
- [Advanced: JSON Output](https://code.claude.com/docs/en/hooks#advanced%3A-json-output)
|
|
63
|
+
- [Common JSON Fields](https://code.claude.com/docs/en/hooks#common-json-fields)
|
|
64
|
+
- [PreToolUse Decision Control](https://code.claude.com/docs/en/hooks#pretooluse-decision-control)
|
|
65
|
+
- [PermissionRequest Decision Control](https://code.claude.com/docs/en/hooks#permissionrequest-decision-control)
|
|
66
|
+
- [PostToolUse Decision Control](https://code.claude.com/docs/en/hooks#posttooluse-decision-control)
|
|
67
|
+
- [UserPromptSubmit Decision Control](https://code.claude.com/docs/en/hooks#userpromptsubmit-decision-control)
|
|
68
|
+
- [Stop/SubagentStop Decision Control](https://code.claude.com/docs/en/hooks#stop%2Fsubagentstop-decision-control)
|
|
69
|
+
- [SessionStart Decision Control](https://code.claude.com/docs/en/hooks#sessionstart-decision-control)
|
|
70
|
+
- [SessionEnd Decision Control](https://code.claude.com/docs/en/hooks#sessionend-decision-control)
|
|
71
|
+
- [Exit Code Example: Bash Command Validation](https://code.claude.com/docs/en/hooks#exit-code-example%3A-bash-command-validation)
|
|
72
|
+
- [JSON Output Example: UserPromptSubmit to Add Context and Validation](https://code.claude.com/docs/en/hooks#json-output-example%3A-userpromptsubmit-to-add-context-and-validation)
|
|
73
|
+
- [JSON Output Example: PreToolUse with Approval](https://code.claude.com/docs/en/hooks#json-output-example%3A-pretooluse-with-approval)
|
|
74
|
+
- [Working with MCP Tools](https://code.claude.com/docs/en/hooks#working-with-mcp-tools)
|
|
75
|
+
- [MCP Tool Naming](https://code.claude.com/docs/en/hooks#mcp-tool-naming)
|
|
76
|
+
- [Configuring Hooks for MCP Tools](https://code.claude.com/docs/en/hooks#configuring-hooks-for-mcp-tools)
|
|
77
|
+
- [Examples](https://code.claude.com/docs/en/hooks#examples)
|
|
78
|
+
- [Security Considerations](https://code.claude.com/docs/en/hooks#security-considerations)
|
|
79
|
+
- [Disclaimer](https://code.claude.com/docs/en/hooks#disclaimer)
|
|
80
|
+
- [Security Best Practices](https://code.claude.com/docs/en/hooks#security-best-practices)
|
|
81
|
+
- [Configuration Safety](https://code.claude.com/docs/en/hooks#configuration-safety)
|
|
82
|
+
- [Hook Execution Details](https://code.claude.com/docs/en/hooks#hook-execution-details)
|
|
83
|
+
- [Debugging](https://code.claude.com/docs/en/hooks#debugging)
|
|
84
|
+
- [Basic Troubleshooting](https://code.claude.com/docs/en/hooks#basic-troubleshooting)
|
|
85
|
+
- [Advanced Debugging](https://code.claude.com/docs/en/hooks#advanced-debugging)
|
|
86
|
+
- [Debug Output Example](https://code.claude.com/docs/en/hooks#debug-output-example)
|
|
27
87
|
|
|
28
|
-
|
|
88
|
+
For a quickstart guide with examples, see [Get started with Claude Code hooks](https://code.claude.com/docs/en/hooks-guide).
|
|
29
89
|
|
|
30
|
-
|
|
90
|
+
## [](https://code.claude.com/docs/en/hooks\#configuration) Configuration
|
|
31
91
|
|
|
32
|
-
|
|
92
|
+
Claude Code hooks are configured in your [settings files](https://code.claude.com/docs/en/settings):
|
|
33
93
|
|
|
34
|
-
|
|
94
|
+
- `~/.claude/settings.json` \- User settings
|
|
95
|
+
- `.claude/settings.json` \- Project settings
|
|
96
|
+
- `.claude/settings.local.json` \- Local project settings (not committed)
|
|
97
|
+
- Enterprise managed policy settings
|
|
98
|
+
|
|
99
|
+
Enterprise administrators can use `allowManagedHooksOnly` to block user, project, and plugin hooks. See [Hook configuration](https://code.claude.com/docs/en/settings#hook-configuration).
|
|
100
|
+
|
|
101
|
+
### [](https://code.claude.com/docs/en/hooks\#structure) Structure
|
|
102
|
+
|
|
103
|
+
Hooks are organized by matchers, where each matcher can have multiple hooks:
|
|
104
|
+
|
|
105
|
+
Copy
|
|
106
|
+
|
|
107
|
+
Ask AI
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
{
|
|
111
|
+
"hooks": {
|
|
112
|
+
"EventName": [\
|
|
113
|
+
{\
|
|
114
|
+
"matcher": "ToolPattern",\
|
|
115
|
+
"hooks": [\
|
|
116
|
+
{\
|
|
117
|
+
"type": "command",\
|
|
118
|
+
"command": "your-command-here"\
|
|
119
|
+
}\
|
|
120
|
+
]\
|
|
121
|
+
}\
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- **matcher**: Pattern to match tool names, case-sensitive (only applicable for
|
|
128
|
+
`PreToolUse`, `PermissionRequest`, and `PostToolUse`)
|
|
129
|
+
|
|
130
|
+
- Simple strings match exactly: `Write` matches only the Write tool
|
|
131
|
+
- Supports regex: `Edit|Write` or `Notebook.*`
|
|
132
|
+
- Use `*` to match all tools. You can also use empty string (`""`) or leave
|
|
133
|
+
`matcher` blank.
|
|
134
|
+
- **hooks**: Array of hooks to execute when the pattern matches
|
|
135
|
+
|
|
136
|
+
- `type`: Hook execution type - `"command"` for bash commands or `"prompt"` for LLM-based evaluation
|
|
137
|
+
- `command`: (For `type: "command"`) The bash command to execute (can use `$CLAUDE_PROJECT_DIR` environment variable)
|
|
138
|
+
- `prompt`: (For `type: "prompt"`) The prompt to send to the LLM for evaluation
|
|
139
|
+
- `timeout`: (Optional) How long a hook should run, in seconds, before canceling that specific hook
|
|
140
|
+
|
|
141
|
+
For events like `UserPromptSubmit`, `Stop`, and `SubagentStop`
|
|
142
|
+
that don’t use matchers, you can omit the matcher field:
|
|
143
|
+
|
|
144
|
+
Copy
|
|
145
|
+
|
|
146
|
+
Ask AI
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
{
|
|
150
|
+
"hooks": {
|
|
151
|
+
"UserPromptSubmit": [\
|
|
152
|
+
{\
|
|
153
|
+
"hooks": [\
|
|
154
|
+
{\
|
|
155
|
+
"type": "command",\
|
|
156
|
+
"command": "/path/to/prompt-validator.py"\
|
|
157
|
+
}\
|
|
158
|
+
]\
|
|
159
|
+
}\
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### [](https://code.claude.com/docs/en/hooks\#project-specific-hook-scripts) Project-Specific Hook Scripts
|
|
166
|
+
|
|
167
|
+
You can use the environment variable `CLAUDE_PROJECT_DIR` (only available when
|
|
168
|
+
Claude Code spawns the hook command) to reference scripts stored in your project,
|
|
169
|
+
ensuring they work regardless of Claude’s current directory:
|
|
170
|
+
|
|
171
|
+
Copy
|
|
172
|
+
|
|
173
|
+
Ask AI
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
{
|
|
177
|
+
"hooks": {
|
|
178
|
+
"PostToolUse": [\
|
|
179
|
+
{\
|
|
180
|
+
"matcher": "Write|Edit",\
|
|
181
|
+
"hooks": [\
|
|
182
|
+
{\
|
|
183
|
+
"type": "command",\
|
|
184
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"\
|
|
185
|
+
}\
|
|
186
|
+
]\
|
|
187
|
+
}\
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### [](https://code.claude.com/docs/en/hooks\#plugin-hooks) Plugin hooks
|
|
194
|
+
|
|
195
|
+
[Plugins](https://code.claude.com/docs/en/plugins) can provide hooks that integrate seamlessly with your user and project hooks. Plugin hooks are automatically merged with your configuration when plugins are enabled.**How plugin hooks work**:
|
|
196
|
+
|
|
197
|
+
- Plugin hooks are defined in the plugin’s `hooks/hooks.json` file or in a file given by a custom path to the `hooks` field.
|
|
198
|
+
- When a plugin is enabled, its hooks are merged with user and project hooks
|
|
199
|
+
- Multiple hooks from different sources can respond to the same event
|
|
200
|
+
- Plugin hooks use the `${CLAUDE_PLUGIN_ROOT}` environment variable to reference plugin files
|
|
201
|
+
|
|
202
|
+
**Example plugin hook configuration**:
|
|
203
|
+
|
|
204
|
+
Copy
|
|
205
|
+
|
|
206
|
+
Ask AI
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
{
|
|
210
|
+
"description": "Automatic code formatting",
|
|
211
|
+
"hooks": {
|
|
212
|
+
"PostToolUse": [\
|
|
213
|
+
{\
|
|
214
|
+
"matcher": "Write|Edit",\
|
|
215
|
+
"hooks": [\
|
|
216
|
+
{\
|
|
217
|
+
"type": "command",\
|
|
218
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",\
|
|
219
|
+
"timeout": 30\
|
|
220
|
+
}\
|
|
221
|
+
]\
|
|
222
|
+
}\
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Plugin hooks use the same format as regular hooks with an optional `description` field to explain the hook’s purpose.
|
|
229
|
+
|
|
230
|
+
Plugin hooks run alongside your custom hooks. If multiple hooks match an event, they all execute in parallel.
|
|
231
|
+
|
|
232
|
+
**Environment variables for plugins**:
|
|
233
|
+
|
|
234
|
+
- `${CLAUDE_PLUGIN_ROOT}`: Absolute path to the plugin directory
|
|
235
|
+
- `${CLAUDE_PROJECT_DIR}`: Project root directory (same as for project hooks)
|
|
236
|
+
- All standard environment variables are available
|
|
237
|
+
|
|
238
|
+
See the [plugin components reference](https://code.claude.com/docs/en/plugins-reference#hooks) for details on creating plugin hooks.
|
|
239
|
+
|
|
240
|
+
## [](https://code.claude.com/docs/en/hooks\#prompt-based-hooks) Prompt-Based Hooks
|
|
241
|
+
|
|
242
|
+
In addition to bash command hooks (`type: "command"`), Claude Code supports prompt-based hooks (`type: "prompt"`) that use an LLM to evaluate whether to allow or block an action. Prompt-based hooks are currently only supported for `Stop` and `SubagentStop` hooks, where they enable intelligent, context-aware decisions.
|
|
243
|
+
|
|
244
|
+
### [](https://code.claude.com/docs/en/hooks\#how-prompt-based-hooks-work) How prompt-based hooks work
|
|
245
|
+
|
|
246
|
+
Instead of executing a bash command, prompt-based hooks:
|
|
247
|
+
|
|
248
|
+
1. Send the hook input and your prompt to a fast LLM (Haiku)
|
|
249
|
+
2. The LLM responds with structured JSON containing a decision
|
|
250
|
+
3. Claude Code processes the decision automatically
|
|
251
|
+
|
|
252
|
+
### [](https://code.claude.com/docs/en/hooks\#configuration-2) Configuration
|
|
253
|
+
|
|
254
|
+
Copy
|
|
255
|
+
|
|
256
|
+
Ask AI
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
{
|
|
260
|
+
"hooks": {
|
|
261
|
+
"Stop": [\
|
|
262
|
+
{\
|
|
263
|
+
"hooks": [\
|
|
264
|
+
{\
|
|
265
|
+
"type": "prompt",\
|
|
266
|
+
"prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."\
|
|
267
|
+
}\
|
|
268
|
+
]\
|
|
269
|
+
}\
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Fields:**
|
|
276
|
+
|
|
277
|
+
- `type`: Must be `"prompt"`
|
|
278
|
+
- `prompt`: The prompt text to send to the LLM
|
|
279
|
+
|
|
280
|
+
- Use `$ARGUMENTS` as a placeholder for the hook input JSON
|
|
281
|
+
- If `$ARGUMENTS` is not present, input JSON is appended to the prompt
|
|
282
|
+
- `timeout`: (Optional) Timeout in seconds (default: 30 seconds)
|
|
283
|
+
|
|
284
|
+
### [](https://code.claude.com/docs/en/hooks\#response-schema) Response schema
|
|
285
|
+
|
|
286
|
+
The LLM must respond with JSON containing:
|
|
287
|
+
|
|
288
|
+
Copy
|
|
289
|
+
|
|
290
|
+
Ask AI
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
{
|
|
294
|
+
"decision": "approve" | "block",
|
|
295
|
+
"reason": "Explanation for the decision",
|
|
296
|
+
"continue": false, // Optional: stops Claude entirely
|
|
297
|
+
"stopReason": "Message shown to user", // Optional: custom stop message
|
|
298
|
+
"systemMessage": "Warning or context" // Optional: shown to user
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Response fields:**
|
|
303
|
+
|
|
304
|
+
- `decision`: `"approve"` allows the action, `"block"` prevents it
|
|
305
|
+
- `reason`: Explanation shown to Claude when decision is `"block"`
|
|
306
|
+
- `continue`: (Optional) If `false`, stops Claude’s execution entirely
|
|
307
|
+
- `stopReason`: (Optional) Message shown when `continue` is false
|
|
308
|
+
- `systemMessage`: (Optional) Additional message shown to the user
|
|
309
|
+
|
|
310
|
+
### [](https://code.claude.com/docs/en/hooks\#supported-hook-events) Supported hook events
|
|
311
|
+
|
|
312
|
+
Prompt-based hooks work with any hook event, but are most useful for:
|
|
313
|
+
|
|
314
|
+
- **Stop**: Intelligently decide if Claude should continue working
|
|
315
|
+
- **SubagentStop**: Evaluate if a subagent has completed its task
|
|
316
|
+
- **UserPromptSubmit**: Validate user prompts with LLM assistance
|
|
317
|
+
- **PreToolUse**: Make context-aware permission decisions
|
|
318
|
+
- **PermissionRequest**: Intelligently allow or deny permission dialogs
|
|
319
|
+
|
|
320
|
+
### [](https://code.claude.com/docs/en/hooks\#example:-intelligent-stop-hook) Example: Intelligent Stop hook
|
|
321
|
+
|
|
322
|
+
Copy
|
|
323
|
+
|
|
324
|
+
Ask AI
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
{
|
|
328
|
+
"hooks": {
|
|
329
|
+
"Stop": [\
|
|
330
|
+
{\
|
|
331
|
+
"hooks": [\
|
|
332
|
+
{\
|
|
333
|
+
"type": "prompt",\
|
|
334
|
+
"prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"decision\": \"approve\" or \"block\", \"reason\": \"your explanation\"}",\
|
|
335
|
+
"timeout": 30\
|
|
336
|
+
}\
|
|
337
|
+
]\
|
|
338
|
+
}\
|
|
339
|
+
]
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### [](https://code.claude.com/docs/en/hooks\#example:-subagentstop-with-custom-logic) Example: SubagentStop with custom logic
|
|
345
|
+
|
|
346
|
+
Copy
|
|
347
|
+
|
|
348
|
+
Ask AI
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
{
|
|
352
|
+
"hooks": {
|
|
353
|
+
"SubagentStop": [\
|
|
354
|
+
{\
|
|
355
|
+
"hooks": [\
|
|
356
|
+
{\
|
|
357
|
+
"type": "prompt",\
|
|
358
|
+
"prompt": "Evaluate if this subagent should stop. Input: $ARGUMENTS\n\nCheck if:\n- The subagent completed its assigned task\n- Any errors occurred that need fixing\n- Additional context gathering is needed\n\nReturn: {\"decision\": \"approve\" or \"block\", \"reason\": \"explanation\"}"\
|
|
359
|
+
}\
|
|
360
|
+
]\
|
|
361
|
+
}\
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### [](https://code.claude.com/docs/en/hooks\#comparison-with-bash-command-hooks) Comparison with bash command hooks
|
|
368
|
+
|
|
369
|
+
| Feature | Bash Command Hooks | Prompt-Based Hooks |
|
|
370
|
+
| --- | --- | --- |
|
|
371
|
+
| **Execution** | Runs bash script | Queries LLM |
|
|
372
|
+
| **Decision logic** | You implement in code | LLM evaluates context |
|
|
373
|
+
| **Setup complexity** | Requires script file | Configure prompt |
|
|
374
|
+
| **Context awareness** | Limited to script logic | Natural language understanding |
|
|
375
|
+
| **Performance** | Fast (local execution) | Slower (API call) |
|
|
376
|
+
| **Use case** | Deterministic rules | Context-aware decisions |
|
|
377
|
+
|
|
378
|
+
### [](https://code.claude.com/docs/en/hooks\#best-practices) Best practices
|
|
379
|
+
|
|
380
|
+
- **Be specific in prompts**: Clearly state what you want the LLM to evaluate
|
|
381
|
+
- **Include decision criteria**: List the factors the LLM should consider
|
|
382
|
+
- **Test your prompts**: Verify the LLM makes correct decisions for your use cases
|
|
383
|
+
- **Set appropriate timeouts**: Default is 30 seconds, adjust if needed
|
|
384
|
+
- **Use for complex decisions**: Bash hooks are better for simple, deterministic rules
|
|
385
|
+
|
|
386
|
+
See the [plugin components reference](https://code.claude.com/docs/en/plugins-reference#hooks) for details on creating plugin hooks.
|
|
387
|
+
|
|
388
|
+
## [](https://code.claude.com/docs/en/hooks\#hook-events) Hook Events
|
|
389
|
+
|
|
390
|
+
### [](https://code.claude.com/docs/en/hooks\#pretooluse) PreToolUse
|
|
391
|
+
|
|
392
|
+
Runs after Claude creates tool parameters and before processing the tool call.**Common matchers:**
|
|
393
|
+
|
|
394
|
+
- `Task` \- Subagent tasks (see [subagents documentation](https://code.claude.com/docs/en/sub-agents))
|
|
395
|
+
- `Bash` \- Shell commands
|
|
396
|
+
- `Glob` \- File pattern matching
|
|
397
|
+
- `Grep` \- Content search
|
|
398
|
+
- `Read` \- File reading
|
|
399
|
+
- `Edit` \- File editing
|
|
400
|
+
- `Write` \- File writing
|
|
401
|
+
- `WebFetch`, `WebSearch` \- Web operations
|
|
402
|
+
|
|
403
|
+
Use [PreToolUse decision control](https://code.claude.com/docs/en/hooks#pretooluse-decision-control) to allow, deny, or ask for permission to use the tool.
|
|
404
|
+
|
|
405
|
+
### [](https://code.claude.com/docs/en/hooks\#permissionrequest) PermissionRequest
|
|
406
|
+
|
|
407
|
+
Runs when the user is shown a permission dialog.
|
|
408
|
+
Use [PermissionRequest decision control](https://code.claude.com/docs/en/hooks#permissionrequest-decision-control) to allow or deny on behalf of the user.Recognizes the same matcher values as PreToolUse.
|
|
409
|
+
|
|
410
|
+
### [](https://code.claude.com/docs/en/hooks\#posttooluse) PostToolUse
|
|
411
|
+
|
|
412
|
+
Runs immediately after a tool completes successfully.Recognizes the same matcher values as PreToolUse.
|
|
413
|
+
|
|
414
|
+
### [](https://code.claude.com/docs/en/hooks\#notification) Notification
|
|
415
|
+
|
|
416
|
+
Runs when Claude Code sends notifications. Supports matchers to filter by notification type.**Common matchers:**
|
|
417
|
+
|
|
418
|
+
- `permission_prompt` \- Permission requests from Claude Code
|
|
419
|
+
- `idle_prompt` \- When Claude is waiting for user input (after 60+ seconds of idle time)
|
|
420
|
+
- `auth_success` \- Authentication success notifications
|
|
421
|
+
- `elicitation_dialog` \- When Claude Code needs input for MCP tool elicitation
|
|
422
|
+
|
|
423
|
+
You can use matchers to run different hooks for different notification types, or omit the matcher to run hooks for all notifications.**Example: Different notifications for different types**
|
|
424
|
+
|
|
425
|
+
Copy
|
|
426
|
+
|
|
427
|
+
Ask AI
|
|
428
|
+
|
|
429
|
+
```
|
|
430
|
+
{
|
|
431
|
+
"hooks": {
|
|
432
|
+
"Notification": [\
|
|
433
|
+
{\
|
|
434
|
+
"matcher": "permission_prompt",\
|
|
435
|
+
"hooks": [\
|
|
436
|
+
{\
|
|
437
|
+
"type": "command",\
|
|
438
|
+
"command": "/path/to/permission-alert.sh"\
|
|
439
|
+
}\
|
|
440
|
+
]\
|
|
441
|
+
},\
|
|
442
|
+
{\
|
|
443
|
+
"matcher": "idle_prompt",\
|
|
444
|
+
"hooks": [\
|
|
445
|
+
{\
|
|
446
|
+
"type": "command",\
|
|
447
|
+
"command": "/path/to/idle-notification.sh"\
|
|
448
|
+
}\
|
|
449
|
+
]\
|
|
450
|
+
}\
|
|
451
|
+
]
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### [](https://code.claude.com/docs/en/hooks\#userpromptsubmit) UserPromptSubmit
|
|
457
|
+
|
|
458
|
+
Runs when the user submits a prompt, before Claude processes it. This allows you
|
|
459
|
+
to add additional context based on the prompt/conversation, validate prompts, or
|
|
460
|
+
block certain types of prompts.
|
|
461
|
+
|
|
462
|
+
### [](https://code.claude.com/docs/en/hooks\#stop) Stop
|
|
463
|
+
|
|
464
|
+
Runs when the main Claude Code agent has finished responding. Does not run if
|
|
465
|
+
the stoppage occurred due to a user interrupt.
|
|
466
|
+
|
|
467
|
+
### [](https://code.claude.com/docs/en/hooks\#subagentstop) SubagentStop
|
|
468
|
+
|
|
469
|
+
Runs when a Claude Code subagent (Task tool call) has finished responding.
|
|
470
|
+
|
|
471
|
+
### [](https://code.claude.com/docs/en/hooks\#precompact) PreCompact
|
|
472
|
+
|
|
473
|
+
Runs before Claude Code is about to run a compact operation.**Matchers:**
|
|
474
|
+
|
|
475
|
+
- `manual` \- Invoked from `/compact`
|
|
476
|
+
- `auto` \- Invoked from auto-compact (due to full context window)
|
|
477
|
+
|
|
478
|
+
### [](https://code.claude.com/docs/en/hooks\#sessionstart) SessionStart
|
|
479
|
+
|
|
480
|
+
Runs when Claude Code starts a new session or resumes an existing session (which
|
|
481
|
+
currently does start a new session under the hood). Useful for loading in
|
|
482
|
+
development context like existing issues or recent changes to your codebase, installing dependencies, or setting up environment variables.**Matchers:**
|
|
483
|
+
|
|
484
|
+
- `startup` \- Invoked from startup
|
|
485
|
+
- `resume` \- Invoked from `--resume`, `--continue`, or `/resume`
|
|
486
|
+
- `clear` \- Invoked from `/clear`
|
|
487
|
+
- `compact` \- Invoked from auto or manual compact.
|
|
488
|
+
|
|
489
|
+
#### [](https://code.claude.com/docs/en/hooks\#persisting-environment-variables) Persisting environment variables
|
|
490
|
+
|
|
491
|
+
SessionStart hooks have access to the `CLAUDE_ENV_FILE` environment variable, which provides a file path where you can persist environment variables for subsequent bash commands.**Example: Setting individual environment variables**
|
|
492
|
+
|
|
493
|
+
Copy
|
|
494
|
+
|
|
495
|
+
Ask AI
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
#!/bin/bash
|
|
499
|
+
|
|
500
|
+
if [ -n "$CLAUDE_ENV_FILE" ]; then
|
|
501
|
+
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
|
|
502
|
+
echo 'export API_KEY=your-api-key' >> "$CLAUDE_ENV_FILE"
|
|
503
|
+
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
exit 0
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**Example: Persisting all environment changes from the hook**When your setup modifies the environment (for example, `nvm use`), capture and persist all changes by diffing the environment:
|
|
510
|
+
|
|
511
|
+
Copy
|
|
512
|
+
|
|
513
|
+
Ask AI
|
|
514
|
+
|
|
515
|
+
```
|
|
516
|
+
#!/bin/bash
|
|
517
|
+
|
|
518
|
+
ENV_BEFORE=$(export -p | sort)
|
|
519
|
+
|
|
520
|
+
# Run your setup commands that modify the environment
|
|
521
|
+
source ~/.nvm/nvm.sh
|
|
522
|
+
nvm use 20
|
|
523
|
+
|
|
524
|
+
if [ -n "$CLAUDE_ENV_FILE" ]; then
|
|
525
|
+
ENV_AFTER=$(export -p | sort)
|
|
526
|
+
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
|
|
527
|
+
fi
|
|
528
|
+
|
|
529
|
+
exit 0
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Any variables written to this file will be available in all subsequent bash commands that Claude Code executes during the session.
|
|
533
|
+
|
|
534
|
+
`CLAUDE_ENV_FILE` is only available for SessionStart hooks. Other hook types do not have access to this variable.
|
|
535
|
+
|
|
536
|
+
### [](https://code.claude.com/docs/en/hooks\#sessionend) SessionEnd
|
|
537
|
+
|
|
538
|
+
Runs when a Claude Code session ends. Useful for cleanup tasks, logging session
|
|
539
|
+
statistics, or saving session state.The `reason` field in the hook input will be one of:
|
|
540
|
+
|
|
541
|
+
- `clear` \- Session cleared with /clear command
|
|
542
|
+
- `logout` \- User logged out
|
|
543
|
+
- `prompt_input_exit` \- User exited while prompt input was visible
|
|
544
|
+
- `other` \- Other exit reasons
|
|
545
|
+
|
|
546
|
+
## [](https://code.claude.com/docs/en/hooks\#hook-input) Hook Input
|
|
547
|
+
|
|
548
|
+
Hooks receive JSON data via stdin containing session information and
|
|
549
|
+
event-specific data:
|
|
550
|
+
|
|
551
|
+
Copy
|
|
552
|
+
|
|
553
|
+
Ask AI
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
{
|
|
557
|
+
// Common fields
|
|
558
|
+
session_id: string
|
|
559
|
+
transcript_path: string // Path to conversation JSON
|
|
560
|
+
cwd: string // The current working directory when the hook is invoked
|
|
561
|
+
permission_mode: string // Current permission mode: "default", "plan", "acceptEdits", "dontAsk", or "bypassPermissions"
|
|
562
|
+
|
|
563
|
+
// Event-specific fields
|
|
564
|
+
hook_event_name: string
|
|
565
|
+
...
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### [](https://code.claude.com/docs/en/hooks\#pretooluse-input) PreToolUse Input
|
|
570
|
+
|
|
571
|
+
The exact schema for `tool_input` depends on the tool.
|
|
572
|
+
|
|
573
|
+
Copy
|
|
574
|
+
|
|
575
|
+
Ask AI
|
|
576
|
+
|
|
577
|
+
```
|
|
578
|
+
{
|
|
579
|
+
"session_id": "abc123",
|
|
580
|
+
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
581
|
+
"cwd": "/Users/...",
|
|
582
|
+
"permission_mode": "default",
|
|
583
|
+
"hook_event_name": "PreToolUse",
|
|
584
|
+
"tool_name": "Write",
|
|
585
|
+
"tool_input": {
|
|
586
|
+
"file_path": "/path/to/file.txt",
|
|
587
|
+
"content": "file content"
|
|
588
|
+
},
|
|
589
|
+
"tool_use_id": "toolu_01ABC123..."
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### [](https://code.claude.com/docs/en/hooks\#posttooluse-input) PostToolUse Input
|
|
594
|
+
|
|
595
|
+
The exact schema for `tool_input` and `tool_response` depends on the tool.
|
|
596
|
+
|
|
597
|
+
Copy
|
|
598
|
+
|
|
599
|
+
Ask AI
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
{
|
|
603
|
+
"session_id": "abc123",
|
|
604
|
+
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
605
|
+
"cwd": "/Users/...",
|
|
606
|
+
"permission_mode": "default",
|
|
607
|
+
"hook_event_name": "PostToolUse",
|
|
608
|
+
"tool_name": "Write",
|
|
609
|
+
"tool_input": {
|
|
610
|
+
"file_path": "/path/to/file.txt",
|
|
611
|
+
"content": "file content"
|
|
612
|
+
},
|
|
613
|
+
"tool_response": {
|
|
614
|
+
"filePath": "/path/to/file.txt",
|
|
615
|
+
"success": true
|
|
616
|
+
},
|
|
617
|
+
"tool_use_id": "toolu_01ABC123..."
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### [](https://code.claude.com/docs/en/hooks\#notification-input) Notification Input
|
|
622
|
+
|
|
623
|
+
Copy
|
|
624
|
+
|
|
625
|
+
Ask AI
|
|
626
|
+
|
|
627
|
+
```
|
|
628
|
+
{
|
|
629
|
+
"session_id": "abc123",
|
|
630
|
+
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
631
|
+
"cwd": "/Users/...",
|
|
632
|
+
"permission_mode": "default",
|
|
633
|
+
"hook_event_name": "Notification",
|
|
634
|
+
"message": "Claude needs your permission to use Bash",
|
|
635
|
+
"notification_type": "permission_prompt"
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### [](https://code.claude.com/docs/en/hooks\#userpromptsubmit-input) UserPromptSubmit Input
|
|
640
|
+
|
|
641
|
+
Copy
|
|
642
|
+
|
|
643
|
+
Ask AI
|
|
644
|
+
|
|
645
|
+
```
|
|
646
|
+
{
|
|
647
|
+
"session_id": "abc123",
|
|
648
|
+
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
649
|
+
"cwd": "/Users/...",
|
|
650
|
+
"permission_mode": "default",
|
|
651
|
+
"hook_event_name": "UserPromptSubmit",
|
|
652
|
+
"prompt": "Write a function to calculate the factorial of a number"
|
|
653
|
+
}
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### [](https://code.claude.com/docs/en/hooks\#stop-and-subagentstop-input) Stop and SubagentStop Input
|
|
657
|
+
|
|
658
|
+
`stop_hook_active` is true when Claude Code is already continuing as a result of
|
|
659
|
+
a stop hook. Check this value or process the transcript to prevent Claude Code
|
|
660
|
+
from running indefinitely.
|
|
661
|
+
|
|
662
|
+
Copy
|
|
663
|
+
|
|
664
|
+
Ask AI
|
|
665
|
+
|
|
666
|
+
```
|
|
667
|
+
{
|
|
668
|
+
"session_id": "abc123",
|
|
669
|
+
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
670
|
+
"permission_mode": "default",
|
|
671
|
+
"hook_event_name": "Stop",
|
|
672
|
+
"stop_hook_active": true
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### [](https://code.claude.com/docs/en/hooks\#precompact-input) PreCompact Input
|
|
677
|
+
|
|
678
|
+
For `manual`, `custom_instructions` comes from what the user passes into
|
|
679
|
+
`/compact`. For `auto`, `custom_instructions` is empty.
|
|
680
|
+
|
|
681
|
+
Copy
|
|
682
|
+
|
|
683
|
+
Ask AI
|
|
684
|
+
|
|
685
|
+
```
|
|
686
|
+
{
|
|
687
|
+
"session_id": "abc123",
|
|
688
|
+
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
689
|
+
"permission_mode": "default",
|
|
690
|
+
"hook_event_name": "PreCompact",
|
|
691
|
+
"trigger": "manual",
|
|
692
|
+
"custom_instructions": ""
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### [](https://code.claude.com/docs/en/hooks\#sessionstart-input) SessionStart Input
|
|
697
|
+
|
|
698
|
+
Copy
|
|
699
|
+
|
|
700
|
+
Ask AI
|
|
701
|
+
|
|
702
|
+
```
|
|
703
|
+
{
|
|
704
|
+
"session_id": "abc123",
|
|
705
|
+
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
706
|
+
"permission_mode": "default",
|
|
707
|
+
"hook_event_name": "SessionStart",
|
|
708
|
+
"source": "startup"
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### [](https://code.claude.com/docs/en/hooks\#sessionend-input) SessionEnd Input
|
|
713
|
+
|
|
714
|
+
Copy
|
|
715
|
+
|
|
716
|
+
Ask AI
|
|
717
|
+
|
|
718
|
+
```
|
|
719
|
+
{
|
|
720
|
+
"session_id": "abc123",
|
|
721
|
+
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
|
722
|
+
"cwd": "/Users/...",
|
|
723
|
+
"permission_mode": "default",
|
|
724
|
+
"hook_event_name": "SessionEnd",
|
|
725
|
+
"reason": "exit"
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
## [](https://code.claude.com/docs/en/hooks\#hook-output) Hook Output
|
|
730
|
+
|
|
731
|
+
There are two mutually exclusive ways for hooks to return output back to Claude Code. The output
|
|
732
|
+
communicates whether to block and any feedback that should be shown to Claude
|
|
733
|
+
and the user.
|
|
734
|
+
|
|
735
|
+
### [](https://code.claude.com/docs/en/hooks\#simple:-exit-code) Simple: Exit Code
|
|
736
|
+
|
|
737
|
+
Hooks communicate status through exit codes, stdout, and stderr:
|
|
738
|
+
|
|
739
|
+
- **Exit code 0**: Success. `stdout` is shown to the user in verbose mode
|
|
740
|
+
(ctrl+o), except for `UserPromptSubmit` and `SessionStart`, where stdout is
|
|
741
|
+
added to the context. JSON output in `stdout` is parsed for structured control
|
|
742
|
+
(see [Advanced: JSON Output](https://code.claude.com/docs/en/hooks#advanced-json-output)).
|
|
743
|
+
- **Exit code 2**: Blocking error. Only `stderr` is used as the error message
|
|
744
|
+
and fed back to Claude. The format is `[command]: {stderr}`. JSON in `stdout`
|
|
745
|
+
is **not** processed for exit code 2. See per-hook-event behavior below.
|
|
746
|
+
- **Other exit codes**: Non-blocking error. `stderr` is shown to the user in verbose mode (ctrl+o) with
|
|
747
|
+
format `Failed with non-blocking status code: {stderr}`. If `stderr` is empty,
|
|
748
|
+
it shows `No stderr output`. Execution continues.
|
|
749
|
+
|
|
750
|
+
Reminder: Claude Code does not see stdout if the exit code is 0, except for
|
|
751
|
+
the `UserPromptSubmit` hook where stdout is injected as context.
|
|
752
|
+
|
|
753
|
+
#### [](https://code.claude.com/docs/en/hooks\#exit-code-2-behavior) Exit Code 2 Behavior
|
|
754
|
+
|
|
755
|
+
| Hook Event | Behavior |
|
|
756
|
+
| --- | --- |
|
|
757
|
+
| `PreToolUse` | Blocks the tool call, shows stderr to Claude |
|
|
758
|
+
| `PermissionRequest` | Denies the permission, shows stderr to Claude |
|
|
759
|
+
| `PostToolUse` | Shows stderr to Claude (tool already ran) |
|
|
760
|
+
| `Notification` | N/A, shows stderr to user only |
|
|
761
|
+
| `UserPromptSubmit` | Blocks prompt processing, erases prompt, shows stderr to user only |
|
|
762
|
+
| `Stop` | Blocks stoppage, shows stderr to Claude |
|
|
763
|
+
| `SubagentStop` | Blocks stoppage, shows stderr to Claude subagent |
|
|
764
|
+
| `PreCompact` | N/A, shows stderr to user only |
|
|
765
|
+
| `SessionStart` | N/A, shows stderr to user only |
|
|
766
|
+
| `SessionEnd` | N/A, shows stderr to user only |
|
|
767
|
+
|
|
768
|
+
### [](https://code.claude.com/docs/en/hooks\#advanced:-json-output) Advanced: JSON Output
|
|
769
|
+
|
|
770
|
+
Hooks can return structured JSON in `stdout` for more sophisticated control.
|
|
771
|
+
|
|
772
|
+
JSON output is only processed when the hook exits with code 0. If your hook
|
|
773
|
+
exits with code 2 (blocking error), `stderr` text is used directly—any JSON in `stdout`
|
|
774
|
+
is ignored. For other non-zero exit codes, only `stderr` is shown to the user in verbose mode (ctrl+o).
|
|
775
|
+
|
|
776
|
+
#### [](https://code.claude.com/docs/en/hooks\#common-json-fields) Common JSON Fields
|
|
777
|
+
|
|
778
|
+
All hook types can include these optional fields:
|
|
779
|
+
|
|
780
|
+
Copy
|
|
781
|
+
|
|
782
|
+
Ask AI
|
|
783
|
+
|
|
784
|
+
```
|
|
785
|
+
{
|
|
786
|
+
"continue": true, // Whether Claude should continue after hook execution (default: true)
|
|
787
|
+
"stopReason": "string", // Message shown when continue is false
|
|
788
|
+
|
|
789
|
+
"suppressOutput": true, // Hide stdout from transcript mode (default: false)
|
|
790
|
+
"systemMessage": "string" // Optional warning message shown to the user
|
|
791
|
+
}
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
If `continue` is false, Claude stops processing after the hooks run.
|
|
795
|
+
|
|
796
|
+
- For `PreToolUse`, this is different from `"permissionDecision": "deny"`, which
|
|
797
|
+
only blocks a specific tool call and provides automatic feedback to Claude.
|
|
798
|
+
- For `PostToolUse`, this is different from `"decision": "block"`, which
|
|
799
|
+
provides automated feedback to Claude.
|
|
800
|
+
- For `UserPromptSubmit`, this prevents the prompt from being processed.
|
|
801
|
+
- For `Stop` and `SubagentStop`, this takes precedence over any
|
|
802
|
+
`"decision": "block"` output.
|
|
803
|
+
- In all cases, `"continue" = false` takes precedence over any
|
|
804
|
+
`"decision": "block"` output.
|
|
805
|
+
|
|
806
|
+
`stopReason` accompanies `continue` with a reason shown to the user, not shown
|
|
807
|
+
to Claude.
|
|
808
|
+
|
|
809
|
+
#### [](https://code.claude.com/docs/en/hooks\#pretooluse-decision-control) `PreToolUse` Decision Control
|
|
810
|
+
|
|
811
|
+
`PreToolUse` hooks can control whether a tool call proceeds.
|
|
812
|
+
|
|
813
|
+
- `"allow"` bypasses the permission system. `permissionDecisionReason` is shown
|
|
814
|
+
to the user but not to Claude.
|
|
815
|
+
- `"deny"` prevents the tool call from executing. `permissionDecisionReason` is
|
|
816
|
+
shown to Claude.
|
|
817
|
+
- `"ask"` asks the user to confirm the tool call in the UI.
|
|
818
|
+
`permissionDecisionReason` is shown to the user but not to Claude.
|
|
819
|
+
|
|
820
|
+
Additionally, hooks can modify tool inputs before execution using `updatedInput`:
|
|
821
|
+
|
|
822
|
+
- `updatedInput` allows you to modify the tool’s input parameters before the tool executes.
|
|
823
|
+
- This is most useful with `"permissionDecision": "allow"` to modify and approve tool calls.
|
|
824
|
+
|
|
825
|
+
Copy
|
|
826
|
+
|
|
827
|
+
Ask AI
|
|
828
|
+
|
|
829
|
+
```
|
|
830
|
+
{
|
|
831
|
+
"hookSpecificOutput": {
|
|
832
|
+
"hookEventName": "PreToolUse",
|
|
833
|
+
"permissionDecision": "allow"
|
|
834
|
+
"permissionDecisionReason": "My reason here",
|
|
835
|
+
"updatedInput": {
|
|
836
|
+
"field_to_modify": "new value"
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
The `decision` and `reason` fields are deprecated for PreToolUse hooks.
|
|
843
|
+
Use `hookSpecificOutput.permissionDecision` and
|
|
844
|
+
`hookSpecificOutput.permissionDecisionReason` instead. The deprecated fields
|
|
845
|
+
`"approve"` and `"block"` map to `"allow"` and `"deny"` respectively.
|
|
846
|
+
|
|
847
|
+
#### [](https://code.claude.com/docs/en/hooks\#permissionrequest-decision-control) `PermissionRequest` Decision Control
|
|
848
|
+
|
|
849
|
+
`PermissionRequest` hooks can allow or deny permission requests shown to the user.
|
|
850
|
+
|
|
851
|
+
- For `"behavior": "allow"` you can also optionally pass in an `"updatedInput"` that modifies the tool’s input parameters before the tool executes.
|
|
852
|
+
- For `"behavior": "deny"` you can also optionally pass in a `"message"` string that tells the model why the permission was denied, and a boolean `"interrupt"` which will stop Claude.
|
|
853
|
+
|
|
854
|
+
Copy
|
|
855
|
+
|
|
856
|
+
Ask AI
|
|
857
|
+
|
|
858
|
+
```
|
|
859
|
+
{
|
|
860
|
+
"hookSpecificOutput": {
|
|
861
|
+
"hookEventName": "PermissionRequest",
|
|
862
|
+
"decision": {
|
|
863
|
+
"behavior": "allow",
|
|
864
|
+
"updatedInput": {
|
|
865
|
+
"command": "npm run lint"
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
#### [](https://code.claude.com/docs/en/hooks\#posttooluse-decision-control) `PostToolUse` Decision Control
|
|
873
|
+
|
|
874
|
+
`PostToolUse` hooks can provide feedback to Claude after tool execution.
|
|
875
|
+
|
|
876
|
+
- `"block"` automatically prompts Claude with `reason`.
|
|
877
|
+
- `undefined` does nothing. `reason` is ignored.
|
|
878
|
+
- `"hookSpecificOutput.additionalContext"` adds context for Claude to consider.
|
|
879
|
+
|
|
880
|
+
Copy
|
|
881
|
+
|
|
882
|
+
Ask AI
|
|
883
|
+
|
|
884
|
+
```
|
|
885
|
+
{
|
|
886
|
+
"decision": "block" | undefined,
|
|
887
|
+
"reason": "Explanation for decision",
|
|
888
|
+
"hookSpecificOutput": {
|
|
889
|
+
"hookEventName": "PostToolUse",
|
|
890
|
+
"additionalContext": "Additional information for Claude"
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
#### [](https://code.claude.com/docs/en/hooks\#userpromptsubmit-decision-control) `UserPromptSubmit` Decision Control
|
|
896
|
+
|
|
897
|
+
`UserPromptSubmit` hooks can control whether a user prompt is processed and add context.**Adding context (exit code 0):**
|
|
898
|
+
There are two ways to add context to the conversation:
|
|
899
|
+
|
|
900
|
+
1. **Plain text stdout** (simpler): Any non-JSON text written to stdout is added
|
|
901
|
+
as context. This is the easiest way to inject information.
|
|
902
|
+
2. **JSON with `additionalContext`** (structured): Use the JSON format below for
|
|
903
|
+
more control. The `additionalContext` field is added as context.
|
|
904
|
+
|
|
905
|
+
Both methods work with exit code 0. Plain stdout is shown as hook output in
|
|
906
|
+
the transcript; `additionalContext` is added more discretely.**Blocking prompts:**
|
|
907
|
+
|
|
908
|
+
- `"decision": "block"` prevents the prompt from being processed. The submitted
|
|
909
|
+
prompt is erased from context. `"reason"` is shown to the user but not added
|
|
910
|
+
to context.
|
|
911
|
+
- `"decision": undefined` (or omitted) allows the prompt to proceed normally.
|
|
912
|
+
|
|
913
|
+
Copy
|
|
914
|
+
|
|
915
|
+
Ask AI
|
|
916
|
+
|
|
917
|
+
```
|
|
918
|
+
{
|
|
919
|
+
"decision": "block" | undefined,
|
|
920
|
+
"reason": "Explanation for decision",
|
|
921
|
+
"hookSpecificOutput": {
|
|
922
|
+
"hookEventName": "UserPromptSubmit",
|
|
923
|
+
"additionalContext": "My additional context here"
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
The JSON format isn’t required for simple use cases. To add context, you can print plain text to stdout with exit code 0. Use JSON when you need to
|
|
929
|
+
block prompts or want more structured control.
|
|
930
|
+
|
|
931
|
+
#### [](https://code.claude.com/docs/en/hooks\#stop/subagentstop-decision-control) `Stop`/`SubagentStop` Decision Control
|
|
932
|
+
|
|
933
|
+
`Stop` and `SubagentStop` hooks can control whether Claude must continue.
|
|
934
|
+
|
|
935
|
+
- `"block"` prevents Claude from stopping. You must populate `reason` for Claude
|
|
936
|
+
to know how to proceed.
|
|
937
|
+
- `undefined` allows Claude to stop. `reason` is ignored.
|
|
938
|
+
|
|
939
|
+
Copy
|
|
940
|
+
|
|
941
|
+
Ask AI
|
|
942
|
+
|
|
943
|
+
```
|
|
944
|
+
{
|
|
945
|
+
"decision": "block" | undefined,
|
|
946
|
+
"reason": "Must be provided when Claude is blocked from stopping"
|
|
947
|
+
}
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
#### [](https://code.claude.com/docs/en/hooks\#sessionstart-decision-control) `SessionStart` Decision Control
|
|
951
|
+
|
|
952
|
+
`SessionStart` hooks allow you to load in context at the start of a session.
|
|
953
|
+
|
|
954
|
+
- `"hookSpecificOutput.additionalContext"` adds the string to the context.
|
|
955
|
+
- Multiple hooks’ `additionalContext` values are concatenated.
|
|
956
|
+
|
|
957
|
+
Copy
|
|
958
|
+
|
|
959
|
+
Ask AI
|
|
960
|
+
|
|
961
|
+
```
|
|
962
|
+
{
|
|
963
|
+
"hookSpecificOutput": {
|
|
964
|
+
"hookEventName": "SessionStart",
|
|
965
|
+
"additionalContext": "My additional context here"
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
#### [](https://code.claude.com/docs/en/hooks\#sessionend-decision-control) `SessionEnd` Decision Control
|
|
971
|
+
|
|
972
|
+
`SessionEnd` hooks run when a session ends. They cannot block session termination
|
|
973
|
+
but can perform cleanup tasks.
|
|
974
|
+
|
|
975
|
+
#### [](https://code.claude.com/docs/en/hooks\#exit-code-example:-bash-command-validation) Exit Code Example: Bash Command Validation
|
|
976
|
+
|
|
977
|
+
Copy
|
|
978
|
+
|
|
979
|
+
Ask AI
|
|
980
|
+
|
|
981
|
+
```
|
|
982
|
+
#!/usr/bin/env python3
|
|
983
|
+
import json
|
|
984
|
+
import re
|
|
985
|
+
import sys
|
|
986
|
+
|
|
987
|
+
# Define validation rules as a list of (regex pattern, message) tuples
|
|
988
|
+
VALIDATION_RULES = [\
|
|
989
|
+
(\
|
|
990
|
+
r"\bgrep\b(?!.*\|)",\
|
|
991
|
+
"Use 'rg' (ripgrep) instead of 'grep' for better performance and features",\
|
|
992
|
+
),\
|
|
993
|
+
(\
|
|
994
|
+
r"\bfind\s+\S+\s+-name\b",\
|
|
995
|
+
"Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",\
|
|
996
|
+
),\
|
|
997
|
+
]
|
|
998
|
+
|
|
999
|
+
def validate_command(command: str) -> list[str]:
|
|
1000
|
+
issues = []
|
|
1001
|
+
for pattern, message in VALIDATION_RULES:
|
|
1002
|
+
if re.search(pattern, command):
|
|
1003
|
+
issues.append(message)
|
|
1004
|
+
return issues
|
|
1005
|
+
|
|
1006
|
+
try:
|
|
1007
|
+
input_data = json.load(sys.stdin)
|
|
1008
|
+
except json.JSONDecodeError as e:
|
|
1009
|
+
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
|
1010
|
+
sys.exit(1)
|
|
1011
|
+
|
|
1012
|
+
tool_name = input_data.get("tool_name", "")
|
|
1013
|
+
tool_input = input_data.get("tool_input", {})
|
|
1014
|
+
command = tool_input.get("command", "")
|
|
1015
|
+
|
|
1016
|
+
if tool_name != "Bash" or not command:
|
|
1017
|
+
sys.exit(1)
|
|
1018
|
+
|
|
1019
|
+
# Validate the command
|
|
1020
|
+
issues = validate_command(command)
|
|
1021
|
+
|
|
1022
|
+
if issues:
|
|
1023
|
+
for message in issues:
|
|
1024
|
+
print(f"• {message}", file=sys.stderr)
|
|
1025
|
+
# Exit code 2 blocks tool call and shows stderr to Claude
|
|
1026
|
+
sys.exit(2)
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
#### [](https://code.claude.com/docs/en/hooks\#json-output-example:-userpromptsubmit-to-add-context-and-validation) JSON Output Example: UserPromptSubmit to Add Context and Validation
|
|
1030
|
+
|
|
1031
|
+
For `UserPromptSubmit` hooks, you can inject context using either method:
|
|
1032
|
+
|
|
1033
|
+
- **Plain text stdout** with exit code 0: Simplest approach, prints text
|
|
1034
|
+
- **JSON output** with exit code 0: Use `"decision": "block"` to reject prompts,
|
|
1035
|
+
or `additionalContext` for structured context injection
|
|
1036
|
+
|
|
1037
|
+
Remember: Exit code 2 only uses `stderr` for the error message. To block using
|
|
1038
|
+
JSON (with a custom reason), use `"decision": "block"` with exit code 0.
|
|
1039
|
+
|
|
1040
|
+
Copy
|
|
1041
|
+
|
|
1042
|
+
Ask AI
|
|
1043
|
+
|
|
1044
|
+
```
|
|
1045
|
+
#!/usr/bin/env python3
|
|
1046
|
+
import json
|
|
1047
|
+
import sys
|
|
1048
|
+
import re
|
|
1049
|
+
import datetime
|
|
1050
|
+
|
|
1051
|
+
# Load input from stdin
|
|
1052
|
+
try:
|
|
1053
|
+
input_data = json.load(sys.stdin)
|
|
1054
|
+
except json.JSONDecodeError as e:
|
|
1055
|
+
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
|
1056
|
+
sys.exit(1)
|
|
1057
|
+
|
|
1058
|
+
prompt = input_data.get("prompt", "")
|
|
1059
|
+
|
|
1060
|
+
# Check for sensitive patterns
|
|
1061
|
+
sensitive_patterns = [\
|
|
1062
|
+
(r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),\
|
|
1063
|
+
]
|
|
1064
|
+
|
|
1065
|
+
for pattern, message in sensitive_patterns:
|
|
1066
|
+
if re.search(pattern, prompt):
|
|
1067
|
+
# Use JSON output to block with a specific reason
|
|
1068
|
+
output = {
|
|
1069
|
+
"decision": "block",
|
|
1070
|
+
"reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
|
|
1071
|
+
}
|
|
1072
|
+
print(json.dumps(output))
|
|
1073
|
+
sys.exit(0)
|
|
1074
|
+
|
|
1075
|
+
# Add current time to context
|
|
1076
|
+
context = f"Current time: {datetime.datetime.now()}"
|
|
1077
|
+
print(context)
|
|
1078
|
+
|
|
1079
|
+
"""
|
|
1080
|
+
The following is also equivalent:
|
|
1081
|
+
print(json.dumps({
|
|
1082
|
+
"hookSpecificOutput": {
|
|
1083
|
+
"hookEventName": "UserPromptSubmit",
|
|
1084
|
+
"additionalContext": context,
|
|
1085
|
+
},
|
|
1086
|
+
}))
|
|
1087
|
+
"""
|
|
1088
|
+
|
|
1089
|
+
# Allow the prompt to proceed with the additional context
|
|
1090
|
+
sys.exit(0)
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
#### [](https://code.claude.com/docs/en/hooks\#json-output-example:-pretooluse-with-approval) JSON Output Example: PreToolUse with Approval
|
|
1094
|
+
|
|
1095
|
+
Copy
|
|
1096
|
+
|
|
1097
|
+
Ask AI
|
|
1098
|
+
|
|
1099
|
+
```
|
|
1100
|
+
#!/usr/bin/env python3
|
|
1101
|
+
import json
|
|
1102
|
+
import sys
|
|
1103
|
+
|
|
1104
|
+
# Load input from stdin
|
|
1105
|
+
try:
|
|
1106
|
+
input_data = json.load(sys.stdin)
|
|
1107
|
+
except json.JSONDecodeError as e:
|
|
1108
|
+
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
|
1109
|
+
sys.exit(1)
|
|
1110
|
+
|
|
1111
|
+
tool_name = input_data.get("tool_name", "")
|
|
1112
|
+
tool_input = input_data.get("tool_input", {})
|
|
1113
|
+
|
|
1114
|
+
# Example: Auto-approve file reads for documentation files
|
|
1115
|
+
if tool_name == "Read":
|
|
1116
|
+
file_path = tool_input.get("file_path", "")
|
|
1117
|
+
if file_path.endswith((".md", ".mdx", ".txt", ".json")):
|
|
1118
|
+
# Use JSON output to auto-approve the tool call
|
|
1119
|
+
output = {
|
|
1120
|
+
"decision": "approve",
|
|
1121
|
+
"reason": "Documentation file auto-approved",
|
|
1122
|
+
"suppressOutput": True # Don't show in verbose mode
|
|
1123
|
+
}
|
|
1124
|
+
print(json.dumps(output))
|
|
1125
|
+
sys.exit(0)
|
|
1126
|
+
|
|
1127
|
+
# For other cases, let the normal permission flow proceed
|
|
1128
|
+
sys.exit(0)
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
## [](https://code.claude.com/docs/en/hooks\#working-with-mcp-tools) Working with MCP Tools
|
|
1132
|
+
|
|
1133
|
+
Claude Code hooks work seamlessly with
|
|
1134
|
+
[Model Context Protocol (MCP) tools](https://code.claude.com/docs/en/mcp). When MCP servers
|
|
1135
|
+
provide tools, they appear with a special naming pattern that you can match in
|
|
1136
|
+
your hooks.
|
|
1137
|
+
|
|
1138
|
+
### [](https://code.claude.com/docs/en/hooks\#mcp-tool-naming) MCP Tool Naming
|
|
1139
|
+
|
|
1140
|
+
MCP tools follow the pattern `mcp__<server>__<tool>`, for example:
|
|
1141
|
+
|
|
1142
|
+
- `mcp__memory__create_entities` \- Memory server’s create entities tool
|
|
1143
|
+
- `mcp__filesystem__read_file` \- Filesystem server’s read file tool
|
|
1144
|
+
- `mcp__github__search_repositories` \- GitHub server’s search tool
|
|
1145
|
+
|
|
1146
|
+
### [](https://code.claude.com/docs/en/hooks\#configuring-hooks-for-mcp-tools) Configuring Hooks for MCP Tools
|
|
1147
|
+
|
|
1148
|
+
You can target specific MCP tools or entire MCP servers:
|
|
1149
|
+
|
|
1150
|
+
Copy
|
|
1151
|
+
|
|
1152
|
+
Ask AI
|
|
1153
|
+
|
|
1154
|
+
```
|
|
1155
|
+
{
|
|
1156
|
+
"hooks": {
|
|
1157
|
+
"PreToolUse": [\
|
|
1158
|
+
{\
|
|
1159
|
+
"matcher": "mcp__memory__.*",\
|
|
1160
|
+
"hooks": [\
|
|
1161
|
+
{\
|
|
1162
|
+
"type": "command",\
|
|
1163
|
+
"command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"\
|
|
1164
|
+
}\
|
|
1165
|
+
]\
|
|
1166
|
+
},\
|
|
1167
|
+
{\
|
|
1168
|
+
"matcher": "mcp__.*__write.*",\
|
|
1169
|
+
"hooks": [\
|
|
1170
|
+
{\
|
|
1171
|
+
"type": "command",\
|
|
1172
|
+
"command": "/home/user/scripts/validate-mcp-write.py"\
|
|
1173
|
+
}\
|
|
1174
|
+
]\
|
|
1175
|
+
}\
|
|
1176
|
+
]
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
## [](https://code.claude.com/docs/en/hooks\#examples) Examples
|
|
1182
|
+
|
|
1183
|
+
For practical examples including code formatting, notifications, and file protection, see [More Examples](https://code.claude.com/docs/en/hooks-guide#more-examples) in the get started guide.
|
|
1184
|
+
|
|
1185
|
+
## [](https://code.claude.com/docs/en/hooks\#security-considerations) Security Considerations
|
|
1186
|
+
|
|
1187
|
+
### [](https://code.claude.com/docs/en/hooks\#disclaimer) Disclaimer
|
|
1188
|
+
|
|
1189
|
+
**USE AT YOUR OWN RISK**: Claude Code hooks execute arbitrary shell commands on
|
|
1190
|
+
your system automatically. By using hooks, you acknowledge that:
|
|
1191
|
+
|
|
1192
|
+
- You are solely responsible for the commands you configure
|
|
1193
|
+
- Hooks can modify, delete, or access any files your user account can access
|
|
1194
|
+
- Malicious or poorly written hooks can cause data loss or system damage
|
|
1195
|
+
- Anthropic provides no warranty and assumes no liability for any damages
|
|
1196
|
+
resulting from hook usage
|
|
1197
|
+
- You should thoroughly test hooks in a safe environment before production use
|
|
1198
|
+
|
|
1199
|
+
Always review and understand any hook commands before adding them to your
|
|
1200
|
+
configuration.
|
|
1201
|
+
|
|
1202
|
+
### [](https://code.claude.com/docs/en/hooks\#security-best-practices) Security Best Practices
|
|
1203
|
+
|
|
1204
|
+
Here are some key practices for writing more secure hooks:
|
|
1205
|
+
|
|
1206
|
+
1. **Validate and sanitize inputs** \- Never trust input data blindly
|
|
1207
|
+
2. **Always quote shell variables** \- Use `"$VAR"` not `$VAR`
|
|
1208
|
+
3. **Block path traversal** \- Check for `..` in file paths
|
|
1209
|
+
4. **Use absolute paths** \- Specify full paths for scripts (use
|
|
1210
|
+
“$CLAUDE\_PROJECT\_DIR” for the project path)
|
|
1211
|
+
5. **Skip sensitive files** \- Avoid `.env`, `.git/`, keys, etc.
|
|
1212
|
+
|
|
1213
|
+
### [](https://code.claude.com/docs/en/hooks\#configuration-safety) Configuration Safety
|
|
1214
|
+
|
|
1215
|
+
Direct edits to hooks in settings files don’t take effect immediately. Claude
|
|
1216
|
+
Code:
|
|
1217
|
+
|
|
1218
|
+
1. Captures a snapshot of hooks at startup
|
|
1219
|
+
2. Uses this snapshot throughout the session
|
|
1220
|
+
3. Warns if hooks are modified externally
|
|
1221
|
+
4. Requires review in `/hooks` menu for changes to apply
|
|
1222
|
+
|
|
1223
|
+
This prevents malicious hook modifications from affecting your current session.
|
|
1224
|
+
|
|
1225
|
+
## [](https://code.claude.com/docs/en/hooks\#hook-execution-details) Hook Execution Details
|
|
1226
|
+
|
|
1227
|
+
- **Timeout**: 60-second execution limit by default, configurable per command.
|
|
1228
|
+
|
|
1229
|
+
- A timeout for an individual command does not affect the other commands.
|
|
1230
|
+
- **Parallelization**: All matching hooks run in parallel
|
|
1231
|
+
- **Deduplication**: Multiple identical hook commands are deduplicated automatically
|
|
1232
|
+
- **Environment**: Runs in current directory with Claude Code’s environment
|
|
1233
|
+
|
|
1234
|
+
- The `CLAUDE_PROJECT_DIR` environment variable is available and contains the
|
|
1235
|
+
absolute path to the project root directory (where Claude Code was started)
|
|
1236
|
+
- The `CLAUDE_CODE_REMOTE` environment variable indicates whether the hook is running in a remote (web) environment (`"true"`) or local CLI environment (not set or empty). Use this to run different logic based on execution context.
|
|
1237
|
+
- **Input**: JSON via stdin
|
|
1238
|
+
- **Output**:
|
|
1239
|
+
|
|
1240
|
+
- PreToolUse/PermissionRequest/PostToolUse/Stop/SubagentStop: Progress shown in verbose mode (ctrl+o)
|
|
1241
|
+
- Notification/SessionEnd: Logged to debug only (`--debug`)
|
|
1242
|
+
- UserPromptSubmit/SessionStart: stdout added as context for Claude
|
|
1243
|
+
|
|
1244
|
+
## [](https://code.claude.com/docs/en/hooks\#debugging) Debugging
|
|
1245
|
+
|
|
1246
|
+
### [](https://code.claude.com/docs/en/hooks\#basic-troubleshooting) Basic Troubleshooting
|
|
1247
|
+
|
|
1248
|
+
If your hooks aren’t working:
|
|
1249
|
+
|
|
1250
|
+
1. **Check configuration** \- Run `/hooks` to see if your hook is registered
|
|
1251
|
+
2. **Verify syntax** \- Ensure your JSON settings are valid
|
|
1252
|
+
3. **Test commands** \- Run hook commands manually first
|
|
1253
|
+
4. **Check permissions** \- Make sure scripts are executable
|
|
1254
|
+
5. **Review logs** \- Use `claude --debug` to see hook execution details
|
|
1255
|
+
|
|
1256
|
+
Common issues:
|
|
1257
|
+
|
|
1258
|
+
- **Quotes not escaped** \- Use `\"` inside JSON strings
|
|
1259
|
+
- **Wrong matcher** \- Check tool names match exactly (case-sensitive)
|
|
1260
|
+
- **Command not found** \- Use full paths for scripts
|
|
1261
|
+
|
|
1262
|
+
### [](https://code.claude.com/docs/en/hooks\#advanced-debugging) Advanced Debugging
|
|
1263
|
+
|
|
1264
|
+
For complex hook issues:
|
|
1265
|
+
|
|
1266
|
+
1. **Inspect hook execution** \- Use `claude --debug` to see detailed hook
|
|
1267
|
+
execution
|
|
1268
|
+
2. **Validate JSON schemas** \- Test hook input/output with external tools
|
|
1269
|
+
3. **Check environment variables** \- Verify Claude Code’s environment is correct
|
|
1270
|
+
4. **Test edge cases** \- Try hooks with unusual file paths or inputs
|
|
1271
|
+
5. **Monitor system resources** \- Check for resource exhaustion during hook
|
|
1272
|
+
execution
|
|
1273
|
+
6. **Use structured logging** \- Implement logging in your hook scripts
|
|
1274
|
+
|
|
1275
|
+
### [](https://code.claude.com/docs/en/hooks\#debug-output-example) Debug Output Example
|
|
1276
|
+
|
|
1277
|
+
Use `claude --debug` to see hook execution details:
|
|
1278
|
+
|
|
1279
|
+
Copy
|
|
1280
|
+
|
|
1281
|
+
Ask AI
|
|
1282
|
+
|
|
1283
|
+
```
|
|
1284
|
+
[DEBUG] Executing hooks for PostToolUse:Write
|
|
1285
|
+
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
|
|
1286
|
+
[DEBUG] Found 1 hook matchers in settings
|
|
1287
|
+
[DEBUG] Matched 1 hooks for query "Write"
|
|
1288
|
+
[DEBUG] Found 1 hook commands to execute
|
|
1289
|
+
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
|
|
1290
|
+
[DEBUG] Hook command completed with status 0: <Your stdout>
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
Progress messages appear in verbose mode (ctrl+o) showing:
|
|
1294
|
+
|
|
1295
|
+
- Which hook is running
|
|
1296
|
+
- Command being executed
|
|
1297
|
+
- Success/failure status
|
|
1298
|
+
- Output or error messages
|
|
1299
|
+
|
|
1300
|
+
Was this page helpful?
|
|
1301
|
+
|
|
1302
|
+
YesNo
|
|
1303
|
+
|
|
1304
|
+
[Checkpointing](https://code.claude.com/docs/en/checkpointing) [Plugins reference](https://code.claude.com/docs/en/plugins-reference)
|
|
1305
|
+
|
|
1306
|
+
Ctrl+I
|
|
1307
|
+
|
|
1308
|
+
Assistant
|
|
1309
|
+
|
|
1310
|
+
Responses are generated using AI and may contain mistakes.
|