Skip to content
← Back to Claude Prompts

Create Hook

InstructionClaude

Slash command to create and configure Claude Code hooks with reference documentation and interactive guidance

💡 Use Case:
Use when setting up automated workflows like ESLint auto-fixing, prettier formatting, test runners, or commit message validation

Prompt Content


description: Create a new Claude Code hook in a settings file argument-hint: [event] [matcher] [command]


/create-hook

#

Purpose

#

Add a hook to a Claude Code settings file. Hooks are shell commands the harness runs automatically at fixed points in Claude’s lifecycle (before a tool runs, after a file edit, when a session starts, and so on). They live in a settings file under the hooks key, not in .claude/agents/ or .claude/commands/.

Contract

#

Inputs:

  • $1 — EVENT (one of the hook events below, e.g., PostToolUse)
  • $2 — MATCHER (tool name or regex, e.g., Edit|Write; use "" for events without matchers)
  • $3 — COMMAND (shell command to run, e.g., npx prettier --write "$CLAUDE_PROJECT_DIR")
  • --user — Write to ~/.claude/settings.json (applies to all projects)
  • --local — Write to .claude/settings.local.json (this project, not committed)
  • Default — Write to .claude/settings.json (this project, shared with the team)
  • --timeout — Optional timeout in seconds for the command

Outputs:

  • STATUS=<CREATED|EXISTS|FAIL> PATH=<settings file> EVENT=<event> MATCHER=<matcher>

Hook Events

#
EventWhen it runsMatcher
PreToolUseBefore a tool call (can block it)Tool name, e.g. Bash, Edit|Write, mcp__.*
PermissionRequestWhen a permission dialog would be shownTool name
PostToolUseAfter a tool call succeedsTool name
NotificationWhen Claude Code sends a notificationNotification type (optional)
UserPromptSubmitWhen the user submits a promptNone
StopWhen the main agent finishes respondingNone
SubagentStopWhen a subagent finishesNone
PreCompactBefore context compactionmanual or auto
SessionStartWhen a session starts or resumesstartup, resume, clear, compact
SessionEndWhen a session endsNone

Instructions

#
  1. Validate inputs:

    • EVENT must be one of the events in the table above (case-sensitive)
    • MATCHER is only meaningful for events that support it; omit it otherwise
    • COMMAND must be non-empty
    • If --timeout is given, it must be a positive integer
  2. Determine the settings file:

    • Default: .claude/settings.json
    • With --local: .claude/settings.local.json
    • With --user: ~/.claude/settings.json
    • Create the directory and file ({}) if they don’t exist
  3. Read and merge, never overwrite:

    • Parse the existing JSON and keep every other key (permissions, env, existing hooks, …)
    • Find hooks.{EVENT}; create it as an empty array if missing
    • Reuse the entry whose matcher equals MATCHER, or append a new one
    • If an identical command already exists under that matcher, output STATUS=EXISTS and stop
  4. Hook configuration shape:

    {
      "hooks": {
        "{EVENT}": [
          {
            "matcher": "{MATCHER}",
            "hooks": [
              {
                "type": "command",
                "command": "{COMMAND}",
                "timeout": 60
              }
            ]
          }
        ]
      }
    }
    • Omit matcher for events that don’t use one
    • Omit timeout unless --timeout was given
  5. If the command needs a script:

    • Put it in .claude/hooks/{name}.sh (project) or ~/.claude/hooks/{name}.sh (user) and make it executable
    • Reference project scripts with "$CLAUDE_PROJECT_DIR"/.claude/hooks/{name}.sh so it works from any working directory
    • The hook receives JSON on stdin (session_id, hook_event_name, tool_name, tool_input, …); read it with jq
    • Exit code 0 = success; exit code 2 = blocking error (stderr goes back to Claude, and for PreToolUse the tool call is blocked); other codes = non-blocking error shown to the user
  6. Write and report:

    • Write the file back as formatted JSON (2-space indent) and confirm it still parses
    • Print: STATUS=CREATED PATH={settings file} EVENT={event} MATCHER={matcher}
    • Remind the user to review it with /hooks; hooks edited on disk are picked up after review or on the next session

Examples

#

Format files with Prettier after every edit

#
/create-hook PostToolUse "Edit|Write" 'jq -r ".tool_input.file_path" | xargs npx prettier --write'
# Output: STATUS=CREATED PATH=.claude/settings.json EVENT=PostToolUse MATCHER=Edit|Write

Resulting .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r \".tool_input.file_path\" | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

Auto-fix ESLint issues after edits

#
/create-hook PostToolUse "Edit|Write" 'jq -r ".tool_input.file_path" | grep -E "\.(ts|tsx|js|vue)$" | xargs -r npx eslint --fix'

Validate commit messages before git commit runs

#
/create-hook PreToolUse "Bash" '"$CLAUDE_PROJECT_DIR"/.claude/hooks/check-commit-msg.sh'

.claude/hooks/check-commit-msg.sh:

#!/usr/bin/env bash
cmd=$(jq -r '.tool_input.command // ""')
if [[ "$cmd" == *"git commit"* ]] && ! grep -qE -- '-m "(feat|fix|docs|chore|refactor|test)(\(.+\))?: ' <<<"$cmd"; then
  echo "Commit message must follow Conventional Commits (feat:, fix:, ...)" >&2
  exit 2
fi
exit 0

Run tests when Claude finishes (user-level)

#
/create-hook Stop "" 'npm test --silent' --user --timeout 300
# Output: STATUS=CREATED PATH=~/.claude/settings.json EVENT=Stop MATCHER=

Constraints

#
  • Idempotent: won’t add a duplicate command under the same event and matcher
  • Never overwrites or reorders unrelated settings
  • Output must be valid JSON
  • No network access required

Security

#
  • Hooks run with your user permissions, automatically, without asking
  • Quote shell variables ("$VAR") and validate paths from tool_input
  • Skip sensitive files (.env, .git/, keys) in hooks that read or write files
  • Review hooks from other people’s repos before trusting them
#
  • /hooks - Interactive interface to view, add, and remove hooks
  • Hooks reference - Full list of events, input fields, and output options