Claude Code AfterCommand Hook
命令执行后触发机制详解
一、概述
AfterCommand 是 Claude Code 提供的一种 Hook(钩子)机制,它在每条命令执行完成后自动触发。无论命令执行成功还是失败,AfterCommand Hook 都会被执行,这使得它成为实现命令后置处理逻辑的理想选择。
AfterCommand Hook 是 Claude Code 事件驱动架构的重要组成部分,与 BeforeCommand(命令执行前触发)形成完整的生命周期管理。通过 AfterCommand,开发者可以拦截命令执行结果,执行清理操作,记录日志,或在特定条件下触发通知。
核心特性
- 自动触发: 每条命令执行完成后无需手动干预
- 双向感知: 可获取命令执行的状态码、输出内容和执行时间
- 非阻塞设计: 默认不阻塞后续命令的执行流程
- 灵活脚本: 支持任何可执行脚本(Shell、Python、Node.js 等)
- 上下文丰富: 可通过环境变量获取完整的命令上下文信息
触发时机
AfterCommand Hook 在 Claude Code 执行完一条命令后立即触发,触发顺序为:命令执行完成 → 收集退出码和输出 → 调用 AfterCommand 脚本 → 继续处理后续命令。整个过程对用户透明,不会影响命令本身的执行结果。
二、用途
AfterCommand Hook 的用途涵盖三个主要方面:结果检查、资源清理和日志记录。这些用途共同构成了 Claude Code 自动化工作流中不可或缺的闭环管理能力。
2.1 结果检查 (Result Checking)
AfterCommand 可自动检查命令的执行结果,验证命令是否按预期完成。通过获取退出码(exit code)和标准输出,可以判断命令执行状态。例如,编译命令返回非零退出码时,可以自动触发错误报告或重试机制。
检查维度
- 退出码验证: 0 表示成功,非 0 表示失败
- 输出内容分析: 检查输出中是否包含错误关键词
- 超时检测: 命令执行是否超过预期时间
- 资源消耗: 监控命令执行的内存和 CPU 使用
2.2 资源清理 (Cleanup)
命令执行后可能留下临时文件、锁文件、后台进程等资源。AfterCommand 可以自动清理这些资源,防止累积占用磁盘空间或导致后续命令执行异常。资源清理是维护开发环境健康的重要手段。
清理场景示例
- 删除命令生成的临时目录和中间文件
- 关闭命令启动的后台进程或数据库连接
- 解锁命令执行期间占用的文件锁或端口
- 还原命令修改的环境变量配置
2.3 日志记录 (Logging)
AfterCommand 可以将每条命令的执行信息持久化到日志文件中,形成完整的命令执行历史。这些日志可用于审计、调试和性能分析。日志记录的内容可以包括命令文本、执行时间、退出码、输出摘要等。
日志记录的价值
- 审计追踪: 记录谁在什么时候执行了什么命令
- 调试分析: 通过历史日志追踪问题根源
- 性能监控: 统计命令执行耗时,识别性能瓶颈
- 安全审计: 检测异常命令执行模式
三、配置方法
AfterCommand Hook 的配置通过 Claude Code 的配置文件 settings.json 或 settings.local.json 完成。配置文件位于项目根目录的 .claude/ 文件夹中,或全局配置目录下。
{
"hooks": {
"AfterCommand": "bash .claude/hooks/after-command.sh"
}
}
配置项 "hooks.AfterCommand" 的值是一个字符串,指向可执行脚本的路径。路径可以是绝对路径,也可以是相对于项目根目录的路径。Claude Code 在每条命令执行完成后,会调用该路径指向的脚本。
配置文件层级
| 配置文件 |
位置 |
优先级 |
说明 |
| settings.local.json |
.claude/settings.local.json |
最高 |
项目本地配置,不纳入版本控制 |
| settings.json |
.claude/settings.json |
中 |
项目共享配置,纳入版本控制 |
| 全局配置 |
~/.claude/settings.json |
最低 |
用户级全局配置 |
配置建议
建议在 settings.local.json 中配置 AfterCommand Hook,避免将可能包含敏感信息或环境特定路径的配置提交到版本控制中。团队共享的 Hook 脚本可放在 settings.json 中,并确保脚本路径对所有成员有效。
四、Hook 脚本格式
AfterCommand Hook 脚本是一个可执行文件,支持 Shell、Python、Node.js、PowerShell 等任何语言编写的脚本。脚本通过环境变量获取命令执行的上下文信息。
环境变量
Claude Code 在执行 AfterCommand 脚本时,会设置以下环境变量:
| 环境变量 |
说明 |
| CLAUDE_COMMAND |
被执行的原始命令文本 |
| CLAUDE_EXIT_CODE |
命令的退出码(0 表示成功) |
| CLAUDE_STDOUT |
命令的标准输出内容 |
| CLAUDE_STDERR |
命令的错误输出内容 |
| CLAUDE_CWD |
命令执行时的工作目录 |
| CLAUDE_TIMESTAMP |
命令执行的 Unix 时间戳 |
| CLAUDE_DURATION |
命令执行耗时(毫秒) |
基本脚本结构
一个典型的 AfterCommand Shell 脚本结构如下:
echo "[$(date)] Command executed: $CLAUDE_COMMAND" >> /tmp/claude-commands.log
echo "[$(date)] Exit code: $CLAUDE_EXIT_CODE" >> /tmp/claude-commands.log
echo "[$(date)] Duration: ${CLAUDE_DURATION}ms" >> /tmp/claude-commands.log
echo "---" >> /tmp/claude-commands.log
if [ "$CLAUDE_EXIT_CODE" -ne 0 ]; then
echo "[WARN] Command failed: $CLAUDE_COMMAND" >> /tmp/claude-errors.log
fi
多语言支持
AfterCommand 脚本不限于 Shell,可以使用任何可执行语言编写。以下是 Python 版本的示例:
import os
import json
import datetime
command = os.environ.get('CLAUDE_COMMAND', '')
exit_code = int(os.environ.get('CLAUDE_EXIT_CODE', '-1'))
duration = os.environ.get('CLAUDE_DURATION', '0')
log_entry = {
'timestamp': datetime.datetime.now().isoformat(),
'command': command,
'exit_code': exit_code,
'duration_ms': duration
}
with open('/tmp/claude-audit.jsonl', 'a') as f:
f.write(json.dumps(log_entry) + '\n')
if exit_code != 0:
print(f"[HOOK] Command failed: {command[:80]}")
脚本权限要求
配置的 Hook 脚本必须具有可执行权限。在 Linux/macOS 上使用 chmod +x script.sh 设置执行权限。在 Windows 上,确保文件关联正确或使用 bash 作为脚本解释器前缀。
五、使用场景
AfterCommand Hook 在多种实际开发场景中发挥重要作用,以下是最常见的三类应用场景。
5.1 命令结果验证 (Command Result Validation)
在持续开发过程中,AfterCommand 可以自动验证命令执行结果,确保每次操作都符合预期。例如,编译命令执行后自动检查产物完整性,测试命令执行后自动解析测试报告,或部署命令执行后验证服务是否正常启动。
验证策略
- 退出码检查: 快速判断命令是否成功执行
- 输出模式匹配: 在输出中搜索特定关键词(如 "ERROR"、"FAIL"、"success")
- 产物验证: 检查命令预期的输出文件是否生成且内容正确
- 服务健康检查: 部署后验证目标服务是否响应正常
5.2 通知推送 (Notification)
当命令执行时间较长或结果需要关注时,AfterCommand 可以触发通知机制。典型场景包括:长时间运行的任务完成通知、命令失败告警、CI/CD 流水线状态更新等。
if [ "$CLAUDE_EXIT_CODE" -ne 0 ]; then
notify-send "Claude Code: Command Failed" \
"Exit code: $CLAUDE_EXIT_CODE\nCommand: $CLAUDE_COMMAND"
fi
5.3 资源清理 (Resource Cleanup)
开发过程中经常会产生临时资源,AfterCommand 可以确保这些资源被及时清理,避免环境污染。这对于长时间运行的开发会话尤其重要。
if [ -d "/tmp/claude-temp-$CLAUDE_TIMESTAMP" ]; then
rm -rf "/tmp/claude-temp-$CLAUDE_TIMESTAMP"
echo "[HOOK] Cleaned up temporary directory"
fi
docker ps -q --filter "label=claude-session=$CLAUDE_TIMESTAMP" | \
xargs -r docker rm -f
if [ "$(df / | tail -1 | awk '{print $5}' | tr -d '%')" -gt 90 ]; then
echo "[HOOK] Disk usage exceeds 90%, triggering cleanup"
docker system prune -f --volumes 2>/dev/null
fi
其他场景
- 性能统计: 收集命令执行耗时数据,识别开发流程瓶颈
- 环境一致性检查: 确保命令执行后环境未发生意外变更
- 自动化备份: 在修改关键文件前自动创建备份快照
- 审计合规: 记录所有命令执行历史,满足合规审计要求
- 交叉验证: 多个命令的结果比对,确保数据一致性
六、代码示例
本节提供完整的 AfterCommand Hook 脚本示例,涵盖不同编程语言和功能组合,可直接用于实际项目。
6.1 综合日志记录器 (Shell)
一个功能完善的日志记录脚本,将命令执行信息记录到结构化的 JSON 日志文件中:
LOGFILE="$HOME/.claude/logs/commands-$(date +%Y%m).jsonl"
mkdir -p "$(dirname "$LOGFILE")"
STDOUT_TRUNC=$(echo "$CLAUDE_STDOUT" | head -c 500)
STDERR_TRUNC=$(echo "$CLAUDE_STDERR" | head -c 500)
cat >> "$LOGFILE" <<- EOFLOG
{"ts":$(date +%s),"cmd":"$(echo "$CLAUDE_COMMAND" | head -c 200)","exit":$CLAUDE_EXIT_CODE,"dur":${CLAUDE_DURATION:-0},"cwd":"$CLAUDE_CWD"}
EOFLOG
if [ "$CLAUDE_EXIT_CODE" -ne 0 ]; then
ERRORLOG="$HOME/.claude/logs/errors-$(date +%Y%m).log"
{
echo "=== [$(date)] ==="
echo "Command: $CLAUDE_COMMAND"
echo "Exit: $CLAUDE_EXIT_CODE"
echo "stderr: $STDERR_TRUNC"
echo
} >> "$ERRORLOG"
fi
6.2 环境状态检查 (Python)
一个 Python 脚本,在每条命令执行后检查环境状态并报告异常:
import os
import psutil
import json
THRESHOLDS = {
'cpu_percent': 80.0,
'memory_percent': 85.0,
'disk_percent': 90.0,
}
alerts = []
if psutil.cpu_percent(interval=0.1) > THRESHOLDS['cpu_percent']:
alerts.append('High CPU usage')
if psutil.virtual_memory().percent > THRESHOLDS['memory_percent']:
alerts.append('High memory usage')
if alerts:
report = {
'timestamp': os.environ.get('CLAUDE_TIMESTAMP'),
'command': os.environ.get('CLAUDE_COMMAND', '')[:100],
'alerts': alerts
}
print(f"[HOOK] Environment alert: {json.dumps(report)}")
6.3 通知与备份 (Node.js)
一个 Node.js 脚本,在重要命令执行后发送通知并备份关键文件:
const fs = require('fs');
const path = require('path');
const cmd = process.env.CLAUDE_COMMAND || '';
const exitCode = parseInt(process.env.CLAUDE_EXIT_CODE || '0');
const modifyPatterns = [/^git\s+add/, /^git\s+commit/, /^rm\s+/, /^mv\s+/];
const isModification = modifyPatterns.some(p => p.test(cmd));
if (isModification && exitCode === 0) {
const backupDir = path.join(process.env.HOME, '.claude/backups');
fs.mkdirSync(backupDir, { recursive: true });
fs.writeFileSync(
path.join(backupDir, `snapshot-${Date.now()}.json`),
JSON.stringify({ cmd, time: new Date().toISOString() })
);
console.log(`[HOOK] Backup snapshot created`);
}
if (exitCode !== 0) {
console.error(`[HOOK] Command failed (exit ${exitCode}): ${cmd.slice(0, 80)}`);
}
脚本调试技巧
在开发 Hook 脚本时,可以使用 echo "[HOOK] debug: ..." 输出调试信息。这些输出会显示在 Claude Code 的界面中。如果脚本执行出错,请检查脚本的可执行权限和环境变量是否正确传递。
七、最佳实践
合理使用 AfterCommand Hook 可以大幅提升开发效率和自动化水平。以下是在实际项目中总结的最佳实践。
7.1 保持脚本轻量
AfterCommand 脚本在每条命令后都会执行,因此必须保持轻量和高效。避免在脚本中执行耗时的操作(如大型文件扫描、远程 API 调用等),这些操作会显著降低交互式开发的流畅度。建议将耗时任务异步化或仅在特定条件下执行。
性能准则
- 脚本执行时间控制在 100ms 以内
- 避免同步网络请求和数据库查询
- 使用文件追加而非文件覆盖操作
- 限制日志输出的大小,必要时进行轮转
7.2 错误隔离
Hook 脚本的错误不应影响主流程。在脚本中使用 set -e 时要谨慎,确保脚本错误不会导致 Claude Code 主进程异常。建议在脚本入口处使用 set +e 或在关键命令后添加 || true。
set +e
cleanup_temp_files || true
log_command 2>/dev/null || true
7.3 环境变量安全性
AfterCommand 脚本可以访问所有环境变量,包括可能包含敏感信息的变量。不要在日志中记录完整的命令文本(可能包含密码或 API 密钥),也不要将环境变量值输出到共享日志中。对输出内容进行脱敏处理。
sanitize() {
local input="$1"
input=$(echo "$input" | sed -E 's/(password|passwd|secret|token|api_key)=[^ ]+/\1=****/gi')
input=$(echo "$input" | sed -E 's/(Authorization:\s*Bearer\s+)[^ ]+/\1****/gi')
echo "$input"
}
safe_cmd=$(sanitize "$CLAUDE_COMMAND")
echo "[LOG] $safe_cmd" >> /var/log/claude/commands.log
7.4 条件触发
不是每条命令后都需要执行完整的 Hook 逻辑。根据命令类型、退出码或执行时间有选择地执行,可以大幅提升性能。
if [ "$CLAUDE_EXIT_CODE" -eq 0 ]; then
exit 0
fi
if [ "${CLAUDE_DURATION:-0}" -lt 10000 ]; then
log_basic_info
else
log_detailed_info
send_notification
fi
7.5 版本控制策略
Hook 脚本和配置文件都建议纳入 Git 版本控制。在项目中创建 .claude/hooks/ 目录,将常用 Hook 脚本放在该目录下。同时将 settings.local.json 添加到 .gitignore 中,避免个人配置被提交。
推荐目录结构
.claude/
settings.json
settings.local.json
hooks/
after-command.sh
before-command.sh
after-command-notify.js
将通用的 Hook 脚本放在 hooks/ 目录下,团队成员共享。个人定制的脚本路径在 settings.local.json 中配置。
八、注意事项
在使用 AfterCommand Hook 时,需要注意以下关键事项,避免产生意外行为。
重要提醒
- 性能影响: Hook 脚本的执行时间会累加到命令的总执行时间中,应严格控制脚本执行效率
- 递归风险: 避免在 Hook 脚本中再次触发 Claude Code 命令,否则可能造成无限递归
- 跨平台兼容: Windows 和 Unix 系统的路径分隔符、换行符和环境变量语法不同,需分别处理
- 输出污染: Hook 脚本的标准输出会显示在 Claude Code 的界面中,注意不要输出无关信息
- 权限管理: Hook 脚本具有当前用户的完整权限,编写时需注意安全性和最小权限原则
常见陷阱
| 陷阱 |
后果 |
解决方案 |
| 脚本中修改了工作目录 |
后续命令在错误目录执行 |
使用子 Shell ( ... ) 或显式还原目录 |
| 日志文件无限增长 |
磁盘空间耗尽 |
使用 logrotate 或按大小轮转日志 |
| 环境变量含特殊字符 |
脚本解析错误或注入攻击 |
始终对变量值进行引用和转义 |
| 脚本路径错误 |
Hook 静默失败 |
使用绝对路径或验证路径存在性 |
调试方法
如果 Hook 脚本未按预期执行,首先检查配置文件路径是否正确,然后确认脚本具有可执行权限。可以通过在脚本顶部添加 set -x(Shell)或在脚本中输出 [HOOK] 前缀的调试信息来跟踪执行过程。查看 Claude Code 的输出面板,Hook 脚本的标准输出会显示在其中。
九、核心要点总结
- 触发机制: AfterCommand Hook 在每条命令执行完成后自动触发,无论成功或失败
- 三大用途: 结果检查、资源清理和日志记录是 AfterCommand 的三大核心用途
- 配置方式: 通过
.claude/settings.json 或 settings.local.json 中的 hooks.AfterCommand 字段配置
- 上下文变量: 脚本通过环境变量
CLAUDE_COMMAND、CLAUDE_EXIT_CODE、CLAUDE_DURATION 等获取命令执行信息
- 多语言支持: 支持 Shell、Python、Node.js 等任何可执行语言编写 Hook 脚本
- 性能第一: 保持脚本轻量高效,避免耗时操作影响开发体验
- 安全隔离: 对日志中的敏感信息进行脱敏处理,Hook 错误不应影响主流程
- 条件触发: 根据退出码、执行时间等条件选择性执行 Hook 逻辑
- 版本管理: 将通用 Hook 脚本纳入版本控制,个人配置使用
settings.local.json