hooksとは — 「お願い」ではなく「機械的な強制」

hooksとは、Claude Codeの特定のイベント(ツール実行の前後、応答の終了時、セッション開始時など)に反応して、あらかじめ決めた処理を必ず実行させる仕組みです。CLAUDE.mdに書いたルールが「守ってね」というお願い(プロンプト)であるのに対し、hooksはイベントが起きたら機械的に発動する点が決定的に違います。

CLAUDE.mdやSkillsはAIへの指示なので、守られる確率は高くても100%ではありません。「絶対に守らせたい」ことは、AIの判断に委ねず、仕組みで強制する。この使い分けが、Claude Codeを安定運用するためのハーネス(AIを安全に動かす枠組み)設計の要点です。

CLAUDE.md・Skills・hooksの役割分担

CLAUDE.mdは毎回読む「憲法」、Skillsは必要なときに読む「業務マニュアル」、hooksは特定イベントで必ず動く「自動処理」。前者2つが“何をどう伝えるか”なら、hooksは“確実に実行させる”担当です。三者は補い合う関係にあります。

主なhookイベント — いつ発動するか

hooksは「どのタイミングで発動するか」をイベント名で指定します。用意されているイベントは多数ありますが、実務でよく使うのは次のあたりです。

表は横にスクロールできます →

イベント発動タイミング使い道の例
PreToolUseツールを実行する直前危険なコマンドや機密ファイルへのアクセスをブロックする
PostToolUseツールの実行が成功した後ファイル変更後の自動チェック、特定のコマンドを検知したときだけ次の作業を促す
UserPromptSubmitユーザーが指示を送った直後入力内容に応じて注意書きや前提を差し込む
SessionStartセッションの開始時最新情報の自動読み込み・リポジトリの同期
Notification / Stop入力待ち・応答の終了時作業が終わったら通知音を鳴らす

このほかにも、サブエージェントの開始・終了、コンテキストの圧縮前後、セッション終了時など、細かなタイミングに対応するイベントが用意されています。まずは上記の代表的なものから使い始めるのがおすすめです。

設定方法 — settings.json に書く

hooksは設定ファイル settings.json に書きます。置き場所は3種類あり、目的で使い分けます

  • ~/.claude/settings.json — 全プロジェクト共通(自分のPC全体)
  • .claude/settings.json — そのプロジェクトだけ(リポジトリにコミットしてチーム共有できる
  • .claude/settings.local.json — そのプロジェクトの自分だけ(共有しない)

書式は「イベント名 → どのツールに反応するか(matcher)→ 実行する処理」の入れ子構造です。下は「Bashコマンドを実行する前に、チェック用スクリプトを必ず通す」最小の例です。

.claude/settings.json — Bash実行前にチェックを挟む例
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check.sh"
          }
        ]
      }
    ]
  }
}

matcher で反応するツールを絞り込み(例: BashEdit|Write のように複数指定も可)、type で処理の種類(シェルコマンドの実行、HTTPリクエスト等)を選びます。処理内容そのものは自分で用意したスクリプトに任せられるので、やれることの幅は広いです。

法人運用での使いどころ

  • 機密情報のガード: PreToolUseで、.env や特定の秘密ファイルへのアクセスを機械的にブロックする。AIが“うっかり”読みに行くことを構造的に防ぐ
  • 品質の自動担保: PostToolUseで、ファイルを変更するたびに自動チェック(フォーマット・検証)を走らせ、崩れた状態のまま進むのを止める
  • 運用の自動化: SessionStartで、作業開始時に最新のルールや共有ファイルを自動で読み込ませ、古い前提で作業が始まらないようにする
  • 気づける仕組み: Stop/Notificationで、AIが入力待ちになったら通知音を鳴らし、待ち時間の取りこぼしをなくす

特に1つ目の「機密情報のガード」は、法人導入で外せない観点です。プロンプト(CLAUDE.mdの禁止事項)だけに頼らず、hooksという機械的な関所を置くことで、情報漏洩リスクを一段下げられます

この考え方はClaude Codeのセキュリティ対策、権限そのものの設計は権限設定の基本で詳しく扱っています。

実例 — 当社が全社のPCに配っている共有hook

ここからは、当社が実際に全社のMacへ配っている共有hookを、設計の考え方に沿って紹介します。いずれも十数行から数十行のシェルスクリプトを settings.json からイベントごとに呼び出しているだけで、難しいことはしていません。

並べてみると分かるのは、hookの大半は「止める」ためではなく「線を引く」ために書かれているということです。全部を禁止すると業務まで止まるので、通す経路を必ず残しています。

表は横にスクロールできます →

hookの役割イベント(matcher)何をしているか
秘密ファイルの表示ブロックPreToolUse(Bash)cat などで .env の中身を出すコマンドを拒否。環境変数としての読み込みは通す
機密スプレッドシートのブロックPreToolUse(全ツール)ツール入力のどこかに対象のシートIDが含まれていたら拒否
メール書き込みのアカウント固定PreToolUse(Bash)下書き作成・送信だけを指定アカウントに縛る。検索・閲覧は通す
規約で自動化を禁じているサービスへのアクセス禁止PreToolUse(全ツール)URLを取りに行く操作だけ拒否。文章中にURLを書く編集は止めない
全リポジトリの自動同期SessionStart作業開始時に共有リポジトリを最新化し、古い前提での作業を防ぐ

秘密ファイルは表示だけ止め、環境変数としての読み込みは通す

.env には外部サービスのAPIキーが入るので、中身をAIの画面に出させたくありません。一方で、スクリプトが環境変数として読み込む使い方まで止めると開発が回らなくなります

そこで線引きを「表示はブロック・ロードは公認」と決めています。判定は1本のhookで行い、.env の中身をコマンドラインに出す表示・持ち出し系の操作だけを止め、スクリプトの内部で環境変数として読み込む使い方は通す、という切り分けをしています。

肝は、スクリプトの内部でdotenvを読み込む正規の使い方は引っかからないことです。その形ならコマンドラインに .env の文字列が出ないため、判定を素通りします。「見せる操作」と「使う操作」を、.env が画面(コマンドライン)に現れるかどうかで機械的に切り分けているわけです。

ブロックするだけのhookは回避される — 拒否メッセージに正規ルートを書く

運用してみて一番効いたのは、拒否メッセージの書き方でした。ただ「禁止です」と返すと、AIは別の手段を探して回り道を始めます。禁止した瞬間に代わりの正規ルートを同じメッセージで示すのが、実運用の肝です。

.env ガードの拒否メッセージにも、この考え方を入れています。ただ「禁止です」と返すのではなく、値を画面に出さずに使う正規のやり方を短く添えるのです。「表示するのではなく環境変数として読み込んでから使う」「キーの有無だけ確かめたいなら値を出さずに存在を確認する」——といった代替手順を示すと、AIは回り道を探さずにその場で正しい手順へ切り替わります。

この一手間が効くのは、hookが止める仕組みであると同時に、正しいやり方を教える場所にもなるからです。「何を禁じたか」だけでなく「代わりに何をすればよいか」まで書いておくと、関所が回り道の入り口ではなく道案内に変わります。

ツール名ではなく入力の中身で判定する

PreToolUseは matcher でツールを絞るのが基本ですが、matcherを空にして全ツールを対象にし、入力の中身で判定する書き方もできます。当社では、社外に出せない特定のスプレッドシートへのアクセスを、この形で全ツール横断に塞いでいます。

やっていることは単純で、ツール入力のJSON全体を対象に、止めたいシートの識別子が含まれていないかだけを見ています。ツールの種類を条件にしていないので、ブラウザ操作でもBashでもMCP経由でも、対象が含まれていれば同じように止まります。matcherを空にして中身で判定すると、経路ごとに個別対応しなくて済むのが利点です。

禁止は最小面積で — 読み取りは通し、書き込みだけ縛る

メール操作のhookは、書き込み系(下書きの作成・更新・送信・転送・自動返信)だけをアカウント指定で縛り、検索・取得・スレッド閲覧はどのアカウントでも通しています。

誤ったアカウントで送ると送信済みが本人のメールボックスに残らず、後から追えなくなります。一方で読む操作まで縛ると調査が止まるので、不可逆な操作にだけ関所を置く形にしました。

判定は2つだけです。その操作が書き込み系か、そして送り先のアカウントが正しく指定されているか。書き込み系なのにアカウントの指定が欠けているときだけ拒否し、拒否メッセージには正しい指定の付け方を書いて示します。読む操作はこの判定に一切かからないので、調査の手は止まりません。

禁止の範囲を広げるほど、hookは邪魔者になって外されます。「どの操作が取り返しのつかない操作か」で線を引くと、縛る範囲は自然と小さくなります。

強すぎるhookは業務を止める — 書くのは許し、取りに行くのだけ止める

自動化ツールからのアクセスを規約で禁じているサービスがあります。そこでそのドメインへAI経由で触れないようにするhookを入れているのですが、単純にドメイン名でブロックすると誤検知が出ます。

記事や投稿文の中にそのサービスのURLを書く編集まで止まってしまうからです。そこでツールの種類ごとに判定を変えて、URLを取りに行く操作だけを拒否しています。

肝は、同じドメインでも「取りに行く操作」と「文字として書く操作」を区別することです。ページを取得・表示しようとする操作は止め、文章の中にそのURLを書くだけの編集は通す。この線引きをツールの性質に応じて分けているので、正当な執筆作業まで巻き込まずに、規約で自動化を禁じているサービスへAI経由で触れる経路だけを塞げます。

hookの誤検知は「AIが動かない」ではなく「担当者がhookを外す」形で表面化します。止める対象をツール別に切り分ける手間は、そのまま運用の寿命になります。

セッション開始時に前提をそろえる

SessionStartのhookは、禁止ではなく準備に使っています。作業用ディレクトリ配下の全リポジトリを起動時に最新化し、AIが古いファイルを前提に作業を始めるのを構造的に防ぐものです。

ただし黙って更新すると事故になるので、条件を細かく決めています。「触らない条件」のほうを先に書くのがコツです。

  • 追跡中のファイルに未コミットの変更があるリポジトリはスキップする(作業中のツリーには一切触らない)
  • 未追跡ファイルだけならpullする(常に未追跡ファイルが置かれるリポジトリが永久に古いままになるのを防ぐため)
  • 取り込みは git merge --ff-only のみ。fast-forwardできない場合は何もしない
  • 出力はセッションのコンテキストを消費するので、動きがあったときだけ1行報告する

取り込みを git pull ではなく git fetchgit merge --ff-only の2段に分けているのも運用の結果です。複数セッションが同時に復帰するとhookが並走し、pullが内部で使う一時参照の競合で全リポジトリが失敗したことがありました。

PostToolUseは毎回ではなく、条件が揃ったときだけ動かす

サイト運用のリポジトリには、PostToolUseのhookを1本だけ置いています。Bashの実行結果からpushコマンドを検出し、そのpushに表示へ影響する変更(src/public/ 配下)が含まれるときだけ動くものです。

動いたときは「デプロイ完了を待って表示速度を計測し、劣化がないか報告する」という指示をその場のコンテキストに差し込みます。条件に当たらなければ何も出力しません。

毎回鳴るhookは読み飛ばされます。PostToolUseは「変更のたびに検査を走らせる」使い方が定番ですが、沈黙する条件を先に決めるほうが実運用では効きます。

複数PC・複数人にどう配るか

hookは書けても、配って更新し続けるところで運用が崩れます。手で settings.json を編集して配ると、PCごとに版が分岐し、どのPCで何が有効なのか誰も分からなくなります。

当社はhook本体を共有リポジトリに1本だけ置き、各PCへの登録は冪等なスクリプトに任せています。何度実行しても結果が同じになるように書いてあり、settings.json を手で触ることはありません。

スクリプトが持っているのは、配るhookの一覧(どのイベントに、どのmatcherで、どのコマンドを登録するか)と、廃止したhookの一覧の2つだけです。実行すると、各PCの settings.json をこの一覧どおりの状態に書き直します。

この形の効きどころは3つです。旧版は目印の文字列で検出して置き換える、廃止したhookは廃止リストに一行加えるだけで全PCから消える、既存の設定は日時つきの退避ファイルに逃がしてから差し替える。

さらに、先ほどのSessionStart hookが共有リポジトリの更新を検知したらこの配布スクリプトを自動実行します。結果として、hookを直してpushすれば全PCに人手なしで反映される状態になっています。

コピーで配らない

hookやルールをPCごとにコピーして配ると、コピーした瞬間から修正漏れの分岐が始まります。配布経路は「共有リポジトリ+git pull」の1本に絞り、各PC側は必ずリンクか自動生成にしておくのが安全です。

非エンジニアでも設定できるのか

設定ファイルの編集自体はテキスト作業で、しかも「こういう条件のときにこの処理を必ず走らせるhooksを設定して」とClaude Code自身に依頼して書いてもらえます。とはいえ、実行される処理(スクリプト)が何をするかは理解しておくべきで、特にファイルをブロック・削除するような処理は動作を一度確認してから運用に載せるのが安全です。

「どのルールをお願い(CLAUDE.md)で済ませ、どれを強制(hooks・権限)にするか」の線引きは、実際に運用しながら育てていくものです。当社のClaude Code研修では、この“守らせ方の設計”を法人の実務に合わせて組み立てるところまで扱っています。

Claude Codeを組織に定着させたい企業様へ。AI Orchestraの法人研修・導入支援をご覧ください。