Claude Code Stop Hook:「完了」の前にテストする
テスト失敗やファイル不足を検知し、終了を止める短いローカルチェックを設定します。
目次
Claude Code Stop Hook:「完了」の前にテストする
Claude Codeが「完了」と返しても、エージェントがそのターンを終えたいと判断したことしか分かりません。テストを実行したことも、build成果物が最新であることも証明されません。Stop Hookを使うと、終了直前に短いローカルチェックを実行し、明確な条件が失敗したときは会話を継続できます。CI、完全なテストスイート、人による承認の代わりにはなりません。
Stop Hook、CLAUDE.md、CIの役割は異なる
- Stop Hook は応答の終わりに短いチェックを実行します。
git diff --check、対象を絞ったテスト、期待するファイルの存在確認などに向いています。 - CLAUDE.md は守るべきルールや実行すべきコマンドをエージェントに伝えますが、ファイル自体がコマンドを実行するわけではありません。
- CI はpushまたはpull requestの後に独立した環境で実行されます。チームにとっての必須ゲートは引き続きCIです。
このhookにdeploy、公開処理、外部サービスへの書き込みを入れてはいけません。ターン終了ごとに実行される副作用は、失敗時に再現もロールバックも難しくなります。
検証できる完了条件を決める
hookを設定する前に、次の4項目を決めます。
- 主張: ターン後にエージェントが言ってよいこと。例は「buildを生成した」です。
- 証拠: その主張を確認するコマンドまたはファイル。たとえば
npm test -- --runInBandとtest -s dist/app.jsです。 - 成功: 両方のチェックが終了コード
0で終わることです。 - Stopのブロック: 失敗時は
decision: "block"と短いreasonを含むJSONを返します。
まずhookなしでコマンドを実行してください。数分かかる、またはネットワークが必要なら、より小さなローカルチェックに絞ります。完全な実行はCIに任せます。
Claude CodeをAPIプロバイダー経由で接続する場合は、まず最新のBetterToken設定ガイドを開き、ツールに自分のAPI Keyを設定して短いテストリクエストを送ります。その後、Dashboardで想定したモデル、ステータス、トークン使用量を確認してください。ローカルhookにキーは不要で、ログにも出力しません。
最小のStop Hookを設定する
.claude/settings.json に、短いtimeoutを持つプロジェクト用のhookを追加します。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "./scripts/check-before-stop.sh",
"timeout": 30
}
]
}
]
}
}
チェック用スクリプトは scripts/check-before-stop.sh として保存します。以下のコマンドはNodeプロジェクト向けの例です。実際のリポジトリに存在するコマンドとパスに置き換えてください。
#!/usr/bin/env sh
set -eu
if [ -t 0 ]; then
input='{"stop_hook_active":false}'
else
input=$(cat)
fi
stop_hook_active=$(printf '%s' "$input" | node -e '
let raw = "";
process.stdin.on("data", chunk => raw += chunk);
process.stdin.on("end", () => {
try { process.stdout.write(String(Boolean(JSON.parse(raw).stop_hook_active))); }
catch { process.stdout.write("false"); }
});')
block_stop() {
if [ "$stop_hook_active" = "true" ]; then
printf '%s\n' "Stopチェックがまだ失敗しています: $block_reason。手動で実行してください。CIは引き続き必須です。" >&2
exit 0
fi
BLOCK_REASON="$block_reason" node -e 'process.stdout.write(JSON.stringify({decision:"block",reason:`Stopチェック失敗: ${process.env.BLOCK_REASON}`}) + "\n")'
exit 0
}
if ! npm test -- --runInBand >/dev/null 2>&1; then
block_reason='npm testを実行し、失敗したテストを修正する'
block_stop
fi
if ! test -s dist/app.js; then
block_reason='dist/app.jsを再生成する'
block_stop
fi
printf '%s\n' 'Stopチェック成功: testsとdist/app.js'
exit 0
ファイルに実行権限を付けます。
chmod +x scripts/check-before-stop.sh
現在のClaude Codeドキュメントでは、Stop Hookはコード0と構造化JSONを返せます。decision: "block" がターンの終了を止め、reason が理由を伝えます。ゼロ以外のコードとtimeoutには別のhookエラー動作があるため、それだけをブロック契約にしないでください。チェックは設定時間内に収めます。
次の終了試行で、Stop HookによりClaude Codeがすでに継続している場合は stop_hook_active が true になります。サンプルのfail-open分岐は、同じ失敗を再ブロックせず、stderrに短い警告を書いて0を返します。ループは避けられますが、CIのゲートは必要です。繰り返しブロックするなら、文書化されていない固定回数を想定せず、独自のカウンターと上限を定義してください。
一連の動作を手動で検証する
スクリプトがターミナルで起動することだけでなく、すべての分岐を確認します。
- スクリプト内の
dist/app.jsを一時的にdist/missing.jsに変更し、printf '%s\n' '{"stop_hook_active":false}' | ./scripts/check-before-stop.sh; echo $?を実行します。"decision":"block"、短いreason、コード0を含むJSONが期待結果です。 - 正しいパスに戻し、buildを生成して同じコマンドを再実行します。期待されるコードは
0です。 - もう一度存在しないファイルを指定し、Claude Codeに小さく元に戻せる変更を依頼します。
decision: "block"が会話を継続させ、チェックの理由を返すことを確認します。 - パスを直さずに、
printf '%s\n' '{"stop_hook_active":true}' | ./scripts/check-before-stop.sh; echo $?を実行します。スクリプトはstderrに短い警告を書き、コード0を返すはずです。これでループ防止分岐を確認できます。 - パスを戻すか、最新の成果物を作成します。次の終了時にはhookが
0を返し、ターンの終了を許可する必要があります。
この手順により、実際に機能するStop Hookと、ターミナルでは失敗するのにClaude Codeの終了を許してしまうスクリプトを区別できます。
ブロックされたStopから復旧する
2つのケースを分けます。hookがdecision: "block"を含むJSONを返した場合はreasonを読みます。これはチェック条件の通常の失敗です。そのチェックを手動で実行し、テストまたはコードを修正し、ファイルが現在のコマンドで生成されたことを確認してからClaude Codeタスクを再試行します。
hookコマンド自体がゼロ以外のコードまたはtimeoutで終わった場合は、reasonによる確認済みブロックではなくhookの実行エラーです。エラーとstderrを読み、スクリプトを手動実行し、パス、権限、依存関係、時間上限を修正してから再検証します。
記録するのはチェック名と結果だけにします。API Key、.env の内容、完全なprompt、テストログ全体は出力しません。git diff --check の成功は業務ロジックの正しさを証明せず、ファイルがあることも最新buildを証明しません。hookが確認できるのは、明示的にコード化した主張だけです。
APIワークフローを分離する
APIプロバイダーを使う場合は、自分のアカウント内でKeyを作成・管理します。Stop Hookはローカルにとどまり、Key、完全なprompt、Dashboardログへアクセスする必要はありません。API障害ではBase URLと設定について最新のドキュメントを確認し、この診断をローカルの終了チェックと混ぜないでください。
参考資料
- Claude Code Hooks Reference — 2026年8月22日確認
- BetterToken: Claude Codeの設定