Free

Claude Code Hooks 完全ガイド:Claudeのすべての動作を制御する

Claude Code Hooksの仕組みを完全解説。5種類のイベント、終了コード制御、4つの実践設定例で、AIの操作を監査・遮断・自動化する方法を紹介。


Claude Codeがファイルを読み込み、コードを書き、コマンドを実行するたびに、バックグラウンドでイベントシステムが動いています。Hooksはそのシステムへの接続インターフェースです——任意のタイミングで独自のロジックを注入し、自動でコードチェックを行い、ログを記録し、危険な操作をブロックし、あらゆるシェルコマンドをトリガーできます。

この記事では、Hooksのメカニズム、設定方法、実践的な使い方を完全に解説します。


Hooksとは何か

Hooksはsettings.jsonに設定されたシェルコマンドで、特定のイベント発生時にClaude Codeが自動的に実行します。

最もわかりやすい例え:git hooksです。gitはcommit・pushなどの操作の前後にスクリプトをトリガーできます。Claude Code Hooksも同じ考え方で、違いはトリガーポイントがAIのツール呼び出しであることです。

なぜこれが重要なのか?

Claude Codeの能力が高まるほど、確定的な制御レイヤーが必要になります。Hooksが提供するもの:
- プロンプトに依存しない実行保証(Claudeは指示を無視することがあっても、hookは必ず実行される)
- 監査可能な操作記録
- 自動化された品質チェック


5種類のHookタイプ

タイプ トリガー 典型的な用途
PreToolUse ツール呼び出し 危険な操作のブロック、意図の記録
PostToolUse ツール呼び出し 自動lint、テスト実行
PreCompact コンテキスト圧縮 現在の状態スナップショットの保存
Notification Claudeが通知を送る時 デスクトップ通知、Slackメッセージ
Stop Claudeが応答を完了した時 ログの集計、後続フローのトリガー

最もよく使われるのはPreToolUsePostToolUseで、ツール呼び出しのインターセプトと後処理に使います。


設定フォーマット

Hooksは~/.claude/settings.json(グローバル)またはプロジェクトルートの.claude/settings.json(プロジェクト単位)に記述します:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint --silent"
          }
        ]
      }
    ]
  }
}

3つのコアフィールド:

  • matcher:ツール名にマッチする正規表現。このhookがどのツール呼び出し後にトリガーするかを決定します。"Write|Edit"はファイルの書き込みまたは編集時にトリガーします。空または".*"で全ツールにマッチします。
  • type:現在は"command"のみ。
  • command:任意のシェルコマンド。

ツール名リファレンス

matcherを書く際に必要なツール名:

ツール名 対応する操作
Write 新規ファイルの書き込み
Edit ファイルの編集
Bash シェルコマンドの実行
Read ファイルの読み込み
Glob ファイル検索
Grep コンテンツ検索
TodoWrite タスクリストの更新

Hookの実行環境

実行時にstdin経由でJSONが渡されます:

{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.rb",
    "content": "..."
  },
  "tool_response": "..."
}

PreToolUsetool_input(呼び出し前の引数)を受け取り、PostToolUsetool_response(ツールの戻り値)も受け取ります。

条件分岐ロジックを持つhookの例:

#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path')

# .rbファイルのみrubocopを実行
if [[ "$file" == *.rb ]]; then
  rubocop "$file" --autocorrect-all --no-color
fi

終了コードの意味

Hookの終了コードがClaude Codeの後続動作を制御します:

終了コード 意味
0 成功、実行継続
2 ブロック:現在のツール呼び出しをキャンセルし、stderrの出力をClaudeにフィードバック
その他の非ゼロ エラーを記録し、実行継続

終了コード2が最も有用です——PreToolUseでインターセプトロジックを書き、Claudeの操作をブロックし、その理由を伝えることができます。


実践:4つのHook設定例

1. ファイル書き込み後の自動フォーマット

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
          }
        ]
      }
    ]
  }
}

2. 特定ディレクトリの削除禁止

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'cmd=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .command); if echo \"$cmd\" | grep -qE \"rm.*/(migrations|seeds)\"; then echo \"migrationsまたはseedsディレクトリの削除は禁止されています\" >&2; exit 2; fi'"
          }
        ]
      }
    ]
  }
}

3. 操作ログ

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') $CLAUDE_TOOL_NAME: $(echo $CLAUDE_TOOL_INPUT | jq -c .)\" >> ~/.claude/audit.log"
          }
        ]
      }
    ]
  }
}

4. 完了時のデスクトップ通知

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claudeが完了しました\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

長時間のタスクが完了したら自動通知。ターミナルを見続ける必要がありません。(macOSはosascript、Linuxはnotify-send


グローバル vs プロジェクト単位

Hooksを配置できる場所は2つ:

グローバル~/.claude/settings.json):すべてのプロジェクトに共通するルール(ログ、通知など)。

プロジェクト単位.claude/settings.json):プロジェクト固有のチェック(このプロジェクトはrubocop、あのプロジェクトはeslintなど)。プロジェクト単位のhooksとグローバルhooksはマージされて両方実行されます。

チームプロジェクトでは、プロジェクト単位のsettings.jsonをgitにコミットして、全員が同じhooksを実行するようにすることを推奨します。


Hookのデバッグ

Hookが機能しない場合のトラブルシューティング:

  1. matcherのスペルを確認:ツール名は大文字小文字を区別します。writeWriteは別物
  2. コマンドを単独で実行:hookのcommandをコピーしてターミナルで直接実行し、動作を確認
  3. Claude Codeの出力を確認:hooksのstderrは会話に表示されます(終了コード2の場合は特に明確)
  4. シンプルなテストから始める:まずecho "hook triggered" >> /tmp/hook.logでトリガーを確認してから複雑なロジックを追加

まとめ

Hooksのコアバリューは予測不可能なAIの行動と確定的なエンジニアリング標準をつなぐことです。Claudeはテストの実行を忘れることがあっても、PostToolUse hookは忘れません。Claudeが誤ってファイルを削除しようとしても、PreToolUseインターセプターは通しません。

1つのhookから始めましょう:ファイル書き込み後の自動lint。それが動いたら、他のhookを追加していけばいい。