scaffold-cc-hooks
Audit the target project first, then scaffold Claude Code hooks with a deterministic bash-first layout.
Decision Tree
What is the user asking for?
- New Claude Code hooks in a project with no hook setup yet: Run live docs verification, audit the project, choose a hook plan, then scaffold.
- Existing
.claude/settings*.jsonor.claude/hooks/files: Audit what already exists, chooseadditiveoroverhaul, then regenerate only the managed hook layer. - Existing hooks that show up in
/hooksbut never actually fire: Treat workspace trust as the first diagnostic. Check~/.claude.jsonfor the exact project path before debugging settings or script logic, then offer to enable trust if it is still off. - Existing hooks plus possible Claude Code feature drift: Verify the live official hook event list before writing files. If the docs changed, update the scaffold inputs first.
- Explanation only, not implementation: Read
references/hook-events.mdandreferences/scaffold-layout.md, then answer without scaffolding.
Quick Reference
| Task | Action |
|---|---|
| Verify the current official hook model | Read the live official docs at https://code.claude.com/docs/en/hooks and https://code.claude.com/docs/en/hooks-guide, then compare them to assets/hook-events.json |
| Audit a target repo | Run scripts/audit_project.sh /path/to/project |
| Check whether Claude Code trusts the target workspace | Run scripts/check_workspace_trust.sh /path/to/project --json |
| Enable workspace trust for the target workspace | Run scripts/check_workspace_trust.sh /path/to/project --enable |
| Understand the event catalog | Read references/hook-events.md |
| Decide additive vs overhaul | Read references/merge-strategy.md |
| Generate or refresh the managed hook scaffold | Run `scripts/scaffold_hooks.sh --project /path/to/project --plan /path/to/plan.json --mode additive |
| Merge generated hooks into settings | Let scripts/scaffold_hooks.sh call scripts/merge_settings.sh, or run the merge script directly |
| Regenerate the hooks README in a target project | Run scripts/render_hooks_readme.sh --project /path/to/project --plan /path/to/plan.json |
Non-Negotiable Workflow
- Verify the live official Claude Code hook docs before planning any scaffold.
- Compare the live event list, hook type support, and async rules with
assets/hook-events.json. - Audit the target project in detail before deciding which events to enable.
- Inspect any existing
.claude/settings.json,.claude/settings.local.json,.claude/hooks/,CLAUDE.md,.claude/rules/, and related automation files before choosing a merge mode. - Produce or update a concrete hook plan JSON. Keep the scaffold deterministic by putting project-specific judgment into the plan, not into the scaffold script.
- Scaffold every current hook event as a commented bash stub under the managed hook root, even if the event stays disabled in settings.
- Wire only the enabled events into the chosen settings file so the project does not pay runtime cost for inactive stubs.
- Regenerate
.claude/hooks/README.mdso the project always has a readable event map. - If the user reports that hooks are registered but not firing, or you just completed a real scaffold and need to verify the setup, check or explicitly offer to check workspace trust for the exact project path before debugging hook logic.
- If trust is disabled, explain that
hasTrustDialogAcceptedis false for that project. Offer the user two recovery paths: accept the dialog in a fresh Claude Code session, or flip the flag directly. Only mutate~/.claude.jsonwhen the user asks you to do so or explicitly asks you to ensure trust is enabled.
Trust First Heuristic
Default to a trust check early when any of these signals appear:
- the user says hooks are not activating, not firing, or being ignored
/hooksshows registered handlers with the expected counts, but nothing executes- the hook scripts work when run by hand, but Claude Code never invokes them
- you just scaffolded hooks and the user wants you to confirm they actually work
Use this flow:
- Canonicalize the target path first. Trust is keyed by the exact absolute project path.
- Run
scripts/check_workspace_trust.sh /path/to/project --json. - If status is
untrusted, tell the user the flag is false and offer to enable it. - If the user wants it fixed, run
scripts/check_workspace_trust.sh /path/to/project --enable. - Only after trust is confirmed should you spend time debugging settings merges, hook matchers, script permissions, or hook logic.
Live Docs First
The official Claude Code docs are the source of truth:
https://code.claude.com/docs/en/hookshttps://code.claude.com/docs/en/hooks-guide
Use the two reading.sh articles only as secondary material for practical patterns and trade-off language:
https://reading.sh/claude-code-hooks-a-bookmarkable-guide-to-git-automation-11b4516adc5dhttps://reading.sh/claude-code-async-hooks-what-they-are-and-when-to-use-them-61b21cd71aad
If the official docs and the secondary articles disagree, follow the official docs and update the local references.
Project Analysis Rules
Before choosing any hook structure, inspect:
- repo root and workspace shape
- the exact absolute project path Claude Code will trust, because trust is keyed by path in
~/.claude.json - languages and package managers
- build, test, lint, and format entry points
- monorepo tools like Turborepo, Nx, pnpm workspaces, Bun workspaces, or custom task runners
- existing Claude Code settings, rules, hooks, plugins, and skills
- existing Git hooks, Husky, Lefthook, or CI gates
- sensitive paths like
.env, secrets, migrations, lockfiles, generated code, and infra directories - environment reload needs such as
direnv,.envrc, or per-directory tooling
Run scripts/audit_project.sh first, then read references/project-analysis.md when you need the full checklist.
Deterministic vs Project-Specific Work
Keep these parts deterministic:
- managed hook root path
- event stub filenames
- generated settings fragment shape
- merge behavior for previously managed hooks
- hooks README generation
- event manifest coverage for every current official hook event
Allow these parts to stay project-specific:
- which events are enabled
- event matchers
- sync vs async choice
iffilters on tool events- timeouts
- the actual logic inside enabled event scripts
- whether the refresh is
additiveoroverhaul
Repeat-Run Rules
When the skill is invoked again against a project:
- Re-run live docs verification before assuming the event set is unchanged.
- Re-audit the project before assuming the current hook plan still fits.
- If the user says hooks never fire, or the scaffold needs verification, re-check workspace trust for the exact project path before assuming the generated settings are wrong.
- Preserve non-managed hooks by default.
- Treat previously generated hooks under the managed root as replaceable in
overhaulmode. - Treat previously generated hooks as append-only in
additivemode unless a missing event or stale README requires a refresh. - If new official hook events exist, add new stubs and README entries even if the project keeps them disabled.
Scaffold Rules
- Generate bash scripts, not Python, for the project hook runtime.
- Comment the generated bash stubs in plain language.
- Use
$CLAUDE_PROJECT_DIRin generated command paths. - Default to a managed root of
.claude/hooks/generatedunless the project already has a stronger convention. - Default to
.claude/settings.jsonwhen the hook setup should be shared. Use.claude/settings.local.jsononly when the project needs machine-local behavior or already uses that pattern. - Keep one managed script per event so the event map stays obvious.
- Keep the merged settings deterministic: remove only previously managed handlers, never unrelated custom hooks.
Reading Guide
| Need | Read |
|---|---|
| Full audit checklist and what to inspect first | references/project-analysis.md |
| Current official event list and support matrix | references/hook-events.md |
| Managed folder layout and plan file shape | references/scaffold-layout.md |
| Additive versus overhaul behavior | references/merge-strategy.md |
Async, if, shell, and settings pitfalls | references/gotchas.md |
Operational Scripts
scripts/audit_project.shbuilds a project profile from real repo signals.scripts/check_workspace_trust.shchecks or enables Claude Code workspace trust for an exact project path.scripts/scaffold_hooks.shrenders the managed hook tree, manifest, README, and settings fragment.scripts/merge_settings.shpreserves non-managed hooks while replacing previously managed handlers.scripts/render_hooks_readme.shrebuilds.claude/hooks/README.mdfrom the manifest and the current plan.
Gotchas
asynconly applies to command hooks, and async hooks cannot block or steer Claude after the triggering action is already done.- The
iffield only works on tool events and requires Claude Code v2.1.85 or later. Stophooks can loop forever unless you honorstop_hook_active.- Hook shells are non-interactive. Shell profile noise can break JSON output.
PermissionRequestdoes not fire in non-interactive-pmode.- Hooks do not fire in untrusted workspaces. Claude Code gates execution on
hasTrustDialogAcceptedin~/.claude.jsonunder.projects["/absolute/path/to/project"]. When hooks look installed but never run, or after a real scaffold, check trust first withscripts/check_workspace_trust.shbefore blaming the hook config. Seereferences/gotchas.mdgotcha 9 for the exact recovery flow.