Codex hooksは、Codexが作業する決まった時点(セッションの開始、コマンドを実行する前後、返答の終わりなど)に、自分で用意したスクリプトを自動で動かす仕組みです。危険なコマンドを止める、作業の前にプロジェクトの決まりを渡す、終わった時に記録を残す、といった「毎回同じ確認」を人の手から外せます。設定はhooks.jsonかconfig.tomlに書き、Codexの/hooksで中身を確認して信頼すると動き始めます。
この記事は、OpenAIの公式資料(Hooks、設定の基本、変更履歴、承認と安全)を2026年10月20日に確認して作成しました。編集部はCodex CLI 0.163.0で、hooksが有効になっていることと、記事で紹介するオプションがあることを確かめました。記事中の練習用スクリプトも、手元で入力と出力を試しています。Codexの作業中にhookが動く様子や、作業時間・事故防止の効果は実測していません。
Hooksは、公式変更履歴の2026年5月14日の項で一般提供(GA)と記載されています。新しく発表された機能ではありません。この記事は、今のCodexでhooksを使い始めるための解説です。
Codex hooksとは:決まった時点に自分の処理を足す仕組み
Codexは依頼を受けると、考える、コマンドを実行する、ファイルを直す、返答する、という流れを繰り返します。hooksは、この流れの決まった時点で自分のスクリプトを動かすための拡張機能です。公式のHooks資料は、使い道の例として次の5つを挙げています。
- 会話の内容を、自社の記録・分析の仕組みに送る
- チームの入力を調べ、APIキーをうっかり貼り付けるのを防ぐ
- 会話を要約して、次回以降に使う記憶を自動で残す
- 返答が終わる時に独自の検証を走らせ、作業の基準を守らせる
- 特定のフォルダーで作業する時だけ、指示を追加する
hookが動く時点(イベント)は、公式資料の表で12種類あり、次の4つの場面に分かれます。
| 場面 | 主なイベント | 使い道の例 |
|---|---|---|
| セッションやサブエージェントの開始 | SessionStart・SubagentStart | 作業の前にプロジェクトの決まりを渡す |
| 1回の依頼(ターン)の途中 | UserPromptSubmit・PreToolUse・PermissionRequest・PostToolUse・PreCompact・PostCompact・SubagentStop・Stop | 入力の確認、コマンドの事前チェック、実行結果の確認、終わる前の検証 |
| 実行中のターンを中断した時 | Interrupt | 中断の記録や後片付け |
| メインの会話(スレッド)の終了 | SessionEnd | 最後のメモや一時ファイルの整理 |
hookの中身には、シェルのコマンドのほか、接続済みのMCPサーバーのツール(mcp_tool)も指定できます。2026年10月20日時点の公式資料では、prompt型とagent型を指定しても、読み込まれるだけで実行されません。
よく似た仕組みにAGENTS.mdがあります。AGENTS.mdは「Codexに読ませて従ってもらう指示」で、hooksは「決まった時点でCodexが呼び出す処理」です。読んだうえで守ってほしい方針はAGENTS.md、毎回機械的に確かめたいことはhooks、と分けると迷いにくくなります。AGENTS.mdの書き方はAGENTS.mdの記事で解説しています。
Codex hooksを書く場所:hooks.jsonとconfig.toml
Codexは、自分用の設定やリポジトリ用の設定など、有効になっている設定の場所(層)ごとに、hooks.jsonまたはconfig.tomlの[hooks]を探します。よく使う場所は次の4つです。
| 置き場所 | 効く範囲 |
|---|---|
~/.codex/hooks.json | 自分のすべての作業 |
~/.codex/config.toml | 同上(TOMLで書く場合) |
<リポジトリ>/.codex/hooks.json | そのリポジトリでの作業だけ |
<リポジトリ>/.codex/config.toml | 同上(TOMLで書く場合) |
複数の場所に書いた時の動き
複数の場所にhooksがあると、条件に合うhookはすべて読み込まれます。優先順位の高い設定の層が、低い層のhooksを置き換えるわけではありません。同じ層にhooks.jsonとconfig.tomlの[hooks]を両方書くと、Codexは両方を合わせたうえで起動時に警告を出します。1つの層では、どちらか一方の書き方に決めましょう。
リポジトリのhooksが読まれる条件
リポジトリ内の.codex/にあるhooksは、そのプロジェクトを信頼済みにした時だけ読み込まれます。公式の設定資料によると、信頼していないプロジェクトでは、プロジェクトの設定・hooks・ルールが読み飛ばされます。自分の設定やシステムの設定にあるhooksは、引き続き読み込まれます。他人のリポジトリを開いただけで、中のスクリプトが動き出すことを防ぐための仕組みです。
Codex hooksの最初の1本:危険なコマンドを止める
練習として、取り消しにくいコマンド(rm -rf、強制プッシュ、git reset --hard)をCodexが実行しようとした時に止めるhookを作ります。使うイベントは、ツールを実行する直前に動くPreToolUseです。
手順1:判定するスクリプトを書く
Codexはhookを起動すると、イベントの内容をJSONにして標準入力へ渡します。シェルのコマンドの場合、実行しようとしている文字列はtool_input.commandに入っています。次のスクリプトは、その文字列が危険なパターンに当たった時だけ「拒否」のJSONを返します。保存先は、リポジトリの.codex/hooks/block_risky_bash.pyです。
#!/usr/bin/env python3
"""PreToolUse: 取り消しにくいコマンドだけを止め、それ以外は何もしない。"""
import json
import re
import sys
event = json.load(sys.stdin)
command = (event.get("tool_input") or {}).get("command", "")
RISKY = [
r"\brm\s+-(?=[a-zA-Z]*[rR])(?=[a-zA-Z]*f)[a-zA-Z]+", # rm -rf / -fr / -Rf(1つにまとめた書き方だけ)
r"\bgit\s+push\b.*(--force\b|\s-f\b)", # 強制プッシュ(--force-with-lease も止める)
r"\bgit\s+reset\s+--hard\b", # 未保存の変更を消す
]
for pattern in RISKY:
if re.search(pattern, command):
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "取り消しにくいコマンドのため、hookで止めました: " + command,
}
}, ensure_ascii=False))
sys.exit(0)
# 何も出力せず終了コード0 = 通常どおり続行
sys.exit(0)
何も出力せずに終了コード0で終わると、Codexは通常どおり作業を続けます。止めたい時は、上のようにpermissionDecision: "deny"を返す方法のほかに、標準エラーへ理由を書いて終了コード2で終わる方法もあります。
手順2:hooks.jsonに登録する
リポジトリの.codex/hooks.jsonに、次のように登録します。
{
"description": "取り消しにくいBashコマンドを止める練習用hook",
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/block_risky_bash.py\"",
"timeout": 10,
"statusMessage": "危険なコマンドか確認しています"
}
]
}
]
}
}
matcherは正規表現で、PreToolUseではツールの名前に当てはめます。シェルのコマンドはBashという名前で届きます。スクリプトの場所は、相対パスではなくgit rev-parse --show-toplevelを使い、リポジトリの最上位から指定するのがおすすめです。こうしておけば、Codexをサブフォルダーから起動しても場所がずれません(公式資料が勧める書き方です)。timeoutは秒単位で、省略すると多くのイベントで600秒になります。
手順3:/hooksで確認して信頼する
新しく書いたhookは、そのままでは動きません。Codex CLIで/hooksを開き、読み込まれたhookの中身を確認して信頼すると、次から実行されます。確認が必要なhookがある時は、起動時に/hooksを開くよう案内が表示されます。リポジトリのhookを使う場合は、そのプロジェクト自体も信頼済みにしておいてください。
編集部の手元でスクリプトだけを試した結果
手順1のスクリプトに、Codexが渡す形をまねたJSONを手で入れ、出力を確かめました(Python 3.9.6)。Codexを起動してhookを動かした結果ではありません。
| 入れたコマンド | スクリプトの出力 | 意味 |
|---|---|---|
git status | なし(終了コード0) | 通常どおり続ける |
rm -rf build・rm -fr build・rm -Rf build | permissionDecision: "deny"のJSON | 実行を拒否する |
git push origin main --force・git push -f origin main・git push --force-with-lease | 同上 | 実行を拒否する(安全寄りに--force-with-leaseも止める) |
git reset --hard HEAD~1 | 同上 | 実行を拒否する |
rm -r -f build | なし(終了コード0) | 止まらない(下の注意を参照) |
この判定は文字列の照合だけなので、rm -r -fやrm --recursive --forceのようにオプションを分けて書いたコマンドや、別のスクリプトを通した削除は止められません。hookは「よくある事故を減らす柵」であり、すべての危険を防ぐ仕組みではない点に注意してください。理由は後の「守れないこと」の節で説明します。
Codex hooksの信頼確認と/hooksの使い方
管理者が配布したhookを除き、Codexは、利用者が内容を確認して信頼するまでhookを実行しません。信頼は、hookの定義から計算したハッシュ値に結び付けて記録されます。そのため、コマンドや設定を書き換えると「変更あり」として扱われ、もう一度信頼するまで飛ばされます。
注意したいのは、公式資料が信頼の対象を「hookの定義」と説明している点です。commandが指すスクリプトファイルの中身まで照合されるとは書かれていません。スクリプトを書き換えた時は、再確認の表示が出なくても、自分で中身を見直してください。リポジトリのスクリプトは、ほかの人の変更が入る前提で扱うのが安全です。
/hooksでは、hookの読み込み元の確認、新しいhookや変更されたhookの確認、信頼、個別の無効化ができます。- システム、MDM、クラウド、
requirements.tomlなど、管理者側から配られたhookは「managed」と表示されます。これらは管理者の方針によって信頼済みとして扱われ、利用者の画面からは無効にできません。 - プラグインに同梱されたhookも、プラグインを入れただけでは信頼されません。ほかのhookと同じ確認を通ります。
--dangerously-bypass-hook-trustを使う前に
--dangerously-bypass-hook-trustは、信頼の記録がないhookでも、有効になっていればその回だけ実行するオプションです。公式資料は、Codexの外でhookの出どころをすでに確認している、一回限りの自動化向けと説明しています。手元のCodex CLI 0.163.0のヘルプにも「DANGEROUS」と表示されました。普段の作業で確認を省く目的には使わないでください。
hooksを丸ごと止める設定
hooksは既定で有効です。手元の0.163.0でも、codex features listでhooksがstable・trueと表示されました。古い解説記事に載っている「codex_hooks = trueで有効にする」手順は、今は必要ありません(codex_hooksは非推奨の別名として残っています)。全体を止めたい時は、config.tomlに次のように書きます。
[features]
hooks = false
組織の管理者は、requirements.tomlで同じ設定を強制できます。反対に、管理者のhookだけを動かし、利用者・プロジェクト・セッション・プラグインのhookを読み飛ばすallow_managed_hooks_only = trueという設定もあります。
Codex hooksの主なイベントと返し方
どのイベントで何を返せるかは、イベントごとに違います。よく使うものを整理しました。
| イベント | 動く時点 | できること |
|---|---|---|
SessionStart | 開始・再開・/clear・会話の圧縮後など | 標準出力の文章やadditionalContextを、追加の文脈として渡す |
UserPromptSubmit | 入力した依頼が送られる直前 | 文脈を足す。decision: "block"で依頼を止める |
PreToolUse | コマンド・ファイル編集・MCPツールなどの実行前 | 実行を拒否する。permissionDecision: "allow"とupdatedInputで入力を書き換える |
PermissionRequest | Codexが承認を求める直前 | 許可・拒否する。判断せずに通常の承認画面へ任せることもできる。許可を返すと承認画面は表示されない |
PostToolUse | ツールの実行後 | 実行結果を見て、Codexに指摘や補足を返す。実行済みの処理は取り消せない |
Stop | 返答が終わる時 | decision: "block"と理由を返し、もう一度作業を続けさせる |
SessionEnd | メインの会話(スレッド)の終了時 | 記録や後片付け。Codexの動きは変えられない |
matcherで対象を絞る
matcherは正規表現です。PreToolUse・PostToolUse・PermissionRequestでは、ツールの名前(Bash、apply_patch、mcp__サーバー名__ツール名など)に当てはめます。SessionStartで当てはめるのは、開始のきっかけ(startup・resume・clear・compact・fork)です。ファイルを編集するツール(apply_patch)は、matcherにEditやWriteと書いても対象にできます。UserPromptSubmit・Stop・Interruptではmatcherは使われず、書いても無視されます。"*"、空の文字列、またはmatcherの省略は、すべてに一致します。
待ち時間と出力の上限
hookの待ち時間(timeout)は秒単位で、省略時は多くのイベントで600秒です。SessionEndとInterruptは、既定が1秒で最大3秒です。hookからモデルに渡す出力の上限は、1回あたり既定で約2,500トークンです。additionalContextに限り、additionalContextLimitで変えられます(ツールへの指摘や作業を続けさせる指示は既定のままです)。上限を超えると全文がディスクに保存され、モデルには先頭と末尾の抜粋と保存先のパスが渡ります。保存先はディスク上なので、hookの出力にパスワードやAPIキーを含めないでください。
Codex hooksで守れないこと・注意点
hooksは便利ですが、公式資料は、ツールのhookを「役に立つ柵」と呼ぶ一方で、操作を必ず止められる仕組みではないと位置付けています。設定する前に、次の限界を知っておきましょう。
- WebSearchのように、手元ではなくOpenAI側で提供されるツールは、hookが通る経路を通らないため、
PreToolUse・PostToolUseの対象外です。 - 一部の特殊なツールは、通常のhookの経路を通らない場合があります。
PreToolUseのhookが失敗した時は、ツールの実行が止まらない場合があります。公式資料は、未対応の項目を返した時は失敗として記録して実行を続けること、管理者のMCP hookではエラー・時間切れ・不正な応答でもツールが止まらない場合があることを説明しています。確実に止めたい時は、明示的に拒否を返します。PostToolUseは、すでに実行された処理を元に戻しません。"async": trueでバックグラウンド実行にしたhookは、操作を止めたり書き換えたりできません。止めたい確認は、通常の(同期の)hookにします。- 同じイベントに当てはまる複数のコマンドhookは同時に起動され、1つのhookが別のhookの起動を止めることはできません。
Codexの安全対策の土台は、OSのしくみで操作できる範囲を限るサンドボックスと、実行の前に確認を求める承認の設定です(公式のAgent approvals & security)。hooksは、その上に重ねる追加の確認として使ってください。hooksを入れたからといって、承認やサンドボックスの設定を外さないようにしましょう。
Codex hooksが動かない時の確認順
hookが思ったとおりに動かない時は、次の順に確かめると原因を絞り込みやすくなります。
| 症状 | 確認すること |
|---|---|
| hookが一度も動かない | /hooksで「確認が必要」のままになっていないか。書き換えた後は信頼し直しが必要 |
| リポジトリのhookだけ動かない | そのプロジェクトを信頼済みにしているか。未信頼なら.codex/は読まれない |
| どのhookも動かない | config.tomlやrequirements.tomlで[features]のhooks = falseになっていないか。管理者がallow_managed_hooks_only = trueを設定していないか |
| 特定のコマンドの時だけ動かない | matcherの正規表現とツールの名前。UserPromptSubmitやStopではmatcherは無視される |
| サブフォルダーから起動すると失敗する | コマンドが相対パスになっていないか。git rev-parse --show-toplevelを使う |
Stopのhookでエラーになる | StopとSubagentStopは、終了コード0の時にJSONを出す必要がある。ただの文章は無効 |
| 古い記事の手順どおりに設定した | 有効化のcodex_hooksは今は不要。バックグラウンド実行のasyncは現在の公式資料で使える。書き方は公式資料の現行版と照らし合わせる |
| 途中で打ち切られる | timeoutの値。SessionEndとInterruptは最大3秒 |
原因を調べる時は、まずスクリプトだけを手で動かし、Codexが渡すのと同じ形のJSONを入れて出力を確かめると、切り分けが速くなります。そのうえで、/hooksの表示と起動時の警告を確認してください。
まとめ:Codex hooksは小さな1本から始める
Codex hooksは、毎回人が確かめていたことを、Codexの決まった時点に自動で差し込む仕組みです。まずは「危険なコマンドを止める」「作業の開始時に決まりを渡す」のように、効果が分かりやすい1本から始めましょう。
進め方は、スクリプトを手で試す、hooks.jsonに登録する、/hooksで確認して信頼する、小さな依頼で動きを見る、の順です。hookは柵であって万能ではないので、サンドボックスと承認の設定は残したまま使ってください。
Codexそのものの使い方から確かめたい方は、Codexとは?使い方と仕事でできることから読んでください。導入がまだの方は、Codex CLIのインストール方法と初回設定を先に済ませておくと、この記事の手順を試しやすくなります。MCPツールをhookから呼ぶ場合は、CodexのMCP設定で接続を済ませておきましょう。






