New hooks slot into the event-type structure and read the shared config.json. No long standalone files.

Steps

  1. Pick the eventUserPromptSubmit (inject) · PreToolUse (deny) · PostToolUse · Stop · …
  2. Write the scriptE:\hooks\<id>.mjs; it finds its record in the config.json hooks list by id (== its filename), reads shared + its settings, honors enabled + failPolicy. Copy the pattern from inject-protocol.mjs.
  3. Register it — add a record to the hooks list: id (= filename), name, description, category (push|gate), event, match.tools, script {path,runtime}, scope {projects,paths}, enabled, failPolicy (open|closed), settings — and embed matching defaults in the script.
  4. Wire it — register under its event in Claude Code settings.json + Codex config.toml.
  5. Document it — keep the side rail to three levels:
    • existing event → add a section to hooks/<event>/index.mdx
    • new event → new folder hooks/<event>/ with only index.mdx, then add the folder to hooks/meta.json

Conventions

  • Config, not constants — every tunable goes in config.json; the script holds only fallback defaults.
  • Fail-open — a broken hook must never block a turn (errors → no-op).
  • Bullets-first docs — one registry row in the Hooks index + one event page. No level-4 side-rail pages.
  • Behavior-preserving edits — capture a baseline, change, confirm output unchanged unless the change is the point.

On this page