MCPInstall

hooks-development

Claude Code hooks development guide. TRIGGERS - create hook, PostToolUse, PreToolUse, Stop hook, hook lifecycle, decision block.

Installs
132
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/terrylica/cc-skills --skill hooks-development
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads hooks-development automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Hooks Development

Guide for developing Claude Code hooks with proper output visibility patterns.

Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.

When to Use This Skill

  • Creating a new PostToolUse or PreToolUse hook
  • Hook output is not visible to Claude (most common issue)
  • User asks about decision: block pattern
  • Debugging why hook messages don't appear
  • User mentions "Claude Code hooks" or "hook visibility"

Quick Reference: Visibility Patterns

Critical insight: PostToolUse hook stdout is only visible to Claude when JSON contains "decision": "block".

Output FormatClaude Visibility
Plain textNot visible
JSON without decision: blockNot visible
JSON with decision: blockVisible

Exit code behavior:

Exit Codestdout BehaviorClaude Visibility
0JSON parsed, shown in verbose mode onlyOnly if "decision": "block"
2Ignored, uses stderr insteadstderr shown to Claude
Otherstderr shown in verbose modeNot shown to Claude

Never rewrite AskUserQuestion

A PreToolUse hook must never return updatedInput for AskUserQuestion — not directly, and not through allowWithInput. For that tool updatedInput is the channel the dialog uses to deliver the user's answers, so a hook that returns it is taken as having answered: the menu never renders and the tool returns "The user did not answer the questions." Measured 2026-09-24 across every session transcript: 86 of 86 rewritten calls went unseen and unanswered, against 465 of 466 plain-allow calls that rendered and were answered.

  • A plain allow is safe; the dialog still shows.
  • To change what the user sees, deny with a reason that names exactly what the agent should re-ask, and make sure the re-ask passes unchanged so the loop settles in one round (pretooluse-pr-premise-annotator.ts is the worked example).
  • Enforced three ways: allowWithInput refuses the tool at runtime, TOOL_SCHEMAS has no entry for it, and scripts/validate-plugins.mjs fails the gate when a hook whose matcher covers AskUserQuestion emits updatedInput or calls allowWithInput.

Minimal Working Pattern

/usr/bin/env bash << 'SKILL_SCRIPT_EOF'
#!/usr/bin/env bash
set -euo pipefail

# Read hook payload from stdin
PAYLOAD=$(cat)
FILE_PATH=$(echo "$PAYLOAD" | jq -r '.tool_input.file_path // empty')

[[ -z "$FILE_PATH" ]] && exit 0

# Your condition here
if [[ condition_met ]]; then
    jq -n \
        --arg reason "[HOOK] Your message to Claude" \
        '{decision: "block", reason: $reason}'
fi

exit 0
SKILL_SCRIPT_EOF

Key points:

  1. Use jq -n to generate valid JSON
  2. Include "decision": "block" for visibility
  3. Exit with code 0
  4. The "blocking error" label is cosmetic - operation continues

Language choice: shell vs Bun/TypeScript

Default to shell (bash) for hooks. A hook fires on every matching event and ~95% bail out immediately, so process-startup latency dominates — not the logic. Measured on a workstation: a full bash hook (keyword match + bail) ≈ 7 ms; a do-nothing Bun script ≈ 17 ms. bash also needs no runtime dependency and matches existing hook convention. Keep the hot-path bail-out a bash builtin case (no jq/process spawn) — see the pre-jq-fastpath in the devops-tools SCS hooks.

Reach for Bun/TypeScript only when a hook does real work that amortizes the ~10 ms startup tax: parsing/transforming structured data, calling an API, sharing typed modules across many hooks, or maintaining state. For simple match-and-emit reminders, shell wins on speed, durability, and convention. (This is the language doctrine's "existing convention + SOTA-native ecosystem override the Bun-first default" clause in action.)


TodoWrite Templates

Creating a PostToolUse Hook

1. [pending] Create hook script with shebang and set -euo pipefail
2. [pending] Parse PAYLOAD from stdin with jq
3. [pending] Add condition check for when to trigger
4. [pending] Output JSON with decision:block pattern
5. [pending] Register hook in hooks.json with matcher
6. [pending] Test by editing a matching file
7. [pending] Verify Claude sees the message in system-reminder

Debugging Invisible Hook Output

1. [pending] Verify hook executes (add debug log to /tmp)
2. [pending] Check JSON format is valid (pipe to jq .)
3. [pending] Confirm decision:block is present in output
4. [pending] Verify exit code is 0
5. [pending] Check hooks.json matcher pattern
6. [pending] Restart Claude Code session

Reference Documentation

  • Lifecycle Reference - All 10 hook events, diagrams, use cases, configuration pitfalls
  • Visibility Patterns - Full exit code and JSON schema details
  • Hook Templates - Copy-paste templates for common patterns
  • Debugging Guide - Troubleshooting invisible output

Post-Change Checklist (Self-Evolution)

When this skill is updated:

  • Update evolution-log.md with discovery
  • Verify code examples still work
  • Check if ADR needs updating: PostToolUse Hook Visibility ADR

Related Resources


Troubleshooting

IssueCauseSolution
Hook output not visibleMissing decision:block in JSONAdd "decision": "block" to JSON output
JSON parse error in hookInvalid JSON syntaxUse jq -n to generate valid JSON
Hook not executingWrong matcher patternCheck hooks.json matcher regex matches tool name
Plain text output ignoredOnly JSON parsedWrap output in JSON with decision:block
Exit code 2 behaviorstderr used instead of stdoutUse exit 0 with JSON, or exit 2 for stderr messages
Session not seeing changesHooks cachedRestart Claude Code session after hook changes
Verbose mode not showingDisabled by defaultEnable verbose mode in Claude Code settings
jq command not foundjq not installedbrew install jq

Post-Execution Reflection

After this skill completes, check before closing:

  1. Did the command succeed? — If not, fix the instruction or error table that caused the failure.
  2. Did parameters or output change? — If the underlying tool's interface drifted, update Usage examples and Parameters table to match.
  3. Was a workaround needed? — If you had to improvise (different flags, extra steps), update this SKILL.md so the next invocation doesn't need the same workaround.

Only update if the issue is real and reproducible — not speculative.

FAQ

What does the hooks-development skill do?

Claude Code hooks development guide. TRIGGERS - create hook, PostToolUse, PreToolUse, Stop hook, hook lifecycle, decision block.

How do I install hooks-development?

Run npx skills add https://github.com/terrylica/cc-skills --skill hooks-development in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/hooks-development/ (personal) or .claude/skills/hooks-development/ (project).

Which AI agents can use hooks-development?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is hooks-development safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains hooks-development?

It is published by terrylica in terrylica/cc-skills. It has 132 recorded installs.

More from terrylica/cc-skills

View all →

mise-tasks

terrylica/cc-skills

Orchestrate multi-step project workflows using mise task definitions with dependency management and argument handling. Use whenever the user.

pandoc-pdf-generation

terrylica/cc-skills

PDF generation from markdown via Pandoc/XeLaTeX. TRIGGERS - markdown for PDF, print document, pandoc

Docs & Writing547 installs

mql5-indicator-patterns

terrylica/cc-skills

MQL5 indicator development patterns. TRIGGERS - MQL5 indicator, OnCalculate, indicator buffers, MetaTrader 5.

501 installs

Python logging with loguru, structlog, and orjson. TRIGGERS - loguru, structlog, structured logging

DevOps & Cloud402 installs

gdrive-access

terrylica/cc-skills

Open the skill to read its SKILL.md instructions.

356 installs

chezmoi-workflows

terrylica/cc-skills

Open the skill to read its SKILL.md instructions.

297 installs

Popular other skills

View category →

hyperframes-cli

heygen-com/hyperframes

The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures.

1.3M installs

triage

mattpocock/skills

Move issues and external PRs through a state machine of triage roles, categorise, verify, grill if needed, and write agent-ready briefs.

1M installs

ask-matt

mattpocock/skills

Ask which skill or flow fits your situation. A router over the skills in this repo.

778.4K installs

to-questionnaire

mattpocock/skills

Turn a decision you can't fully answer into a questionnaire for someone else to fill in.

508.9K installs

wait-what

mattpocock/skills

Stop. That last message did not land: re-pitch it.

459.7K installs

loop-me

mattpocock/skills

Grill me about specs for the workflows I want to build, within this workspace.

447.9K installs