new-hook
Skill2 starsChanged 7 days agoMITView on GitHub
Scaffold a new Claude Code hook end-to-end from a recipe — produces the hook file, payload fixtures, a Node-based runner, and the ~/.claude/settings.json registration (diff previewed). Use when the user types /new-hook or asks to create / scaffold / add a hook.
- Reads credentials
2 other projects use a version of this.
Adopted by
Most recently taken up by
- childrentime/reactusetheir own version, first seen Oct 9, 2026
- jajupmochi/agent-harnesstheir own version, first seen Jul 10, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- childrentime/reactuse★ 1.1ksame name
- jajupmochi/agent-harness★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository47 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .github/copilot-instructions.mdCopilot instructions
- autonomizeSkill
- blueprintSkill
- brainstormSkill
- captureSkill
- checkpointSkill
- close-outSkill
- coordinateSkill
- cycleSkill
- deliverSkill
- design-proposalSkill
- discoverSkill
- dispatchSkill
- draft-planSkill
- envisionSkill
- executeSkill
- extract-conventionsSkill
- glossarySkill
- humanizeSkill
- human-viewSkill
- improve-promptSkill
- memory-auditSkill
- new-agentSkill
- new-agents-mdSkill
- new-mcpSkill
- orchestrateSkill
- pr-authorSkill
- prepSkill
- pre-pushSkill
- pr-triageSkill
- recapSkill
- retrospect-coreSkill
- reviewSkill
- rulesSkill
- shortcutsSkill
- specSkill
- systematic-debuggingSkill
- test-driven-developmentSkill
- update-changelogSkill
- update-codemapSkill
- using-git-worktreesSkill
- visualise-codemapSkill
- visualSkill
- work-stateSkill
- wrapSkill
- write-testsSkill
The file
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 4 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 15
Do not auto-fire. No session-start or event-hook triggers.
- Distinctive: line 40
Wait for the user's choice (number or name).
- Very specific: line 69
If contexture/hooks/ exists and ~/.claude/hooks/ resolves (via symlink or shared inode) to the same tree → write to contexture/hooks/.
- Distinctive: line 90
abort: stop cleanly. No writes.
- Very specific: line 96
Read ~/.claude/settings.json.
- Distinctive: line 145
Use forward-slash paths in the runner regardless of platform — Node accepts both, and the test scripts must work cross-platform.
- Very specific: line 168
Do not invoke /review. Do not commit.
- Very specific: line 192
Does not edit existing hooks: Use Edit directly. Modifying registration is a manual settings.json edit.
- Very specific: line 195
Does not support languages other than Node.js in v1: All existing hooks are Node, the shared lib is Node, settings.json registers node ....
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
new-hook
The hook scaffold organ. Closes the gap that no other skill helps with: building new Claude Code hooks correctly, including the matcher / event vocabulary, the fail-open exit-code contract, and the high-stakes ~/.claude/settings.json registration edit.
When to run
- User types
/new-hook(explicit trigger). - User says "scaffold a hook," "add a new hook," "create a PreToolUse blocker for X."
- Do not auto-fire. No session-start or event-hook triggers. Mode A only — every run goes through the interview.
Inputs
- Triggering message. Anything after
/new-hookis hint text — used to bias recipe selection but never enough to skip the interview. - Bootstrap state. Whether
contexture/hooks/is the symlink source for~/.claude/hooks/. Detected at scaffold time, not configured. - Existing hooks tree.
~/.claude/hooks/(andcontexture/hooks/when bootstrapped) — used for the collision check on the chosen hook name. - Existing settings.
~/.claude/settings.json— read once for the merge contract.
Procedure
1. Recipe selection
List the recipes available in recipes/. Each recipe folder has a README.md with a one-line description, the matcher, and the event. Show them as a numbered list:
Which recipe?
1. bash-command-blocker (PreToolUse, matcher: Bash) — pattern-match a Bash command, block on hit
2. file-write-blocker (PreToolUse, matcher: Write|Edit|MultiEdit|NotebookEdit) — block file writes by path
3. mcp-tool-blocker (PreToolUse, matcher: mcp__*) — block a specific MCP tool by exact name
4. context-injector (SessionStart, matcher: startup|compact) — inject context at session start
5. session-recovery-advisory (SessionStart, matcher: clear|compact) — scan the prior transcript and nudge to recover something lost across a clear/compact
6. rule-prime (SessionStart[startup|clear|compact] + UserPromptSubmit) — mechanically prime resolved architectural rules into context; floor at session start, incremental tiers per promptWait for the user's choice (number or name). Reject anything outside the list with the same prompt.
2. Hook name
Prompt:
What should the hook be called? (lowercase-with-dashes, no extension, e.g. secrets-write-blocker)Validate: lowercase letters, digits, hyphens only. No leading/trailing hyphen, no double hyphens. Reject otherwise with the rule restated.
3. Recipe-specific parameter interview
Each recipe's README.md declares its placeholders. Ask one question per placeholder, in order. Recipe-by-recipe:
- bash-command-blocker:
__PATTERN__(regex, no surrounding slashes — e.g.\bgit\s+push\s+--force\b),__BLOCK_REASON__(one-line message shown to the assistant when blocked). - file-write-blocker:
__PATH_GLOB__(gitignore-style glob, e.g.**/secrets/**or**/.env*),__BLOCK_REASON__. - mcp-tool-blocker:
__TOOL_NAME__(exact tool name, e.g.mcp__plugin_some-server_search__do),__BLOCK_REASON__. - context-injector:
__MATCHER__(one ofstartup,compact,startup|compact),__CONTEXT_SOURCE_TYPE__(one ofliteral,file,command),__CONTEXT_SOURCE_VALUE__(the literal string, the file path to read, or the command to invoke depending on the type). - session-recovery-advisory:
__SIGNAL_SCAN_FN__(body ofscan(events) -> string[]returning human labels for unresolved signals; empty = silent),__WATERMARK_FN__(body ofisWatermark(event) -> boolmarking a transcript event as resolved/saved),__ADVISORY_PREFIX__(leading sentence of the nudge),__MATCHER_TRIGGER__(fixture-only — a matchingsourcevalue,clearorcompact). - rule-prime: no template placeholders — the hook logic is the recipe, copied verbatim. One fixture-only placeholder:
__REPO_ROOT__(absolute path to a repo the resolver can census for the floor; set it to the target project root so the runner is green out of the box). Registers two events via therulePrimesettings bundle.
Quote-escape user input before substitution so a stray backtick or quote in __BLOCK_REASON__ cannot break the template.
4. Bootstrap detection
Decide where the hook artefacts go:
- If
contexture/hooks/exists and~/.claude/hooks/resolves (via symlink or shared inode) to the same tree → write tocontexture/hooks/. The symlink propagates to~/.claude/. - Otherwise → write to
~/.claude/hooks/directly.
Surface the chosen root to the user before any write happens (in the confirmation step). User-facing behaviour is identical either way; only the storage location differs.
~/.claude/settings.json is always edited at ~/.claude/settings.json. It is machine-specific and not part of contexture — this split is deliberate.
5. Collision check
Compute the target hook path: <root>/<name>.js. If a file already exists there:
Hook '<name>' already exists at <root>/<name>.js.
(o)verwrite — replace file, regenerate fixtures, keep existing settings.json registration intact
(r)ename — pick a new hook name
(a)bort — exit without changes
Choice?- overwrite: warn explicitly that the existing settings.json registration stays. Proceed.
- rename: loop back to step 2.
- abort: stop cleanly. No writes.
Hooks are exempt from version-evolving discipline (they are machine-executable artefacts with one canonical form).
6. Settings.json merge — read + plan
Read ~/.claude/settings.json. If parsing fails, abort with a clear error pointing to the file; do not write anything else.
Invoke the merger at lib/settings-merge.js with:
{
event: <recipe's event — PreToolUse | SessionStart | UserPromptSubmit>,
matcher: <recipe's matcher, e.g. "Bash" | "Write|Edit|MultiEdit|NotebookEdit" | "startup|compact">,
command: "node " + <absolute path to the new hook file>,
timeout: 5
}The merger returns one of:
{ status: 'duplicate' }— samecommandis already registered under that event+matcher. Abort with: "Already registered, no change made." No writes anywhere.{ status: 'merged', next: <new settings object> }— proceed with the new object as the candidate write.
7. Confirmation summary
Show the user:
About to scaffold:
Recipe: <r>
Name: <name>
Hook file: <root>/<name>.js
Fixtures: <root>/tests/<name>.{block,allow}.json
Runner: <root>/tests/<name>.test.js
Settings: ~/.claude/settings.json — registration appended
Settings.json diff:
<unified diff from lib/settings-merge.js#unifiedDiff>
Proceed? (y/N)On n → abort cleanly, no writes anywhere. On y → step 8.
8. Write artefacts
In order:
- Substitute placeholders in the recipe's
template.js→ write<root>/<name>.js. - Substitute placeholders in
block.json.template→ write<root>/tests/<name>.block.json. - Substitute placeholders in
allow.json.template→ write<root>/tests/<name>.allow.json. - Generate the runner via
lib/runner-template.js→ write<root>/tests/<name>.test.js. - Atomic write of settings.json: write the new content (formatted via the merger's
formatSettings) to~/.claude/settings.json.tmp, then rename to~/.claude/settings.json.
Use forward-slash paths in the runner regardless of platform — Node accepts both, and the test scripts must work cross-platform.
9. Verify by running the runner
Spawn node <root>/tests/<name>.test.js. Capture stdout + exit code.
Exit 0 with PASS lines for both fixtures → report success:
✓ <name> scaffolded and verified. Hook: <root>/<name>.js Tests: PASS (block + allow) Settings: registered under <event>/<matcher>Anything else → surface the failure verbatim. Do not claim success. The artefacts stay on disk for the user to inspect; the user decides whether to revert. Suggest:
Runner reported FAIL. Check
<root>/tests/<name>.test.js output above. To revert, delete the four generated files and undo the settings.json change.
The skill stops without auto-reverting — fail-noisy beats silent rollback.
10. Stop
Do not invoke /review. Do not commit. Hooks ship in their own user-confirmed commits, often alongside other changes. Leave the next step to the user.
Recipe library
Each recipe under recipes/<name>/ ships:
template.js— the hook source with__PLACEHOLDER__markers.block.json.template— the should-block payload fixture (or, for non-blocker recipes like context-injector, the should-trigger payload).allow.json.template— the should-pass-through payload.README.md— the recipe's contract: event, matcher, placeholders, what "block" and "allow" mean for this recipe (not all recipes are blockers).
Recipes import ./lib/hook-io.js from the existing hooks tree (contexture/hooks/lib/hook-io.js, propagated to ~/.claude/hooks/lib/hook-io.js). No new shared library code is added by this skill.
Context-injector divergence
The context-injector recipe is a SessionStart hook, not a blocker. Its exit-code contract differs:
block.json.templaterepresents a matching SessionStart event (e.g.{ "source": "startup", ... }—sourceis the documented payload field;matcherexists only in the settings.json registration). The hook reads the event, emits the configured context viahookSpecificOutput.additionalContext, exits 0. Verification is exit 0 + non-empty stdout, not exit 2.allow.json.templaterepresents a non-matching event (e.g.{ "source": "compact" }when configured forstartuponly). The hook silently exits 0 with no stdout.
The runner generator at lib/runner-template.js reads the recipe's README to decide which assertion mode to use. Document this in the recipe's README so future readers know why context-injector is special.
What this skill does NOT do
- Does not edit existing hooks. Use
Editdirectly. Modifying registration is a manual settings.json edit. - Does not unregister hooks. Removing a hook from settings.json and deleting its file are manual. v2 candidate.
- Does not scaffold plugin hooks. Plugin-bundled hooks live inside the plugin's directory and are registered via the plugin's manifest, not the user's settings.json.
- Does not support languages other than Node.js in v1. All existing hooks are Node, the shared lib is Node, settings.json registers
node .... Other languages are deferred until a real case forces it. - Does not validate the rest of settings.json. The merger only validates the structure it touches. A pre-existing malformed
statusLineblock is the user's problem. - Does not auto-fire. Mode A only — every run is user-initiated.
- Does not auto-revert on verification failure. Fail-noisy. The user decides whether to delete the generated files and undo the settings.json change.
Exclusion list — events not yet supported
PostToolUse, Stop, SubagentStop, Notification, PreCompact. No current use case. Add reactively when one surfaces — each new recipe is a small additive change (template + fixtures + README under recipes/<name>/); the skill's procedure does not change.
UserPromptSubmit is supported (via the rule-prime recipe) — its additionalContext is a model-visible non-blocking channel, exactly the property a per-prompt rule injector needs. The recipe registers it paired with SessionStart through the rulePrime settings bundle.
PreCompact / SessionEnd stay excluded by design, not by neglect. They are unsuitable for a non-blocking advisory: per code.claude.com/docs/en/hooks, their stdout on exit 0 goes to the debug log (the model never sees it), and exit 2 — the only model-visible channel — blocks the action (and SessionEnd cannot block at all). The recovery use case is served instead by the session-recovery-advisory recipe on SessionStart[clear|compact], whose hookSpecificOutput.additionalContext output is model-visible. Add a PreCompact/SessionEnd recipe only when a genuinely blocking or cleanup-only use case appears.
Relationship to other organs
- prep — when designing a new recipe, prep with
[skills, hooks]scope. The recipe README + template is the artefact. - capture — surprises during a scaffold run (a settings.json edge case, a recipe placeholder that didn't substitute cleanly) are capture candidates.
- review — review's drift detection catches recipe templates that fall out of sync with the recipe README contract.
- architectural-rules tree —
universal/skill-auto-fire.mdapplies: this skill fires via description on/new-hook, no SessionStart hook involved.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

