在日常使用 Codex 的过程中,你是否遇到过这些场景:希望 Codex 每次完成响应后播放提示音,避免反复检查窗口;想在 AI 执行高风险命令前自动进行安全审计;需要在每次会话启动时自动加载项目上下文。这些需求都可以通过 Codex 的 Hooks 钩子系统 来实现。
Hooks 是 Codex 从 v0.114 版本开始引入的实验性功能,它允许开发者在 Codex 会话的关键生命周期节点上挂载自定义脚本,实现对 AI 行为的精细化和自动化控制。本文将深入介绍 Codex Hooks 的配置方式、支持的事件类型以及实际应用场景。
Hooks(钩子)是一种事件驱动的回调机制。你可以在 Codex 的生命周期节点(如会话启动、命令执行前后、会话结束等)注册自定义命令或脚本,Codex 会在对应的时机自动执行它们。根据钩子的返回结果,你可以选择放行、阻止或修改 Codex 的后续行为。
Hooks 的设计灵感来源于 Git Hooks 和 Claude Code 的 Hooks 系统,它为 Codex 提供了一种轻量级但极为灵活的扩展方式——无需开发插件,仅通过配置文件和 Shell 脚本即可实现丰富的自动化能力。
由于 Hooks 目前处于实验阶段,需要通过特性开关来启用:
codex -c features.codex_hooks=true
你也可以在 config.toml 中持久化这个设置:
[features] codex_hooks = true
Codex 支持两种 Hooks 配置方式:独立的 hooks.json 文件和嵌入在 config.toml 中的配置。
在 .codex 目录下创建 hooks.json 文件:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo 'session started' >> /tmp/codex.log",
"statusMessage": "Initializing session...",
"timeout": 10
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "echo 'turn ended' >> /tmp/codex.log",
"statusMessage": "Wrapping up turn...",
"timeout": 10
}
]
}
]
}
}
[hooks]
SessionStart = [
{ type = "command", command = "bash ~/.codex/on-start.sh", timeout = 30 }
]
Stop = [
{ type = "command", command = "bash ~/.codex/on-stop.sh", timeout = 10 }
]
Codex Hooks 围绕以下生命周期事件展开,你可以根据需求选择合适的触发时机:
在每次 Codex 会话开始时触发。这是初始化环境的理想时机,比如加载项目特定配置、设置环境变量或预热缓存。
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "bash .codex/hooks/on-session-start.sh",
"statusMessage": "Setting up project environment...",
"timeout": 60
}
]
}
]
}
}
对应的 on-session-start.sh 脚本示例:
#!/bin/bash # 加载项目环境变量 export $(cat .env | xargs) # 预热类型检查 npx tsc --noEmit & # 记录会话日志 echo "[$(date)] Session started" >> .codex/session.log
当 Codex 完成一轮对话或工具调用后触发。这是通知用户 AI 已完成工作的最佳时机。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell -NoProfile -Command \"(New-Object System.Media.SoundPlayer 'C:\\Windows\\Media\\Windows Exclamation.wav').PlaySync()\"",
"statusMessage": "Task completed",
"timeout": 10
}
]
}
]
}
}
在 macOS 上可以播放系统提示音:
{
"type": "command",
"command": "afplay /System/Library/Sounds/Glass.aiff",
"timeout": 5
}
在 Linux 上可以使用 notify-send 发送桌面通知:
{
"type": "command",
"command": "notify-send 'Codex' 'Task completed'",
"timeout": 5
}
在 Codex 准备执行某个工具(如 Shell 命令、文件编辑)之前触发。这是实现安全审计和执行策略的核心钩子——你可以在此检查、拦截甚至修改即将执行的操作。
PreToolUse 支持 matcher 字段来过滤特定的工具类型:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash .codex/hooks/protect-files.sh",
"statusMessage": "Checking file write permissions..."
}
]
},
{
"matcher": "Shell",
"hooks": [
{
"type": "command",
"command": "python3 .codex/hooks/audit-command.py",
"statusMessage": "Auditing shell command..."
}
]
}
]
}
}
钩子脚本可以通过 stdin 接收上下文信息(JSON 格式),通过 stdout 返回决策:
stdin 输入示例:
{
"tool_name": "Shell",
"tool_input": { "command": "rm -rf /tmp/build/*" },
"session_id": "sess-abc123",
"cwd": "/home/user/project"
}
stdout 输出格式:
{
"decision": "deny",
"reason": "Deletion of build artifacts must be confirmed manually"
}
退出码约定:
0:允许操作继续2:拒绝操作执行以下是一个实用的文件保护脚本 protect-files.sh:
#!/bin/bash
INPUT=$(cat)
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
# 定义受保护的文件列表
PROTECTED=(
"package.json"
"tsconfig.json"
".github/workflows/"
"src/core/"
)
for pattern in "${PROTECTED[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo '{"decision": "deny", "reason": "Protected file: '$FILE_PATH'"}'
exit 2
fi
done
echo '{"decision": "allow"}'
exit 0
在工具执行完成后触发,用于记录日志、触发后续任务或做执行结果校验。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Shell",
"hooks": [
{
"type": "command",
"command": "bash .codex/hooks/log-command.sh",
"statusMessage": "Logging command execution...",
"timeout": 5
}
]
}
]
}
}
log-command.sh 脚本示例:
#!/bin/bash INPUT=$(cat) echo "[$(date)] Tool: $(echo $INPUT | jq -r '.tool_name')" >> .codex/command-history.log echo "Exit code: $(echo $INPUT | jq -r '.exit_code')" >> .codex/command-history.log exit 0
除了生命周期事件钩子,Codex 还提供了一个简化的通知机制:notify 配置项。它在 Codex 完成 agent-turn 时触发,适合播放提示音或发送桌面通知。
在 config.toml 中配置:
notify = ["bash", "-lc", "notify-send 'Codex' 'Task completed'"]
或者在 Windows 上:
notify = [ "powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", "(New-Object System.Media.SoundPlayer 'C:\\Windows\\Media\\Windows Exclamation.wav').PlaySync()" ]
通知钩子会收到一个 JSON 对象作为参数,包含事件类型信息:
{
"type": "agent-turn-complete"
}
你可以根据事件类型做不同的处理:
#!/bin/bash NOTIFICATION_JSON="$1" EVENT_TYPE=$(echo "$NOTIFICATION_JSON" | jq -r '.type') if [ "$EVENT_TYPE" = "agent-turn-complete" ]; then notify-send "Codex" "Response ready" fi
对于团队或企业环境,管理员可能希望控制哪些 Hooks 可以在 Codex 中运行。Codex 提供了 allow_managed_hooks_only 配置项,当设置为 true 时,只有通过 requirements.toml 和 managed config 层定义的 Hooks 才会生效。
在 requirements.toml 中配置:
allow_managed_hooks_only = true
这样可以确保:
在每次 PreToolUse 时自动运行 lint 检查,如果代码格式不达标则阻止继续操作:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash .codex/hooks/lint-gate.sh",
"statusMessage": "Running lint check...",
"timeout": 30
}
]
}
]
}
}
#!/bin/bash
# lint-gate.sh
FILES_CHANGED=$(git diff --name-only --diff-filter=M)
if [ -z "$FILES_CHANGED" ]; then
exit 0
fi
# 运行 ESLint
npx eslint $FILES_CHANGED --max-warnings 0 2>/dev/null
if [ $? -ne 0 ]; then
echo '{"decision": "deny", "reason": "ESLint found errors. Fix them first."}'
exit 2
fi
echo '{"decision": "allow"}'
exit 0
拦截所有 rm、drop、delete 等危险命令,要求用户额外确认:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Shell",
"hooks": [
{
"type": "command",
"command": "bash .codex/hooks/dangerous-ops.sh",
"statusMessage": "Checking command safety..."
}
]
}
]
}
}
#!/bin/bash
# dangerous-ops.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
DANGEROUS_PATTERNS=("rm -rf /" "DROP DATABASE" "DROP TABLE" "git push --force" "shutdown")
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if [[ "$COMMAND" == *"$pattern"* ]]; then
echo "{\"decision\": \"deny\", \"reason\": \"Dangerous command detected: $COMMAND. Manual confirmation required.\"}"
exit 2
fi
done
echo '{"decision": "allow"}'
exit 0
编写一个通用的通知脚本 notify.sh,自动检测操作系统并播放对应的提示音:
#!/bin/bash
EVENT_TYPE=$(echo "$1" | jq -r '.type // "unknown"')
notify() {
case "$(uname -s)" in
Darwin)
afplay /System/Library/Sounds/Glass.aiff
osascript -e "display notification \"Codex: $EVENT_TYPE\" with title \"Codex\""
;;
Linux)
notify-send "Codex" "Event: $EVENT_TYPE"
paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null
;;
CYGWIN*|MINGW*|MSYS*)
powershell -c "(New-Object System.Media.SoundPlayer 'C:\\Windows\\Media\\Windows Exclamation.wav').PlaySync()"
;;
esac
}
notify
在 PreToolUse 时检查 Codex 是否即将执行 git commit,如果是则自动运行测试套件:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Shell",
"hooks": [
{
"type": "command",
"command": "bash .codex/hooks/pre-commit-check.sh",
"statusMessage": "Running pre-commit checks...",
"timeout": 120
}
]
}
]
}
}
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
if [[ "$COMMAND" == *"git commit"* ]]; then
echo "Running pre-commit validation..."
npm test 2>&1
if [ $? -ne 0 ]; then
echo '{"decision": "deny", "reason": "Tests failed. Commit aborted."}'
exit 2
fi
npm run lint 2>&1
if [ $? -ne 0 ]; then
echo '{"decision": "deny", "reason": "Lint failed. Commit aborted."}'
exit 2
fi
fi
echo '{"decision": "allow"}'
exit 0
超时设置:每个 Hook 都应设置合理的 timeout,避免因脚本卡死导致 Codex 会话无法继续。
幂等性:Hook 脚本应尽量设计为幂等的,同一事件多次触发不会产生副作用。
日志记录:在 Hook 脚本中添加日志记录,方便问题排查。
错误处理:确保 Hook 脚本在任何情况下都能正常退出(即使失败也应返回明确的退出码),避免阻塞 Codex 主流程。
性能考量:PreToolUse 钩子会直接影响 Codex 的响应速度,避免在其中执行耗时操作。长时间运行的任务应考虑异步触发。
跨平台兼容:如果团队成员使用不同操作系统,应提供对应的脚本适配方案。
Codex Hooks 钩子系统虽然目前仍处于实验阶段,但它为 AI 编程助手提供了强大的可编程扩展能力。通过合理配置 SessionStart、Stop、PreToolUse 和 PostToolUse 等事件钩子,你可以:
随着 Hooks 系统的持续完善,未来可能会支持更多的生命周期事件(如 PreModelCall、PostModelCall)和异步执行模式。现在就开始尝试为你的项目配置 Hooks,让 Codex 更好地融入你的开发工作流吧。