Codex CLI 作为 OpenAI 开源的终端 AI 编程助手,除了提供基础的对话式编程能力外,还内置了一套强大的生命周期钩子系统(Lifecycle Hooks)。这套系统允许你在 AI Agent 执行任务的关键节点插入自定义逻辑,实现对工具调用、权限决策、会话生命周期的精细控制。
本文将从钩子事件类型、配置方式、实战示例三个维度,带你全面掌握 Codex Hooks 的使用方法。
Hook 本质上是一个外部命令或脚本,Codex 在特定的生命周期事件发生时,会调用你配置的 Hook 程序,通过 stdin 传入 JSON 格式的上下文信息,并根据 Hook 的退出码和 stdout 输出来决定后续行为。
Hook 的能力包括:
Codex 支持 11 种 Hook 事件,覆盖从会话启动到结束的完整生命周期:
| 事件名称 | 触发时机 | 关键能力 |
|---------|---------|---------|
| PreToolUse | 工具执行之前 | 拦截/修改工具参数,阻止执行 |
| PermissionRequest | 权限请求时 | 自动化权限决策 |
| PostToolUse | 工具执行之后 | 审计、记录、注入上下文 |
| PreCompact | 上下文压缩之前 | 判断是否允许压缩 |
| PostCompact | 上下文压缩之后 | 压缩后处理 |
| SessionStart | 会话启动时 | 初始化、环境检查 |
| SessionEnd | 会话结束时 | 清理、通知 |
| UserPromptSubmit | 用户提交提示词时 | 提示词预处理、过滤 |
| SubagentStart | 子 Agent 启动时 | 子任务上下文准备 |
| SubagentStop | 子 Agent 停止时 | 子任务结果处理 |
| Stop | 回合结束时 | 回合级别收尾工作 |
Hook 配置通过 JSON 文件定义,放在以下位置之一:
~/.codex/hooks.json<项目根>/.codex/hooks.json{
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/security_check.py",
"timeout_sec": 10
}
]
}
],
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "~/.codex/hooks/on_session_start.sh"
}
]
}
]
}
}
"command"(执行外部命令)false一个 Matcher Group 可以包含多个 Hook,它们会按顺序依次执行。
当 Hook 被触发时,Codex 会通过 stdin 传入一个 JSON 对象。以 PreToolUse 为例:
{
"session_id": "thread_abc123",
"turn_id": "turn_xyz789",
"cwd": "/home/user/my-project",
"hook_event_name": "PreToolUse",
"model": "gpt-5",
"permission_mode": "default",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /important-data"
},
"tool_use_id": "tool_use_001"
}
Hook 脚本可以解析这个 JSON,根据上下文件信息做出决策。
Hook 通过退出码和 stdout 输出来表达其决定:
| 退出码 | 含义 |
|-------|------|
| 0 | 成功,允许操作继续 |
| 2 | 阻止当前操作(仅 PreToolUse) |
| 其他非零 | Hook 执行失败 |
当需要阻止操作时,返回 JSON:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "禁止删除重要数据目录"
}
}
当需要修改工具输入时:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "ls -la /important-data"
}
}
}
当需要注入额外上下文时:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"additionalContext": "该目录包含生产数据,请谨慎操作"
}
}
如果 stdout 输出为空或不是合法 JSON,Hook 视为成功通过(不阻止)。
创建一个安全审计 Hook,拦截 rm -rf、DROP TABLE 等危险操作:
#!/usr/bin/env python3
import json
import sys
DANGEROUS_PATTERNS = [
"rm -rf /",
"rm -rf --no-preserve-root",
"DROP TABLE",
"DROP DATABASE",
"shutdown",
"reboot",
"format C:",
":(){ :|:& };:", # fork bomb
]
def main():
input_data = json.load(sys.stdin)
tool_name = input_data.get("tool_name", "")
command = input_data.get("tool_input", {}).get("command", "")
for pattern in DANGEROUS_PATTERNS:
if pattern.lower() in command.lower():
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"检测到危险操作: 匹配模式 '{pattern}'"
}
}))
return
# 安全检查通过,允许执行
sys.exit(0)
if __name__ == "__main__":
main()
配置 hook:
{
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/security_check.py",
"timeout_sec": 10
}
]
}
]
}
}
当 Codex 尝试执行 rm -rf /tmp/cache 时,Hook 会检测到 rm -rf 模式并阻止操作,同时向用户反馈阻止原因。
在会话启动时自动加载项目环境变量、检查依赖:
#!/bin/bash
# ~/.codex/hooks/on_session_start.sh
input=$(cat)
session_id=$(echo "$input" | jq -r '.session_id')
cwd=$(echo "$input" | jq -r '.cwd')
echo "会话 $session_id 已启动,工作目录: $cwd" >> ~/.codex/session.log
# 检查 Node.js 依赖是否安装
if [ -f "$cwd/package.json" ] && [ ! -d "$cwd/node_modules" ]; then
echo '{"additionalContext": "项目依赖未安装,建议先执行 npm install"}'
fi
exit 0
配置:
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "~/.codex/hooks/on_session_start.sh"
}
]
}
]
}
}
记录每次工具调用的详细信息,用于审计:
#!/usr/bin/env python3
import json
import sys
from datetime import datetime
def main():
input_data = json.load(sys.stdin)
log_entry = {
"timestamp": datetime.now().isoformat(),
"session_id": input_data.get("session_id"),
"turn_id": input_data.get("turn_id"),
"tool_name": input_data.get("tool_name"),
"tool_input": input_data.get("tool_input"),
"tool_response": input_data.get("tool_response"),
}
# 追加到审计日志
with open(f"{__file__}/../../../audit.log", "a") as f:
f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
sys.exit(0)
if __name__ == "__main__":
main()
在一个生产项目中,你可以组合多个 Hook 事件,形成完整的安全网:
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "~/.codex/hooks/check_env.sh" }
]
}
],
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": [
{ "type": "command", "command": "python3 ~/.codex/hooks/safety_check.py" }
]
},
{
"matcher": "^(GitHub|GitLab|Bitbucket)",
"hooks": [
{ "type": "command", "command": "python3 ~/.codex/hooks/secret_scan.py" }
]
}
],
"PostToolUse": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "~/.codex/hooks/audit_logger.sh" }
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "~/.codex/hooks/notify_complete.sh" }
]
}
]
}
}
Hook 可以从多个来源加载,不同来源的 Hook 可以共存:
| 来源 | 说明 | 配置路径 |
|------|------|---------|
| User | 用户个人配置 | ~/.codex/hooks.json |
| Project | 项目级配置 | <project>/.codex/hooks.json |
| Plugin | 插件内置 | 插件包内 |
| Managed Config | 企业统一管理 | 远程配置下发 |
| System | 系统级 | 由管理员控制 |
多个来源的 Hook 会合并执行。管理员还可以通过设置 allow_managed_hooks_only = true 来禁用用户和项目级的 Hook,确保企业安全策略不被绕过。
你可以直接向 Hook 脚本传入模拟 JSON 来测试其行为:
echo '{
"session_id": "test_session",
"turn_id": "test_turn",
"cwd": "'$PWD'",
"hook_event_name": "PreToolUse",
"model": "gpt-5",
"permission_mode": "default",
"tool_name": "Bash",
"tool_input": {"command": "rm -rf /tmp/test"},
"tool_use_id": "test_001"
}' | python3 ~/.codex/hooks/security_check.py
echo "Exit code: $?"
在 Codex TUI 中,Hook 的执行状态(Running、Completed、Failed、Blocked)会以事件的形式展示在界面上,你可以直观地看到每个 Hook 的执行结果。
性能影响:同步 Hook(async: false)会阻塞主流程,超时时间不宜设置过长,建议 5-10 秒
幂等性:Hook 可能被多次触发(如重试场景),应设计为幂等的
错误处理:Hook 返回非零退出码(除 2 外)视为失败但不阻止操作,这是一种"安全失败"策略
权限:Hook 脚本需要有可执行权限(Linux/macOS: chmod +x)
环境隔离:Hook 运行在独立的子进程中,不会污染 Codex 的环境变量
Codex 的 Hooks 系统为 AI 编程助手提供了一层可编程的"中间件",让你能够在 AI 执行任务的每个关键节点插入自定义逻辑。无论是安全审计、环境初始化、还是自动化流程编排,Hooks 都能帮你实现。
合理使用 Hooks,可以让你的 Codex 工作流更加安全、高效和可控。从简单的命令拦截到复杂的多阶段流程编排,Hooks 的灵活性足以应对各种场景。
如果你还没有尝试过,不妨从最简单的 SessionStart Hook 开始——在每次会话启动时自动加载项目环境,体验一下钩子系统带来的便利。