ginskill-init 1.0.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.
- package/README.md +77 -0
- package/agents/developer.md +56 -0
- package/agents/frontend-design.md +69 -0
- package/agents/mobile-reviewer.md +36 -0
- package/agents/review-code.md +49 -0
- package/agents/security-scanner.md +50 -0
- package/agents/tester.md +72 -0
- package/bin/cli.js +226 -0
- package/package.json +20 -0
- package/skills/ai-asset-generator/SKILL.md +255 -0
- package/skills/ai-asset-generator/docs/gen-image.md +274 -0
- package/skills/ai-asset-generator/docs/genvideo.md +341 -0
- package/skills/ai-asset-generator/docs/remove-background.md +19 -0
- package/skills/ai-asset-generator/generate-credit-assets.mjs +180 -0
- package/skills/ai-asset-generator/generate-ginbrowser-assets.mjs +242 -0
- package/skills/ai-asset-generator/generate-sty-icon.mjs +149 -0
- package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
- package/skills/ai-asset-generator/lib/env.mjs +38 -0
- package/skills/ai-asset-generator/lib/kie-client.mjs +88 -0
- package/skills/ai-asset-generator/scripts/scaffold-generator.mjs +203 -0
- package/skills/ai-build-ai/SKILL.md +124 -0
- package/skills/ai-build-ai/docs/agent-teams.md +293 -0
- package/skills/ai-build-ai/docs/checkpointing.md +161 -0
- package/skills/ai-build-ai/docs/create-agent.md +399 -0
- package/skills/ai-build-ai/docs/create-mcp.md +395 -0
- package/skills/ai-build-ai/docs/create-skill.md +299 -0
- package/skills/ai-build-ai/docs/headless-mode.md +614 -0
- package/skills/ai-build-ai/docs/hooks.md +578 -0
- package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
- package/skills/ai-build-ai/docs/output-styles.md +208 -0
- package/skills/ai-build-ai/docs/overview.md +162 -0
- package/skills/ai-build-ai/docs/permissions.md +391 -0
- package/skills/ai-build-ai/docs/plugins.md +396 -0
- package/skills/ai-build-ai/docs/sandbox.md +262 -0
- package/skills/ai-build-ai/scripts/load-tutorial.sh +54 -0
- package/skills/icon-generator/SKILL.md +270 -0
- package/skills/mobile-app-review/SKILL.md +321 -0
- package/skills/mobile-app-review/references/apple-review.md +132 -0
- package/skills/mobile-app-review/references/google-play-review.md +203 -0
- package/skills/mongodb/SKILL.md +667 -0
- package/skills/mongodb/references/mongoose-patterns.md +368 -0
- package/skills/nestjs-architecture/SKILL.md +1086 -0
- package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
- package/skills/performance/SKILL.md +509 -0
- package/skills/react-fsd-architecture/SKILL.md +693 -0
- package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
- package/skills/react-query/SKILL.md +685 -0
- package/skills/react-query/references/query-patterns.md +365 -0
- package/skills/review-code/SKILL.md +321 -0
- package/skills/review-code/references/clean-code-principles.md +395 -0
- package/skills/review-code/references/frontend-patterns.md +136 -0
- package/skills/review-code/references/nestjs-patterns.md +184 -0
- package/skills/review-code/scripts/check-module.sh +201 -0
- package/skills/review-code/scripts/deep-scan.sh +604 -0
- package/skills/review-code/scripts/dep-check.sh +522 -0
- package/skills/review-code/scripts/detect-duplicates.sh +466 -0
- package/skills/review-code/scripts/format-check.sh +577 -0
- package/skills/review-code/scripts/run-review.sh +167 -0
- package/skills/review-code/scripts/scan-codebase.sh +152 -0
- package/skills/security-scanner/SKILL.md +327 -0
- package/skills/security-scanner/references/nestjs-security.md +260 -0
- package/skills/security-scanner/references/nextjs-security.md +201 -0
- package/skills/security-scanner/references/react-native-security.md +199 -0
- package/skills/security-scanner/scripts/security-scan.sh +478 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
# Tutorial: Hooks
|
|
2
|
+
|
|
3
|
+
Hooks are shell commands, HTTP endpoints, or LLM prompts that fire automatically at specific lifecycle points in Claude Code. They give you **deterministic control** — certain actions always happen regardless of what Claude decides to do.
|
|
4
|
+
|
|
5
|
+
Use hooks to: auto-format files after edits, block dangerous commands, inject context after compaction, send notifications, audit tool use, enforce project rules.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Step 1: The Hooks Lifecycle
|
|
10
|
+
|
|
11
|
+
All 16 hook events in order:
|
|
12
|
+
|
|
13
|
+
| Event | When it fires | Can block? |
|
|
14
|
+
|-------|--------------|-----------|
|
|
15
|
+
| `SessionStart` | Session begins or resumes | No (but can inject context) |
|
|
16
|
+
| `UserPromptSubmit` | You submit a prompt, before Claude processes | No (but can inject context) |
|
|
17
|
+
| `PreToolUse` | Before any tool executes | **Yes** |
|
|
18
|
+
| `PermissionRequest` | When a permission dialog appears | **Yes** |
|
|
19
|
+
| `PostToolUse` | After a tool succeeds | No (but can block continuation) |
|
|
20
|
+
| `PostToolUseFailure` | After a tool fails | No |
|
|
21
|
+
| `Notification` | Claude needs your attention | No |
|
|
22
|
+
| `SubagentStart` | A subagent is spawned | No |
|
|
23
|
+
| `SubagentStop` | A subagent finishes | No |
|
|
24
|
+
| `Stop` | Claude finishes responding | No (but can keep Claude working) |
|
|
25
|
+
| `TeammateIdle` | Agent team teammate goes idle | **Yes** (send feedback) |
|
|
26
|
+
| `TaskCompleted` | A task marked as completed | **Yes** (block completion) |
|
|
27
|
+
| `ConfigChange` | A settings/skills file changes live | **Yes** |
|
|
28
|
+
| `WorktreeCreate` | A worktree is being created | Replaces default behavior |
|
|
29
|
+
| `WorktreeRemove` | A worktree is being removed | Replaces default behavior |
|
|
30
|
+
| `PreCompact` | Before context compaction | No |
|
|
31
|
+
| `SessionEnd` | Session terminates | No |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Step 2: Where to Configure Hooks
|
|
36
|
+
|
|
37
|
+
Hooks live in settings JSON files:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
~/.claude/settings.json ← All your projects (personal)
|
|
41
|
+
.claude/settings.json ← This project only (commit to git)
|
|
42
|
+
.claude/settings.local.json ← This project, not committed
|
|
43
|
+
plugin/hooks/hooks.json ← Bundled with a plugin
|
|
44
|
+
skill/agent frontmatter ← Active only while component runs
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Basic structure:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"hooks": {
|
|
52
|
+
"PostToolUse": [
|
|
53
|
+
{
|
|
54
|
+
"matcher": "Edit|Write",
|
|
55
|
+
"hooks": [
|
|
56
|
+
{
|
|
57
|
+
"type": "command",
|
|
58
|
+
"command": "npx prettier --write $(jq -r '.tool_input.file_path')"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
You can also manage hooks interactively: run `/hooks` inside Claude Code.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Step 3: Hook Types
|
|
72
|
+
|
|
73
|
+
### Type 1: `command` (most common)
|
|
74
|
+
|
|
75
|
+
Runs a shell command. Receives event data as JSON on stdin.
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"type": "command",
|
|
80
|
+
"command": "./scripts/my-hook.sh",
|
|
81
|
+
"timeout": 30
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Type 2: `http`
|
|
86
|
+
|
|
87
|
+
POSTs event JSON to an HTTP endpoint. Response body uses same JSON format as command output.
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"type": "http",
|
|
92
|
+
"url": "https://my-server.com/hooks/tool-use",
|
|
93
|
+
"headers": {
|
|
94
|
+
"Authorization": "Bearer $MY_TOKEN"
|
|
95
|
+
},
|
|
96
|
+
"allowedEnvVars": ["MY_TOKEN"]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Header values support `$VAR` interpolation — only vars listed in `allowedEnvVars` are expanded.
|
|
101
|
+
|
|
102
|
+
### Type 3: `prompt` (LLM-based judgment)
|
|
103
|
+
|
|
104
|
+
Sends your prompt + event data to Claude Haiku for a yes/no decision. Returns `{"ok": true}` or `{"ok": false, "reason": "..."}`.
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"type": "prompt",
|
|
109
|
+
"prompt": "Check if all requested tasks are complete. If any remain, respond with {\"ok\": false, \"reason\": \"what remains\"}.",
|
|
110
|
+
"model": "haiku"
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use prompt hooks when you need judgment rather than deterministic rules.
|
|
115
|
+
|
|
116
|
+
### Type 4: `agent` (multi-step verification)
|
|
117
|
+
|
|
118
|
+
Spawns a subagent that can use tools (read files, run commands) to verify conditions, then returns `{"ok": true/false}`.
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"type": "agent",
|
|
123
|
+
"prompt": "Run the test suite. If any tests fail, return {\"ok\": false, \"reason\": \"list of failing tests\"}.",
|
|
124
|
+
"timeout": 120
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use agent hooks when verification requires inspecting actual code/files.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Step 4: Exit Codes and Output
|
|
133
|
+
|
|
134
|
+
For `command` hooks, communication is via stdin/stdout/stderr + exit codes:
|
|
135
|
+
|
|
136
|
+
| Exit Code | Meaning |
|
|
137
|
+
|-----------|---------|
|
|
138
|
+
| `0` | Allow the action. Stdout is added to Claude's context (for SessionStart/UserPromptSubmit hooks). |
|
|
139
|
+
| `2` | **Block** the action. Stderr content is shown to Claude as feedback. |
|
|
140
|
+
| Other | Allow the action. Stderr is logged but NOT shown to Claude. |
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
#!/bin/bash
|
|
144
|
+
INPUT=$(cat) # Read JSON event from stdin
|
|
145
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
|
|
146
|
+
|
|
147
|
+
if echo "$COMMAND" | grep -q "rm -rf"; then
|
|
148
|
+
echo "Blocked: destructive command not allowed" >&2
|
|
149
|
+
exit 2 # Block
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
exit 0 # Allow
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Structured JSON output (instead of exit codes)
|
|
156
|
+
|
|
157
|
+
For more control, exit 0 and print JSON to stdout:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"hookSpecificOutput": {
|
|
162
|
+
"hookEventName": "PreToolUse",
|
|
163
|
+
"permissionDecision": "deny",
|
|
164
|
+
"permissionDecisionReason": "Use rg instead of grep for performance"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Permission decisions** (PreToolUse only):
|
|
170
|
+
- `"allow"` — proceed without showing permission prompt
|
|
171
|
+
- `"deny"` — cancel tool call, send reason to Claude
|
|
172
|
+
- `"ask"` — show normal permission prompt to user
|
|
173
|
+
|
|
174
|
+
**Block continuation** (PostToolUse/Stop):
|
|
175
|
+
```json
|
|
176
|
+
{ "decision": "block", "reason": "Tests are still failing" }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Inject context** (UserPromptSubmit):
|
|
180
|
+
```json
|
|
181
|
+
{ "additionalContext": "Remember: we're in a migration sprint, don't change DB schemas." }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Step 5: Matcher Patterns
|
|
187
|
+
|
|
188
|
+
The `matcher` field is a **regex** that filters when hooks fire. Omit or set to `""` or `"*"` to match everything.
|
|
189
|
+
|
|
190
|
+
| Event | Matcher filters on |
|
|
191
|
+
|-------|--------------------|
|
|
192
|
+
| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | Tool name |
|
|
193
|
+
| `SessionStart` | How session started: `startup` \| `resume` \| `clear` \| `compact` |
|
|
194
|
+
| `SessionEnd` | Why session ended: `clear` \| `logout` \| `prompt_input_exit` \| `other` |
|
|
195
|
+
| `Notification` | Type: `permission_prompt` \| `idle_prompt` \| `auth_success` |
|
|
196
|
+
| `SubagentStart`, `SubagentStop` | Agent type name |
|
|
197
|
+
| `PreCompact` | Trigger: `manual` \| `auto` |
|
|
198
|
+
| `ConfigChange` | Source: `user_settings` \| `project_settings` \| `local_settings` \| `skills` |
|
|
199
|
+
| `UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove` | No matcher — always fires |
|
|
200
|
+
|
|
201
|
+
Matcher is a regex, so:
|
|
202
|
+
- `"Edit|Write"` — matches either Edit or Write tool
|
|
203
|
+
- `"Notebook.*"` — matches any Notebook tool
|
|
204
|
+
- `"mcp__github__.*"` — matches all GitHub MCP tools
|
|
205
|
+
- `"mcp__.*__write.*"` — matches any MCP write tool across servers
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Step 6: JSON Input Schemas
|
|
210
|
+
|
|
211
|
+
Every hook receives a JSON object on stdin. Common fields across all events:
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"session_id": "abc123",
|
|
216
|
+
"cwd": "/Users/abc/project",
|
|
217
|
+
"hook_event_name": "PreToolUse"
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**PreToolUse / PostToolUse / PermissionRequest:**
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"tool_name": "Bash",
|
|
225
|
+
"tool_input": {
|
|
226
|
+
"command": "npm test"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**For Edit/Write tools:**
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"tool_name": "Edit",
|
|
235
|
+
"tool_input": {
|
|
236
|
+
"file_path": "/path/to/file.ts",
|
|
237
|
+
"old_string": "...",
|
|
238
|
+
"new_string": "..."
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**UserPromptSubmit:**
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"prompt": "user's message text here"
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**SessionStart:**
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"source": "startup"
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Stop:**
|
|
258
|
+
```json
|
|
259
|
+
{
|
|
260
|
+
"stop_hook_active": false
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**ConfigChange:**
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"source": "project_settings",
|
|
268
|
+
"file_path": "/path/to/.claude/settings.json"
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Step 7: Common Hook Recipes
|
|
275
|
+
|
|
276
|
+
### Auto-format after edits
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"hooks": {
|
|
281
|
+
"PostToolUse": [{
|
|
282
|
+
"matcher": "Edit|Write",
|
|
283
|
+
"hooks": [{
|
|
284
|
+
"type": "command",
|
|
285
|
+
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null || true"
|
|
286
|
+
}]
|
|
287
|
+
}]
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Desktop notification when Claude needs input
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"hooks": {
|
|
297
|
+
"Notification": [{
|
|
298
|
+
"matcher": "",
|
|
299
|
+
"hooks": [{
|
|
300
|
+
"type": "command",
|
|
301
|
+
"command": "osascript -e 'display notification \"Claude needs your attention\" with title \"Claude Code\"'"
|
|
302
|
+
}]
|
|
303
|
+
}]
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Block edits to protected files
|
|
309
|
+
|
|
310
|
+
**`.claude/hooks/protect-files.sh`:**
|
|
311
|
+
```bash
|
|
312
|
+
#!/bin/bash
|
|
313
|
+
INPUT=$(cat)
|
|
314
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
315
|
+
PROTECTED=(".env" "package-lock.json" ".git/")
|
|
316
|
+
for pattern in "${PROTECTED[@]}"; do
|
|
317
|
+
if [[ "$FILE" == *"$pattern"* ]]; then
|
|
318
|
+
echo "Blocked: $FILE matches protected pattern '$pattern'" >&2
|
|
319
|
+
exit 2
|
|
320
|
+
fi
|
|
321
|
+
done
|
|
322
|
+
exit 0
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**`.claude/settings.json`:**
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"hooks": {
|
|
329
|
+
"PreToolUse": [{
|
|
330
|
+
"matcher": "Edit|Write",
|
|
331
|
+
"hooks": [{
|
|
332
|
+
"type": "command",
|
|
333
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
|
|
334
|
+
}]
|
|
335
|
+
}]
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Re-inject context after compaction
|
|
341
|
+
|
|
342
|
+
```json
|
|
343
|
+
{
|
|
344
|
+
"hooks": {
|
|
345
|
+
"SessionStart": [{
|
|
346
|
+
"matcher": "compact",
|
|
347
|
+
"hooks": [{
|
|
348
|
+
"type": "command",
|
|
349
|
+
"command": "echo 'Reminder: use Bun not npm. Current sprint: auth refactor. Run bun test before commits.'"
|
|
350
|
+
}]
|
|
351
|
+
}]
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Log all bash commands
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"hooks": {
|
|
361
|
+
"PostToolUse": [{
|
|
362
|
+
"matcher": "Bash",
|
|
363
|
+
"hooks": [{
|
|
364
|
+
"type": "command",
|
|
365
|
+
"command": "jq -r '[now|todate, .tool_input.command] | @tsv' >> ~/.claude/bash-log.tsv"
|
|
366
|
+
}]
|
|
367
|
+
}]
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Keep Claude working until tests pass (Stop hook)
|
|
373
|
+
|
|
374
|
+
```json
|
|
375
|
+
{
|
|
376
|
+
"hooks": {
|
|
377
|
+
"Stop": [{
|
|
378
|
+
"hooks": [{
|
|
379
|
+
"type": "agent",
|
|
380
|
+
"prompt": "Run `npm test` and check the output. If any tests fail, respond with {\"ok\": false, \"reason\": \"X tests failing: [test names]\"}. If all tests pass or there are no tests, respond with {\"ok\": true}.",
|
|
381
|
+
"timeout": 120
|
|
382
|
+
}]
|
|
383
|
+
}]
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Validate only READ-only SQL (PreToolUse + hook)
|
|
389
|
+
|
|
390
|
+
**`.claude/hooks/validate-readonly-sql.sh`:**
|
|
391
|
+
```bash
|
|
392
|
+
#!/bin/bash
|
|
393
|
+
INPUT=$(cat)
|
|
394
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
395
|
+
if echo "$CMD" | grep -iE '\b(INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE|REPLACE|MERGE)\b' > /dev/null 2>&1; then
|
|
396
|
+
echo "Blocked: only SELECT queries are allowed in this context" >&2
|
|
397
|
+
exit 2
|
|
398
|
+
fi
|
|
399
|
+
exit 0
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Audit config changes
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"hooks": {
|
|
407
|
+
"ConfigChange": [{
|
|
408
|
+
"matcher": "",
|
|
409
|
+
"hooks": [{
|
|
410
|
+
"type": "command",
|
|
411
|
+
"command": "jq -c '{ts: now|todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
|
|
412
|
+
}]
|
|
413
|
+
}]
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Stop hook infinite loop prevention
|
|
419
|
+
|
|
420
|
+
Stop hooks can re-trigger themselves. Always check `stop_hook_active`:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
#!/bin/bash
|
|
424
|
+
INPUT=$(cat)
|
|
425
|
+
# If already triggered once, let Claude stop
|
|
426
|
+
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
|
|
427
|
+
exit 0
|
|
428
|
+
fi
|
|
429
|
+
# Your check logic here
|
|
430
|
+
# ...
|
|
431
|
+
exit 2 # Keep Claude working
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Step 8: Hooks in Skills and Agents
|
|
437
|
+
|
|
438
|
+
Define hooks scoped to a skill's or agent's active lifetime in the frontmatter:
|
|
439
|
+
|
|
440
|
+
**In a skill's SKILL.md:**
|
|
441
|
+
```yaml
|
|
442
|
+
---
|
|
443
|
+
name: safe-reviewer
|
|
444
|
+
allowed-tools: Read, Grep
|
|
445
|
+
hooks:
|
|
446
|
+
PostToolUse:
|
|
447
|
+
- matcher: "Bash"
|
|
448
|
+
hooks:
|
|
449
|
+
- type: command
|
|
450
|
+
command: "echo 'Bash used by skill' >> /tmp/skill-log.txt"
|
|
451
|
+
---
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**In an agent's .md file:**
|
|
455
|
+
```yaml
|
|
456
|
+
---
|
|
457
|
+
name: db-agent
|
|
458
|
+
hooks:
|
|
459
|
+
PreToolUse:
|
|
460
|
+
- matcher: "Bash"
|
|
461
|
+
hooks:
|
|
462
|
+
- type: command
|
|
463
|
+
command: ".claude/hooks/validate-sql.sh"
|
|
464
|
+
Stop:
|
|
465
|
+
- hooks:
|
|
466
|
+
- type: command
|
|
467
|
+
command: "./scripts/cleanup.sh"
|
|
468
|
+
---
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
`Stop` hooks in skill/agent frontmatter automatically become `SubagentStop` events at runtime.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Step 9: Troubleshooting
|
|
476
|
+
|
|
477
|
+
**Hook not firing:**
|
|
478
|
+
- Run `/hooks` and confirm it's listed under the correct event
|
|
479
|
+
- Check matcher regex is correct (case-sensitive)
|
|
480
|
+
- `PermissionRequest` hooks don't fire in headless mode (`-p`) — use `PreToolUse` instead
|
|
481
|
+
|
|
482
|
+
**Hook errors in output:**
|
|
483
|
+
- Test manually: `echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh`
|
|
484
|
+
- Make scripts executable: `chmod +x ./my-hook.sh`
|
|
485
|
+
- Use absolute paths or `$CLAUDE_PROJECT_DIR` to reference scripts
|
|
486
|
+
- Install `jq` if missing: `brew install jq`
|
|
487
|
+
|
|
488
|
+
**Stop hook loops forever:**
|
|
489
|
+
```bash
|
|
490
|
+
# Add this at the top of your stop hook script:
|
|
491
|
+
INPUT=$(cat)
|
|
492
|
+
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
|
|
493
|
+
exit 0
|
|
494
|
+
fi
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**JSON output ignored:**
|
|
498
|
+
- Don't mix: if you exit 2, JSON is ignored. For JSON output, exit 0.
|
|
499
|
+
- Profile echo statements can corrupt JSON: wrap them in `if [[ $- == *i* ]]; then`.
|
|
500
|
+
|
|
501
|
+
**Debug all hook execution:**
|
|
502
|
+
- Press `Ctrl+O` to toggle verbose mode — shows hook output in transcript
|
|
503
|
+
- Or run `claude --debug` for full hook execution details
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Step 10: Notification Hook Matchers
|
|
508
|
+
|
|
509
|
+
The `Notification` event supports specific matchers to target different notification types:
|
|
510
|
+
|
|
511
|
+
| Matcher | Fires when |
|
|
512
|
+
|---------|-----------|
|
|
513
|
+
| `permission_prompt` | Claude needs approval for a tool (permission dialog appearing) |
|
|
514
|
+
| `idle_prompt` | Claude has finished responding and is waiting for your input |
|
|
515
|
+
| `auth_success` | Authentication / OAuth flow completes |
|
|
516
|
+
| `elicitation_dialog` | Claude is asking you a question (AskUserQuestion tool) |
|
|
517
|
+
| `""` (empty) | All notification types |
|
|
518
|
+
|
|
519
|
+
**Example: different sounds for different notifications:**
|
|
520
|
+
|
|
521
|
+
```json
|
|
522
|
+
{
|
|
523
|
+
"hooks": {
|
|
524
|
+
"Notification": [
|
|
525
|
+
{
|
|
526
|
+
"matcher": "permission_prompt",
|
|
527
|
+
"hooks": [{
|
|
528
|
+
"type": "command",
|
|
529
|
+
"command": "afplay /System/Library/Sounds/Ping.aiff"
|
|
530
|
+
}]
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
"matcher": "idle_prompt",
|
|
534
|
+
"hooks": [{
|
|
535
|
+
"type": "command",
|
|
536
|
+
"command": "afplay /System/Library/Sounds/Glass.aiff"
|
|
537
|
+
}]
|
|
538
|
+
}
|
|
539
|
+
]
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## Step 11: Managed Settings for Hooks
|
|
547
|
+
|
|
548
|
+
For enterprise / team deployments, these settings control hooks at the org level (set in managed policy files):
|
|
549
|
+
|
|
550
|
+
| Setting | Effect |
|
|
551
|
+
|---------|--------|
|
|
552
|
+
| `allowManagedHooksOnly: true` | Blocks user/project/plugin hooks — only managed hooks run |
|
|
553
|
+
| `allowedHttpHookUrls: ["https://hooks.company.com/*"]` | HTTP hooks can only call these URLs (wildcard supported) |
|
|
554
|
+
| `allowedHttpHookUrls: []` | Block all HTTP hook calls |
|
|
555
|
+
| `httpHookAllowedEnvVars: ["MY_TOKEN"]` | Env vars HTTP hooks can interpolate (intersection with hook's own `allowedEnvVars`) |
|
|
556
|
+
|
|
557
|
+
**Example managed settings file:**
|
|
558
|
+
|
|
559
|
+
```json
|
|
560
|
+
{
|
|
561
|
+
"allowManagedHooksOnly": true,
|
|
562
|
+
"allowedHttpHookUrls": [
|
|
563
|
+
"https://hooks.company.com/*",
|
|
564
|
+
"https://audit.internal.company.com/claude"
|
|
565
|
+
],
|
|
566
|
+
"httpHookAllowedEnvVars": ["AUDIT_TOKEN", "TEAM_ID"]
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
When `allowedHttpHookUrls` is `undefined` (not set), there are no URL restrictions. When set to `[]`, all HTTP hooks are blocked.
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## Environment Variables Available in Hooks
|
|
575
|
+
|
|
576
|
+
- `$CLAUDE_PROJECT_DIR` — absolute path to the project root
|
|
577
|
+
- `$CLAUDE_SESSION_ID` — current session ID
|
|
578
|
+
- `$MY_VAR` — any env var listed in `allowedEnvVars` (HTTP hooks only)
|