Claude Code Hooks — AI を制約する5つの仕組み

お疲れ様です。みんみんです。

今回は Claude CodeHooks について書きます。
正直、自分のなかで一番効果があった機能かもしれません。

CLAUDE.md に書いたルール、守ってくれない問題

Claude Code を使っていると、こんなことが起きます。

  • 勝手に git commit して push する
  • 確認なしで subagent を大量に起動する
  • 頼んでもいないファイルを新規作成する
  • コードに長い説明コメントを書く

全部 CLAUDE.md に「やるな」と書いてあります。
でも守らない。毎回守らない。何度書いても守らない。

これは Claude が悪いというより、CLAUDE.md は「お願い」であって「強制」ではないということです。
LLM は確率的に動くので、ルールを100%守る保証がありません。

Hooks = 機械的な強制力

Hooks はシェルスクリプトです。
Claude Code のライフサイクルの特定のタイミングで必ず実行されます。

CLAUDE.md との違いを一言でいうと:

  • CLAUDE.md → 「これやらないでね」(お願い。忘れることがある)
  • Hooks → 「これやったらブロックする」(強制。100%発火する)

設定は .claude/settings.jsonhooks キーに書きます。
イベント(PreToolUse / PostToolUse / SessionStart 等)ごとにスクリプトを指定できます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Agent",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/.claude/hooks/your-script.sh"
          }
        ]
      }
    ]
  }
}

matcher でどのツールに反応するかを指定して、command で実行するスクリプトを指定します。
PreToolUse ならツール実行のに走るので、条件に合えばブロックできます。

実際に使っている5つの Hook

全部、実際に事故が起きてから作ったものです。
予防じゃなくて対処。痛い目を見てから増えていきました。

1. subagent の無断起動をブロック

イベント:PreToolUse(matcher: Agent

Claude Code は複雑なタスクを分割して subagent に投げることがあります。
便利ですが、確認なしで5〜6個同時に起動されると制御不能になります。

#!/bin/bash
echo '{"decision":"block","reason":"subagent を開く前に許可を取ること。"}'

シンプルですが効果は絶大。
無条件ブロック。ユーザーに許可を求めさせます。

2. git commit / push の無断実行を防止

イベント:PreToolUse(matcher: Bash

CLAUDE.md に「commit/push は明確な指示があるまで禁止」と書いていましたが、普通に無視されました。
特に auto モードだと、作業が終わったら勝手にコミットして push まで行くことがあります。

#!/usr/bin/env bash
INPUT=$(cat)
CMD=$(jq -r '.tool_input.command // empty' <<<"$INPUT")

if echo "$CMD" | grep -qE '\bgit\b[^&|;]*\b(commit|push)\b'; then
  jq -n --arg reason "commit/push は明確な確認が必要" \
    '{"hookSpecificOutput":{"permissionDecision":"ask"}}'
else
  exit 0
fi

Bash コマンドの中に git commitgit push が含まれていたら、"permissionDecision": "ask" で確認を挟みます。
block ではなく ask にしているのは、指示済みの commit まで止めたくないからです。

3. 新規ファイル作成の事前確認

イベント:PreToolUse(matcher: Write

「ちょっとしたヘルパー関数」のつもりで勝手にファイルを作られることがあります。
既存ファイルの編集は OK、新規作成だけ確認を入れます。

#!/usr/bin/env bash
f=$(jq -r '.tool_input.file_path')
if [ -n "$f" ] && [ ! -e "$f" ]; then
  echo '{"hookSpecificOutput":{"permissionDecision":"ask","permissionDecisionReason":"新規ファイル作成は事前確認が必要"}}'
fi

ファイルパスを取得して、ファイルが存在しなければ確認を挟む
4行。これだけで不要なファイル増殖を防げます。

4. 説明コメントを書かせない

イベント:PostToolUse(matcher: Edit|Write

Claude Code はコードを書くと、丁寧に説明コメントをつけてくれます。
「// ユーザー名を取得する」みたいなやつ。コードを読めばわかることを日本語で書かないでほしい。

Python スクリプトで、Edit / Write の後に新しく追加されたコメントを検査します。
判定ロジックはシンプルで、以下の3つに引っかかったら「説明コメント」と判定して警告を出します:

  • 句点(。)が含まれている
  • 「する」「した」「です」「ます」等の述語で終わっている
  • 40文字以上ある

TODONOTE: や URL はホワイトリストで許可しています。

コメントは見出しだけ。句点なし、文にしない。これがルールです。

5. SessionStart で環境を自動整備

イベント:SessionStart

新しいセッションが始まるたびに実行されるイベントです。
環境チェックや初期設定の自動化に使えます。

自分の場合は、VSCode 拡張の特定の設定が更新のたびにリセットされる問題があり、
SessionStart で自動的にパッチを当てるスクリプトを走らせています。

他にも使い方はいろいろあります:

  • 環境変数の読み込み確認
  • 必要なツール(jq, gh 等)のインストールチェック
  • 作業開始時のリマインドメッセージ表示

全部、事故から生まれた

正直に言うと、最初から Hooks を設計したわけじゃありません。

subagent のブロック → 確認なしで大量起動されて制御不能になったから。
commit/push のゲート → 作業が終わったら勝手にコミットされたから。
新規ファイルの確認 → 頼んでないファイルが増えていたから。
コメントの検査 → 毎回消す手間が発生していたから。

事故が起きるたびに Hook が1つ増える。Hook の数 = 過去の事故の数です。

Hooks の限界

万能ではありません。

Hooks が得意なのは「やってはいけないことを止める」です。
苦手なのは「やるべきことをやらなかった」を検知すること。

例えば「コミット前にレビュー結果を表示して確認ボタンを出す」というルールがあったとして、
Claude がそのステップを丸ごとスキップした場合、Hook では検知できません。
「実行された」ことは捕まえられるけど、「実行されなかった」ことは捕まえられない。

なので使い分けとしては:

  • 禁止事項 → Hooks で機械的にブロック
  • 手順・フロー → Skills で定義して従わせる
  • 方針・考え方 → CLAUDE.md に書く

3層で守る、というイメージです。

まとめ

AI に仕事を任せるなら、信頼するな、でも使え
ルールは CLAUDE.md に、制約は Hooks に。

Hooks は Claude Code の中で唯一、LLM の判断を介さずに100%実行される仕組みです。
CLAUDE.md だけでは守れないことも、Hooks なら機械的に止められます。

自分の Hook は全部、事故から生まれました。
まだ事故が起きていない人は、先に入れておくと幸せになれるかもしれません。

参考になれば嬉しいです。

この記事を気に入ったら

この記事を書いた人

みんみん

みんみん

この人が書いた記事を見る >>