在 Claude Code 的世界里,Hooks 是一套事件驱动的自动化机制。它们是你预先配置的 shell 命令,会在 Claude Code 的特定生命周期事件发生时自动执行。你可以把 Hooks 想象成 Git Hooks 的类比——就像 pre-commit 和 post-commit 钩子一样,Claude Code 的 Hooks 让你在工具调用的前后插入自定义逻辑。
与让 Claude 自主决定是否执行某个操作不同,Hooks 提供了确定性的保证:只要事件触发,命令就一定会执行。这对于代码格式化、lint 检查、安全防护等场景至关重要。
Claude Code 支持以下五种 Hook 事件类型:
其中最常用的是 PreToolUse 和 PostToolUse,它们让你在工具调用的前后注入自定义行为。
Hooks 在 .claude/settings.json(项目级)或 ~/.claude/settings.json(用户级)中配置。配置结构如下:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"command": "echo 'About to write a file: $CLAUDE_FILE_PATH'"
}
],
"PostToolUse": [
{
"matcher": "Write",
"command": "npx prettier --write $CLAUDE_FILE_PATH"
}
],
"Stop": [
{
"command": "echo 'Claude has finished responding'"
}
]
}
}每个 Hook 条目包含:
matcher(可选):匹配工具名称的字符串。仅当工具名称匹配时才触发该 Hook。省略 matcher 表示匹配所有工具。command:要执行的 shell 命令。matcher 字段用于过滤触发 Hook 的工具。它按照工具名称进行匹配:
// 仅在 Write 工具调用时触发
{ "matcher": "Write", "command": "..." }
// 仅在 Bash 工具调用时触发
{ "matcher": "Bash", "command": "..." }
// 在任何工具调用时都触发(省略 matcher)
{ "command": "..." }Hook 命令执行时,Claude Code 会注入一系列环境变量,让你的脚本能获取当前操作的上下文:
$CLAUDE_FILE_PATH — 当前操作的文件路径$CLAUDE_TOOL_NAME — 触发 Hook 的工具名称$CLAUDE_TOOL_INPUT — 工具的输入参数(JSON 格式)$CLAUDE_TOOL_OUTPUT — 工具的输出结果(仅 PostToolUse 可用)PreToolUse Hook 有一个强大的能力:如果 Hook 命令的退出码为非零值(exit code != 0),Claude Code 将阻止该工具的执行。这让你可以构建安全防护机制。
每次 Claude 写入文件后自动运行 Prettier 格式化:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "npx prettier --write $CLAUDE_FILE_PATH 2>/dev/null || true"
}
]
}
}注意末尾的 || true——这确保即使 Prettier 遇到不支持的文件类型也不会导致错误。
使用 PreToolUse Hook 拦截危险的 shell 命令:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "echo $CLAUDE_TOOL_INPUT | grep -q 'rm -rf /' && echo 'BLOCKED: dangerous rm -rf command' && exit 1 || exit 0"
}
]
}
}当 Claude 尝试执行包含 rm -rf / 的命令时,Hook 检测到危险模式,输出警告信息并以非零退出码退出,从而阻止命令执行。Claude 会收到 Hook 的输出,并据此调整行为。
每次编辑测试文件后自动运行对应的测试:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "echo $CLAUDE_FILE_PATH | grep -q '\.test\.' && npm test -- --testPathPattern=$CLAUDE_FILE_PATH || true"
}
]
}
}Hook 的标准输出(stdout)会被 Claude Code 捕获并反馈给 Claude 模型。这意味着 Hook 不仅能执行操作,还能影响 Claude 的后续行为。比如,一个 lint Hook 输出的错误信息会让 Claude 自动修复代码问题。
|| true 或 2>/dev/null 避免非预期的 Hook 失败导致工具被拦截。.claude/settings.json,个人偏好放在 ~/.claude/settings.json。In the world of Claude Code, Hooks are an event-driven automation mechanism. They are shell commands you pre-configure that execute automatically at specific lifecycle events within Claude Code. Think of them like Git Hooks — just as pre-commit and post-commit hooks work, Claude Code Hooks let you inject custom logic before and after tool calls.
Unlike letting Claude autonomously decide whether to perform an action, Hooks provide deterministic guarantees: as long as the event fires, the command will execute. This is crucial for code formatting, linting, security enforcement, and similar scenarios.
Claude Code supports five Hook event types:
The most commonly used are PreToolUse and PostToolUse, which let you inject custom behavior before and after tool calls.
Hooks are configured in .claude/settings.json (project-level) or ~/.claude/settings.json (user-level). The structure looks like this:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"command": "echo 'About to write a file: $CLAUDE_FILE_PATH'"
}
],
"PostToolUse": [
{
"matcher": "Write",
"command": "npx prettier --write $CLAUDE_FILE_PATH"
}
],
"Stop": [
{
"command": "echo 'Claude has finished responding'"
}
]
}
}Each Hook entry contains:
matcher (optional): A string to match tool names. The Hook fires only when the tool name matches. Omitting matcher means it matches all tools.command: The shell command to execute.The matcher field filters which tools trigger the Hook. It matches against tool names:
// Only fires on Write tool calls
{ "matcher": "Write", "command": "..." }
// Only fires on Bash tool calls
{ "matcher": "Bash", "command": "..." }
// Fires on any tool call (matcher omitted)
{ "command": "..." }When a Hook command executes, Claude Code injects several environment variables so your script can access context about the current operation:
$CLAUDE_FILE_PATH — The file path being operated on$CLAUDE_TOOL_NAME — The name of the tool that triggered the Hook$CLAUDE_TOOL_INPUT — The tool’s input parameters (JSON format)$CLAUDE_TOOL_OUTPUT — The tool’s output result (PostToolUse only)PreToolUse Hooks have a powerful capability: if the Hook command exits with a non-zero exit code, Claude Code will block the tool from executing. This lets you build security guardrails.
Automatically run Prettier after every file write by Claude:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "npx prettier --write $CLAUDE_FILE_PATH 2>/dev/null || true"
}
]
}
}Note the trailing || true — this ensures that even if Prettier encounters an unsupported file type, it won’t cause an error.
Use a PreToolUse Hook to intercept dangerous shell commands:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "echo $CLAUDE_TOOL_INPUT | grep -q 'rm -rf /' && echo 'BLOCKED: dangerous rm -rf command' && exit 1 || exit 0"
}
]
}
}When Claude attempts to execute a command containing rm -rf /, the Hook detects the dangerous pattern, outputs a warning message, and exits with a non-zero code, blocking execution. Claude receives the Hook’s output and adjusts its behavior accordingly.
Automatically run the corresponding test after editing a test file:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "echo $CLAUDE_FILE_PATH | grep -q '\.test\.' && npm test -- --testPathPattern=$CLAUDE_FILE_PATH || true"
}
]
}
}A Hook’s standard output (stdout) is captured by Claude Code and fed back to the Claude model. This means Hooks can not only perform actions but also influence Claude’s subsequent behavior. For instance, error messages output by a lint Hook will prompt Claude to automatically fix code issues.
|| true or 2>/dev/null to avoid unintended Hook failures blocking tools..claude/settings.json; personal preferences go in ~/.claude/settings.json.