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- **`PreToolUse`**: Runs before tool calls. If a hook fails (non-zero exit code), the tool execution is blocked.
13- **`PostToolUse`**: Runs after tool calls complete, can be used to process results or trigger actions.
14
15### Session Events
16- **`UserPromptSubmit`**: Runs when the user submits a prompt, before processing
17- **`Stop`**: Runs when Crush finishes responding to a prompt
18- **`SubagentStop`**: Runs when subagent tasks complete (e.g., fetch tool, agent tool)
19- **`SessionStart`**: Runs when a session starts or resumes
20- **`SessionEnd`**: Runs when a session ends
21
22### Other Events
23- **`Notification`**: Runs when Crush sends notifications
24- **`PreCompact`**: Runs before running a compact operation
25
26## Configuration Format
27
28Hooks are configured in your Crush configuration file (e.g., `crush.json` or `~/.crush/crush.json`):
29
30```json
31{
32 "hooks": {
33 "PreToolUse": [
34 {
35 "matcher": "bash",
36 "hooks": [
37 {
38 "type": "command",
39 "command": "jq -r '.tool_name + \": \" + .tool_input.command' >> ~/crush-commands.log",
40 "timeout": 5
41 }
42 ]
43 }
44 ],
45 "PostToolUse": [
46 {
47 "matcher": "*",
48 "hooks": [
49 {
50 "type": "command",
51 "command": "echo \"Tool $(jq -r .tool_name) completed\" | notify-send \"Crush Hook\""
52 }
53 ]
54 }
55 ],
56 "Stop": [
57 {
58 "hooks": [
59 {
60 "type": "command",
61 "command": "echo \"Prompt completed. Tokens used: $(jq -r .tokens_used)\""
62 }
63 ]
64 }
65 ]
66 }
67}
68```
69
70## Hook Context
71
72Each hook receives a JSON context object via stdin containing information about the event:
73
74```json
75{
76 "event_type": "PreToolUse",
77 "session_id": "abc123",
78 "tool_name": "bash",
79 "tool_input": {
80 "command": "echo hello",
81 "description": "Print hello"
82 },
83 "tool_result": "",
84 "tool_error": false,
85 "user_prompt": "",
86 "timestamp": "2025-10-30T12:00:00Z",
87 "working_dir": "/path/to/project",
88 "message_id": "msg123",
89 "provider": "anthropic",
90 "model": "claude-3-5-sonnet-20241022",
91 "tokens_used": 1000,
92 "tokens_input": 500
93}
94```
95
96### Context Fields by Event Type
97
98Different events include different fields:
99
100- **PreToolUse**: `event_type`, `session_id`, `tool_name`, `tool_input`, `message_id`, `provider`, `model`
101- **PostToolUse**: `event_type`, `session_id`, `tool_name`, `tool_input`, `tool_result`, `tool_error`, `message_id`, `provider`, `model`
102- **UserPromptSubmit**: `event_type`, `session_id`, `user_prompt`, `provider`, `model`
103- **Stop**: `event_type`, `session_id`, `message_id`, `provider`, `model`, `tokens_used`, `tokens_input`
104
105All events include: `event_type`, `timestamp`, `working_dir`
106
107## Environment Variables
108
109Hooks also receive environment variables:
110
111- `CRUSH_HOOK_CONTEXT`: Full JSON context as a string
112- `CRUSH_HOOK_EVENT`: The event type (e.g., "PreToolUse")
113- `CRUSH_SESSION_ID`: The session ID (if applicable)
114- `CRUSH_TOOL_NAME`: The tool name (for tool events)
115
116## Hook Configuration
117
118### Matchers
119
120For tool events (`PreToolUse`, `PostToolUse`), you can specify matchers to target specific tools:
121
122- `"bash"` - Only matches the bash tool
123- `"edit"` - Only matches the edit tool
124- `"*"` or `""` - Matches all tools
125
126For non-tool events, leave the matcher empty or use `"*"`.
127
128### Hook Properties
129
130- `type`: Currently only `"command"` is supported
131- `command`: The shell command to execute
132- `timeout`: (optional) Maximum execution time in seconds (default: 30, max: 300)
133
134## Examples
135
136### Log All Bash Commands
137
138```json
139{
140 "hooks": {
141 "PreToolUse": [
142 {
143 "matcher": "bash",
144 "hooks": [
145 {
146 "type": "command",
147 "command": "jq -r '.timestamp + \" - \" + .tool_input.command' >> ~/.crush/bash-log.txt"
148 }
149 ]
150 }
151 ]
152 }
153}
154```
155
156### Auto-format Files After Editing
157
158```json
159{
160 "hooks": {
161 "PostToolUse": [
162 {
163 "matcher": "edit",
164 "hooks": [
165 {
166 "type": "command",
167 "command": "jq -r .tool_input.file_path | xargs prettier --write"
168 }
169 ]
170 }
171 ]
172 }
173}
174```
175
176### Notify on Completion
177
178```json
179{
180 "hooks": {
181 "Stop": [
182 {
183 "hooks": [
184 {
185 "type": "command",
186 "command": "osascript -e 'display notification \"Crush completed\" with title \"Crush\"'"
187 }
188 ]
189 }
190 ]
191 }
192}
193```
194
195### Track Token Usage
196
197```json
198{
199 "hooks": {
200 "Stop": [
201 {
202 "hooks": [
203 {
204 "type": "command",
205 "command": "jq -r '\"\\(.timestamp): \\(.tokens_used) tokens\"' >> ~/.crush/token-usage.log"
206 }
207 ]
208 }
209 ]
210 }
211}
212```
213
214### Validate Tool Usage
215
216```json
217{
218 "hooks": {
219 "PreToolUse": [
220 {
221 "matcher": "bash",
222 "hooks": [
223 {
224 "type": "command",
225 "command": "if jq -e '.tool_input.command | contains(\"rm -rf\")' > /dev/null; then echo \"Dangerous command detected\" >&2; exit 1; fi"
226 }
227 ]
228 }
229 ]
230 }
231}
232```
233
234### Multiple Hooks
235
236You can execute multiple hooks for the same event:
237
238```json
239{
240 "hooks": {
241 "PostToolUse": [
242 {
243 "matcher": "*",
244 "hooks": [
245 {
246 "type": "command",
247 "command": "jq -r .tool_name >> ~/.crush/tool-usage.log"
248 },
249 {
250 "type": "command",
251 "command": "if jq -e .tool_error > /dev/null; then echo 'Error in tool' | pbcopy; fi"
252 }
253 ]
254 }
255 ]
256 }
257}
258```
259
260## Best Practices
261
2621. **Keep hooks fast**: Hooks run synchronously and can slow down Crush if they take too long.
2632. **Set appropriate timeouts**: Use shorter timeouts (1-5 seconds) for quick operations.
2643. **Handle errors gracefully**: Hooks should not crash or hang.
2654. **Use jq for JSON processing**: The context is piped to stdin as JSON.
2665. **Test hooks independently**: Run your shell commands manually with test data before configuring them.
2676. **Use absolute paths**: Hooks run in the project directory, but absolute paths are more reliable.
2687. **Consider privacy**: Don't log sensitive information like API keys or passwords.
269
270## Debugging Hooks
271
272Hooks log errors and warnings to Crush's log output. To see hook execution:
273
2741. Run Crush with debug logging enabled: `crush --debug`
2752. Check the logs for hook-related messages
2763. Test hook shell commands manually:
277 ```bash
278 echo '{"event_type":"PreToolUse","tool_name":"bash"}' | jq -r '.tool_name'
279 ```
280
281## Limitations
282
283- Hooks must complete within their timeout (default 30 seconds)
284- Hooks run in a shell environment and require shell utilities (bash, jq, etc.)
285- Hooks cannot modify Crush's internal state
286- Hook errors are logged but don't stop Crush execution (except for PreToolUse)
287- Interactive hooks are not supported