@eltonssouza/development-utility-kit 0.10.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.
Files changed (131) hide show
  1. package/.claude/agents/README.md +24 -0
  2. package/.claude/agents/analyst.md +198 -0
  3. package/.claude/agents/backend-developer.md +126 -0
  4. package/.claude/agents/brain-keeper.md +229 -0
  5. package/.claude/agents/code-reviewer.md +181 -0
  6. package/.claude/agents/database-engineer.md +94 -0
  7. package/.claude/agents/devops-engineer.md +141 -0
  8. package/.claude/agents/frontend-developer.md +97 -0
  9. package/.claude/agents/gate-keeper.md +118 -0
  10. package/.claude/agents/migrator.md +291 -0
  11. package/.claude/agents/mobile-developer.md +80 -0
  12. package/.claude/agents/n8n-specialist.md +94 -0
  13. package/.claude/agents/product-owner.md +115 -0
  14. package/.claude/agents/qa-engineer.md +232 -0
  15. package/.claude/agents/release-engineer.md +204 -0
  16. package/.claude/agents/scaffold.md +87 -0
  17. package/.claude/agents/security-engineer.md +199 -0
  18. package/.claude/agents/sprint-runner.md +46 -0
  19. package/.claude/agents/stack-resolver.md +104 -0
  20. package/.claude/agents/tech-lead.md +182 -0
  21. package/.claude/agents/update-template.md +54 -0
  22. package/.claude/agents/ux-designer.md +118 -0
  23. package/.claude/hooks/flow-guard.js +261 -0
  24. package/.claude/hooks/flow-state.js +197 -0
  25. package/.claude/local/CLAUDE.md +71 -0
  26. package/.claude/settings.json +55 -0
  27. package/.claude/skills/README.md +331 -0
  28. package/.claude/skills/active-project/SKILL.md +131 -0
  29. package/.claude/skills/api-integration-test/SKILL.md +84 -0
  30. package/.claude/skills/auto-test-guard/SKILL.md +239 -0
  31. package/.claude/skills/auto-test-guard/resources/backend-tests.md +20 -0
  32. package/.claude/skills/auto-test-guard/resources/e2e-tests.md +24 -0
  33. package/.claude/skills/auto-test-guard/resources/execution-report.md +49 -0
  34. package/.claude/skills/auto-test-guard/resources/frontend-tests.md +18 -0
  35. package/.claude/skills/auto-test-guard/resources/initial-setup.md +108 -0
  36. package/.claude/skills/auto-test-guard/resources/run-suite.md +48 -0
  37. package/.claude/skills/auto-test-guard/resources/senior-gate.md +19 -0
  38. package/.claude/skills/brain-keeper/SKILL.md +62 -0
  39. package/.claude/skills/brain-keeper/obsidian/app.json +9 -0
  40. package/.claude/skills/brain-keeper/obsidian/appearance.json +4 -0
  41. package/.claude/skills/brain-keeper/obsidian/core-plugins.json +20 -0
  42. package/.claude/skills/brain-keeper/obsidian/daily-notes.json +5 -0
  43. package/.claude/skills/brain-keeper/obsidian/graph.json +32 -0
  44. package/.claude/skills/brain-keeper/obsidian/snippets/folder-colors.css +90 -0
  45. package/.claude/skills/brain-keeper/obsidian/templates.json +5 -0
  46. package/.claude/skills/brain-keeper/templates/README.md +51 -0
  47. package/.claude/skills/brain-keeper/templates/adr.md +40 -0
  48. package/.claude/skills/brain-keeper/templates/bug.md +35 -0
  49. package/.claude/skills/brain-keeper/templates/daily.md +38 -0
  50. package/.claude/skills/brain-keeper/templates/feature.md +62 -0
  51. package/.claude/skills/brain-keeper/templates/meeting.md +34 -0
  52. package/.claude/skills/brain-keeper/templates/tech-debt.md +21 -0
  53. package/.claude/skills/caveman/SKILL.md +189 -0
  54. package/.claude/skills/create-stack-pack/SKILL.md +281 -0
  55. package/.claude/skills/grill-me/SKILL.md +80 -0
  56. package/.claude/skills/pair-debug/SKILL.md +288 -0
  57. package/.claude/skills/prd-ready-check/SKILL.md +86 -0
  58. package/.claude/skills/project-manager/SKILL.md +334 -0
  59. package/.claude/skills/quality-standards/SKILL.md +203 -0
  60. package/.claude/skills/quick-feature/SKILL.md +266 -0
  61. package/.claude/skills/run-sprint/SKILL.md +41 -0
  62. package/.claude/skills/scaffold/SKILL.md +60 -0
  63. package/.claude/skills/stack-discovery/SKILL.md +161 -0
  64. package/.claude/skills/test-coverage-auditor/SKILL.md +87 -0
  65. package/.claude/skills/to-issues/SKILL.md +163 -0
  66. package/.claude/skills/to-prd/SKILL.md +130 -0
  67. package/.claude/skills/update-template/SKILL.md +256 -0
  68. package/.claude/stacks/CODEOWNERS +30 -0
  69. package/.claude/stacks/README.md +97 -0
  70. package/.claude/stacks/_template.md +116 -0
  71. package/.claude/stacks/dotnet/aspire-9.md +528 -0
  72. package/.claude/stacks/go/gin-1.10.md +570 -0
  73. package/.claude/stacks/java/spring-boot-3.md +376 -0
  74. package/.claude/stacks/java/spring-boot-4.md +438 -0
  75. package/.claude/stacks/node/express-5.md +538 -0
  76. package/.claude/stacks/python/django-5.md +483 -0
  77. package/.claude/stacks/python/fastapi-0.115.md +522 -0
  78. package/.claude/stacks/typescript/angular-18.md +420 -0
  79. package/.claude/stacks/typescript/angular-19.md +397 -0
  80. package/.claude/stacks/typescript/angular-21.md +494 -0
  81. package/CLAUDE.md +472 -0
  82. package/README.md +412 -0
  83. package/bin/cli.js +848 -0
  84. package/bin/lib/adr.js +146 -0
  85. package/bin/lib/backup.js +62 -0
  86. package/bin/lib/detect-stack.js +476 -0
  87. package/bin/lib/doctor.js +527 -0
  88. package/bin/lib/help.js +328 -0
  89. package/bin/lib/identity.js +108 -0
  90. package/bin/lib/lint-allowlist.json +15 -0
  91. package/bin/lib/lint.js +798 -0
  92. package/bin/lib/local-dir.js +68 -0
  93. package/bin/lib/manifest.js +236 -0
  94. package/bin/lib/sync-all.js +394 -0
  95. package/bin/lib/version-check.js +398 -0
  96. package/dashboard/db.js +321 -0
  97. package/dashboard/package.json +22 -0
  98. package/dashboard/public/app.js +853 -0
  99. package/dashboard/public/content/docs/agents-reference.en.md +911 -0
  100. package/dashboard/public/content/docs/architecture-overview.en.md +252 -0
  101. package/dashboard/public/content/docs/autonomy-matrix.en.md +186 -0
  102. package/dashboard/public/content/docs/cli-reference.en.md +538 -0
  103. package/dashboard/public/content/docs/git-flow.en.md +525 -0
  104. package/dashboard/public/content/docs/honcho-memory.en.md +394 -0
  105. package/dashboard/public/content/docs/hooks-reference.en.md +404 -0
  106. package/dashboard/public/content/docs/pipeline.en.md +414 -0
  107. package/dashboard/public/content/docs/plugins.en.md +289 -0
  108. package/dashboard/public/content/docs/quality-gate.en.md +315 -0
  109. package/dashboard/public/content/docs/skills-reference.en.md +484 -0
  110. package/dashboard/public/content/docs/stack-rules.en.md +362 -0
  111. package/dashboard/public/content/docs/troubleshooting.en.md +565 -0
  112. package/dashboard/public/content/manifest.json +114 -0
  113. package/dashboard/public/content/manual/backend.en.md +1053 -0
  114. package/dashboard/public/content/manual/existing-project.en.md +848 -0
  115. package/dashboard/public/content/manual/frontend.en.md +1008 -0
  116. package/dashboard/public/content/manual/fullstack.en.md +1459 -0
  117. package/dashboard/public/content/manual/mobile.en.md +837 -0
  118. package/dashboard/public/content/manual/quickstart.en.md +169 -0
  119. package/dashboard/public/index.html +217 -0
  120. package/dashboard/public/style.css +857 -0
  121. package/dashboard/public/vendor/marked.min.js +69 -0
  122. package/dashboard/rtk.js +143 -0
  123. package/dashboard/server-app.js +421 -0
  124. package/dashboard/server.js +104 -0
  125. package/dashboard/test/sprint1.test.js +406 -0
  126. package/dashboard/test/sprint2.test.js +571 -0
  127. package/dashboard/test/sprint3.test.js +560 -0
  128. package/package.json +33 -0
  129. package/scripts/hooks/subagent-telemetry.sh +14 -0
  130. package/scripts/hooks/telemetry-writer.js +250 -0
  131. package/scripts/latest-versions.json +56 -0
@@ -0,0 +1,404 @@
1
+ # Hooks Reference
2
+
3
+ This document describes every Claude Code hook active in the `development-utility-kit` harness. Hooks are extension points executed by the Claude Code runtime on specific lifecycle events — do not confuse them with Git hooks (`pre-commit`, `pre-push`), which live under `.git/hooks/` and are managed by Git.
4
+
5
+ Claude Code hooks are registered in `.claude/settings.json` (project scope) or in `~/.claude/settings.json` (user global scope). Each hook is an `event + shell command` pair, optionally filtered by a `matcher` that restricts which tool or prompt pattern fires the hook.
6
+
7
+ Audience: Java/Angular developers working inside the local harness dashboard or in projects that adopted the template.
8
+
9
+ ---
10
+
11
+ ## Overview — Claude Code events
12
+
13
+ | Event | Fires when | Can block? |
14
+ |---|---|---|
15
+ | `PreToolUse` | Before a tool (Bash, Edit, Write, etc.) runs | Yes — exit != 0 cancels the tool |
16
+ | `PostToolUse` | After a tool completes successfully | No — exit code is informational |
17
+ | `UserPromptSubmit` | Before the user prompt is sent to the model | Stdin/stdout can alter the prompt; exit != 0 cancels |
18
+ | `Stop` | When the session / turn ends | Does not block |
19
+ | `Notification` | When Claude Code needs user attention | Does not block |
20
+ | `SubagentStop` | When a sub-agent (Task tool) finishes | Does not block |
21
+
22
+ The `matcher` is matched against the tool name (`Bash`, `Edit`, `MultiEdit`, `Write`, `Read`, ...) or uses `*` as wildcard for events that have no tool (`UserPromptSubmit`, `Stop`, `Notification`).
23
+
24
+ ---
25
+
26
+ ## Master table — Active hooks in the harness
27
+
28
+ The table summarizes every hook registered in `.claude/settings.json`. Per-hook details follow in the next sections.
29
+
30
+ | Event | Matcher | Script / Command | Purpose | Failure mode |
31
+ |---|---|---|---|---|
32
+ | `PreToolUse` | `Bash` | `scripts/hooks/pre-push-guard.sh` | Block unsafe `git push` (protected branch, no approval) | Exit != 0 blocks the Bash command |
33
+ | `PreToolUse` | `Bash` | `scripts/hooks/block-test-deletion.sh` | Forbid `rm` / destructive edits on test files without justification | Exit != 0 blocks the Bash command |
34
+ | `PostToolUse` | `Edit\|MultiEdit\|Write` | `npx prettier --write` (inline) | Auto-format `.ts/.tsx/.html/.scss/.css` after editing | Silent — prettier failures never break the edit |
35
+ | `PostToolUse` | `Edit\|MultiEdit\|Write` | `scripts/hooks/post-edit-test-reminder.sh` | Remind to run tests / coverage after editing source | Always exit 0 — warning only |
36
+ | `UserPromptSubmit` | `*` | `scripts/hooks/prompt-gate-reminder.sh` | Remind senior+ gate before critical tasks | Always exit 0 — informational |
37
+ | `Stop` | `*` | `scripts/hooks/stop-brain-reminder.sh` | Remind to invoke `brain-keeper` at end of PLAN | Always exit 0 |
38
+ | `Notification` | `*` | inline `notify-send` / `osascript` | Desktop notification when Claude needs attention | Silent |
39
+ | `SubagentStop` (global) | `*` | `scripts/hooks/subagent-telemetry.sh` -> `telemetry-writer.js` | Persist sub-agent execution telemetry to `~/.claude/telemetry.db` | Silent — `exit 0` even on failure |
40
+
41
+ ---
42
+
43
+ ## Harness hooks (`scripts/hooks/`)
44
+
45
+ ### PreToolUse — `pre-push-guard.sh`
46
+
47
+ **Trigger**: before any execution of the `Bash` tool.
48
+
49
+ **Command registered in `.claude/settings.json`**:
50
+
51
+ ```json
52
+ {
53
+ "matcher": "Bash",
54
+ "hooks": [
55
+ {
56
+ "type": "command",
57
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/hooks/pre-push-guard.sh"
58
+ }
59
+ ]
60
+ }
61
+ ```
62
+
63
+ **What the script does**: inspects the Bash command about to run. If it contains a `git push` targeting a protected branch (`main`, `develop`, `master`) without expected override flags, it fails with exit code 1 and prints the reason to stderr.
64
+
65
+ **When it blocks**:
66
+ - `git push origin main` directly, without an approved PR
67
+ - `git push --force` on a shared branch
68
+ - Push to a remote considered "protected" by the harness
69
+
70
+ **How to debug**:
71
+
72
+ ```bash
73
+ echo '{"command":"git push origin main"}' | bash scripts/hooks/pre-push-guard.sh
74
+ echo $? # expected: != 0
75
+ ```
76
+
77
+ ---
78
+
79
+ ### PreToolUse — `block-test-deletion.sh`
80
+
81
+ **Trigger**: before any execution of the `Bash` tool.
82
+
83
+ **Command**:
84
+
85
+ ```json
86
+ {
87
+ "matcher": "Bash",
88
+ "hooks": [
89
+ {
90
+ "type": "command",
91
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/hooks/block-test-deletion.sh"
92
+ }
93
+ ]
94
+ }
95
+ ```
96
+
97
+ **What the script does**: parses the Bash command looking for destructive operations on test files (`rm -rf src/**/*.test.ts`, `git rm src/**/*Test.java`, etc.). Sub-agents must not delete tests — only `tech-lead` approves removal, and it must come with a justification recorded in the PLAN.
98
+
99
+ **When it blocks**:
100
+ - Direct `rm` on `*.test.ts`, `*.spec.ts`, `*Test.java`, `*IT.java` files
101
+ - `git rm` inside `tests/` or `src/test/` directories
102
+ - `> /dev/null` redirect that wipes test content
103
+
104
+ **How to debug**:
105
+
106
+ ```bash
107
+ echo '{"command":"rm src/app/foo.spec.ts"}' | bash scripts/hooks/block-test-deletion.sh
108
+ ```
109
+
110
+ ---
111
+
112
+ ### PostToolUse — Inline auto-formatting (prettier)
113
+
114
+ **Trigger**: after `Edit`, `MultiEdit` or `Write` completes successfully.
115
+
116
+ **Inline command in `.claude/settings.json`**:
117
+
118
+ ```bash
119
+ json=$(cat); file_path=$(echo "$json" | jq -r '.file_path // empty'); \
120
+ if [[ "$file_path" =~ \.(ts|tsx|html|scss|css)$ ]] \
121
+ && command -v npx &>/dev/null \
122
+ && [ -f node_modules/.bin/prettier ]; then \
123
+ npx prettier --write "$file_path" 2>/dev/null; \
124
+ fi
125
+ ```
126
+
127
+ **What it does**: reads the hook JSON payload (which contains `file_path`), checks the extension and fires `npx prettier --write` if prettier is installed locally. Keeps formatting consistent with no manual intervention.
128
+
129
+ **When it blocks**: never. Everything is silenced by `2>/dev/null` and the exit code is ignored.
130
+
131
+ **How to debug**:
132
+
133
+ ```bash
134
+ echo '{"file_path":"src/app/foo.component.ts"}' | bash -c '<command above>'
135
+ ```
136
+
137
+ **Prerequisite**: `prettier` must exist at `node_modules/.bin/prettier` (install with `npm i -D prettier`).
138
+
139
+ ---
140
+
141
+ ### PostToolUse — `post-edit-test-reminder.sh`
142
+
143
+ **Trigger**: after `Edit`, `MultiEdit` or `Write`.
144
+
145
+ **Command**:
146
+
147
+ ```json
148
+ {
149
+ "matcher": "Edit|MultiEdit|Write",
150
+ "hooks": [
151
+ {
152
+ "type": "command",
153
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/hooks/post-edit-test-reminder.sh"
154
+ }
155
+ ]
156
+ }
157
+ ```
158
+
159
+ **What it does**: reminds the running agent to execute the relevant suite (`mvn test` on backend, `npm test` on frontend) if the edited file belongs to `domain/`, `application/` or `src/app/`. Prints the message to stderr; never blocks.
160
+
161
+ ---
162
+
163
+ ### UserPromptSubmit — `prompt-gate-reminder.sh`
164
+
165
+ **Trigger**: before each user prompt is sent to the model.
166
+
167
+ **Command**:
168
+
169
+ ```json
170
+ {
171
+ "matcher": "*",
172
+ "hooks": [
173
+ {
174
+ "type": "command",
175
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/hooks/prompt-gate-reminder.sh"
176
+ }
177
+ ]
178
+ }
179
+ ```
180
+
181
+ **What it does**: detects critical keywords in the prompt ("merge", "deploy", "release", "production") and appends a stderr reminder to invoke `gate-keeper` before proceeding. Useful in long sessions where the senior+ gate might have been forgotten.
182
+
183
+ ---
184
+
185
+ ### Stop — `stop-brain-reminder.sh`
186
+
187
+ **Trigger**: when the session turn ends.
188
+
189
+ **Command**:
190
+
191
+ ```json
192
+ {
193
+ "matcher": "*",
194
+ "hooks": [
195
+ {
196
+ "type": "command",
197
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/hooks/stop-brain-reminder.sh"
198
+ }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ **What it does**: at the end of a `PLAN_*.md` or a relevant feature, reminds the user to invoke `brain-keeper` to record daily / feature / ADR / bug / tech-debt entries under `docs/brain/`. Does not block.
204
+
205
+ ---
206
+
207
+ ### Notification — Desktop notify
208
+
209
+ **Trigger**: when Claude Code requires user attention (e.g., waiting for tool approval).
210
+
211
+ **Inline command**:
212
+
213
+ ```bash
214
+ if command -v notify-send &>/dev/null; then
215
+ notify-send 'Claude Code' 'Needs your attention'
216
+ elif command -v osascript &>/dev/null; then
217
+ osascript -e 'display notification "Needs your attention" with title "Claude Code"'
218
+ fi
219
+ ```
220
+
221
+ **What it does**: fires a native notification (Linux via `notify-send`, macOS via `osascript`). On Windows with WSL, you need `notify-send` available in the WSL PATH for it to work.
222
+
223
+ ---
224
+
225
+ ## Telemetry hook — `subagent-telemetry.sh`
226
+
227
+ **Trigger**: global `SubagentStop`. Fires every time a `Task` call (sub-agent) ends, in any project.
228
+
229
+ **Script** (`scripts/hooks/subagent-telemetry.sh`):
230
+
231
+ ```bash
232
+ #!/usr/bin/env bash
233
+ set -uo pipefail
234
+
235
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
236
+ WRITER="${SCRIPT_DIR}/telemetry-writer.js"
237
+
238
+ if [ ! -f "$WRITER" ]; then
239
+ exit 0
240
+ fi
241
+
242
+ node "$WRITER" || exit 0
243
+ ```
244
+
245
+ **Writer** (`scripts/hooks/telemetry-writer.js`): reads the `SubagentStop` JSON payload from stdin, normalises the model name (opus / sonnet / haiku / other) and persists the event in `~/.claude/telemetry.db` via `better-sqlite3` (fallback `sql.js`, final fallback NDJSON queue at `~/.claude/telemetry-queue.ndjson`).
246
+
247
+ **Schema** (created idempotently):
248
+
249
+ ```sql
250
+ CREATE TABLE IF NOT EXISTS events (
251
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
252
+ project_path TEXT NOT NULL,
253
+ project_name TEXT NOT NULL,
254
+ model TEXT NOT NULL,
255
+ input_tokens INTEGER NOT NULL DEFAULT 0,
256
+ output_tokens INTEGER NOT NULL DEFAULT 0,
257
+ subagent_type TEXT,
258
+ session_id TEXT,
259
+ agent_id TEXT,
260
+ agent_type TEXT,
261
+ ts INTEGER NOT NULL
262
+ );
263
+ ```
264
+
265
+ **Failure behavior**: everything is silent. If `node` is not on PATH, if the writer is missing, if SQLite fails — the hook returns exit 0 and the session proceeds normally. Errors are recorded in `~/.claude/telemetry-errors.log` when possible.
266
+
267
+ **Historical bug**: see `docs/brain/bugs/bash-source-hook-path.md`. The original script used `$CLAUDE_PROJECT_DIR` to locate the writer, which fails when the hook is registered globally — `$CLAUDE_PROJECT_DIR` resolves to whatever project is active at the moment, not to the harness repo. Fixed by switching to `$(dirname "${BASH_SOURCE[0]}")` in commit `b7397e2`.
268
+
269
+ ---
270
+
271
+ ## Current state — Pending scripts
272
+
273
+ The following scripts are **referenced** in `.claude/settings.json` but do **not yet exist** under `scripts/hooks/`. They are "configured but not implemented" hooks — create them as the feature demands:
274
+
275
+ - `scripts/hooks/pre-push-guard.sh`
276
+ - `scripts/hooks/block-test-deletion.sh`
277
+ - `scripts/hooks/post-edit-test-reminder.sh`
278
+ - `scripts/hooks/prompt-gate-reminder.sh`
279
+ - `scripts/hooks/stop-brain-reminder.sh`
280
+
281
+ While missing, Claude Code merely logs an execution error per hook and moves on — a `PreToolUse` with a missing script does **not** block (the runtime treats the missing-bash error as a config issue, not a gate failure).
282
+
283
+ To author any of them, follow the next section.
284
+
285
+ ---
286
+
287
+ ## How to add a new hook
288
+
289
+ 1. Edit `.claude/settings.json` and append a block to the desired event array:
290
+
291
+ ```json
292
+ "PreToolUse": [
293
+ {
294
+ "matcher": "Bash",
295
+ "hooks": [
296
+ {
297
+ "type": "command",
298
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/hooks/my-hook.sh"
299
+ }
300
+ ]
301
+ }
302
+ ]
303
+ ```
304
+
305
+ 2. Create the script at `scripts/hooks/my-hook.sh`:
306
+
307
+ ```bash
308
+ #!/usr/bin/env bash
309
+ set -uo pipefail
310
+
311
+ # Read JSON payload from stdin
312
+ payload=$(cat)
313
+
314
+ # Extract relevant field (depends on event)
315
+ command=$(echo "$payload" | jq -r '.command // empty')
316
+
317
+ # Gate logic
318
+ if [[ "$command" =~ rm.*-rf ]]; then
319
+ echo "ERROR: rm -rf blocked by hook" >&2
320
+ exit 1
321
+ fi
322
+
323
+ exit 0
324
+ ```
325
+
326
+ 3. Make the script executable (POSIX):
327
+
328
+ ```bash
329
+ chmod +x scripts/hooks/my-hook.sh
330
+ ```
331
+
332
+ 4. Test by invoking manually with a sample payload:
333
+
334
+ ```bash
335
+ echo '{"command":"rm -rf /"}' | bash scripts/hooks/my-hook.sh
336
+ echo "exit code: $?"
337
+ ```
338
+
339
+ 5. Start a new Claude Code session (hooks are loaded at startup). Trigger the target tool and observe behavior.
340
+
341
+ ---
342
+
343
+ ## Common pitfalls
344
+
345
+ ### 1. Always start with shebang + `set -uo pipefail`
346
+
347
+ ```bash
348
+ #!/usr/bin/env bash
349
+ set -uo pipefail
350
+ ```
351
+
352
+ Without `-u` the script silently tolerates undefined variables; without `pipefail` pipe failures stay invisible. **Do not use `set -e`** in hooks — exit code is your gate and you want explicit control.
353
+
354
+ ### 2. Exit 0 vs exit != 0
355
+
356
+ - `exit 0` on any event = does not block.
357
+ - `exit != 0` on **PreToolUse** = cancels the tool execution (gate).
358
+ - `exit != 0` on **PostToolUse**, **Stop**, **Notification** = informational only; the tool already ran.
359
+ - `exit != 0` on **UserPromptSubmit** = cancels sending the prompt to the model (hard gate).
360
+
361
+ ### 3. Path resolution — global vs project
362
+
363
+ **Global hooks** (`~/.claude/settings.json`): NEVER use `$CLAUDE_PROJECT_DIR` to point to the hook's own artifacts. That variable resolves to the **currently active project**, which may not contain the script. Use `$(dirname "${BASH_SOURCE[0]}")` (the running Bash script's own directory) or a hardcoded absolute path.
364
+
365
+ **Project hooks** (`.claude/settings.json`): `$CLAUDE_PROJECT_DIR` correctly resolves to the repo root, so it is safe.
366
+
367
+ History: the `bash-source-hook-path.md` bug (P1, fixed in `b7397e2`) was precisely this — the global `subagent-telemetry.sh` used `$CLAUDE_PROJECT_DIR` and the writer was never found.
368
+
369
+ ### 4. Windows-friendly
370
+
371
+ Claude Code on Windows runs hooks via Git Bash or WSL. To stay portable:
372
+
373
+ - Use forward slashes in paths whenever possible
374
+ - Do not call CMD/PowerShell utilities — POSIX only (`bash`, `jq`, `node`, `git`)
375
+ - Test in Git Bash before committing
376
+ - Windows absolute paths work if prefixed with `/c/...` (Git Bash) or `C:\\...` escaped
377
+
378
+ ### 5. JSON payload via stdin
379
+
380
+ Most events deliver a JSON payload on stdin. Parse with `jq`:
381
+
382
+ ```bash
383
+ payload=$(cat)
384
+ file_path=$(echo "$payload" | jq -r '.file_path // empty')
385
+ command=$(echo "$payload" | jq -r '.command // empty')
386
+ ```
387
+
388
+ Always use `// empty` in `jq` to avoid the literal `"null"` string when the field is missing.
389
+
390
+ ### 6. Debug logs do not reach the user
391
+
392
+ Stderr is silenced in Claude Code for non-blocking events. For persistent debugging, write to a file:
393
+
394
+ ```bash
395
+ echo "DEBUG: $(date) - hook fired with $file_path" >> /tmp/my-hook.log
396
+ ```
397
+
398
+ ---
399
+
400
+ ## Cross-references
401
+
402
+ - [Architecture overview](architecture-overview) — how hooks fit into the broader pipeline
403
+ - `docs/brain/bugs/bash-source-hook-path.md` — historical path resolution bug
404
+ - `scripts/hooks/telemetry-writer.js` — SQLite writer that feeds the telemetry dashboard