コンテンツにスキップ

hook で仕組みにする

tier 2(効いてくる)確認 1 か月前(2026年8月23日)

どこまで来たら使えたと言えるか

面: 両方で同じ

段達成したと言える状態
1. 触るSessionStart か PostToolUse の command hook を 1 つ書き、/hooks に出ることと実際に発火することを確認した
2. 入れる毎回言わないと守られない規約を 1 つ hook に移し、matcher と if で対象を絞ってある
3. 効かせる終了コード 0 / 2 と JSON 出力の使い分けを説明でき、hook が動かないときデバッグログで一致状況を追える

何のためのものか

Claude Code のライフサイクルの特定の時点で自動実行される、ユーザー定義のシェルコマンド・HTTP エンドポイント・LLM プロンプト。

CLAUDE.md が「守ってほしい」なら、hook は「守らせる」。 Claude の判断に関わらず動作をブロックしたいなら、こちらしかない。

触る

3 段のネストで書く。イベント → matcher group → handler の順。

  1. ~/.claude/settings.json か .claude/settings.json に hooks を書く
  2. /hooks メニューで設定を確認する
  3. 実際に発火させる。settings ファイルの hook の直接編集はファイル監視で自動的に反映される

まず触るなら、セッションに 1 回だけ動く SessionStart か、編集のたびに動く PostToolUse が分かりやすい。

入れる

対象を絞る。ここが雑だと、関係ない場面で毎回動く hook ができる。

  • matcher はイベントごとに絞る対象が違う。 PreToolUse / PostToolUse はツール名、SessionStart は開始方法(startup / resume / clear / compact / fork)、PreCompact は manual / auto
  • matcher の書き方で評価が変わる。 英数字・_・-・スペース・,・| だけなら完全一致(または区切りリスト)、それ以外の文字を含むとanchor 無しの正規表現になる。Edit.* は NotebookEdit にも一致するので、全体一致には ^Edit$ と書く
  • if でさらに絞る。 permission rule 構文で "Bash(git *)"、"Edit(*.ts)" のように書く。ただしツールイベントでしか評価されない(それ以外のイベントでは if 付き hook は決して動かない)
  • timeout の既定はイベントと種類で違う。 command / http / mcp_tool は 600 秒だが、UserPromptSubmit では 30 秒、SessionEnd の hook は 1.5 秒の予算を共有する
  • 待たせたくない処理は async: true。終了時に Claude を起こしたいなら asyncRewake: true

効かせる

返し方を使い分ける。

やりたいこと返し方
成功。何も伝えないexit 0(stdout はデバッグログ行き)
止める。理由を Claude に伝えるexit 2(stderr がエラーメッセージとして Claude に返る)
情報だけ Claude に渡すexit 0 + hookSpecificOutput の additionalContext
ユーザーに警告を見せるJSON の systemMessage
Claude の処理を完全に止めるJSON の continue: false(stopReason はユーザーにだけ見える)
  • exit 1 は非ブロッキングエラー。 ポリシーを強制するなら exit 2 を使う
  • 終了コードと JSON はどちらか一方。 JSON は exit 0 のときだけ処理される
  • 止められないイベントがある。 PostToolUse(ツールは既に実行済み)、Notification、SessionStart などは exit 2 でもブロックにならない
  • 動かないときはデバッグログ。 どの hook が一致したか、終了コード、stdout / stderr の全文が書かれる。claude --debug で ~/.claude/debug/<session-id>.txt、CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose で matcher 照合の詳細まで出る

落とし穴

  • command hook はユーザーの完全な権限でシェルコマンドを実行する。 設定に追加する前にレビューとテストを行う。シェル変数は常にクォートし、.. のパストラバーサルをブロックする
  • hook は制御端末を持たない。 /dev/tty へエスケープシーケンスを書いても失敗する。通知やタイトルは terminalSequence、ユーザー向けメッセージは systemMessage
  • 個別の hook だけを無効化する方法は無い。 一時停止は "disableAllHooks": true で全部か、settings からエントリを消すか
  • matcher 非対応のイベントに matcher を書いても黙って無視される
  • additionalContext は命令ではなく事実として書く。 システムコマンドのように書かれたテキストは prompt-injection 防御を起こし、コンテキストとして扱われないことがある
  • MCP ツールに一致させるには mcp__<server>__<tool> の形。mcp__memory のような完全一致文字だけの形は何にも一致しない

根拠と前後

このページが書ける範囲は、根拠にした facts に書かれていることだけです。 仕様そのものはそちらを読んでください。