1. 一句话理解 Hooks
Codex Hooks 是插入到 Codex 生命周期中的脚本回调机制:当会话开始、用户提交提示词、工具执行前后、申请权限、上下文压缩或任务准备结束时,Codex 可以自动执行指定脚本。
Hooks 更适合做“必须执行的机械动作”,例如:
- 执行命令前拦截危险操作;
- 提交 Prompt 前检测 API Key、Token 等敏感信息;
- 修改代码后执行格式化、测试或静态检查;
- 任务结束前检查交付内容是否完整;
- 记录工具调用和执行结果;
- 在会话开始时注入项目约定。
它与AGENTS.md的区别是:AGENTS.md告诉模型“应该怎么做”,Hooks 会在固定事件上真实运行脚本。稳定的开发规范通常使用AGENTS.md + Hooks:前者负责解释和指导,后者负责检查和兜底。
2. 配置位置与作用范围
Hooks 可以写成独立的hooks.json,也可以内联到同级的config.toml。
| 作用范围 | 推荐位置 | 适用场景 |
|---|---|---|
| 当前用户的所有项目 | ~/.codex/hooks.json | 个人统一习惯、通知、日志 |
| 当前用户的全局配置 | ~/.codex/config.toml | Hooks 与其他 Codex 配置集中管理 |
| 单个 Git 仓库 | <repo>/.codex/hooks.json | 团队共享的测试、安全和交付规则 |
| 单个仓库的统一配置 | <repo>/.codex/config.toml | 项目配置与 Hooks 集中管理 |
注意:
- 项目级 Hooks 只有在该项目被 Codex 标记为可信时才会加载。
- 多个配置来源命中的 Hooks 会全部执行,不是高优先级覆盖低优先级。
- 同一层不要同时写
hooks.json和内联[hooks],否则虽然会合并,但启动时会警告。 - 非管理员托管的 Hook 首次运行或内容变更后,需要通过
/hooks重新审核和信任。
3. Hook 的结构
一个 Hook 分三层:
- 事件:什么时候触发,例如
PreToolUse、PostToolUse、Stop; - 匹配器:过滤哪些工具或触发来源;
- 处理器:实际执行的脚本命令。
{"hooks":{"PostToolUse":[{"matcher":"Bash|apply_patch","hooks":[{"type":"command","command":"/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use.py\"","timeout":30,"statusMessage":"Checking code changes"}]}]}}当前应以type: "command"为准。prompt和agent类型虽然能够被解析,但尚不会执行;async也尚未真正支持。timeout单位为秒,省略时默认值为 600 秒。
4. 常用生命周期事件
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
SessionStart | 新建、恢复、清空或压缩后启动会话 | 加载项目说明、环境检查 |
UserPromptSubmit | 用户 Prompt 发给模型之前 | 敏感信息检测、补充上下文 |
PreToolUse | Bash、apply_patch或 MCP 工具执行前 | 拦截危险命令、改写参数 |
PermissionRequest | Codex 准备请求用户授权时 | 按企业或仓库策略允许/拒绝 |
PostToolUse | 支持的工具产生结果之后 | 校验输出、运行后置检查、补充反馈 |
PreCompact | 上下文压缩前 | 保存状态、决定是否允许压缩 |
PostCompact | 上下文压缩后 | 恢复必要上下文、记录压缩事件 |
SubagentStart | 子智能体启动时 | 注入子任务约束 |
SubagentStop | 子智能体准备结束时 | 检查子任务结果、要求继续完善 |
Stop | 当前任务回合准备结束时 | 验收测试、交付检查、要求补充说明 |
其中PreToolUse、PostToolUse目前主要覆盖 Bash、apply_patch和 MCP 工具,并不能拦截所有执行路径,因此它是工作流护栏,不应被视为完整的安全边界。
5. Matcher 匹配规则
matcher是正则表达式。"*"、空字符串或省略表示全部匹配。
常见写法:
Bash ^apply_patch$ Edit|Write mcp__filesystem__.* startup|resume manual|auto不同事件匹配的字段不同:
PreToolUse、PostToolUse、PermissionRequest:匹配工具名;SessionStart:匹配startup、resume、clear、compact;PreCompact、PostCompact:匹配manual或auto;SubagentStart、SubagentStop:匹配子智能体类型;UserPromptSubmit、Stop:当前不使用 matcher,配置了也会被忽略。
6. Hook 脚本的输入与输出
6.1 输入
Codex 会通过标准输入stdin向脚本传入一个 JSON 对象。常用公共字段包括:
| 字段 | 含义 |
|---|---|
session_id | 当前会话 ID |
turn_id | 当前任务回合 ID,主要出现在回合级事件中 |
cwd | 当前工作目录 |
hook_event_name | 当前 Hook 事件名 |
model | 当前模型标识 |
permission_mode | 当前权限模式 |
transcript_path | 会话记录路径;格式并非稳定接口,不建议强依赖 |
工具事件还会提供tool_name、tool_input、tool_response等字段。
Python 脚本读取方式:
importjsonimportsys payload=json.load(sys.stdin)event=payload["hook_event_name"]cwd=payload["cwd"]6.2 输出
- 退出码
0且无输出:检查通过,继续执行。 - 某些事件可以在
stdout输出 JSON,用于放行、拒绝、注入上下文或要求 Codex 继续。 - 在支持的阻断场景中,可用退出码
2,并把原因写入stderr。 - 不同事件支持的输出字段并不相同,不要把某个事件的返回结构直接复制给另一个事件。
例如,在PreToolUse中拒绝危险工具调用:
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Destructive command blocked by hook."}}例子:我的需求是开发完成后自动讲解修改逻辑
7.1 推荐方案
这个需求有两层:
- “说明应该写什么”属于模型行为规范,适合写进仓库的
AGENTS.md; - “没有说明就不允许结束”属于机械验收,适合用
StopHook 兜底。
建议先在AGENTS.md中加入:
## 代码修改后的交付要求 凡是修改代码,最终回复必须包含: 1. 修改目的; 2. 核心实现逻辑; 3. 关键文件或函数; 4. 验证方式与结果; 5. 尚存风险或限制(没有则明确写“无”)。这样通常已经够用,而且比每次结束时强制多跑一轮更节省 Token。
7.2 Stop Hook 兜底示例
项目目录:
<repo>/ ├── AGENTS.md └── .codex/ ├── hooks.json └── hooks/ └── require_change_explanation.py.codex/hooks.json:
{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/require_change_explanation.py\"","timeout":15,"statusMessage":"Checking delivery explanation"}]}]}}.codex/hooks/require_change_explanation.py:
importjsonimportsys payload=json.load(sys.stdin)# 防止 Stop Hook 已经要求继续后再次形成循环。ifpayload.get("stop_hook_active"):print(json.dumps({"continue":True}))raiseSystemExit(0)answer=payload.get("last_assistant_message")or""required_markers=("修改目的","实现逻辑","验证")ifnotall(markerinanswerformarkerinrequired_markers):print(json.dumps({"decision":"block","reason":("如果本轮修改了代码,请补充交付说明:修改目的、核心实现逻辑、""关键文件或函数、验证方式与结果、风险或限制;如果没有修改代码,""请直接说明本轮未修改代码。")},ensure_ascii=False))else:print(json.dumps({"continue":True}))工作原理:当 Codex 准备结束回合时,脚本检查最后一条回复是否包含必要说明;缺失时返回decision: "block"。在Stop事件中,这不是拒绝结果,而是让 Codex 根据reason自动继续一轮。脚本必须检查stop_hook_active,否则可能反复要求继续。
局限:该简化示例只能检查最终回复结构,不能准确判断“本轮是否真的修改过代码”。如果仓库经常存在用户自己的未提交改动,不建议简单使用git diff判断是否由 Codex 修改;更严格的方案需要在SessionStart或任务开始时记录基线,再在Stop时比较。
8. 配置后的启用与验证
- 创建
.codex/hooks.json和脚本文件。 - 确认项目已被 Codex 信任。
- 重启 Codex 或新开会话。
- 在 CLI 输入
/hooks。 - 检查 Hook 来源、命令和脚本路径,并信任新增 Hook。
- 发起一个小型代码修改任务,观察状态提示和最终说明。
- 修改 Hook 内容后再次打开
/hooks,重新审核新的内容哈希。
排查命令:
| 命令 | 用途 |
|---|---|
/hooks | 查看、信任或禁用 Hooks |
/status | 查看当前模型、权限、上下文和会话配置 |
/debug-config | 排查配置层级和生效来源 |
/diff | 查看当前工作区改动 |
/review | 对当前工作区执行代码审查 |
如需临时关闭全部 Hooks,可在config.toml中配置:
[features] hooks = false实践建议
- 从一个窄用途 Hook 开始,例如只检查最终交付说明,不要一开始就拦截所有事件。
- Hook 脚本要快、确定、可重复;不要在里面执行耗时且不稳定的大模型调用。
- 对
Stop和SubagentStop必须处理“已经继续过”的状态,防止死循环。 - 不要把 Hook 当作唯一安全边界;危险操作仍应结合 Codex 权限、沙箱和人工审批。
- 团队共享的 Hooks 放在仓库
.codex/中,脚本路径从 Git 根目录解析,避免从子目录启动时找不到文件。 - 项目规范优先放在
AGENTS.md,格式化、测试、扫描、阻断等确定性动作再交给 Hooks。 - 第三方文章和视频可能基于早期版本;事件类型、返回结构和支持范围应以当前官方 Hooks 页面为准。
资料来源
- OpenAI 官方:Codex Hooks
- OpenAI 官方:Codex CLI Slash Commands
- Google 搜索中的 Codex Hooks 视频入口
- 菜鸟教程:Codex 斜杆命令