{"id":"hook-development","name":"Hook Development","summary":"このスキルは、ユーザーが「フックを作成」「PreToolUse/PostToolUse/Stop フックを追加」「ツール使用の検証」「プロンプトベースのフックを実装する」「${CLAUDE_PLUGIN_ROOT}を使う」「イベント駆動型自動化の設定」「危険なコマンドをブロックする」、またはフックイベント(PreTo…","body":"# Hook Development for Claude Code Plugins\n\n## Overview\n\nHooks are event-driven automation scripts that execute in response to Claude Code events. Use hooks to validate operations, enforce policies, add context, and integrate external tools into workflows.\n\n**Key capabilities:**\n- Validate tool calls before execution (PreToolUse)\n- React to tool results (PostToolUse)\n- Enforce completion standards (Stop, SubagentStop)\n- Load project context (SessionStart)\n- Automate workflows across the development lifecycle\n\n## Hook Types\n\n### Prompt-Based Hooks (Recommended)\n\nUse LLM-driven decision making for context-aware validation:\n\n```json\n{\n  \"type\": \"prompt\",\n  \"prompt\": \"Evaluate if this tool use is appropriate: $TOOL_INPUT\",\n  \"timeout\": 30\n}\n```\n\n**Supported events:** Stop, SubagentStop, UserPromptSubmit, PreToolUse\n\n**Benefits:**\n- Context-aware decisions based on natural language reasoning\n- Flexible evaluation logic without bash scripting\n- Better edge case handling\n- Easier to maintain and extend\n\n### Command Hooks\n\nExecute bash commands for deterministic checks:\n\n```json\n{\n  \"type\": \"command\",\n  \"command\": \"bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh\",\n  \"timeout\": 60\n}\n```\n\n**Use for:**\n- Fast deterministic validations\n- File system operations\n- External tool integrations\n- Performance-critical checks\n\n## Hook Configuration Formats\n\n### Plugin hooks.json Format\n\n**For plugin hooks** in `hooks/hooks.json`, use wrapper format:\n\n```json\n{\n  \"description\": \"Brief explanation of hooks (optional)\",\n  \"hooks\": {\n    \"PreToolUse\": [...],\n    \"Stop\": [...],\n    \"SessionStart\": [...]\n  }\n}\n```\n\n**Key points:**\n- `description` field is optional\n- `hooks` field is required wrapper containing actual hook events\n- This is the **plugin-specific format**\n\n**Example:**\n```json\n{\n  \"description\": \"Validation hooks for code quality\",\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n### Settings Format (Direct)\n\n**For user settings** in `.claude/settings.json`, use direct format:\n\n```json\n{\n  \"PreToolUse\": [...],\n  \"Stop\": [...],\n  \"SessionStart\": [...]\n}\n```\n\n**Key points:**\n- No wrapper - events directly at top level\n- No description field\n- This is the **settings format**\n\n**Important:** The examples below show the hook event structure that goes inside either format. For plugin hooks.json, wrap these in `{\"hooks\": {...}}`.\n\n## Hook Events\n\n### PreToolUse\n\nExecute before any tool runs. Use to approve, deny, or modify tool calls.\n\n**Example (prompt-based):**\n```json\n{\n  \"PreToolUse\": [\n    {\n      \"matcher\": \"Write|Edit\",\n      \"hooks\": [\n        {\n          \"type\": \"prompt\",\n          \"prompt\": \"Validate file write safety. Check: system paths, credentials, path traversal, sensitive content. Return 'approve' or 'deny'.\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n**Output for PreToolUse:**\n```json\n{\n  \"hookSpecificOutput\": {\n    \"permissionDecision\": \"allow|deny|ask\",\n    \"updatedInput\": {\"field\": \"modified_value\"}\n  },\n  \"systemMessage\": \"Explanation for Claude\"\n}\n```\n\n### PostToolUse\n\nExecute after tool completes. Use to react to results, provide feedback, or log.\n\n**Example:**\n```json\n{\n  \"PostToolUse\": [\n    {\n      \"matcher\": \"Edit\",\n      \"hooks\": [\n        {\n          \"type\": \"prompt\",\n          \"prompt\": \"Analyze edit result for potential issues: syntax errors, security vulnerabilities, breaking changes. Provide feedback.\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n**Output behavior:**\n- Exit 0: stdout shown in transcript\n- Exit 2: stderr fed back to Claude\n- systemMessage included in context\n\n### Stop\n\nExecute when main agent considers stopping. Use to validate completeness.\n\n**Example:**\n```json\n{\n  \"Stop\": [\n    {\n      \"matcher\": \"*\",\n      \"hooks\": [\n        {\n          \"type\": \"prompt\",\n          \"prompt\": \"Verify task completion: tests run, build succeeded, questions answered. Return 'approve' to stop or 'block' with reason to continue.\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n**Decision output:**\n```json\n{\n  \"decision\": \"approve|block\",\n  \"reason\": \"Explanation\",\n  \"systemMessage\": \"Additional context\"\n}\n```\n\n### SubagentStop\n\nExecute when subagent considers stopping. Use to ensure subagent completed its task.\n\nSimilar to Stop hook, but for subagents.\n\n### UserPromptSubmit\n\nExecute when user submits a prompt. Use to add context, validate, or block prompts.\n\n**Example:**\n```json\n{\n  \"UserPromptSubmit\": [\n    {\n      \"matcher\": \"*\",\n      \"hooks\": [\n        {\n          \"type\": \"prompt\",\n          \"prompt\": \"Check if prompt requires security guidance. If discussing auth, permissions, or API security, return relevant warnings.\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n### SessionStart\n\nExecute when Claude Code session begins. Use to load context and set environment.\n\n**Example:**\n```json\n{\n  \"SessionStart\": [\n    {\n      \"matcher\": \"*\",\n      \"hooks\": [\n        {\n          \"type\": \"command\",\n          \"command\": \"bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n**Special capability:** Persist environment variables using `$CLAUDE_ENV_FILE`:\n```bash\necho \"export PROJECT_TYPE=nodejs\" >> \"$CLAUDE_ENV_FILE\"\n```\n\nSee `examples/load-context.sh` for complete example.\n\n### SessionEnd\n\nExecute when session ends. Use for cleanup, logging, and state preservation.\n\n### PreCompact\n\nExecute before context compaction. Use to add critical information to preserve.\n\n### Notification\n\nExecute when Claude sends notifications. Use to react to user notifications.\n\n## Hook Output Format\n\n### Standard Output (All Hooks)\n\n```json\n{\n  \"continue\": true,\n  \"suppressOutput\": false,\n  \"systemMessage\": \"Message for Claude\"\n}\n```\n\n- `continue`: If false, halt processing (default true)\n- `suppressOutput`: Hide output from transcript (default false)\n- `systemMessage`: Message shown to Claude\n\n### Exit Codes\n\n- `0` - Success (stdout shown in transcript)\n- `2` - Blocking error (stderr fed back to Claude)\n- Other - Non-blocking error\n\n## Hook Input Format\n\nAll hooks receive JSON via stdin with common fields:\n\n```json\n{\n  \"session_id\": \"abc123\",\n  \"transcript_path\": \"/path/to/transcript.txt\",\n  \"cwd\": \"/current/working/dir\",\n  \"permission_mode\": \"ask|allow\",\n  \"hook_event_name\": \"PreToolUse\"\n}\n```\n\n**Event-specific fields:**\n\n- **PreToolUse/PostToolUse:** `tool_name`, `tool_input`, `tool_result`\n- **UserPromptSubmit:** `user_prompt`\n- **Stop/SubagentStop:** `reason`\n\nAccess fields in prompts using `$TOOL_INPUT`, `$TOOL_RESULT`, `$USER_PROMPT`, etc.\n\n## Environment Variables\n\nAvailable in all command hooks:\n\n- `$CLAUDE_PROJECT_DIR` - Project root path\n- `$CLAUDE_PLUGIN_ROOT` - Plugin directory (use for portable paths)\n- `$CLAUDE_ENV_FILE` - SessionStart only: persist env vars here\n- `$CLAUDE_CODE_REMOTE` - Set if running in remote context\n\n**Always use ${CLAUDE_PLUGIN_ROOT} in hook commands for portability:**\n\n```json\n{\n  \"type\": \"command\",\n  \"command\": \"bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh\"\n}\n```\n\n## Plugin Hook Configuration\n\nIn plugins, define hooks in `hooks/hooks.json`:\n\n```json\n{\n  \"PreToolUse\": [\n    {\n      \"matcher\": \"Write|Edit\",\n      \"hooks\": [\n        {\n          \"type\": \"prompt\",\n          \"prompt\": \"Validate file write safety\"\n        }\n      ]\n    }\n  ],\n  \"Stop\": [\n    {\n      \"matcher\": \"*\",\n      \"hooks\": [\n        {\n          \"type\": \"prompt\",\n          \"prompt\": \"Verify task completion\"\n        }\n      ]\n    }\n  ],\n  \"SessionStart\": [\n    {\n      \"matcher\": \"*\",\n      \"hooks\": [\n        {\n          \"type\": \"command\",\n          \"command\": \"bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh\",\n          \"timeout\": 10\n        }\n      ]\n    }\n  ]\n}\n```\n\nPlugin hooks merge with user's hooks and run in parallel.\n\n## Matchers\n\n### Tool Name Matching\n\n**Exact match:**\n```json\n\"matcher\": \"Write\"\n```\n\n**Multiple tools:**\n```json\n\"matcher\": \"Read|Write|Edit\"\n```\n\n**Wildcard (all tools):**\n```json\n\"matcher\": \"*\"\n```\n\n**Regex patterns:**\n```json\n\"matcher\": \"mcp__.*__delete.*\"  // All MCP delete tools\n```\n\n**Note:** Matchers are case-sensitive.\n\n### Common Patterns\n\n```json\n// All MCP tools\n\"matcher\": \"mcp__.*\"\n\n// Specific plugin's MCP tools\n\"matcher\": \"mcp__plugin_asana_.*\"\n\n// All file operations\n\"matcher\": \"Read|Write|Edit\"\n\n// Bash commands only\n\"matcher\": \"Bash\"\n```\n\n## Security Best Practices\n\n### Input Validation\n\nAlways validate inputs in command hooks:\n\n```bash\n#!/bin/bash\nset -euo pipefail\n\ninput=$(cat)\ntool_name=$(echo \"$input\" | jq -r '.tool_name')\n\n# Validate tool name format\nif [[ ! \"$tool_name\" =~ ^[a-zA-Z0-9_]+$ ]]; then\n  echo '{\"decision\": \"deny\", \"reason\": \"Invalid tool name\"}' >&2\n  exit 2\nfi\n```\n\n### Path Safety\n\nCheck for path traversal and sensitive files:\n\n```bash\nfile_path=$(echo \"$input\" | jq -r '.tool_input.file_path')\n\n# Deny path traversal\nif [[ \"$file_path\" == *\"..\"* ]]; then\n  echo '{\"decision\": \"deny\", \"reason\": \"Path traversal detected\"}' >&2\n  exit 2\nfi\n\n# Deny sensitive files\nif [[ \"$file_path\" == *\".env\"* ]]; then\n  echo '{\"decision\": \"deny\", \"reason\": \"Sensitive file\"}' >&2\n  exit 2\nfi\n```\n\nSee `examples/validate-write.sh` and `examples/validate-bash.sh` for complete examples.\n\n### Quote All Variables\n\n```bash\n# GOOD: Quoted\necho \"$file_path\"\ncd \"$CLAUDE_PROJECT_DIR\"\n\n# BAD: Unquoted (injection risk)\necho $file_path\ncd $CLAUDE_PROJECT_DIR\n```\n\n### Set Appropriate Timeouts\n\n```json\n{\n  \"type\": \"command\",\n  \"command\": \"bash script.sh\",\n  \"timeout\": 10\n}\n```\n\n**Defaults:** Command hooks (60s), Prompt hooks (30s)\n\n## Performance Considerations\n\n### Parallel Execution\n\nAll matching hooks run **in parallel**:\n\n```json\n{\n  \"PreToolUse\": [\n    {\n      \"matcher\": \"Write\",\n      \"hooks\": [\n        {\"type\": \"command\", \"command\": \"check1.sh\"},  // Parallel\n        {\"type\": \"command\", \"command\": \"check2.sh\"},  // Parallel\n        {\"type\": \"prompt\", \"prompt\": \"Validate...\"}   // Parallel\n      ]\n    }\n  ]\n}\n```\n\n**Design implications:**\n- Hooks don't see each other's output\n- Non-deterministic ordering\n- Design for independence\n\n### Optimization\n\n1. Use command hooks for quick deterministic checks\n2. Use prompt hooks for complex reasoning\n3. Cache validation results in temp files\n4. Minimize I/O in hot paths\n\n## Temporarily Active Hooks\n\nCreate hooks that activate conditionally by checking for a flag file or configuration:\n\n**Pattern: Flag file activation**\n```bash\n#!/bin/bash\n# Only active when flag file exists\nFLAG_FILE=\"$CLAUDE_PROJECT_DIR/.enable-strict-validation\"\n\nif [ ! -f \"$FLAG_FILE\" ]; then\n  # Flag not present, skip validation\n  exit 0\nfi\n\n# Flag present, run validation\ninput=$(cat)\n# ... validation logic ...\n```\n\n**Pattern: Configuration-based activation**\n```bash\n#!/bin/bash\n# Check configuration for activation\nCONFIG_FILE=\"$CLAUDE_PROJECT_DIR/.claude/plugin-config.json\"\n\nif [ -f \"$CONFIG_FILE\" ]; then\n  enabled=$(jq -r '.strictMode // false' \"$CONFIG_FILE\")\n  if [ \"$enabled\" != \"true\" ]; then\n    exit 0  # Not enabled, skip\n  fi\nfi\n\n# Enabled, run hook logic\ninput=$(cat)\n# ... hook logic ...\n```\n\n**Use cases:**\n- Enable strict validation only when needed\n- Temporary debugging hooks\n- Project-specific hook behavior\n- Feature flags for hooks\n\n**Best practice:** Document activation mechanism in plugin README so users know how to enable/disable temporary hooks.\n\n## Hook Lifecycle and Limitations\n\n### Hooks Load at Session Start\n\n**Important:** Hooks are loaded when Claude Code session starts. Changes to hook configuration require restarting Claude Code.\n\n**Cannot hot-swap hooks:**\n- Editing `hooks/hooks.json` won't affect current session\n- Adding new hook scripts won't be recognized\n- Changing hook commands/prompts won't update\n- Must restart Claude Code: exit and run `claude` again\n\n**To test hook changes:**\n1. Edit hook configuration or scripts\n2. Exit Claude Code session\n3. Restart: `claude` or `cc`\n4. New hook configuration loads\n5. Test hooks with `claude --debug`\n\n### Hook Validation at Startup\n\nHooks are validated when Claude Code starts:\n- Invalid JSON in hooks.json causes loading failure\n- Missing scripts cause warnings\n- Syntax errors reported in debug mode\n\nUse `/hooks` command to review loaded hooks in current session.\n\n## Debugging Hooks\n\n### Enable Debug Mode\n\n```bash\nclaude --debug\n```\n\nLook for hook registration, execution logs, input/output JSON, and timing information.\n\n### Test Hook Scripts\n\nTest command hooks directly:\n\n```bash\necho '{\"tool_name\": \"Write\", \"tool_input\": {\"file_path\": \"/test\"}}' | \\\n  bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh\n\necho \"Exit code: $?\"\n```\n\n### Validate JSON Output\n\nEnsure hooks output valid JSON:\n\n```bash\noutput=$(./your-hook.sh < test-input.json)\necho \"$output\" | jq .\n```\n\n## Quick Reference\n\n### Hook Events Summary\n\n| Event | When | Use For |\n|-------|------|---------|\n| PreToolUse | Before tool | Validation, modification |\n| PostToolUse | After tool | Feedback, logging |\n| UserPromptSubmit | User input | Context, validation |\n| Stop | Agent stopping | Completeness check |\n| SubagentStop | Subagent done | Task validation |\n| SessionStart | Session begins | Context loading |\n| SessionEnd | Session ends | Cleanup, logging |\n| PreCompact | Before compact | Preserve context |\n| Notification | User notified | Logging, reactions |\n\n### Best Practices\n\n**DO:**\n- ✅ Use prompt-based hooks for complex logic\n- ✅ Use ${CLAUDE_PLUGIN_ROOT} for portability\n- ✅ Validate all inputs in command hooks\n- ✅ Quote all bash variables\n- ✅ Set appropriate timeouts\n- ✅ Return structured JSON output\n- ✅ Test hooks thoroughly\n\n**DON'T:**\n- ❌ Use hardcoded paths\n- ❌ Trust user input without validation\n- ❌ Create long-running hooks\n- ❌ Rely on hook execution order\n- ❌ Modify global state unpredictably\n- ❌ Log sensitive information\n\n## Additional Resources\n\n### Reference Files\n\nFor detailed patterns and advanced techniques, consult:\n\n- **`references/patterns.md`** - Common hook patterns (8+ proven patterns)\n- **`references/migration.md`** - Migrating from basic to advanced hooks\n- **`references/advanced.md`** - Advanced use cases and techniques\n\n### Example Hook Scripts\n\nWorking examples in `examples/`:\n\n- **`validate-write.sh`** - File write validation example\n- **`validate-bash.sh`** - Bash command validation example\n- **`load-context.sh`** - SessionStart context loading example\n\n### Utility Scripts\n\nDevelopment tools in `scripts/`:\n\n- **`validate-hook-schema.sh`** - Validate hooks.json structure and syntax\n- **`test-hook.sh`** - Test hooks with sample input before deployment\n- **`hook-linter.sh`** - Check hook scripts for common issues and best practices\n\n### External Resources\n\n- **Official Docs**: https://docs.claude.com/en/docs/claude-code/hooks\n- **Examples**: See security-guidance plugin in marketplace\n- **Testing**: Use `claude --debug` for detailed logs\n- **Validation**: Use `jq` to validate hook JSON output\n\n## Implementation Workflow\n\nTo implement hooks in a plugin:\n\n1. Identify events to hook into (PreToolUse, Stop, SessionStart, etc.)\n2. Decide between prompt-based (flexible) or command (deterministic) hooks\n3. Write hook configuration in `hooks/hooks.json`\n4. For command hooks, create hook scripts\n5. Use ${CLAUDE_PLUGIN_ROOT} for all file references\n6. Validate configuration with `scripts/validate-hook-schema.sh hooks/hooks.json`\n7. Test hooks with `scripts/test-hook.sh` before deployment\n8. Test in Claude Code with `claude --debug`\n9. Document hooks in plugin README\n\nFocus on prompt-based hooks for most use cases. Reserve command hooks for performance-critical or deterministic checks.","author":"@tzachbon","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/tzachbon/smart-ralph/tree/main/.agents/skills/Hook Development","license":"MIT","category":null,"lang":"en","tokens":3862,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"examples/load-context.sh","size":1690,"sha256":"0d614599e711c087b381a4ac7ee3c40758af1022669fc998649d199b9dfe15ed"},{"path":"examples/validate-bash.sh","size":1304,"sha256":"348c58f4cf38a2e7080ea5272935a7658c7122d83dd6c52d0a1f2ef599c176ab"},{"path":"examples/validate-write.sh","size":1222,"sha256":"89a8ef9bb7508e4819080e65834d6783afa5a5a614d458a3821b80a329eb32e5"},{"path":"references/advanced.md","size":10148,"sha256":"5b139ceeb6a3ef16555a6eef421e2642e6ca286b5527ee2b4e02c11ae5b75dd5"},{"path":"references/migration.md","size":8299,"sha256":"7f8e7a795829837f46a0c058c26c8f5c4aa8be5c9fb5316b828229d1802ded77"},{"path":"references/patterns.md","size":7144,"sha256":"cb607fca192bfa892fd198141b86dac9d0ca2c573a280b054b7beaa56fc9a8bf"},{"path":"scripts/hook-linter.sh","size":4200,"sha256":"85a7d55488f8f38f9b98350b5cb597fb306432d5e61eaa3444e7226ca9309e0b"},{"path":"scripts/test-hook.sh","size":5336,"sha256":"ded642e811b4731f6a2c90c73b02e00c937511d9ee65236bb8cf9964f0069393"},{"path":"scripts/validate-hook-schema.sh","size":5081,"sha256":"e7785d6c61d368f5ea5270c979753aea02f776f5b6aa220137ec4585abd720c2"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[{"code":"net.endpoints","kind":"exfiltration","excerpt":"docs.claude.com","message":"bundled scripts reach 1 external host(s)","severity":"warn"}],"scannedAt":"2026-08-22","hasScripts":true,"networkEndpoints":["docs.claude.com"]}}