Codex Hooks使用实践
一、为什么要了解 Hooks?
修复 bug 时,先写出会失败的测试,确认测试失败,提交这个测试文件;再让 Agent 修复代码,并且禁止 Agent 修改这份测试。同时配套钩子做强制防护。
这是《The AI-Native SDLC Playbook------How to transform your software development lifecycle with AI-stage by stage》中的一段话。 可见要用好 AI 编程就绕不开Hooks(钩子)。
生命周期钩子(Lifecycle Hooks)可以让 AI 在执行任务的特定阶段,自动运行脚本或调用 MCP 工具。我平时用Codex比较多,因此就拿Codex举例。
例如:
-
Codex 准备执行 Shell 命令时,自动检查有没有危险操作。
-
Codex 修改完代码后,自动运行 ESLint、Prettier 或其他检查工具。
-
Codex 完成一轮任务时,检查测试是否通过、是否遗漏必要操作。
-
Codex 启动新会话时,自动加载项目上下文。
-
子 Agent 启动或结束时,自动执行日志记录、上下文注入或质量检查。
它最重要的价值是:将一些本来依赖 AI 自觉遵守的开发规范,变成确定性执行的自动化流程。
举个例子,要求 Codex:
修改登录模块,修复一个 Bug,并确保单元测试通过。
如果配置了 Hooks,执行过程可以是:
- UserPromptSubmit:检查用户输入,必要时添加项目约束。
- PreToolUse:Codex 即将执行命令,检查是否包含危险操作。
- Codex 修改代码或运行工具。
- PostToolUse:自动检测刚修改的文件是否违反规范。
- Codex 继续修复和验证。
- Stop:检查本轮任务的完成条件,必要时提示 Codex 继续。
二、如何配置 Hooks?
Codex 支持全局和项目级配置。
C:\
└── Users\
└── <username>\
└── .codex\
├── config.toml
├── hooks.json # 全局 Hooks
└── hooks\
└── ...
D:\
└── Workbench\
└── my-project\
├── AGENTS.md
├── src\
└── .codex\
├── config.toml
├── hooks.json # 项目 Hooks
└── hooks\
├── pre_tool.py
├── post_tool.py
└── stop.py
这两种范围适合不同的需求:
| 配置位置 | 作用范围 | 适合 |
|---|---|---|
%USERPROFILE%\.codex\hooks.json |
当前用户的多个 Codex 项目 | 通用安全策略、通知、日志 |
<项目根目录>\.codex\hooks.json |
当前项目 | 自动测试、格式检查、项目规范 |
Codex 会加载多个匹配的 Hook 来源,而不是简单地让项目配置覆盖全局配置。
因此,全局 Hooks 应尽量少,项目级 Hooks 应根据技术栈定制。
另外,当前 Codex 需要用户审核并信任非托管 Hooks。比如下图就是我配置Hooks后Chatgpt Desktop在对话框下方展示的钩子图标上标注了一个小黄点提示我需要审核。

点击钩子图标就可以进行审批操作。

对于 CLI,可以通过 /hooks 查看、授权或禁用。
三、使用实践举例
3.1 Bug 修复的实践
以实践 AI-Native SDLC Playbook 中提到的"修 bug 先写失败测试,再让 agent 修复代码(不准改测试内容)"就需要使用钩子做强制防护。
- 首先是在项目的.codex目录中增加hooks.json文件,内容可以自己手写也可以让Codex自己编写。目的就是缺陷修复时进行测试防护。示例中的hooks.json文件内容如下(commandWindows中使用的代码可以是其他语言,比如python):
json
{
"description": "ContentFerry bug-fix test protection",
"hooks": {
"PreToolUse": [
{
"matcher": "^(?:apply_patch|Bash|mcp__.*|Write|Edit|Delete|Move|Rename|Create|Update|Save|Append|Remove|Replace|Truncate|Overwrite|Patch)$",
"hooks": [
{
"type": "command",
"command": "node \"$(git rev-parse --show-toplevel)/.codex/hooks/verify-fix-test-immutability.mjs\" --hook",
"commandWindows": "node (Join-Path (git rev-parse --show-toplevel) '.codex/hooks/verify-fix-test-immutability.mjs') --hook",
"statusMessage": "Checking protected test files"
}
]
}
],
"PostToolUse": [
{
"matcher": "^(?:apply_patch|Bash|mcp__.*|Write|Edit|Delete|Move|Rename|Create|Update|Save|Append|Remove|Replace|Truncate|Overwrite|Patch)$",
"hooks": [
{
"type": "command",
"command": "node \"$(git rev-parse --show-toplevel)/.codex/hooks/verify-fix-test-immutability.mjs\" --post-hook",
"commandWindows": "node (Join-Path (git rev-parse --show-toplevel) '.codex/hooks/verify-fix-test-immutability.mjs') --post-hook",
"statusMessage": "Verifying protected test files"
}
]
}
]
}
}
这个示例只是基础演示。正则表达式并不能完整识别 PowerShell、CMD、命令包装或经过编码的危险操作,不能替代沙箱及权限控制。
- 当遇到bug时,先复现bug,然后写回归测试,并确保这个测试是因为这个bug才失败的。
- 提交测试文件,并启动保护:
npm run fix:begin -- HEAD("fix:begin" 对应我在package.json中设置的 "node .codex/hooks/verify-fix-test-immutability.mjs --begin")

以下是verify-fix-test-immutability.mjs中开启保护的片段,具体内容可以让codex根据"修 bug 先写失败测试,再让 agent 修复代码(不准改测试内容)"这样的要求进行编写
javascript
function beginProtection(testCommit) {
validateTestCommit(testCommit);
const changedSinceTestCommit = changedPaths(["diff", "--name-only", testCommit, "--"]);
const untrackedPaths = changedPaths(["ls-files", "--others", "--exclude-standard"]);
const existingTestChanges = findProtectedTestChanges(changedSinceTestCommit, untrackedPaths);
if (existingTestChanges.length > 0) {
throw new Error(`开启保护前工作区已存在测试文件改动:${existingTestChanges.join("、")}`);
}
const markerPath = runGit(["rev-parse", "--path-format=absolute", "--git-path", TEST_COMMIT_MARKER_NAME]).stdout;
writeFileSync(markerPath, `${testCommit}\n`, "utf8");
if (getConfiguredTestCommit() !== testCommit) {
throw new Error("测试保护标记写入后无法回读,未能开启测试保护。");
}
console.log(`已开启测试保护,基准提交为 ${testCommit}。Codex hook 将拦截已识别的测试文件写入;结束前请运行 npm run fix:verify。`);
}
- 让Agent修复bug。hook 会拦截已识别的测试文件写入。完成后再运行针对性测试和该改动要求的其他检查。
以下是我故意让codex修改测试文件的示例,修改过程中被hook拦截了

- 运行 npm run fix:verify,确认基线之后没有测试文件变化。随后运行 npm run fix:end,再 review 最终差异。
javascript
function endProtection() {
const testCommit = getConfiguredTestCommit();
if (testCommit) verifyTestCommit(testCommit);
const markerPath = runGit(["rev-parse", "--path-format=absolute", "--git-path", TEST_COMMIT_MARKER_NAME]).stdout;
try {
unlinkSync(markerPath);
} catch (error) {
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
console.log("本地测试保护当前未开启。");
return;
}
throw error;
}
console.log("已关闭本地测试保护。");
}
3.2 其他注意事项
不要在每次 PostToolUse 触发后,立即对整个项目运行所有测试。
因为 Codex 可能连续修改十几个文件,每改一次就全量测试,很容易导致运行缓慢甚至资源竞争。
更好的策略是:
-
PostToolUse:记录被修改的文件,或进行轻量检查。
-
Stop:执行一次针对性的 lint、typecheck 和测试。
-
CI:执行全量测试及完整质量检查。
四、Codex Hooks 支持哪些事件?
目前官方文档列出的主要事件如下。
| Hook 事件 | 触发时机 | 推荐用途 |
|---|---|---|
SessionStart |
新建(startup)、恢复(resume)、清空重建(clear)、压缩(compact)、分叉(fork) | 加载或恢复项目上下文 |
UserPromptSubmit |
用户提交消息 | 输入校验、动态上下文 |
PreToolUse |
工具执行前 | 风险拦截、参数检查 |
PermissionRequest |
Codex 请求权限时 | 审批策略 |
PostToolUse |
工具执行后 | 检查结果、代码规范 |
PreCompact |
上下文压缩前 | 保存关键任务状态 |
PostCompact |
上下文压缩后 | 恢复任务上下文 |
SubagentStart |
子 Agent 启动 | 注入角色规范 |
SubagentStop |
子 Agent 完成 | 收集结果、质量反馈 |
Stop |
Codex 本轮响应即将结束 | 检查任务完成情况 |
Interrupt |
用户中断正在执行的任务 | 清理、记录 |
SessionEnd |
主会话结束 | 日志、状态持久化 |
其中,我最建议优先掌握的是 PreToolUse、PostToolUse、Stop、SessionStart。
它们分别解决执行安全、开发质量、任务完整性和上下文管理这四类常见问题。
4.1 实际项目推荐的目录结构
对于需要长期维护、使用多 Agent 的开发项目,我会推荐:
my-project/
├── AGENTS.md
├── .codex/
│ ├── config.toml
│ ├── hooks.json
│ │
│ └── hooks/
│ ├── pre_tool.py
│ ├── post_tool.py
│ ├── stop.py
│ ├── session_start.py
│ └── subagent_stop.py
│
├── scripts/
│ ├── lint.py
│ ├── test.py
│ └── validate.py
│
└── src/
我的建议是从最小配置开始,逐步增加 Hook,而不是一次启用全部生命周期事件。
尤其注意以下工程原则:
- 快速执行:高频 Hook 尽量在数百毫秒内完成,避免每次工具调用都产生明显延迟。
- 幂等设计:同一个事件可能重复出现,Hook 不能因为重复执行而破坏项目状态。
- 明确超时:不同事件使用不同超时,避免脚本卡住整个工作流。
- 减少副作用:检查类 Hook 应尽量只读;自动格式化和修改文件需要防止反复触发。
- 控制输出:仅输出关键错误和必要的上下文,避免消耗大量模型 token。
- 不要依赖 Hook 作为唯一安全边界:高危行为还应通过 Codex 权限、操作系统及 CI 限制。
- 每次修改 Hook 都重新验证:尤其要验证脚本直接执行成功和 Codex 生命周期实际触发是两回事。
五、hooks.json、AGENTS.md、Skills、Rules 应该如何分工?
Codex 提供多种扩展机制,但每一种都适合不同的职责。
| 机制 | 核心作用 | 典型例子 |
|---|---|---|
AGENTS.md |
告诉 AI 应遵守什么规范 | 代码风格、架构约束 |
| Skills | 教 AI 如何完成某类任务 | 代码 Review、生成 PRD |
| Hooks | 在指定时机自动执行程序 | 自动测试、校验、拦截 |
| Rules | 约束命令执行与权限行为 | 禁止或限制特定命令 |
| MCP | 连接外部工具和服务 | GitHub、数据库、浏览器 |