{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://catalog.lintel.tools/schemas/claude-code/hooks/latest.json",
  "title": "Claude Code Hooks",
  "description": "Map of [hook event](https://code.claude.com/docs/en/hooks#hook-events) names to arrays of matcher groups. Each matcher group optionally filters by a regex pattern and contains an array of hook handlers that execute when the event fires.",
  "x-lintel": {
    "source": "https://raw.githubusercontent.com/lintel-rs/catalog/master/schemas/claude-code/hooks.json",
    "sourceSha256": "f3a1ef83b81c68adbcb69a9a9601a2d943fc56c52d6c03e32e1657fba6bbcda7"
  },
  "type": "object",
  "properties": {
    "SessionStart": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a session starts. Matcher filters by session source: `startup` (new session), `resume` (resuming previous session), `clear` (after `/clear`), `compact` (after context compaction). Command hooks have access to `$CLAUDE_ENV_FILE` for [persisting environment variables](https://code.claude.com/docs/en/hooks#persist-environment-variables) across the session."
    },
    "UserPromptSubmit": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when the user submits a prompt, before Claude processes it. **No matcher support** — always fires on every user message. Hooks can block the prompt (preventing Claude from seeing it) or modify it before processing."
    },
    "PreToolUse": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires before a tool is executed. Matcher filters by tool name (e.g. `Bash`, `Edit|Write`, `mcp__.*`). Hooks can [block the tool call](https://code.claude.com/docs/en/hooks#pretooluse-decision-control) (exit code 2 or `{ok: false}`), modify its input via `hookSpecificOutput.updatedInput`, auto-allow/deny permission via `hookSpecificOutput.permissionDecision` (`allow`, `deny`, `ask`), or inject additional context via `hookSpecificOutput.additionalContext`."
    },
    "PermissionRequest": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a tool requests user permission before executing. Matcher filters by tool name. Hooks can [auto-allow or deny](https://code.claude.com/docs/en/hooks#permissionrequest-decision-control) the permission via `hookSpecificOutput.decision.behavior` (`allow` or `deny`), optionally modifying the tool input via `decision.updatedInput` or updating permissions via `decision.updatedPermissions`. If denied, `decision.message` is shown to the user."
    },
    "PostToolUse": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires after a tool executes successfully. Matcher filters by tool name. Hooks can inject additional context for Claude via `hookSpecificOutput.additionalContext` (e.g. lint results after a file write) or replace MCP tool output via `hookSpecificOutput.updatedMCPToolOutput`. Supports [async hooks](https://code.claude.com/docs/en/hooks#run-hooks-in-the-background) that run in the background without blocking."
    },
    "PostToolUseFailure": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires after a tool execution fails. Matcher filters by tool name. Hooks can inject additional context to help Claude understand and recover from the failure via `hookSpecificOutput.additionalContext`. Supports [async hooks](https://code.claude.com/docs/en/hooks#run-hooks-in-the-background)."
    },
    "Notification": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires on notifications. Matcher filters by type: `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`."
    },
    "SubagentStart": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a [subagent](https://code.claude.com/docs/en/sub-agents) is spawned. Matcher filters by agent type name (built-in: `Bash`, `Explore`, `Plan`, or custom agent names)."
    },
    "SubagentStop": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a [subagent](https://code.claude.com/docs/en/sub-agents) stops. Matcher filters by agent type name."
    },
    "Stop": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when Claude's turn ends and it is about to stop. **No matcher support** — always fires. Hooks can force Claude to continue working by returning `{decision: \"block\", reason: \"...\"}`. In subagent frontmatter, `Stop` hooks are automatically converted to `SubagentStop` events at runtime. Does not fire on user interrupt."
    },
    "TeammateIdle": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when an [agent team](https://code.claude.com/docs/en/agent-teams) teammate is about to go idle. **No matcher support** — always fires. Exit code 2 prevents the teammate from going idle."
    },
    "TaskCompleted": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a task is being marked as completed. **No matcher support** — always fires. Exit code 2 prevents the task from being marked completed and feeds stderr back as feedback."
    },
    "ConfigChange": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a configuration file changes during a session. Matcher filters by source: `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills`. Can block configuration changes from taking effect (except `policy_settings`)."
    },
    "WorktreeCreate": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a worktree is being created via `--worktree` or `isolation: \"worktree\"`. Replaces default git behavior — the hook must print the absolute path to the created worktree on stdout. **No matcher support** — always fires. Any non-zero exit code causes worktree creation to fail."
    },
    "WorktreeRemove": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a worktree is being removed, either at session exit or when a subagent finishes. **No matcher support** — always fires. Failures are logged in debug mode only and cannot block removal."
    },
    "PreCompact": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires before context compaction. Matcher filters by trigger type: `manual`, `auto`."
    },
    "SessionEnd": {
      "$ref": "#/$defs/MatcherGroupArray",
      "description": "Fires when a session ends. Matcher filters by exit reason: `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other`. Cannot block session termination."
    }
  },
  "additionalProperties": false,
  "$defs": {
    "MatcherGroupArray": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/MatcherGroup"
      }
    },
    "MatcherGroup": {
      "type": "object",
      "description": "A matcher group pairs an optional regex filter with an array of hook handlers. If `matcher` is omitted, the hooks fire on every occurrence of the event. See [matcher patterns](https://code.claude.com/docs/en/hooks#matcher-patterns).",
      "properties": {
        "matcher": {
          "type": "string",
          "description": "Regex pattern to filter when this group's hooks fire. Use `|` for OR (e.g. `Edit|Write`), `.*` for wildcards (e.g. `mcp__.*`). MCP tools follow the pattern `mcp__<server>__<tool>`. Omit to match all occurrences.",
          "examples": [
            "Bash",
            "Edit|Write",
            "mcp__.*"
          ]
        },
        "hooks": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/HookHandler"
          }
        }
      },
      "required": [
        "hooks"
      ]
    },
    "HookHandler": {
      "oneOf": [
        {
          "$ref": "#/$defs/CommandHook"
        },
        {
          "$ref": "#/$defs/HttpHook"
        },
        {
          "$ref": "#/$defs/PromptHook"
        },
        {
          "$ref": "#/$defs/AgentHook"
        }
      ]
    },
    "CommandHook": {
      "type": "object",
      "description": "Runs a shell command or script. The command receives [hook context as JSON on stdin](https://code.claude.com/docs/en/hooks#common-input-fields), including `session_id`, `transcript_path`, `cwd`, `permission_mode`, `hook_event_name`, and event-specific fields. **Exit 0**: success — stdout is parsed as JSON for [decision control](https://code.claude.com/docs/en/hooks#json-output). **Exit 2**: blocking error — stderr is fed back to Claude. **Other exit codes**: non-blocking error, logged in verbose mode.",
      "properties": {
        "type": {
          "const": "command"
        },
        "command": {
          "type": "string",
          "description": "Shell command or script path to execute. Runs in the project's working directory. Use `$CLAUDE_PROJECT_DIR` for the project root (always quote it: `\"$CLAUDE_PROJECT_DIR\"/path`) and `${CLAUDE_PLUGIN_ROOT}` for plugin-relative paths.",
          "examples": [
            "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate.sh",
            "${CLAUDE_PLUGIN_ROOT}/scripts/lint-check.sh"
          ]
        },
        "timeout": {
          "type": "number",
          "description": "Max execution time in seconds.",
          "default": 600,
          "examples": [
            10,
            30,
            120
          ]
        },
        "statusMessage": {
          "type": "string",
          "description": "Custom spinner message displayed while the hook runs.",
          "examples": [
            "Validating...",
            "Running lint check..."
          ]
        },
        "async": {
          "type": "boolean",
          "description": "Run in background without blocking. Only supported for `PostToolUse` and `PostToolUseFailure` events. Async hooks cannot block or return decisions. See [async hooks](https://code.claude.com/docs/en/hooks#run-hooks-in-the-background).",
          "default": false
        },
        "once": {
          "type": "boolean",
          "description": "Run once per session then remove. Primarily useful in skill-scoped hooks.",
          "default": false
        }
      },
      "required": [
        "type",
        "command"
      ],
      "additionalProperties": false
    },
    "HttpHook": {
      "type": "object",
      "description": "Sends the event's [JSON input](https://code.claude.com/docs/en/hooks#hook-input-and-output) as an HTTP POST request to a URL. The endpoint communicates results back through the response body using the same [JSON output format](https://code.claude.com/docs/en/hooks#json-output) as command hooks. Non-2xx responses, connection failures, and timeouts produce non-blocking errors. To block a tool call or deny a permission, return a 2xx response with a JSON body containing the appropriate decision fields. HTTP hooks must be configured by editing settings JSON directly — the `/hooks` interactive menu only supports command hooks.",
      "properties": {
        "type": {
          "const": "http"
        },
        "url": {
          "type": "string",
          "format": "uri",
          "description": "URL to send the POST request to. Must match an entry in `allowedHttpHookUrls` if that setting is configured.",
          "examples": [
            "http://localhost:8080/hooks/pre-tool-use",
            "https://hooks.example.com/validate"
          ]
        },
        "headers": {
          "type": "object",
          "description": "Additional HTTP headers as key-value pairs. Values support environment variable interpolation using `$VAR_NAME` or `${VAR_NAME}` syntax. Only variables listed in `allowedEnvVars` are resolved; references to unlisted variables are replaced with empty strings.",
          "examples": [
            {
              "Authorization": "Bearer $MY_TOKEN",
              "X-Custom-Header": "value"
            }
          ],
          "additionalProperties": {
            "type": "string"
          }
        },
        "allowedEnvVars": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "List of environment variable names that may be interpolated into header values. References to unlisted variables are replaced with empty strings. Required for any env var interpolation to work.",
          "examples": [
            [
              "MY_TOKEN",
              "HOOK_SECRET"
            ]
          ]
        },
        "timeout": {
          "type": "number",
          "description": "Max execution time in seconds.",
          "default": 30,
          "examples": [
            10,
            30,
            60
          ]
        },
        "statusMessage": {
          "type": "string",
          "description": "Custom spinner message displayed while the hook runs.",
          "examples": [
            "Validating with remote service..."
          ]
        },
        "once": {
          "type": "boolean",
          "description": "Run once per session then remove. Primarily useful in skill-scoped hooks.",
          "default": false
        }
      },
      "required": [
        "type",
        "url"
      ],
      "additionalProperties": false
    },
    "PromptHook": {
      "type": "object",
      "description": "Evaluates a single-turn LLM [prompt-based hook](https://code.claude.com/docs/en/hooks#prompt-based-hooks) to make a pass/fail decision. A small, fast model receives the prompt and must respond with `{ok: true}` to allow the action, or `{ok: false, reason: \"explanation\"}` to block it. Cannot use tools — decides based solely on the prompt content.",
      "properties": {
        "type": {
          "const": "prompt"
        },
        "prompt": {
          "type": "string",
          "description": "The evaluation prompt sent to the LLM. Use `$ARGUMENTS` to interpolate the hook's full input context as JSON (includes `session_id`, `cwd`, `tool_name`, `tool_input`, and other event-specific fields). If `$ARGUMENTS` is not present, input JSON is appended to the prompt.",
          "examples": [
            "Is this Bash command safe to run? Review for destructive operations: $ARGUMENTS",
            "Does this file edit follow our coding standards? $ARGUMENTS"
          ]
        },
        "model": {
          "type": "string",
          "description": "Model for evaluation. Defaults to a fast model for low latency.",
          "examples": [
            "haiku"
          ]
        },
        "timeout": {
          "type": "number",
          "description": "Max execution time in seconds.",
          "default": 30
        },
        "statusMessage": {
          "type": "string",
          "description": "Custom spinner message displayed while the hook runs.",
          "examples": [
            "Evaluating safety..."
          ]
        },
        "once": {
          "type": "boolean",
          "description": "Run once per session then remove. Primarily useful in skill-scoped hooks.",
          "default": false
        }
      },
      "required": [
        "type",
        "prompt"
      ],
      "additionalProperties": false
    },
    "AgentHook": {
      "type": "object",
      "description": "Runs a multi-turn LLM [agent-based hook](https://code.claude.com/docs/en/hooks#agent-based-hooks) that can use tools (`Read`, `Grep`, `Glob`, `Bash`, etc.) to gather additional context before making a decision. Unlike prompt hooks, agent hooks can explore the codebase and run commands to verify conditions. Must respond with `{ok: true}` or `{ok: false, reason: \"explanation\"}`.",
      "properties": {
        "type": {
          "const": "agent"
        },
        "prompt": {
          "type": "string",
          "description": "The verification prompt sent to the agent. Use `$ARGUMENTS` to interpolate the hook's full input context as JSON. The agent can then use tools to investigate further before deciding. If `$ARGUMENTS` is not present, input JSON is appended to the prompt.",
          "examples": [
            "Verify this operation is safe and won't cause data loss: $ARGUMENTS"
          ]
        },
        "model": {
          "type": "string",
          "description": "Model for the agent. Defaults to a fast model.",
          "examples": [
            "sonnet"
          ]
        },
        "timeout": {
          "type": "number",
          "description": "Max execution time in seconds.",
          "default": 60
        },
        "statusMessage": {
          "type": "string",
          "description": "Custom spinner message displayed while the hook runs.",
          "examples": [
            "Verifying operation..."
          ]
        },
        "once": {
          "type": "boolean",
          "description": "Run once per session then remove. Primarily useful in skill-scoped hooks.",
          "default": false
        }
      },
      "required": [
        "type",
        "prompt"
      ],
      "additionalProperties": false
    }
  }
}
