HOOKS.md

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