CLAUDE LABEN
BILLING — 6/15の課金変更まで残り1日。Agent SDK・headless・GitHub Actions・他社エージェントが別枠の月次クレジット($20/$100/$200・full APIレート・繰越なし)へ移行しますFABLE5 — Claude Fable 5(Mythosクラス)は一般提供で最高性能とされ、Claude Code v2.1.170以降で利用できます(6/9公開)SUBAGENTS — Claude Codeでサブエージェントがさらにサブエージェントを生成可能に。モデル・リージョン選択もより賢くなりましたENTERPRISE — Enterpriseのカスタムロールにadmin権限を追加。Owner権限なしで請求・プライバシー設定へアクセスできますPLUGINS — プラグイン検索とChrome・VSCode・ターミナル連携が改善。セッション・メモリ・権限まわりの不具合も修正されましたUI — フルスクリーン時のマウスホイール加速を無効化する設定を追加。/modelピッカーがモデルファミリーを正しく表示するよう修正BILLING — 6/15の課金変更まで残り1日。Agent SDK・headless・GitHub Actions・他社エージェントが別枠の月次クレジット($20/$100/$200・full APIレート・繰越なし)へ移行しますFABLE5 — Claude Fable 5(Mythosクラス)は一般提供で最高性能とされ、Claude Code v2.1.170以降で利用できます(6/9公開)SUBAGENTS — Claude Codeでサブエージェントがさらにサブエージェントを生成可能に。モデル・リージョン選択もより賢くなりましたENTERPRISE — Enterpriseのカスタムロールにadmin権限を追加。Owner権限なしで請求・プライバシー設定へアクセスできますPLUGINS — プラグイン検索とChrome・VSCode・ターミナル連携が改善。セッション・メモリ・権限まわりの不具合も修正されましたUI — フルスクリーン時のマウスホイール加速を無効化する設定を追加。/modelピッカーがモデルファミリーを正しく表示するよう修正
記事一覧/Claude Code
Claude Code/2026-06-14上級

Claude Code の Hooks で品質ゲートを壊さず回す — exit code の意味・ブロック挙動・観測設計の実装メモ

Claude Code Hooks を自動化の安全弁として運用するための実装メモ。exit code 0/2/その他の使い分け、JSON 出力による決定制御、ブロックが暴発したときの切り分け、観測ログの設計までを実例で整理します。

claude-code117hooks11automation35ci2reliability4

プレミアム記事

個人開発で複数のサイトを無人運用している私自身、Claude Code を自動で回すようになって最初に痛い目を見たのは、自分で仕込んだ PostToolUse フックが原因でセッションが止まったときでした。フォーマッタを「ファイルを書いたら自動で整える」つもりで PostToolUse に載せたところ、整形でファイルが書き換わり、それがまた次の編集として扱われ、フォーマッタが再び走る、という往復に近い挙動になったのです。

Hooks は「AI にお願いする」のではなく「必ずこうなる」を担保する仕組みなので、便利な反面、設計を誤ると自動運用そのものを壊します。ここでは公式ドキュメントに載っている挙動を踏まえつつ、自動投稿パイプラインで実際に運用して分かった「壊さないための勘所」を、コードと一緒に整理していきます。

まず押さえるべきは「終了コードと標準出力の契約」

Hooks の挙動を理解するうえで最初に固めておきたいのは、フックが Claude Code に何を返すか、という契約です。ここが曖昧なまま複雑なスクリプトを書くと、ブロックしたいのに素通りしたり、逆に通したいのに止まったりします。

フックは大きく二つの方法で結果を返します。ひとつは終了コード、もうひとつは標準出力に流す JSON です。

終了コードの意味は次の三つに整理できます。

  • exit 0 — 正常終了。標準出力の内容は、フックの種類によって扱いが変わります(後述)。
  • exit 2 — ブロッキングエラー。標準エラー出力の内容が Claude にフィードバックされ、操作が止められます。
  • それ以外(exit 1 など)— 非ブロッキングエラー。警告としてユーザーに表示されますが、処理は続行します。

この三分法のうち、自動化で最も重要なのは exit 2 です。「危険なコマンドを止める」「規約違反の編集を差し戻す」といったゲートは、すべて exit 2 と標準エラー出力の組み合わせで成立します。逆に言えば、ブロックしたいのに exit 1 を返していると、警告は出るのに処理は通ってしまうので、ゲートとして機能しません。

#!/usr/bin/env bash
# block-force-push.sh — PreToolUse(Bash) で危険な push を止める
input=$(cat)                      # フックは JSON を stdin で受け取る
cmd=$(echo "$input" | jq -r '.tool_input.command // empty')
 
if echo "$cmd" | grep -qiE 'git +push.*(--force|-f)\b'; then
  # stderr に理由を書いて exit 2 → Claude にフィードバックされ操作が止まる
  echo "force push はこのリポジトリでは禁止です。--force-with-lease を検討してください。" >&2
  exit 2
fi
exit 0

ここでのポイントは三つあります。フック入力は引数ではなく標準入力に JSON で渡ること、ブロック理由は標準出力ではなく標準エラー出力に書くこと、そして判定に使うフィールド(tool_input.command)を正確に取り出すことです。私は最初、理由を標準出力に書いてしまい、ブロックはされるのにフィードバックが Claude に届かない、という分かりにくい状態に陥りました。

JSON 出力で「止める/続ける」を明示的に制御する

終了コードによる制御はシンプルで頑健ですが、表現力には限界があります。「止めるが、こういう理由で」「続けるが、追加のコンテキストを渡す」といった細かい制御をしたいときは、標準出力に JSON を流す方法が向いています。

PreToolUse の場合、次のような JSON を返すと、終了コードに頼らずに許可・拒否を表現できます。

#!/usr/bin/env bash
# guard-writes.sh — PreToolUse(Write|Edit) で保護パスへの書き込みを拒否
input=$(cat)
path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
 
case "$path" in
  *.env|*/secrets/*|*/.git/*)
    cat <<JSON
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "保護対象のパスです($path)。意図的な変更なら手動で行ってください。"
  }
}
JSON
    exit 0
    ;;
esac
exit 0

JSON 方式の利点は、終了コードを 0 に保ったまま「拒否」を表現できる点です。これにより、フック自体のエラー(スクリプトのバグで非ゼロ終了)と、業務ロジックとしての拒否を切り分けられます。運用していると、この切り分けが後でとても効いてきます。「フックが落ちたのか」「ルールで弾いたのか」がログ上で区別できるからです。

Stop フックでは decision フィールドが使えます。"decision": "block" を返すと Claude の停止を差し止め、reason に書いた内容に従って作業を続行させられます。たとえば「テストが緑になるまで止まるな」というゲートはこう書けます。

#!/usr/bin/env bash
# require-green-tests.sh — Stop フックでテスト未通過なら続行を強制
input=$(cat)
# 無限ループ防止: 既にこのフックが止めた状態なら、もう止めない
if [ "$(echo "$input" | jq -r '.stop_hook_active // false')" = "true" ]; then
  exit 0
fi
 
if ! npm test --silent >/tmp/test.log 2>&1; then
  cat <<JSON
{"decision": "block", "reason": "テストが失敗しています。/tmp/test.log を確認し、修正してから完了してください。"}
JSON
  exit 0
fi
exit 0

このスクリプトで最も重要なのは stop_hook_active のチェックです。これが無いと、Stop フックがブロック→作業→また Stop→またブロック、という終わらない往復を生みます。後述しますが、自動運用での事故の多くはこの「ループの口」が塞がれていないことに起因します。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
exit code 2 と JSON 出力 decision のどちらでブロックすべきか、PreToolUse・Stop ごとの判断基準
フォーマッタやリンタを Hooks に載せたときに無限ループ・誤ブロックを起こさない安全設計
フックの発火・所要時間・ブロック回数を JSONL で記録し、暴発を後から追える観測ログの実装
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

Claude Code2026-06-12
Claude Code の fallbackModel を三段構成にする — 過負荷の朝でも止まらない無人実行の設計記録
Claude Code の fallbackModel を三段構成で運用し、過負荷時も夜間バッチを止めない設計を実測つきでまとめます。実行モデルの記録方法、下位モデルに落ちた日の品質差への備え、deny ルール併用まで。
Claude Code2026-04-25
Claude Code Hooksが無限ループする問題 — PostToolUseが自己発火するときの止め方
Claude Code の Hooks を仕掛けたら、ツール呼び出しが止まらなくなった。PostToolUse が自分で書き換えたファイルを再び拾って延々と発火し続ける、典型的な無限ループの原因と止め方を実例ベースで解説します。
Claude Code2026-03-24
Claude Code Hooks — 開発ワークフローを自動化する実践テクニック集
Claude Code Hooksを使って開発ワークフローを自動化する実践テクニックを解説。PreToolUse・PostToolUseフックの設定方法から、コードレビュー自動化、セキュリティチェック、デプロイパイプライン連携まで網羅します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →