Codex 钩子技巧:在命令执行前后自动跑检查脚本
Codex 的钩子(Hooks)能在工具调用前后、发出提示词时、每轮结束时自动运行脚本。本文讲清楚钩子写在哪、有哪些时间点,以及拦截危险命令、自动复查、拦住误贴密钥三个实用写法。
想在 Codex 执行命令前自动检查一遍、在每轮对话结束时自动跑校验,或者防止把 API 密钥误贴进对话,可以用 Codex 的「钩子」(Hooks)。钩子会在 Codex 工作的固定时间点运行你写的脚本或 MCP 工具。本文按官方文档讲清楚钩子写在哪、有哪些时间点、怎么拦截命令,以及几个实用写法。
钩子能做什么
官方文档列出的典型用法有:
- 把对话记录发到自己的日志或统计系统;
- 扫描团队的提示词,拦住误贴的 API 密钥;
- 自动总结对话,生成长期记忆;
- 每轮对话结束时跑一遍自定义检查,确保符合团队规范;
- 在特定目录下自动补充提示内容。
钩子默认开启。不想用时,在 config.toml 里写:
[features]
hooks = false
钩子写在哪里
Codex 会在配置所在的位置查找钩子,可以写成单独的 hooks.json,也可以写在 config.toml 的 [hooks] 表里。最常用的四个位置:
~/.codex/hooks.json~/.codex/config.toml<项目>/.codex/hooks.json<项目>/.codex/config.toml
几处都有钩子时,Codex 会全部加载,高优先级的配置不会覆盖低优先级的钩子。同一层里既有 hooks.json 又有 [hooks] 时会合并并在启动时警告,建议每层只用一种写法。项目里的钩子只有在项目的 .codex/ 被信任后才会加载。
有哪些时间点
- 一轮对话进行中:
PreToolUse(工具调用前)、PermissionRequest(申请权限时)、PostToolUse(工具调用后)、PreCompact/PostCompact(压缩对话前后)、UserPromptSubmit(你发出提示词时)、SubagentStop、Stop(一轮结束时)。 - 你打断正在进行的一轮时:
Interrupt。 - 会话或子代理开始时:
SessionStart、SubagentStart。 - 主会话结束时:
SessionEnd。
配置长什么样
钩子分三层:事件 → 匹配规则(matcher)→ 要运行的处理程序。下面这个例子在 Codex 每次执行 Bash 命令前运行一个检查脚本:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"",
"statusMessage": "Checking Bash command",
"timeout": 30
}
]
}
]
}
}
几个要点:
matcher是正则表达式,写"*"、留空或不写就匹配全部。工具类事件按工具名匹配,例如Bash、Edit|Write、mcp__filesystem__.*。timeout单位是秒,不写时大多数钩子默认 600 秒;SessionEnd和Interrupt默认 1 秒,最多 3 秒。- 脚本在会话的工作目录下运行。项目里的钩子建议像上例那样从 git 根目录找脚本,避免从子目录启动 Codex 时找不到。
- Windows 上可以用
commandWindows单独指定命令。 - 处理程序支持
command(运行脚本)和mcp_tool(调用已连接的 MCP 服务器上的工具)两种。
技巧一:在命令执行前拦截危险操作
PreToolUse 钩子会从标准输入收到一个 JSON,里面有 tool_name、tool_input 等字段,Bash 命令的内容在 tool_input.command 里。脚本判断要拦截时,输出:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook."
}
}
更简单的做法是以退出码 2 结束,并把原因写到标准错误输出。不想拦截、只想提醒模型时,返回 additionalContext 字段,这段文字会作为补充信息交给模型。还可以返回 permissionDecision: "allow" 加 updatedInput,直接改写要执行的命令。
技巧二:一轮结束时自动再检查一遍
Stop 钩子在每轮结束时运行,必须输出 JSON。如果检查没通过,返回:
{
"decision": "block",
"reason": "Run one more pass over the failing tests."
}
这里的 block 不是拒绝,而是让 Codex 继续干活:Codex 会把 reason 当作一条新的提示词自动接着做。输入里的 stop_hook_active 字段告诉你这一轮是不是已经被 Stop 钩子续过,脚本里判断一下,避免无限循环。
技巧三:拦住误贴的密钥
UserPromptSubmit 钩子会收到你即将发送的提示词(prompt 字段)。脚本发现里面有密钥格式的内容时,返回 {"decision": "block", "reason": "..."} 或以退出码 2 结束,这条提示词就不会发出去。
安全机制:先审查再运行
非托管的钩子必须先经过你审查、信任,才会运行。Codex 按钩子定义的哈希值记录信任,钩子一改就要重新审查。在命令行里输入 /hooks 可以查看钩子来源、审查新钩子、信任或停用某个钩子。插件自带的钩子也一样,装了插件不等于自动信任。
官方也提醒:钩子是有用的防护,但不是完整的强制边界,像网页搜索这类托管工具不走钩子。
上手建议
先从一个只记录日志的 PostToolUse 钩子开始,确认能收到 JSON、能正常运行,再加拦截规则。钩子输出要简短,多个钩子的内容会叠加进上下文,太长会影响模型表现。
钩子在本地 Codex 里使用,用 ChatGPT 账号登录后,用量跟着会员套餐走。还没有会员的,可以在 BuyBuy代充(buybuytopup.cn) 开通 ChatGPT Plus,开通后就能照本文配置钩子。