AgentSkillsCN

hook-development

将 Expo 应用部署至 iOS App Store、Android Play Store、Web 托管,以及 API 路由。

SKILL.md
--- frontmatter
name: hook-development
description: "Use when creating hooks, configuring hook events, debugging hooks not firing, choosing hook types (command vs prompt), or understanding exit codes. Triggers: create hook, hook not working, PreToolUse, PostToolUse, Stop, settings.json hooks"
argument-hint: "[event-type]"
disable-model-invocation: true

Hook Development

Before creating hooks, check latest docs: /docs hooks or /docs hooks-guide for current syntax and best practices.

Hooks run scripts automatically at specific points in Claude's workflow. Unlike CLAUDE.md instructions which are advisory, hooks are deterministic.

Quick Setup (Scaffolding)

bash
# Create structure
mkdir -p .claude/hooks

# Create settings.json with a basic hook
cat > .claude/settings.json << 'EOF'
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
EOF

For plugins, use hooks/hooks.json at plugin root with ${CLAUDE_PLUGIN_ROOT}.

Hook Events

EventWhenMatcherCan Block
SessionStartSession begins/resumessource¹No
UserPromptSubmitUser submits promptNoYes (exit 2)
PreToolUseBefore tool runsYesYes (exit 2)
PermissionRequestPermission dialog shownYesYes (deny)
PostToolUseAfter tool succeedsYesNo
PostToolUseFailureAfter tool failsYesNo
SubagentStartSubagent spawnsNoNo
SubagentStopSubagent finishesNoYes (exit 2)
StopClaude finishesNoYes (exit 2)
PreCompactBefore compactiontrigger²No
Setup--init or --maintenancetrigger³No
NotificationNotification senttype⁴No
SessionEndSession endsNoNo

¹ startup, resume, clear, compact ² manual, auto ³ init, maintenancepermission_prompt, idle_prompt, auth_success

Configuration

Hooks in settings files (~/.claude/settings.json, .claude/settings.json):

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint:fix $FILE",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Plugin hooks use ${CLAUDE_PLUGIN_ROOT} for portability.

Hook Types

Command Hooks (type: "command")

Run bash commands. Most common type.

json
{
  "type": "command",
  "command": "bash /path/to/script.sh",
  "timeout": 30
}

Prompt Hooks (type: "prompt")

LLM-evaluated hooks for context-aware decisions. Best for Stop and SubagentStop.

json
{
  "type": "prompt",
  "prompt": "Evaluate if Claude should stop. Check if all tasks complete. Return {\"ok\": true} to allow, or {\"ok\": false, \"reason\": \"explanation\"} to continue.",
  "timeout": 30
}

Exit Codes

ExitBehavior
0Success - parse JSON stdout if present
2Block action - stderr shown to Claude
OtherNon-blocking error - logged only

Input (JSON via stdin)

Common fields:

json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse"
}

Tool hooks add: tool_name, tool_input, tool_use_id PostToolUse adds: tool_response

Output (JSON via stdout)

PreToolUse

json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "Safe operation",
    "updatedInput": {"field": "modified value"},
    "additionalContext": "Info for Claude"
  },
  "systemMessage": "User-only message"
}

permissionDecision: "allow" | "deny" | "ask"

Stop/SubagentStop (force continue)

json
{
  "decision": "block",
  "reason": "Not finished - still need to run tests"
}

SessionStart (persist env vars)

Use CLAUDE_ENV_FILE:

bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
fi

Environment Variables

Always available:

  • CLAUDE_PROJECT_DIR - Project root
  • CLAUDE_CODE_REMOTE - "true" in remote environments

SessionStart/Setup only:

  • CLAUDE_ENV_FILE - Write env vars here to persist

Matchers

For PreToolUse, PostToolUse, PermissionRequest:

PatternMatches
"Write"Write tool only
"Write|Edit"Write or Edit
"mcp__memory__.*"All memory MCP tools
".*" or "*"All tools (avoid)

Case-sensitive regex.

Quick Patterns

Block dangerous commands:

bash
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$CMD" | grep -qE 'rm -rf|git push.*--force'; then
  echo "Blocked: Dangerous command" >&2
  exit 2
fi
exit 0

Auto-approve safe files:

bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.md || "$FILE" == *.txt ]]; then
  echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
fi
exit 0

Add context on prompt:

bash
#!/bin/bash
echo "Current time: $(date)"
exit 0

Skills/Agents Hooks

Hooks in skill/agent frontmatter (scoped to that component):

yaml
---
name: my-skill
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./validate.sh"
  Stop:
    - hooks:
        - type: prompt
          prompt: "Check if all tasks complete"
---

Add once: true to run only once per session.

Debugging

  1. Check /hooks to see registered hooks
  2. Use claude --debug for execution details
  3. Test commands manually first
  4. Verify scripts are executable

Best Practices

  • Use ${CLAUDE_PROJECT_DIR} for paths
  • Quote all variables ("$VAR")
  • Keep PreToolUse < 100ms
  • Exit 0 for success, 2 to block
  • Validate JSON input exists before parsing

Supporting Files

For deeper content, see:

Related Components

  • Skills: writing-skills, ecosystem-analysis
  • Used by agents: hook-creator, workflow-auditor