はじめに
Codex に作業を頼み、完了報告を読んでから、テストや確認が抜けていることに気付く。そこで、次の依頼では終了前に確認するよう伝える。それでも別の作業で同じことが起きる。この繰り返しに心当たりがある方も多いのではないでしょうか。
私も AGENTS.md に注意事項を書き足していました。ただ、行動方針を伝えることと、作業終了時に必ず確認処理を動かすことは別の問題です。Codex hooks なら、Codex の動作中に決まったスクリプトを実行できます。この記事では、終了時に動く Stop hook を試した所感について紹介します。
以下のリポジトリで使用しています。
検証環境は Windows 11 Home (OS ビルド 26200)、Codex CLI 0.147.0、PowerShell 7.4.18 です。
検証
Codex hooks は、Codex のライフサイクル中に任意のスクリプトを実行する仕組みです。Stop はターンの終了時に動きます。条件を満たしていなければ、理由を添えて Codex に作業の継続を促せます。
今回は仕組みを確かめるため、プロジェクトに .codex/finish-check.txt がある場合だけ、その内容を終了前の確認事項として返す最小例を作りました。
中心となる処理は次の部分です。すでに Stop hook から継続したターンでは何も返しません。初回だけ decision: "block" と確認文を JSON で返します。
if ($null -ne $activeProperty -and [bool]$activeProperty.Value) {
exit 0
}
$result = [ordered]@{
decision = 'block'
reason = $reason
}
[Console]::Out.Write((ConvertTo-Json -InputObject $result -Compress))
完全な設定とスクリプト
.codex/hooks.json へ Stop hook を登録します。
{
"description": "作業終了前に確認文を返す",
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "pwsh -NoProfile -File \".codex/hooks/stop-check.ps1\"",
"timeout": 5,
"statusMessage": "終了前の確認を実行中"
}
]
}
]
}
}
.codex/hooks/stop-check.ps1 は、標準入力の JSON と確認文を読みます。不要な場合やエラー時は何も出力せず終了します。
[CmdletBinding()]
param()
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
try {
$inputText = [Console]::In.ReadToEnd()
if ([string]::IsNullOrWhiteSpace($inputText)) {
exit 0
}
$inputObject = ConvertFrom-Json -InputObject $inputText
$activeProperty = $inputObject.PSObject.Properties['stop_hook_active']
if ($null -ne $activeProperty -and $activeProperty.Value -isnot [bool]) {
exit 0
}
if ($null -ne $activeProperty -and [bool]$activeProperty.Value) {
exit 0
}
$cwdProperty = $inputObject.PSObject.Properties['cwd']
if ($null -eq $cwdProperty -or
[string]::IsNullOrWhiteSpace([string]$cwdProperty.Value)) {
exit 0
}
$checkPath = Join-Path ([string]$cwdProperty.Value) '.codex\finish-check.txt'
if (-not [System.IO.File]::Exists($checkPath)) {
exit 0
}
$reason = [System.IO.File]::ReadAllText($checkPath).Trim()
if ([string]::IsNullOrWhiteSpace($reason)) {
exit 0
}
$result = [ordered]@{
decision = 'block'
reason = $reason
}
[Console]::Out.Write((ConvertTo-Json -InputObject $result -Compress))
}
catch {
exit 0
}
exit 0
.codex/finish-check.txt には、Codex に最後に確認してほしい内容を書きます。
テスト結果と変更内容を確認し、必要なら作業記録を更新してください。
ファイルを配置したら Codex を再起動し、/hooks で内容を確認して信頼します。管理対象ではない command hook は、現在の定義を利用者が信頼するまで実行されません。定義が変わると再確認が必要になります。
このサンプルについて、不正な入力、確認文がない場合、継続済みの場合には何も出力しないことをテストしました。確認文がある場合だけ、継続理由を含む JSON を返すことも確認しています。
所感
良かったのは、終了前の確認を毎回忘れずに依頼するという手順がなくなったことです。現行のプロジェクトでは、長期的な進行管理のため終了時に進捗を保存するルールがあるのですが、hook は機械的に動いてくれるので安心です。
一方で、設定しただけで何でも判断してくれるわけではありません。この最小例は finish-check.txt がある間、どのターンでも一度は確認を促します。短い質問への回答でも動くため、実運用では変更の有無や作業記録の状態など、通知する条件を絞りたくなりました。
ここ最近、タスク進行管理のために Beads を導入してみたのですが、Beads で進捗がしばらく更新されていない場合だけ通知し、短時間に同じ通知を繰り返さないようにしてみました。Stop 側で必要な条件だけを絞れる点が便利でした。
Stop の decision: "block" は、ターンを拒否する命令ではありません。reason を新しい継続プロンプトとして使い、Codex にもう一度作業させる指定です。
stop_hook_active を確認しないと、終了と継続を繰り返す可能性があります。また、hook の失敗で通常の作業まで止めないよう、判定不能やエラー時には何も返さない設計にしました。
この例はプロジェクトルートで Codex を起動する前提です。サブディレクトリから起動する運用では、公式資料の案内に従い、Git ルートや絶対パスからスクリプトを解決したほうが安定します。
まとめ
Codex に毎回同じ注意を伝えているなら、指示文を増やす前に、その確認を実行すべき時点が決まっているか考えてみるのがよいです。行動方針は AGENTS.md に書き、終了時に行う機械的な確認は Stop hook に任せると、役割を分けられます。
hooks は成果の正しさを保証する仕組みではないので、設定やスクリプトの保守も必要です。まずは小さな手順短縮ひとつから試すのが扱いやすいと思います。