How to Create a New Hook
Relevant source files
The following files were used as context for generating this wiki page:Hooks are automated scripts that trigger on specific events within the Claude Code environment. They serve as powerful tools for enforcing conventions, running quality gates, and automating repetitive tasks. For example, hooks can be used to block edits on the main branch, auto-format code after an edit, or suggest relevant skills based on a user's prompt.
This guide explains the architecture of the hook system, how to configure a new hook, and the expected response format for hook scripts. By following these steps, you can create custom automations tailored to your project's workflow.
Sources: README.md:13-16, README.md:126-132
Hook Architecture and Lifecycle
Hooks are defined in .claude/settings.json and are tied to specific events. When an event occurs, Claude Code checks for any matching hooks, executes the associated command, and processes its output to determine the next action.
The general flow for a hook is as follows:
This diagram illustrates the lifecycle of a hook, from an event trigger to the final action taken based on the hook script's response.
Sources: README.md:126-132, .claude/settings.json:3-23
Hook Events
Hooks can be triggered by four distinct events in the lifecycle.
| Event | When It Fires | Common Use Case |
|---|---|---|
PreToolUse | Before tool execution | Block edits on main, validate commands |
PostToolUse | After tool completes | Auto-format, run tests, lint |
UserPromptSubmit | When a user submits a prompt | Add context, suggest skills |
Stop | When the agent finishes | Decide if Claude should continue processing |
Sources: README.md:135-143, .claude/settings.md:15-23
Hook Configuration
All hooks are configured within the .claude/settings.json file under the top-level hooks key. The configuration is structured by event type.
Sources: .claude/settings.json:3-23, README.md:119-125
Structure in settings.json
The hooks object contains keys for each event type. Each event key holds an array of hook definitions. A hook definition can include a matcher to conditionally run the hook and a hooks array specifying the commands to execute.
This diagram shows the nested structure for defining a hook.
Sources: .claude/settings.json:3-23, README.md:121-132
Example Configuration
Below is an example of a PreToolUse hook that prevents edits on the main branch. It uses a matcher to run only for Edit or Write tools.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "[ \"$(git branch --show-current)\" != \"main\" ] || { echo '{\"block\": true, \"message\": \"Cannot edit on main branch\"}' >&2; exit 2; }",
"timeout": 5
}
]
}
]
}
}
This configuration demonstrates how a simple shell command can act as a powerful guardrail. If the current git branch is main, it prints a JSON message to stderr and exits with code 2 to block the action.
Sources: README.md:121-132, .claude/settings.json:4-15
Another example is the UserPromptSubmit hook which runs the skill-eval.sh script to provide skill suggestions to the user.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/skill-eval.sh",
"timeout": 5
}
]
}
]
}
}
This hook is configured to run a shell script located in the project's .claude/hooks directory. The script itself contains more complex logic, such as running a Node.js engine.
Sources: .claude/settings.json:16-23, .claude/hooks/skill-eval.sh:1-12
Creating a Hook Script
A hook is executed as a command, which is typically a shell script. This script is responsible for performing the desired logic and returning a structured JSON response.
Script Execution
The command field in settings.json specifies the script to run. The script can be a one-liner or a path to a file. The skill-eval.sh script is a good example of a file-based hook. It's a wrapper that executes a more complex Node.js script, passing along the prompt arguments.
#!/bin/bash
# .claude/hooks/skill-eval.sh
# Ensure the script is run from the project root
cd "$CLAUDE_PROJECT_DIR" || exit 1
# The prompt is passed as arguments to the script
PROMPT_CONTENT="$*"
# Run the Node.js skill evaluation engine
# The output of this Node.js script is the JSON response for the hook
node ./.claude/hooks/skill-eval.js "$PROMPT_CONTENT"
This script demonstrates how a hook can act as a bridge to more complex logic written in other languages, like the Node.js engine that reads rules from .claude/hooks/skill-rules.json.
Sources: .claude/hooks/skill-eval.sh:1-12, .claude/hooks/skill-rules.json
Hook Response Format
Hook scripts must communicate their result by printing a JSON object to either stdout or stderr. The structure of this JSON object determines the action Claude Code will take.
| Field | Type | Description |
|---|---|---|
block | boolean | If true, blocks the action (PreToolUse only). |
message | string | A message to show the user. |
feedback | string | Non-blocking informational feedback. |
suppressOutput | boolean | If true, hides the command's output. |
continue | boolean | For Stop hooks, whether to continue generation. |
Sources: README.md:145-153, .claude/settings.md:25-31
Exit Codes
The exit code of the hook script is also significant.
| Exit Code | Meaning |
|---|---|
0 | Success. |
2 | Blocking error. For PreToolUse only, blocks the tool. |
| Other | Non-blocking error. |
The following diagram shows how the exit code and block property interact for a PreToolUse hook.
Sources: README.md:155-159, .claude/settings.md:33-36
Summary
Creating a new hook involves three main steps:
- Write a script that performs the desired action and outputs a specific JSON format.
- Choose the correct event (
PreToolUse,PostToolUse, etc.) that should trigger your script. - Add a new entry to the
hooksobject in.claude/settings.json, specifying the event, an optional matcher, and the command to run your script.
By leveraging hooks, you can significantly enhance automation and enforce project standards, making Claude Code a more integrated and powerful development partner.
Sources: README.md:437-444