麻花视频播放器
42.03MB · 2025-10-19
前面对Claude Code CLI有了基本了解,今天继续深度探索Claude Code CLI Hooks的使用方式。对往期内容感兴趣的小伙伴也可以看往期内容:
1.0.128 (Claude Code)
Claude Code CLI钩子配置在 settings.json 文件,提供了 用户(全局)配置、项目配置、本地项目配置 3种配置方式:
钩子由匹配器组织,每个匹配器可以有多个钩子, 格式如下:
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 100
}
]
}
]
}
}
Claude Code提供了几个在工作流程不同时间点运行的Hook事件,其中不同的Hook事件提供了不同的匹配器。
Claude Code Hook事件:
PreToolUse、PostToolUse常见匹配器:
PreCompact常见匹配器:
SessionStart常见匹配器:
SessionEnd常见匹配器:
Claude Code CLI提供了创建Hooks的交互式命令,对交互式命令创建Hooks流程还不了解的小伙伴可以看往期内容:Claude Code CLI初体验
在使用示例中我们主要以使用手动配置为主。
钩子输入JSON格式可以查看官网文档:docs.anthropic.com/en/docs/cla…
从命令行读取输入参数主要用到jq,简单了解一下jq。
jq 是一个轻量级且灵活的命令行 JSON 处理器,它可以对 JSON 数据进行解析、提取、转换和格式化等操作,广泛应用于在 Shell 脚本中处理 JSON 数据,或者在命令行下快速查看和处理 JSON 格式的文件或输出。
jq官网:jqlang.org/
以macOS为例,可以使用brew安装jq
$ brew install jq
在 .claude/settings.local.json 添加如下配置:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '"\(.tool_input.command) - \(.tool_input.description // "No description")"' >> ~/.claude/bash-command-log.txt"
}
]
}
]
}
}
重启Claude Code CLI,让Claude Code运行一个简单的命令,比如 ls
执行完成后,可以看到在项目目录下多了一个 bash-command-log.txt 文件,内容为 ls - List files in current directory 正是Claude Code执行的ls命令及ls命令描述
通过命令行也可以实现代码格式化功能,编辑文件后自动格式化文件,这里以格式化TypeScript文件为例,在 .claude/settings.local.json 添加如下配置:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; if echo "$file_path" | grep -q '\.ts$'; then npx prettier --write "$file_path"; fi; }"
}
]
}
]
}
}
随便创建一个ts文件,输入示例内容
重启Claude Code CLI,修改ts文件内容
使用 Ctrl+R 快捷键查看详细调用信息,可以看到调了 PostToolUse:Edit 钩子并执行了自定义的钩子命令
从前面例子可以看到Shell命令很强大,通过Hooks事件配合Shell命令可以完成很多事件,但是Shell命令自身也有很多局限性,如需要一定学习成本,对于某些复杂和需要灵活定义的场景表现力不从心,不过我们没必要担心,因为Claude Code CLI也支持调用脚本。其次就是使用Shell命令我们无法查看工具调用的输入参数,导致很多脚本基本上都是在盲写,只有通过验证才看到脚本的错误,这些问题通过脚本将不复存在。
在Hooks中使用脚本也很简单,只需要将 command 执行内容改成脚本执行方式即可
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "sh ./hello.sh"
}
]
}
]
}
}
在项目根目录创建 hello.sh 脚本文件,脚本内容如下:
重启Claude Code CLI,在交互模式随便输入提示词
在Claude Code CLI Hooks事件中也可以使用环境变量 CLAUDE_PROJECT_DIR (仅限于Claude Code CLI Hooks命令中使用) 来引用项目脚本
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "sh $CLAUDE_PROJECT_DIR/.claude/hooks/hello.sh"
}
]
}
]
}
}
我们也可以使用Python脚本,修改 settings.local.json 配置如下:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python $CLAUDE_PROJECT_DIR/.claude/hooks/hooks_input.py"
}
]
}
]
}
}
创建 .claude/hooks/hooks_input.py 脚本,注意在Hooks命令中工具调用参数是以标准输入输出方式传递的,使用Python读取参数需要从 sys.stdin 中读取
重启Claude Code CLI,在交互模式读取或修改任意文件内容,使用 Ctrl+R 快捷键查看详细调用信息
将钩子改为 PostToolUse 输出信息如下
执行Ruby脚本和其他两种脚本方式类似,修改 settings.local.json 配置如下:
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "ruby $CLAUDE_PROJECT_DIR/.claude/hooks/hooks_input.rb"
}
]
}
]
}
}
创建 .claude/hooks/hooks_input.rb 脚本
重启Claude Code CLI,在交互模式读取或修改任意文件内容,使用 Ctrl+R 快捷键查看详细调用信息
借助Hooks事件和苹果脚本我们可以在需要的时候手动触发系统通知,首先修改 settings.local.json 配置如下:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification "Awaiting your input" with title "Claude Code" sound name "default"'"
}
]
}
]
}
}
重启Claude Code CLI,输入任意提示词,就会系统右侧看到通知信息
使用脚本可以阻止对敏感文件的编辑,修改 settings.local.json 配置如下:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "python -c "import json, sys; data=json.load(sys.stdin); path=data.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(p in path for p in ['.env', 'package-lock.json', '.git/']) else 0)""
}
]
}
]
}
}
重启Claude Code CLI,输入任意提示词
当编辑或写入安全目录时就会触发安全拦截阻止文件编辑和写入
在 .claude/hooks/ 创建一个 sensitive_prompt.py 脚本文件
#!/usr/bin/env python3
import json
import sys
import re
import datetime
# Load input from stdin
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
sys.exit(1)
prompt = input_data.get("prompt", "")
# Check for sensitive patterns
sensitive_patterns = [
(r"(?i)b(password|secret|key|token)s*[:=]", "Prompt contains potential secrets"),
]
for pattern, message in sensitive_patterns:
if re.search(pattern, prompt):
# Use JSON output to block with a specific reason
output = {
"decision": "block",
"reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
}
print(json.dumps(output))
sys.exit(0)
# Add current time to context
context = f"Current time: {datetime.datetime.now()}"
print(context)
"""
The following is also equivalent:
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": context,
},
}))
"""
# Allow the prompt to proceed with the additional context
sys.exit(0)
代码中输出使用了 "decision": "block",官方文档列举了permissionDecision的几种状态值,更详细内容查看官方文档:docs.anthropic.com/en/docs/cla…
PreToolUse决策控制:
PostToolUse决策控制:
UserPromptSubmit决策控制:
Stop/SubagentStop决策控制:
修改 settings.local.json 配置如下:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python $CLAUDE_PROJECT_DIR/.claude/hooks/sensitive_prompt.py"
}
]
}
]
}
}
重启Claude Code CLI,输入带有关键词 password 、secret 的提示词就会触发敏感词拦截
不涉及敏感词则打印时间,继续任务
该示例功能为自动修复Markdown文件中缺少的语言标记和格式问题,在 .claude/hooks/ 创建一个 markdown_formatter.py 脚本文件
#!/usr/bin/env python3
"""
Markdown formatter for Claude Code output.
Fixes missing language tags and spacing issues while preserving code content.
"""
import json
import sys
import re
import os
def detect_language(code):
"""Best-effort language detection from code content."""
s = code.strip()
# JSON detection
if re.search(r'^s*[{[]', s):
try:
json.loads(s)
return 'json'
except:
pass
# Python detection
if re.search(r'^s*defs+w+s*(', s, re.M) or
re.search(r'^s*(import|from)s+w+', s, re.M):
return 'python'
# JavaScript detection
if re.search(r'b(functions+w+s*(|consts+w+s*=)', s) or
re.search(r'=>|console.(log|error)', s):
return 'javascript'
# Bash detection
if re.search(r'^#!.*b(bash|sh)b', s, re.M) or
re.search(r'b(if|then|fi|for|in|do|done)b', s):
return 'bash'
# SQL detection
if re.search(r'b(SELECT|INSERT|UPDATE|DELETE|CREATE)s+', s, re.I):
return 'sql'
return 'text'
def format_markdown(content):
"""Format markdown content with language detection."""
# Fix unlabeled code fences
def add_lang_to_fence(match):
indent, info, body, closing = match.groups()
if not info.strip():
lang = detect_language(body)
return f"{indent}```{lang}n{body}{closing}n"
return match.group(0)
fence_pattern = r'(?ms)^([ t]{0,3})```([^n]*)n(.*?)(n1```)s*$'
content = re.sub(fence_pattern, add_lang_to_fence, content)
# Fix excessive blank lines (only outside code fences)
content = re.sub(r'n{3,}', 'nn', content)
return content.rstrip() + 'n'
# Main execution
try:
input_data = json.load(sys.stdin)
file_path = input_data.get('tool_input', {}).get('file_path', '')
if not file_path.endswith(('.md', '.mdx')):
sys.exit(0) # Not a markdown file
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
formatted = format_markdown(content)
if formatted != content:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(formatted)
print(f" Fixed markdown formatting in {file_path}")
except Exception as e:
print(f"Error formatting markdown: {e}", file=sys.stderr)
sys.exit(1)
修改 settings.local.json 配置如下:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "python $CLAUDE_PROJECT_DIR/.claude/hooks/markdown_formatter.py"
}
]
}
]
}
}
重启Claude Code CLI,让AI帮我们创建一个Markdown任务列表,创建完成会触发 PostToolUse:Write 钩子
在任务列表添加一个未格式化的代码块
重新让AI添加一些任务项,此时会触发 PostToolUse:Edit 钩子
格式化完成后,可以看到代码块也被标记上了指定语言类型
在 .claude/hooks/ 创建一个 auto-approval.py 脚本文件,添加脚本内容:
#!/usr/bin/env python3
import json
import sys
# Load input from stdin
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
sys.exit(1)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# Example: Auto-approve file reads for documentation files
if tool_name == "Read":
file_path = tool_input.get("file_path", "")
if file_path.endswith((".md", ".mdx", ".txt", ".json")):
# Use JSON output to auto-approve the tool call
output = {
"decision": "approve",
"reason": "Documentation file auto-approved",
"suppressOutput": True # Don't show in transcript mode
}
print(json.dumps(output))
sys.exit(0)
# For other cases, let the normal permission flow proceed
sys.exit(0)
修改 settings.local.json 配置如下:
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "python $CLAUDE_PROJECT_DIR/.claude/hooks/auto-approval.py"
}
]
}
]
}
}
重启Claude Code CLI,让AI执行读取文件操作触发 PreToolUse:Read 钩子
在Claude Code CLI中MCP 工具遵循模式 mcp____,例如:
修改 settings.local.json 配置如下:
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__context7__.*",
"hooks": [
{
"type": "command",
"command": "echo 'context7 operation'"
}
]
},
{
"matcher": "mcp__.*__write.*",
"hooks": [
{
"type": "command",
"command": "echo 'mcp write'"
}
]
}
]
}
}
重启Claude Code CLI,输入提示词
React 新特性,use context7
当AI调用context7 MCP工具时就会触发 PreToolUse:mcp__context7__.* 钩子
Claude Code CLI提供了功能强大的Hooks事件及反馈机制,开发者可以使用自己熟悉的不同类型脚本来完成自定义Hooks操作,除了示例中的代码格式化、系统通知、文件保护以及敏感词拦截等还可以利用Hooks做更多事情,比如任务完成进行语音提示、任务完成自动触发commit等等都是可以的,场景越多越能感受到SubAgent虽然强大,Hooks才是Claude Code CLI的革命性创新。
见原文:Hooks才是Claude Code CLI 的革命性更新