- ✅ 理解 Hooks 系统的工作原理
- ✅ 掌握不同类型 Hooks 的配置
- ✅ 创建自动化质量检查流程
- ✅ 实现 CI/CD 集成触发
Hooks 是 Claude Code 的事件监听系统,允许你在特定时刻自动执行自定义脚本。
类比:就像 Git Hooks,但用于 Claude Code 的工作流。
用户提交提示词
↓
[user-prompt-submit-hook] 触发
↓
运行自定义脚本(如:代码检查)
↓
通过/阻止 → 继续/停止流程
问题场景:
- ❌ 忘记运行 lint 就提交代码
- ❌ 提交包含敏感信息(API keys)
- ❌ 代码格式不统一
- ❌ 测试没通过就合并
解决方案:
- ✅ 自动运行 lint 检查
- ✅ 自动扫描敏感信息
- ✅ 自动格式化代码
- ✅ 自动运行测试
触发时机:用户提交提示词前
用途:
- 提交前验证
- 代码质量检查
- 敏感信息扫描
- 自动格式化
示例:
{
"hooks": {
"user-prompt-submit-hook": {
"command": "bash .claude/hooks/pre-submit.sh",
"blocking": true
}
}
}blocking 参数:
true: 阻塞模式 - 脚本失败会阻止操作false: 非阻塞模式 - 仅显示警告
触发时机:Claude Code 调用工具前
用途:
- 工具使用审计
- 权限检查
- 敏感操作确认
- 日志记录
示例:
{
"hooks": {
"tool-call-hook": {
"command": "node .claude/hooks/tool-audit.js",
"blocking": false
}
}
}触发时机:新对话开始时
用途:
- 环境检查
- 配置加载
- 项目状态检查
- 欢迎信息
触发时机:对话结束时
用途:
- 清理临时文件
- 保存会话日志
- 生成报告
- 备份工作
~/.config/claude/settings.json # 全局配置
.claude/settings.json # 项目配置(优先级更高)
{
"hooks": {
"user-prompt-submit-hook": {
"command": "bash .claude/hooks/pre-submit.sh",
"blocking": true,
"env": {
"PROJECT_ROOT": "${workspaceFolder}"
}
},
"tool-call-hook": {
"command": "python .claude/hooks/tool-logger.py {{tool_name}}",
"blocking": false
},
"conversation-start-hook": {
"command": "npm run check-env",
"blocking": false
}
}
}Hooks 脚本可以访问:
${workspaceFolder}: 项目根目录${tool_name}: 工具名称(tool-call-hook)- 自定义
env中定义的变量
pre-submit.sh:
#!/bin/bash
echo "🔍 Running code quality checks..."
# 1. ESLint 检查
echo "📝 Checking with ESLint..."
npx eslint . --max-warnings 0
if [ $? -ne 0 ]; then
echo "❌ ESLint check failed!"
exit 1
fi
# 2. TypeScript 类型检查
echo "🔷 Checking TypeScript types..."
npx tsc --noEmit
if [ $? -ne 0 ]; then
echo "❌ Type check failed!"
exit 1
fi
# 3. 运行测试
echo "🧪 Running tests..."
npm test -- --bail
if [ $? -ne 0 ]; then
echo "❌ Tests failed!"
exit 1
fi
echo "✅ All checks passed!"
exit 0配置:
{
"hooks": {
"user-prompt-submit-hook": {
"command": "bash .claude/hooks/pre-submit.sh",
"blocking": true
}
}
}secret-scan.sh:
#!/bin/bash
echo "🔒 Scanning for secrets..."
# 检查常见的敏感信息模式
SECRETS_FOUND=false
# API Keys
if git diff --cached | grep -E "(api[_-]?key|apikey)['\"]?\s*[:=]\s*['\"][a-zA-Z0-9]{20,}['\"]"; then
echo "⚠️ Found potential API key!"
SECRETS_FOUND=true
fi
# AWS Keys
if git diff --cached | grep -E "AKIA[0-9A-Z]{16}"; then
echo "⚠️ Found potential AWS key!"
SECRETS_FOUND=true
fi
# Private Keys
if git diff --cached | grep -E "-----BEGIN (RSA |EC )?PRIVATE KEY-----"; then
echo "⚠️ Found private key!"
SECRETS_FOUND=true
fi
# Passwords
if git diff --cached | grep -E "(password|passwd|pwd)['\"]?\s*[:=]\s*['\"][^'\"]{8,}['\"]"; then
echo "⚠️ Found potential password!"
SECRETS_FOUND=true
fi
if [ "$SECRETS_FOUND" = true ]; then
echo "❌ Sensitive information detected! Please remove it."
exit 1
fi
echo "✅ No secrets found."
exit 0auto-format.sh:
#!/bin/bash
echo "🎨 Auto-formatting code..."
# Prettier
echo "📝 Running Prettier..."
npx prettier --write "src/**/*.{js,jsx,ts,tsx,json,css,md}"
# 自动修复 ESLint 问题
echo "🔧 Auto-fixing ESLint issues..."
npx eslint . --fix
echo "✅ Code formatted successfully!"
exit 0非阻塞配置(自动修复后继续):
{
"hooks": {
"user-prompt-submit-hook": {
"command": "bash .claude/hooks/auto-format.sh",
"blocking": false
}
}
}tool-logger.js:
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const toolName = process.argv[2] || 'unknown';
const timestamp = new Date().toISOString();
const logFile = path.join(__dirname, '../../logs/tool-usage.log');
// 确保日志目录存在
const logDir = path.dirname(logFile);
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
// 记录工具使用
const logEntry = `[${timestamp}] Tool used: ${toolName}\n`;
fs.appendFileSync(logFile, logEntry);
console.log(`✓ Logged tool usage: ${toolName}`);
process.exit(0);comprehensive-check.sh:
#!/bin/bash
set -e # 任何错误立即退出
echo "🚀 Running comprehensive pre-submit checks..."
echo ""
# 计数器
TOTAL_CHECKS=0
PASSED_CHECKS=0
FAILED_CHECKS=0
# 辅助函数
run_check() {
local check_name=$1
local check_cmd=$2
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
echo "▶ Running: $check_name"
if eval "$check_cmd" > /dev/null 2>&1; then
echo " ✅ $check_name passed"
PASSED_CHECKS=$((PASSED_CHECKS + 1))
return 0
else
echo " ❌ $check_name failed"
FAILED_CHECKS=$((FAILED_CHECKS + 1))
return 1
fi
}
# 执行检查
echo "📋 Code Quality Checks:"
run_check "ESLint" "npx eslint ."
run_check "Prettier" "npx prettier --check ."
run_check "TypeScript" "npx tsc --noEmit"
echo ""
echo "🧪 Testing:"
run_check "Unit Tests" "npm test -- --coverage --coverageThreshold='{\"global\":{\"branches\":80,\"functions\":80,\"lines\":80}}'"
echo ""
echo "🔒 Security:"
run_check "Dependency Audit" "npm audit --audit-level=moderate"
run_check "Secret Scan" "bash .claude/hooks/secret-scan.sh"
echo ""
echo "📦 Build:"
run_check "Build Check" "npm run build"
# 总结
echo ""
echo "═══════════════════════════════════════"
echo "Summary: $PASSED_CHECKS/$TOTAL_CHECKS checks passed"
echo "═══════════════════════════════════════"
if [ $FAILED_CHECKS -gt 0 ]; then
echo "❌ $FAILED_CHECKS check(s) failed. Please fix before submitting."
exit 1
fi
echo "✅ All checks passed! Ready to submit."
exit 0目录结构:
.claude/
├── settings.json
└── hooks/
├── pre-submit.sh # 主检查脚本
├── lint-check.sh # Lint 检查
├── test-check.sh # 测试检查
├── security-check.sh # 安全检查
├── build-check.sh # 构建检查
└── tool-audit.js # 工具审计
settings.json:
{
"hooks": {
"user-prompt-submit-hook": {
"command": "bash .claude/hooks/pre-submit.sh",
"blocking": true,
"env": {
"MIN_COVERAGE": "80",
"MAX_WARNINGS": "0"
}
},
"tool-call-hook": {
"command": "node .claude/hooks/tool-audit.js {{tool_name}}",
"blocking": false
}
}
}pre-submit.sh(主脚本):
#!/bin/bash
SCRIPTS_DIR="$(dirname "$0")"
echo "🔍 Quality Gate System"
echo "======================"
echo ""
# 运行所有检查
"$SCRIPTS_DIR/lint-check.sh" || exit 1
"$SCRIPTS_DIR/test-check.sh" || exit 1
"$SCRIPTS_DIR/security-check.sh" || exit 1
"$SCRIPTS_DIR/build-check.sh" || exit 1
echo ""
echo "🎉 Quality gate passed!"
exit 0# ❌ 差的做法
npm test
# ✅ 好的做法
echo "🧪 Running tests..."
npm test
if [ $? -eq 0 ]; then
echo "✅ Tests passed"
else
echo "❌ Tests failed"
exit 1
fi#!/bin/bash
set -e # 遇到错误立即退出
# 不需要每次检查 if
eslint .
tsc --noEmit
npm test# 从环境变量读取配置
MIN_COVERAGE=${MIN_COVERAGE:-80}
MAX_WARNINGS=${MAX_WARNINGS:-0}
npm test -- --coverage --coverageThreshold='{"global":{"lines":'$MIN_COVERAGE'}}'# 并行运行独立检查
eslint . &
PID1=$!
tsc --noEmit &
PID2=$!
# 等待所有进程
wait $PID1 || exit 1
wait $PID2 || exit 1if ! npm test; then
echo ""
echo "❌ Tests failed!"
echo ""
echo "💡 Tips:"
echo " - Run 'npm test' locally to see details"
echo " - Check the test output above"
echo " - Fix failing tests before submitting"
echo ""
exit 1
fi任务:创建基本的质量检查 Hook
步骤:
- 创建
.claude/hooks/目录 - 编写
pre-submit.sh脚本 - 配置 Hook
- 测试 Hook
检查项:
- Lint 检查
- 代码格式检查
- 基础测试
验证:
# 故意制造问题
echo "const unused = 1" >> test.js
# 提交提示词,应该被 Hook 阻止
"请创建新文件"任务:实现敏感信息检测
要求:
- 检测 API keys
- 检测密码
- 检测私钥
- 提供清晰的警告
测试用例:
// 应该被检测到
const apiKey = "sk_live_1234567890abcdef";
const password = "MySecretPass123!";任务:创建完整的自动化工作流
场景:
- 对话开始:检查环境
- 提交前:运行质量检查
- 工具调用:记录审计日志
- 对话结束:生成报告
实现:
- 4 种 Hooks 都配置
- 脚本完整可用
- 日志清晰可读
- 正确配置 settings.json
- Hooks 能正常触发
- blocking 模式工作正常
- 编写至少 3 个 Hook 脚本
- 脚本逻辑正确
- 错误处理完善
- 输出清晰友好
- 集成到实际项目
- 阻止了低质量提交
- 提高了代码质量
总分 100,60分及格
检查:
- settings.json 路径正确
- 脚本有执行权限:
chmod +x script.sh - command 路径正确(相对于项目根目录)
- 重启 Claude Code
调试:
# 手动运行脚本
bash .claude/hooks/pre-submit.sh
# 查看详细错误
bash -x .claude/hooks/pre-submit.sh- 通常不建议跳过
- 可以临时修改
blocking: false - 或注释掉 Hook 配置
优化方法:
- 并行运行检查
- 只检查变更文件
- 使用缓存
- 将耗时检查放到 CI
完成本模块后,继续学习:
👉 模块 6:外部集成 - MCP 与 Sub-Agents
学习如何集成外部服务和使用 Sub-Agents 处理复杂任务。
- Hooks 官方文档
- Git Hooks 文档
- 本项目的示例 Hooks:
.claude/hooks/
记住:好的 Hooks 应该:
- 快速执行(< 30秒)
- 提供清晰反馈
- 只阻止真正的问题
- 易于调试和维护