1# Hooks Guide
  2
  3⚠️ **Security Warning**: Hooks run automatically with your user's permissions and have full access to your filesystem and environment. Only configure hooks from trusted sources and review all commands before adding them.
  4
  5Hooks are user-defined shell commands that execute at various points in Crush's lifecycle. They provide deterministic control over Crush's behavior, ensuring certain actions always occur rather than relying on the LLM to choose to run them.
  6
  7## Hook Events
  8
  9Crush provides several lifecycle events where hooks can run:
 10
 11### Tool Events
 12- **`pre_tool_use`**: Runs before tool calls. If a hook fails (non-zero exit code), the tool execution is blocked.
 13- **`post_tool_use`**: Runs after tool calls complete, can be used to process results or trigger actions.
 14
 15### Session Events
 16- **`user_prompt_submit`**: Runs when the user submits a prompt, before processing
 17- **`stop`**: Runs when Crush finishes responding to a prompt
 18- **`subagent_stop`**: Runs when subagent tasks complete (e.g., fetch tool, agent tool)
 19
 20### Other Events
 21- **`pre_compact`**: Runs before running a compact operation
 22- **`permission_requested`**: Runs when a permission is requested from the user
 23
 24## Configuration Format
 25
 26Hooks are configured in your Crush configuration file. Configuration files are searched in the following order:
 27
 281. `.crush.json` (project-specific, hidden)
 292. `crush.json` (project-specific)
 303. `$HOME/.config/crush/crush.json` (global, Linux/macOS)
 31   or `%LOCALAPPDATA%\crush\crush.json` (global, Windows)
 32
 33Example configuration:
 34
 35```json
 36{
 37  "hooks": {
 38    "pre_tool_use": [
 39      {
 40        "matcher": "bash",
 41        "hooks": [
 42          {
 43            "type": "command",
 44            "command": "jq -r '.tool_name + \": \" + .tool_input.command' >> ~/crush-commands.log",
 45            "timeout": 5
 46          }
 47        ]
 48      }
 49    ],
 50    "post_tool_use": [
 51      {
 52        "matcher": "*",
 53        "hooks": [
 54          {
 55            "type": "command",
 56            "command": "echo \"Tool $(jq -r .tool_name) completed\" | notify-send \"Crush Hook\""
 57          }
 58        ]
 59      }
 60    ],
 61    "stop": [
 62      {
 63        "hooks": [
 64          {
 65            "type": "command",
 66            "command": "echo \"Prompt completed. Tokens used: $(jq -r .tokens_used)\""
 67          }
 68        ]
 69      }
 70    ]
 71  }
 72}
 73```
 74
 75## Hook Context
 76
 77Each hook receives a JSON context object via stdin containing information about the event:
 78
 79```json
 80{
 81  "event_type": "pre_tool_use",
 82  "session_id": "abc123",
 83  "tool_name": "bash",
 84  "tool_input": {
 85    "command": "echo hello",
 86    "description": "Print hello"
 87  },
 88  "tool_result": "",
 89  "tool_error": false,
 90  "user_prompt": "",
 91  "timestamp": "2025-10-30T12:00:00Z",
 92  "working_dir": "/path/to/project",
 93  "message_id": "msg123",
 94  "provider": "anthropic",
 95  "model": "claude-3-5-sonnet-20241022",
 96  "tokens_used": 1000,
 97  "tokens_input": 500
 98}
 99```
100
101### Context Fields by Event Type
102
103Different events include different fields:
104
105- **pre_tool_use**: `event_type`, `session_id`, `tool_name`, `tool_input`, `message_id`, `provider`, `model`
106- **post_tool_use**: `event_type`, `session_id`, `tool_name`, `tool_input`, `tool_result`, `tool_error`, `message_id`, `provider`, `model`
107- **user_prompt_submit**: `event_type`, `session_id`, `user_prompt`, `provider`, `model`
108- **stop**: `event_type`, `session_id`, `message_id`, `provider`, `model`, `tokens_used`, `tokens_input`
109
110All events include: `event_type`, `timestamp`, `working_dir`
111
112## Environment Variables
113
114Hooks also receive environment variables:
115
116- `CRUSH_HOOK_CONTEXT`: Full JSON context as a string
117- `CRUSH_HOOK_EVENT`: The event type (e.g., "PreToolUse")
118- `CRUSH_SESSION_ID`: The session ID (if applicable)
119- `CRUSH_TOOL_NAME`: The tool name (for tool events)
120
121## Hook Configuration
122
123### Matchers
124
125For tool events (`pre_tool_use`, `post_tool_use`), you can specify matchers to target specific tools:
126
127- `"bash"` - Only matches the bash tool
128- `"edit"` - Only matches the edit tool
129- `"edit|write|multiedit"` - Matches any of the specified tools (pipe-separated)
130- `"*"` or `""` - Matches all tools
131
132For non-tool events, leave the matcher empty or use `"*"`.
133
134### Hook Command
135
136Each hook has these properties:
137- `type`: Currently only `"command"` is supported
138- `command`: Shell command to execute. Receives JSON context via stdin
139- `timeout`: (optional) Maximum execution time in seconds (default: 30, max: 300)
140
141**Important**: When processing JSON with `jq`, be aware that `tool_result` fields can contain large content or special characters that may cause parse errors. For reliability:
142- Use `cat` instead of `jq` to output raw JSON: `cat >> hooks.log`
143- Extract only specific fields: `jq -r '.tool_name, .session_id'`
144- For `post_tool_use` hooks, tool results can be very large (e.g., entire file contents)
145
146## Examples
147
148### Log All Bash Commands
149
150```json
151{
152  "hooks": {
153    "pre_tool_use": [
154      {
155        "matcher": "bash",
156        "hooks": [
157          {
158            "type": "command",
159            "command": "jq -r '.timestamp + \" - \" + .tool_input.command' >> ~/crush-bash.log"
160          }
161        ]
162      }
163    ]
164  }
165}
166```
167
168### Auto-format Files After Editing
169
170```json
171{
172  "hooks": {
173    "post_tool_use": [
174      {
175        "matcher": "edit|write|multiedit",
176        "hooks": [
177          {
178            "type": "command",
179            "command": "jq -r .tool_input.file_path | xargs prettier --write"
180          }
181        ]
182      }
183    ]
184  }
185}
186```
187
188### Notify on Completion
189
190Note: Examples use macOS-specific tools. For cross-platform alternatives, use `notify-send` (Linux) or custom scripts.
191
192```json
193{
194  "hooks": {
195    "stop": [
196      {
197        "hooks": [
198          {
199            "type": "command",
200            "command": "osascript -e 'display notification \"Crush completed\" with title \"Crush\"'"
201          }
202        ]
203      }
204    ]
205  }
206}
207```
208
209### Track Token Usage
210
211```json
212{
213  "hooks": {
214    "stop": [
215      {
216        "hooks": [
217          {
218            "type": "command",
219            "command": "jq -r '\"\\(.timestamp): \\(.tokens_used) tokens\"' >> ~/crush-tokens.log"
220          }
221        ]
222      }
223    ]
224  }
225}
226```
227
228### Validate Tool Usage
229
230```json
231{
232  "hooks": {
233    "pre_tool_use": [
234      {
235        "matcher": "bash",
236        "hooks": [
237          {
238            "type": "command",
239            "command": "if jq -e '.tool_input.command | contains(\"rm -rf\")' > /dev/null; then echo \"Dangerous command detected\" >&2; exit 1; fi"
240          }
241        ]
242      }
243    ]
244  }
245}
246```
247
248### Multiple Hooks
249
250You can execute multiple hooks for the same event:
251
252```json
253{
254  "hooks": {
255    "post_tool_use": [
256      {
257        "matcher": "*",
258        "hooks": [
259          {
260            "type": "command",
261            "command": "jq -r .tool_name >> ~/crush-tools.log"
262          },
263          {
264            "type": "command",
265            "command": "if jq -e .tool_error > /dev/null; then echo 'Error in tool' | pbcopy; fi"
266          }
267        ]
268      }
269    ]
270  }
271}
272```
273
274### Debug: Log All Hook Events
275
276For debugging or monitoring, log complete JSON for all events:
277
278```json
279{
280  "hooks": {
281    "pre_tool_use": [
282      {
283        "matcher": "*",
284        "hooks": [
285          {
286            "type": "command",
287            "command": "(echo \"[$(date '+%Y-%m-%d %H:%M:%S')] pre_tool_use:\" && cat && echo \"\") >> hooks.log"
288          }
289        ]
290      }
291    ],
292    "post_tool_use": [
293      {
294        "matcher": "*",
295        "hooks": [
296          {
297            "type": "command",
298            "command": "(echo \"[$(date '+%Y-%m-%d %H:%M:%S')] post_tool_use:\" && cat && echo \"\") >> hooks.log"
299          }
300        ]
301      }
302    ]
303  }
304}
305```
306
307Note: Using `cat` avoids potential jq parsing errors with large or complex tool results.
308
309### Track Subagent Completion
310
311```json
312{
313  "hooks": {
314    "subagent_stop": [
315      {
316        "hooks": [
317          {
318            "type": "command",
319            "command": "echo \"Subagent task completed: $(jq -r .tool_name)\" | tee -a ~/crush-subagent.log"
320          }
321        ]
322      }
323    ]
324  }
325}
326```
327
328### Pre-Compact Notification
329
330```json
331{
332  "hooks": {
333    "pre_compact": [
334      {
335        "hooks": [
336          {
337            "type": "command",
338            "command": "osascript -e 'display notification \"Compacting conversation...\" with title \"Crush\"'"
339          }
340        ]
341      }
342    ]
343  }
344}
345```
346
347### Permission Requested Notification
348
349```json
350{
351  "hooks": {
352    "permission_requested": [
353      {
354        "hooks": [
355          {
356            "type": "command",
357            "command": "jq -r '\"Permission requested: \\(.tool_name) \\(.permission_action) \\(.permission_path)\"' | tee -a ~/crush-permissions.log"
358          }
359        ]
360      }
361    ]
362  }
363}
364```
365
366## Best Practices
367
3681. **Keep hooks fast**: Hooks run synchronously and can slow down Crush if they take too long.
3692. **Set appropriate timeouts**: Use shorter timeouts (1-5 seconds) for quick operations.
3703. **Handle errors gracefully**: Hooks should not crash or hang.
3714. **Use jq for JSON processing**: The context is piped to stdin as JSON.
3725. **Test hooks independently**: Run your shell commands manually with test data before configuring them.
3736. **Use absolute paths**: Hooks run in the project directory, but absolute paths are more reliable.
3747. **Consider privacy**: Don't log sensitive information like API keys or passwords.
375
376## Debugging Hooks
377
378Hooks log errors and warnings to Crush's log output. To see hook execution:
379
3801. Run Crush with debug logging enabled: `crush --debug`
3812. Check the logs for hook-related messages
3823. Test hook shell commands manually:
383   ```bash
384   echo '{"event_type":"pre_tool_use","tool_name":"bash"}' | jq -r '.tool_name'
385   ```
386
387## Limitations
388
389- Hooks must complete within their timeout (default 30 seconds)
390- Hooks run in a shell environment and require shell utilities (bash, jq, etc.)
391- Hooks cannot modify Crush's internal state
392- Hook errors are logged but don't stop Crush execution (except for pre_tool_use)
393- Interactive hooks are not supported