Get the FREE Ultimate OpenClaw Setup Guide →

claude-code:hook

npx machina-cli add skill bendrucker/claude/hook --openclaw
Files (1)
SKILL.md
3.8 KB

Claude Code Hooks

Reference for creating and configuring Claude Code hooks. When uncertain about syntax or features, use the Task tool with subagent_type='claude-code-guide' to consult official documentation.

Hook Types

TypeTriggerUse Cases
PreToolUseBefore tool executionValidate inputs, block operations, modify parameters
PostToolUseAfter tool completesCheck results, run linters, provide feedback
UserPromptSubmitWhen user sends messagePre-process input, add context
StopSession endsCleanup, save state
SubagentStopSubagent completesProcess results
PreCompactBefore context compactionSave important state
NotificationSystem notificationLog events

Configuration Files

  • ~/.claude/settings.json - User-level (global)
  • .claude/settings.json - Project-level
  • .claude/settings.local.json - Local (not committed)
  • Plugin hooks: plugins/<name>/hooks/hooks.json

Hook Structure

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "bun ./hooks/biome"
          }
        ]
      }
    ]
  }
}

Matcher Patterns:

  • Simple: "Write", "Edit"
  • Multiple: "Edit|Write|MultiEdit"
  • With args: "Bash(npm:*)", "Bash(osascript:*)|Bash(open:*)"
  • MCP tools: "mcp__linear__create_issue"
  • Plugin MCP tools: "mcp__plugin_<plugin>_<namespace>__<tool>"
  • Both patterns: "mcp__linear__create_issue|mcp__plugin_linear_linear__create_issue"

Hook Input

Commands receive JSON on stdin:

{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.ts",
    "content": "..."
  },
  "cwd": "/project/root",
  "session_id": "...",
  "transcript_path": "..."
}

Parse in shell:

input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')

Or use @constellos/claude-code-kit/runners:

import { readStdinJson, writeStdoutJson } from "@constellos/claude-code-kit/runners";
const input = await readStdinJson<PreToolUseHookInput>();

Hook Output

PreToolUse - Control execution:

{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Use gh cli instead"}}
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "updatedInput": {"state": "Todo"}}}

PostToolUse - Provide feedback:

{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Lint errors found..."}}

Exit with no output to allow without modification.

Script Storage

Store complex hooks in .claude/hooks/ or project hooks/ directory:

.claude/
├── settings.json
└── hooks/
    └── my-hook.ts

Reference with:

"command": "bun $CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.ts"

Examples

See these repositories for hook implementations:

Debugging

For troubleshooting hook failures, see debugging.

Source

git clone https://github.com/bendrucker/claude/blob/main/plugins/claude-code/skills/hook/SKILL.mdView on GitHub

Overview

This skill covers configuring, creating, and debugging Claude Code hooks across PreToolUse, PostToolUse, and UserPromptSubmit. It helps automate workflows within Claude Code and resolve issues like hooks not firing or partial execution.

How This Skill Works

Hooks are configured in JSON files stored under ~/.claude/settings.json, .claude/settings.json, or plugin-specific hooks.json. Supported hook types include PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, PreCompact, and Notification, each with matcher patterns and a command to run. Hook inputs and outputs are exchanged via JSON on stdin/stdout, enabling precise control over tool execution and feedback.

When to Use It

  • You want to run validations before a tool executes (PreToolUse).
  • You need to run post-execution checks or linting (PostToolUse).
  • You're debugging why a hook isn't firing or only some hooks ran.
  • You want to format or transform JSON output with a hook (e.g., using jq).
  • You want to automate workflows by storing complex hooks in .claude/hooks or a project hooks directory.

Quick Start

  1. Step 1: Create or edit a hook JSON in the appropriate directory (e.g., plugins/<name>/hooks/hooks.json) to register PreToolUse or PostToolUse and specify the command to run.
  2. Step 2: Write the hook script under .claude/hooks/ (for example .claude/hooks/my-hook.ts) and reference it in the hook JSON with a command like "bun $CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.ts".
  3. Step 3: Trigger tool actions to test, then inspect the hookOutput for fields like hookEventName, permissionDecision, or updatedInput to verify behavior.

Best Practices

  • Define clear matcher patterns (e.g., Edit|Write) to target specific tools.
  • Test hooks in a controlled environment before enabling them in production.
  • Prefer non-destructive PreToolUse hooks and use updatedInput to pass changes downstream.
  • Store large or complex hooks in .claude/hooks/ or project hooks/ and reference them via a bun command.
  • Document hook behavior with explicit permissionDecision and reason for easy auditing.

Example Use Cases

  • Input modification: see plugins/linear/hooks/ for adjusting user input before processing.
  • Permission decisions: plugins/github/scripts/ define rules to allow or block actions.
  • PostToolUse feedback: .claude/hooks/biome/ provide lint results or summaries after tool runs.
  • Hook structure: a PostToolUse entry with a matcher and a bun command like bun ./hooks/biome.
  • Hook storage and reference: store complex hooks in .claude/hooks/ or project hooks/ and reference with a bun path.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers