[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70P0HF73DY28ZXZMCS9ZZ59":3,"skill-same:01M70P0HF73DY28ZXZMCS9ZZ59":85},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":34,"publishedAt":55,"linkOnly":56,"markdownContent":57,"markdownBody":58,"frontMatter":59,"refName":61,"commitSha":62,"skillDirectory":63,"forksCount":64,"repositoryHtmlUrl":65,"repositoryHomepageUrl":66,"ownerHtmlUrl":67,"ownerBlogUrl":68,"ownerDescription":69,"updatedAt":70,"install":71,"resources":84},"01M70P0HF73DY28ZXZMCS9ZZ59","writing-hooks",null,"How to write Claude Code hooks -- event selection, hook types, matcher patterns, blocking vs advisory, portable paths. Use when creating hooks for quality gates, automation, or policy enforcement.","shengdabai\u002FTony-Claude-Code-Skills","shengdabai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F116417419?v=4","01M70F6D2WY5PQV5A92BKYJQEW","Tony Sheng","01M70F6D3GAAAYZ5H4CC1G8VXM",3,"MIT","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-hooks\u002FSKILL.md",[18,23,26,31],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":32,"categoryName":33,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000036","内容创作者",[35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54],"writing","writing hooks","hooks","automation","use","policy","event","for","enforcement","patterns","hook","paths","write","how","code","when","claude code hooks","how to","selection","types","2026-11-10 10:12:30",false,"---\nname: writing-hooks\ndescription: \"How to write Claude Code hooks -- event selection, hook types, matcher patterns, blocking vs advisory, portable paths. Use when creating hooks for quality gates, automation, or policy enforcement.\"\nversion: 0.1.0\n---\n\n# Writing Hooks\n\n> Scope: covers hooks.json authoring and hook script design. For plugin architecture, see [[writing-plugins]]. For rules (which are simpler but static), see [[writing-rules]].\n\n## 1. Three Hook Types\n\n| Type | What it does | When to use | Complexity |\n|------|-------------|-------------|------------|\n| `command` | Runs a shell script, reads JSON from stdin | Deterministic checks: file existence, JSON validation, regex matching | Medium |\n| `prompt` | Injects text into Claude's context | Advisory: reminders, context injection, style guidance | Low |\n| `agent` | Spawns a verification agent | Complex verification: code quality, semantic analysis, multi-file checks | High |\n\n### Type Selection Flowchart\n\n```\nIs the check deterministic (regex, file exists, JSON schema)?\n  YES --> command hook (shell script)\n  NO  --> Does it need AI judgment?\n    YES --> agent hook\n    NO  --> prompt hook (context injection)\n```\n\n### Command Hook Example\n\n```json\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write|Edit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"${CLAUDE_PLUGIN_ROOT}\u002Fscripts\u002Fcheck-loc.sh\",\n            \"timeout\": 10000\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\nHook script receives JSON on stdin with tool name and parameters. It outputs JSON to stdout.\n\n### Prompt Hook Example\n\n```json\n{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"prompt\",\n            \"prompt\": \"Remember: this project uses Result\u003CT, E> for error handling. Never use try\u002Fcatch directly.\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n### Agent Hook Example\n\n```json\n{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write|Edit\",\n        \"hooks\": [\n          {\n            \"type\": \"agent\",\n            \"agent\": \"Verify the written file follows project conventions. Check: import order, export style, naming conventions. Report any violations.\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n## 2. Blocking vs Advisory\n\n### Blocking (PreToolUse with deny)\n\nThe hook prevents the tool from executing. Use for hard quality gates.\n\n**Script output for blocking**:\n```json\n{\n  \"hookSpecificOutput\": {\n    \"permissionDecision\": \"deny\",\n    \"permissionDecisionReason\": \"File exceeds 300 LOC limit (current: 342). Extract logic before writing.\"\n  }\n}\n```\n\n**When to block**:\n- Tests must pass before committing\n- File exceeds size limit\n- Required field missing from config\n- Dangerous operation detected (force push, drop table)\n\n### Advisory (PostToolUse with message)\n\nThe hook adds a message to Claude's context after the action completes. Use for suggestions and reminders.\n\n**Script output for advisory**:\n```json\n{\n  \"hookSpecificOutput\": {\n    \"message\": \"The file you just edited has no tests. Consider adding tests in __tests__\u002F.\"\n  }\n}\n```\n\n**When to advise**:\n- Suggest related actions (run tests, update docs)\n- Remind about conventions\n- Surface contextual information\n- Warn about potential issues without blocking\n\n### Decision Matrix\n\n| Situation | Block or Advise? | Rationale |\n|-----------|-----------------|-----------|\n| Test failure on commit | Block | Broken tests should never be committed |\n| File over LOC limit | Block | Enforce hard limit |\n| Missing JSDoc on export | Advise | Nice to have, not a hard requirement |\n| No tests for new file | Advise | Reminder, not a gate |\n| Force push to main | Block | Destructive, irreversible |\n| Large file creation (>500 lines) | Advise | Might be intentional (generated code) |\n\n**Rule of thumb**: block only what you would reject in a code review. Advise on everything else.\n\n## 3. Event Selection Guide\n\n| Event | When it fires | Common use cases |\n|-------|--------------|-----------------|\n| `PreToolUse` | Before a tool executes | Block dangerous operations, validate inputs, check preconditions |\n| `PostToolUse` | After a tool succeeds | Trigger follow-up actions, lint changed files, update state |\n| `PostToolUseFailure` | After a tool fails | Error recovery, suggest alternatives, log failures |\n| `UserPromptSubmit` | When user sends a message | Context injection, session setup, mode activation |\n| `Stop` | When Claude stops responding | Cleanup, summary generation, state persistence |\n| `SessionStart` | Session begins | Environment validation, context loading, config checks |\n\n### Event Selection by Goal\n\n| Goal | Event | Hook type |\n|------|-------|-----------|\n| Prevent bad writes | `PreToolUse` + matcher `Write\\|Edit` | command |\n| Lint after edit | `PostToolUse` + matcher `Write\\|Edit` | command |\n| Inject project context | `UserPromptSubmit` | prompt |\n| Validate environment on start | `SessionStart` | command |\n| Save session summary on exit | `Stop` | agent |\n| Recover from failed bash commands | `PostToolUseFailure` + matcher `Bash` | prompt |\n\n## 4. Matcher Patterns\n\nThe `matcher` field uses regex to match tool names. It applies only to `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` events.\n\n| Pattern | Matches | Use case |\n|---------|---------|----------|\n| `\"Bash\"` | Bash tool only | Guard shell commands |\n| `\"Write\\|Edit\"` | Write or Edit | Guard file modifications |\n| `\"Write\"` | Write only | Guard new file creation |\n| `\"Edit\"` | Edit only | Guard file edits (not creation) |\n| `\"Read\"` | Read tool | Track what files Claude reads |\n| `\"mcp__.*\"` | All MCP tool calls | Guard external integrations |\n| `\"mcp__github__.*\"` | GitHub MCP tools | Guard GitHub operations |\n| `\"Task\"` | Task tool (agent dispatch) | Monitor agent dispatching |\n| `\".*\"` | Everything | Use carefully -- fires on every tool call |\n\n### Matcher Testing\n\nBefore deploying, verify your matcher with test cases:\n\n| Matcher | Should match | Should NOT match |\n|---------|-------------|-----------------|\n| `\"Write\\|Edit\"` | Write, Edit | Bash, Read, WriteFile |\n| `\"Bash\"` | Bash | BashScript, mcp__bash |\n| `\"mcp__github__.*\"` | mcp__github__create_pr | mcp__slack__send |\n\n## 5. Portable Paths\n\nAlways use `${CLAUDE_PLUGIN_ROOT}` for script paths in hooks.json. This variable resolves to the plugin's installation directory at runtime.\n\n### Correct\n\n```json\n{\n  \"command\": \"${CLAUDE_PLUGIN_ROOT}\u002Fscripts\u002Fcheck-loc.sh\"\n}\n```\n\n### Wrong (breaks on other machines)\n\n```json\n{\n  \"command\": \"\u002FUsers\u002Fjoker\u002F.claude\u002Fplugins\u002Fcache\u002Fxiaolai\u002Fmy-plugin\u002F0.1.0\u002Fscripts\u002Fcheck-loc.sh\"\n}\n```\n\n### Script Location Convention\n\n```\nmy-plugin\u002F\n  hooks\u002F\n    hooks.json          # hook definitions\n  scripts\u002F\n    check-loc.sh        # hook scripts\n    validate-config.sh\n    lint-output.sh\n```\n\n### Script Requirements\n\nEvery hook script must have:\n\n1. **Shebang line**: `#!\u002Fbin\u002Fbash` or `#!\u002Fusr\u002Fbin\u002Fenv node`\n2. **Executable permission**: `chmod +x scripts\u002F*.sh`\n3. **JSON output**: scripts must output valid JSON to stdout\n4. **Stderr for logging**: debug output goes to stderr, not stdout (stdout is parsed as JSON)\n\n```bash\n#!\u002Fbin\u002Fbash\n# Read input from stdin\ninput=$(cat)\n\n# Debug logging goes to stderr\necho \"Hook triggered: $(date)\" >&2\n\n# Business logic\nfile_path=$(echo \"$input\" | jq -r '.toolInput.file_path \u002F\u002F empty')\n\nif [ -z \"$file_path\" ]; then\n  # Allow if we can't determine the file\n  echo '{\"hookSpecificOutput\":{\"decision\":\"allow\"}}'\n  exit 0\nfi\n\nloc=$(wc -l \u003C \"$file_path\" 2>\u002Fdev\u002Fnull || echo \"0\")\n\nif [ \"$loc\" -gt 300 ]; then\n  echo \"{\\\"hookSpecificOutput\\\":{\\\"permissionDecision\\\":\\\"deny\\\",\\\"permissionDecisionReason\\\":\\\"File has $loc lines, exceeds 300 LOC limit\\\"}}\"\nelse\n  echo '{\"hookSpecificOutput\":{\"decision\":\"allow\"}}'\nfi\n```\n\n## 6. Fail-Open vs Fail-Closed\n\nWhat happens when your hook script crashes?\n\n### Fail-Open (recommended default)\n\nIf the script crashes, **allow** the action. Safer for advisory hooks and non-critical checks.\n\n```bash\n#!\u002Fbin\u002Fbash\n# Fail-open wrapper\nset +e  # Don't exit on error\n\nresult=$(your_check_logic 2>\u002Fdev\u002Fnull)\nexit_code=$?\n\nif [ $exit_code -ne 0 ]; then\n  # Script failed -- allow the action (fail-open)\n  echo '{\"hookSpecificOutput\":{\"decision\":\"allow\"}}'\n  exit 0\nfi\n\n# Normal processing...\necho \"$result\"\n```\n\n### Fail-Closed (security-critical only)\n\nIf the script crashes, **deny** the action. Use only for critical security gates.\n\n```bash\n#!\u002Fbin\u002Fbash\n# Fail-closed wrapper\nset +e\n\nresult=$(your_check_logic 2>\u002Fdev\u002Fnull)\nexit_code=$?\n\nif [ $exit_code -ne 0 ]; then\n  # Script failed -- deny the action (fail-closed)\n  echo '{\"hookSpecificOutput\":{\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Safety check script failed -- blocking action as precaution\"}}'\n  exit 0\nfi\n\n# Normal processing...\necho \"$result\"\n```\n\n### When to Use Each\n\n| Hook purpose | Fail mode | Rationale |\n|-------------|-----------|-----------|\n| LOC limit enforcement | Fail-open | Better to allow a large file than block all writes |\n| Style reminder | Fail-open | Non-critical advisory |\n| Prevent force push to main | Fail-closed | Destructive action, err on side of caution |\n| Secret detection | Fail-closed | Security-critical, must not leak |\n| Test runner | Fail-open | Test infra failures shouldn't block development |\n\n## 7. Common Mistakes\n\n| Mistake | Why it's wrong | Fix |\n|---------|---------------|-----|\n| Blocking on `PostToolUse` | Action already happened -- too late to block | Use `PreToolUse` for blocking |\n| Wrong event case | `pretooluse` instead of `PreToolUse` -- case-sensitive | Use exact case: `PreToolUse`, `PostToolUse`, etc. |\n| Script not executable | Hook fails silently | Run `chmod +x scripts\u002F*.sh` |\n| Missing shebang | Script may run with wrong interpreter | Add `#!\u002Fbin\u002Fbash` or `#!\u002Fusr\u002Fbin\u002Fenv node` |\n| Hardcoded paths | Breaks on other machines | Use `${CLAUDE_PLUGIN_ROOT}` |\n| stdout pollution | Debug output mixed into JSON response | Use stderr for logging: `echo \"debug\" >&2` |\n| No timeout | Slow script blocks Claude indefinitely | Set `\"timeout\": 10000` (10 seconds) |\n| Matcher too broad (`\".*\"`) | Fires on every tool call, performance impact | Narrow to specific tools |\n| No fail-open wrapper | Script crash = broken hook = frustrated user | Wrap in fail-open try\u002Fcatch |\n\n## 8. Quality Checklist\n\nBefore deploying hooks, verify:\n\n- [ ] Each hook has the correct event type for its purpose\n- [ ] Blocking hooks use `PreToolUse`, not `PostToolUse`\n- [ ] Matchers are tested against expected and unexpected tool names\n- [ ] All script paths use `${CLAUDE_PLUGIN_ROOT}`\n- [ ] All scripts have shebangs and executable permissions\n- [ ] All scripts output valid JSON to stdout\n- [ ] Debug logging goes to stderr, not stdout\n- [ ] Fail-open or fail-closed is explicitly chosen for each hook\n- [ ] Timeouts are set (default: 10 seconds)\n- [ ] Hooks are tested with: normal input, edge case input, missing input\n","# Writing Hooks\n\n> Scope: covers hooks.json authoring and hook script design. For plugin architecture, see [[writing-plugins]]. For rules (which are simpler but static), see [[writing-rules]].\n\n## 1. Three Hook Types\n\n| Type | What it does | When to use | Complexity |\n|------|-------------|-------------|------------|\n| `command` | Runs a shell script, reads JSON from stdin | Deterministic checks: file existence, JSON validation, regex matching | Medium |\n| `prompt` | Injects text into Claude's context | Advisory: reminders, context injection, style guidance | Low |\n| `agent` | Spawns a verification agent | Complex verification: code quality, semantic analysis, multi-file checks | High |\n\n### Type Selection Flowchart\n\n```\nIs the check deterministic (regex, file exists, JSON schema)?\n  YES --> command hook (shell script)\n  NO  --> Does it need AI judgment?\n    YES --> agent hook\n    NO  --> prompt hook (context injection)\n```\n\n### Command Hook Example\n\n```json\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write|Edit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"${CLAUDE_PLUGIN_ROOT}\u002Fscripts\u002Fcheck-loc.sh\",\n            \"timeout\": 10000\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\nHook script receives JSON on stdin with tool name and parameters. It outputs JSON to stdout.\n\n### Prompt Hook Example\n\n```json\n{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"prompt\",\n            \"prompt\": \"Remember: this project uses Result\u003CT, E> for error handling. Never use try\u002Fcatch directly.\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n### Agent Hook Example\n\n```json\n{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write|Edit\",\n        \"hooks\": [\n          {\n            \"type\": \"agent\",\n            \"agent\": \"Verify the written file follows project conventions. Check: import order, export style, naming conventions. Report any violations.\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n## 2. Blocking vs Advisory\n\n### Blocking (PreToolUse with deny)\n\nThe hook prevents the tool from executing. Use for hard quality gates.\n\n**Script output for blocking**:\n```json\n{\n  \"hookSpecificOutput\": {\n    \"permissionDecision\": \"deny\",\n    \"permissionDecisionReason\": \"File exceeds 300 LOC limit (current: 342). Extract logic before writing.\"\n  }\n}\n```\n\n**When to block**:\n- Tests must pass before committing\n- File exceeds size limit\n- Required field missing from config\n- Dangerous operation detected (force push, drop table)\n\n### Advisory (PostToolUse with message)\n\nThe hook adds a message to Claude's context after the action completes. Use for suggestions and reminders.\n\n**Script output for advisory**:\n```json\n{\n  \"hookSpecificOutput\": {\n    \"message\": \"The file you just edited has no tests. Consider adding tests in __tests__\u002F.\"\n  }\n}\n```\n\n**When to advise**:\n- Suggest related actions (run tests, update docs)\n- Remind about conventions\n- Surface contextual information\n- Warn about potential issues without blocking\n\n### Decision Matrix\n\n| Situation | Block or Advise? | Rationale |\n|-----------|-----------------|-----------|\n| Test failure on commit | Block | Broken tests should never be committed |\n| File over LOC limit | Block | Enforce hard limit |\n| Missing JSDoc on export | Advise | Nice to have, not a hard requirement |\n| No tests for new file | Advise | Reminder, not a gate |\n| Force push to main | Block | Destructive, irreversible |\n| Large file creation (>500 lines) | Advise | Might be intentional (generated code) |\n\n**Rule of thumb**: block only what you would reject in a code review. Advise on everything else.\n\n## 3. Event Selection Guide\n\n| Event | When it fires | Common use cases |\n|-------|--------------|-----------------|\n| `PreToolUse` | Before a tool executes | Block dangerous operations, validate inputs, check preconditions |\n| `PostToolUse` | After a tool succeeds | Trigger follow-up actions, lint changed files, update state |\n| `PostToolUseFailure` | After a tool fails | Error recovery, suggest alternatives, log failures |\n| `UserPromptSubmit` | When user sends a message | Context injection, session setup, mode activation |\n| `Stop` | When Claude stops responding | Cleanup, summary generation, state persistence |\n| `SessionStart` | Session begins | Environment validation, context loading, config checks |\n\n### Event Selection by Goal\n\n| Goal | Event | Hook type |\n|------|-------|-----------|\n| Prevent bad writes | `PreToolUse` + matcher `Write\\|Edit` | command |\n| Lint after edit | `PostToolUse` + matcher `Write\\|Edit` | command |\n| Inject project context | `UserPromptSubmit` | prompt |\n| Validate environment on start | `SessionStart` | command |\n| Save session summary on exit | `Stop` | agent |\n| Recover from failed bash commands | `PostToolUseFailure` + matcher `Bash` | prompt |\n\n## 4. Matcher Patterns\n\nThe `matcher` field uses regex to match tool names. It applies only to `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` events.\n\n| Pattern | Matches | Use case |\n|---------|---------|----------|\n| `\"Bash\"` | Bash tool only | Guard shell commands |\n| `\"Write\\|Edit\"` | Write or Edit | Guard file modifications |\n| `\"Write\"` | Write only | Guard new file creation |\n| `\"Edit\"` | Edit only | Guard file edits (not creation) |\n| `\"Read\"` | Read tool | Track what files Claude reads |\n| `\"mcp__.*\"` | All MCP tool calls | Guard external integrations |\n| `\"mcp__github__.*\"` | GitHub MCP tools | Guard GitHub operations |\n| `\"Task\"` | Task tool (agent dispatch) | Monitor agent dispatching |\n| `\".*\"` | Everything | Use carefully -- fires on every tool call |\n\n### Matcher Testing\n\nBefore deploying, verify your matcher with test cases:\n\n| Matcher | Should match | Should NOT match |\n|---------|-------------|-----------------|\n| `\"Write\\|Edit\"` | Write, Edit | Bash, Read, WriteFile |\n| `\"Bash\"` | Bash | BashScript, mcp__bash |\n| `\"mcp__github__.*\"` | mcp__github__create_pr | mcp__slack__send |\n\n## 5. Portable Paths\n\nAlways use `${CLAUDE_PLUGIN_ROOT}` for script paths in hooks.json. This variable resolves to the plugin's installation directory at runtime.\n\n### Correct\n\n```json\n{\n  \"command\": \"${CLAUDE_PLUGIN_ROOT}\u002Fscripts\u002Fcheck-loc.sh\"\n}\n```\n\n### Wrong (breaks on other machines)\n\n```json\n{\n  \"command\": \"\u002FUsers\u002Fjoker\u002F.claude\u002Fplugins\u002Fcache\u002Fxiaolai\u002Fmy-plugin\u002F0.1.0\u002Fscripts\u002Fcheck-loc.sh\"\n}\n```\n\n### Script Location Convention\n\n```\nmy-plugin\u002F\n  hooks\u002F\n    hooks.json          # hook definitions\n  scripts\u002F\n    check-loc.sh        # hook scripts\n    validate-config.sh\n    lint-output.sh\n```\n\n### Script Requirements\n\nEvery hook script must have:\n\n1. **Shebang line**: `#!\u002Fbin\u002Fbash` or `#!\u002Fusr\u002Fbin\u002Fenv node`\n2. **Executable permission**: `chmod +x scripts\u002F*.sh`\n3. **JSON output**: scripts must output valid JSON to stdout\n4. **Stderr for logging**: debug output goes to stderr, not stdout (stdout is parsed as JSON)\n\n```bash\n#!\u002Fbin\u002Fbash\n# Read input from stdin\ninput=$(cat)\n\n# Debug logging goes to stderr\necho \"Hook triggered: $(date)\" >&2\n\n# Business logic\nfile_path=$(echo \"$input\" | jq -r '.toolInput.file_path \u002F\u002F empty')\n\nif [ -z \"$file_path\" ]; then\n  # Allow if we can't determine the file\n  echo '{\"hookSpecificOutput\":{\"decision\":\"allow\"}}'\n  exit 0\nfi\n\nloc=$(wc -l \u003C \"$file_path\" 2>\u002Fdev\u002Fnull || echo \"0\")\n\nif [ \"$loc\" -gt 300 ]; then\n  echo \"{\\\"hookSpecificOutput\\\":{\\\"permissionDecision\\\":\\\"deny\\\",\\\"permissionDecisionReason\\\":\\\"File has $loc lines, exceeds 300 LOC limit\\\"}}\"\nelse\n  echo '{\"hookSpecificOutput\":{\"decision\":\"allow\"}}'\nfi\n```\n\n## 6. Fail-Open vs Fail-Closed\n\nWhat happens when your hook script crashes?\n\n### Fail-Open (recommended default)\n\nIf the script crashes, **allow** the action. Safer for advisory hooks and non-critical checks.\n\n```bash\n#!\u002Fbin\u002Fbash\n# Fail-open wrapper\nset +e  # Don't exit on error\n\nresult=$(your_check_logic 2>\u002Fdev\u002Fnull)\nexit_code=$?\n\nif [ $exit_code -ne 0 ]; then\n  # Script failed -- allow the action (fail-open)\n  echo '{\"hookSpecificOutput\":{\"decision\":\"allow\"}}'\n  exit 0\nfi\n\n# Normal processing...\necho \"$result\"\n```\n\n### Fail-Closed (security-critical only)\n\nIf the script crashes, **deny** the action. Use only for critical security gates.\n\n```bash\n#!\u002Fbin\u002Fbash\n# Fail-closed wrapper\nset +e\n\nresult=$(your_check_logic 2>\u002Fdev\u002Fnull)\nexit_code=$?\n\nif [ $exit_code -ne 0 ]; then\n  # Script failed -- deny the action (fail-closed)\n  echo '{\"hookSpecificOutput\":{\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Safety check script failed -- blocking action as precaution\"}}'\n  exit 0\nfi\n\n# Normal processing...\necho \"$result\"\n```\n\n### When to Use Each\n\n| Hook purpose | Fail mode | Rationale |\n|-------------|-----------|-----------|\n| LOC limit enforcement | Fail-open | Better to allow a large file than block all writes |\n| Style reminder | Fail-open | Non-critical advisory |\n| Prevent force push to main | Fail-closed | Destructive action, err on side of caution |\n| Secret detection | Fail-closed | Security-critical, must not leak |\n| Test runner | Fail-open | Test infra failures shouldn't block development |\n\n## 7. Common Mistakes\n\n| Mistake | Why it's wrong | Fix |\n|---------|---------------|-----|\n| Blocking on `PostToolUse` | Action already happened -- too late to block | Use `PreToolUse` for blocking |\n| Wrong event case | `pretooluse` instead of `PreToolUse` -- case-sensitive | Use exact case: `PreToolUse`, `PostToolUse`, etc. |\n| Script not executable | Hook fails silently | Run `chmod +x scripts\u002F*.sh` |\n| Missing shebang | Script may run with wrong interpreter | Add `#!\u002Fbin\u002Fbash` or `#!\u002Fusr\u002Fbin\u002Fenv node` |\n| Hardcoded paths | Breaks on other machines | Use `${CLAUDE_PLUGIN_ROOT}` |\n| stdout pollution | Debug output mixed into JSON response | Use stderr for logging: `echo \"debug\" >&2` |\n| No timeout | Slow script blocks Claude indefinitely | Set `\"timeout\": 10000` (10 seconds) |\n| Matcher too broad (`\".*\"`) | Fires on every tool call, performance impact | Narrow to specific tools |\n| No fail-open wrapper | Script crash = broken hook = frustrated user | Wrap in fail-open try\u002Fcatch |\n\n## 8. Quality Checklist\n\nBefore deploying hooks, verify:\n\n- [ ] Each hook has the correct event type for its purpose\n- [ ] Blocking hooks use `PreToolUse`, not `PostToolUse`\n- [ ] Matchers are tested against expected and unexpected tool names\n- [ ] All script paths use `${CLAUDE_PLUGIN_ROOT}`\n- [ ] All scripts have shebangs and executable permissions\n- [ ] All scripts output valid JSON to stdout\n- [ ] Debug logging goes to stderr, not stdout\n- [ ] Fail-open or fail-closed is explicitly chosen for each hook\n- [ ] Timeouts are set (default: 10 seconds)\n- [ ] Hooks are tested with: normal input, edge case input, missing input\n",{"name":5,"version":60,"description":7},"0.1.0","main","7850740bd837adb32f0a109ab2af82482196c6f2","skills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-hooks",1,"https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills","https:\u002F\u002Fzturnsgo.com","https:\u002F\u002Fgithub.com\u002Fshengdabai","https:\u002F\u002Fzturnsgo.com\u002F","AI × education creator — producer, not consumer. Taught 6,000+ learners; shipping learning tools & daily essays with Claude Code, practicing @xiaolai's way 🥋","2026-11-01 21:28:23",{"source":8,"skillName":5,"commands":72,"prompt":82,"zipAvailable":83},[73,76,79],{"tool":74,"command":75},"npx","npx skills add shengdabai\u002FTony-Claude-Code-Skills --skill writing-hooks",{"tool":77,"command":78},"bunx","bunx skills add shengdabai\u002FTony-Claude-Code-Skills --skill writing-hooks",{"tool":80,"command":81},"pnpm","pnpm dlx skills add shengdabai\u002FTony-Claude-Code-Skills --skill writing-hooks","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-hooks\u002FSKILL.md",true,[],[86,100,127,149,171,188],{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":93,"publishedAt":55},"01M70P17DAD4C1C47NPH6A96EM","xiaolai-methodology","以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 \"李笑来\"、\"笑来\"、\"笑来老师\"、\"扮演李笑来\"、\"找笑来聊聊\"、\"笑来怎么看\"、\"xiaolai\"、\"把时间当作朋友\"、\"通往财富自由之路\"、\"定投改变命运\"、\"韭菜的自我修养\"、\"自学是门手艺\"、\"让时间陪你慢慢变富\"、\"七年就是一辈子\"、\"元认知\"、\"活在未来\"、\"复利\u002F长期主义\"、\"践行\"、\"痴迷改进\"、\"个人商业模式\u002F出售时间\"、\"睡后收入\"、\"定投\"、\"财富自由\"、\"操作系统升级\"、\"最少必要知识\"、\"北极星提示词\"、\"用 AI 该怎么学\u002F思考\" 等任一触发词,或当用户带着关于创业\u002F赚钱\u002F投资\u002F学习\u002F职业\u002F重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fxiaolai-methodology\u002FSKILL.md",[92],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[94,95,96,97,98,35,99],"methodology","xiaolai","xiaolai methodology","朋友","投资","收入",{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":108,"publishedAt":55},"01M70P13NB9YP7ACJR1S861CDS","zread","Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `.\u002F.zread\u002Fwiki\u002F`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module\u002Fpackage explanations, or \"what does this repo do\"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread \u002F zread.ai directly. Also use it proactively before diving into a large unknown repo — if `.\u002F.zread\u002Fwiki\u002Fcurrent` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word \"zread\".","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fzread\u002FSKILL.md",[106,107],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},[102,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,49,125,126],"knowledge","wiki","explore","walkthrough","project","get","knowledge base","run","generate","intent","cli","map","documentation","project wiki","disk","architecture","unknown","generated",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":135,"publishedAt":55},"01M70P10S4SWMZQ8BJAFBFDRW3","youtube","Launch the YouTube Creator Agent to auto-generate videos.\nUse when: user types \u002Fyoutube, wants to create YouTube videos,\ngenerate video content, or upload to YouTube.\nKeywords: youtube, video, 视频, 创作视频, 发布视频","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube\u002FSKILL.md",[133,134],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":32,"categoryName":33,"taxonomyType":29,"taxonomyTypeDesc":30},[129,136,137,138,139,140,141,142,117,143,144,145,50,39,146,54,147,148],"user","content","视频","creator","generate video","videos","launch","video content","create","upload","auto","Video","创作",{"skillNo":150,"name":151,"nameZh":6,"description":152,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":153,"categories":154,"keywords":156,"publishedAt":55},"01M70P0YE95D8BACZCGV8BK90K","youtube-script-sync","Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新\u002F同步\u002F优化 Youtube视频逐字稿知识库”, or says “更新\u002F同步\u002F优化该知识库” while this knowledge base is the active context.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-sync\u002FSKILL.md",[155],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[157,158,129,159,160,147,161,162,163,164,165,166,167,168,115,169,50,39,109,170],"sync","script","youtube script sync","recording","shorts","first","base","person","update","active","child","optimize","idempotent","long",{"skillNo":172,"name":173,"nameZh":6,"description":174,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":175,"categories":176,"keywords":181,"publishedAt":55},"01M70P0WMV5XT0EEC0N445PW07","writing-skills","How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-skills\u002FSKILL.md",[177,178,179,180],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},{"categoryNo":32,"categoryName":33,"taxonomyType":29,"taxonomyTypeDesc":30},[182,35,48,183,184,47,49,50,39,185,186,52,187],"writing skills","teach","write skill","claude code skills","files","trigger",{"skillNo":189,"name":190,"nameZh":6,"description":191,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":192,"categories":193,"keywords":196,"publishedAt":55},"01M70P0SJFR4YVJQBGMHKGSKXT","writing-rules","How to write .claude\u002Frules\u002F files that Claude actually follows. Use when creating, improving, or reviewing project rules.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-rules\u002FSKILL.md",[194,195],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":32,"categoryName":33,"taxonomyType":29,"taxonomyTypeDesc":30},[197,35,198,113,199,186,50,48,47,39,52],"Rules","writing rules","project rules"]