Anthropicが提供するAIコーディングアシスタント「Claude Code」のhooks機能は、開発ワークフローの自動化において極めて強力なツールである。Claude CodeはAIエージェント時代の幕開けを象徴する存在だが、その真価はhooksによるカスタマイズ性にこそ現れる。PreToolUse、PostToolUse、Stop、SessionStartといったライフサイクルイベントにカスタムコマンドやHTTPエンドポイント、さらにはLLMプロンプトを紐付けることで、コード品質ゲート、自動フォーマッタ、CI/CDパイプライン連携を実現できる。筆者自身、複数のプロジェクトでhooksを活用したワークフロー自動化を運用しており、その知見を体系的に整理して共有する。本記事では、hooks機能のセットアップから実践的な設計パターンまでを、具体的な設定例とともに解説する。

Claude Code hooksの全体像 ── イベント駆動アーキテクチャの理解

Claude Code hooksは、セッションのライフサイクル内で特定のイベントが発生した際に、ユーザー定義のシェルコマンド、HTTPエンドポイント、LLMプロンプト、またはエージェントを自動実行する仕組みである。2025年中盤に初期実装が導入され、2026年3月現在では17種類のイベントタイプをサポートする包括的なフック基盤へと成長した。

hooksは3階層のネスト構造で設定される。第1層がフックイベント(いつ発火するか)、第2層がマッチャーグループ(どの条件で発火するか)、第3層がフックハンドラー(何を実行するか)である。この構造は、Webフレームワークのミドルウェアパイプラインやgitのpre-commit/post-commitフックに類似しており、既存のCI/CDの知識がそのまま応用できる。

サポートされるイベントタイプは以下の通りである:

イベント発火タイミングブロック可否
SessionStartセッション開始・再開時不可
UserPromptSubmitユーザーがプロンプト送信時
PreToolUseツール呼び出し実行前可(allow/deny/ask)
PermissionRequestパーミッションダイアログ表示時
PostToolUseツール正常完了後不可(フィードバック可)
PostToolUseFailureツール失敗後不可
StopClaude応答完了時可(継続指示可)
SubagentStart / SubagentStopサブエージェント起動・終了時Stopのみ可
Notification通知送信時不可
TaskCompletedタスク完了マーク時
ConfigChange設定ファイル変更時
SessionEndセッション終了時不可

フックハンドラーは4種類存在する。シェルコマンド(type: "command")、HTTPエンドポイント(type: "http")、LLMプロンプト(type: "prompt")、そしてツールアクセスを持つエージェント(type: "agent")である。command型が最も汎用的で、stdinからJSON入力を受け取り、終了コードとstdoutで結果を返す。

セットアップと基本設定 ── 最初のhookを動かすまで

hooks設定は3つのスコープで配置できる。ユーザーグローバル(~/.claude/settings.json)、プロジェクト共有(.claude/settings.json)、プロジェクトローカル(.claude/settings.local.json)である。チーム共有のhooksはプロジェクト設定に、個人的な拡張はローカル設定に記述するのが推奨パターンである。

最もシンプルなhookとして、ファイル書き込み後にフォーマッタを実行する例を示す:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$(jq -r .tool_input.file_path)\""
          }
        ]
      }
    ]
  }
}

この設定は、ClaudeがEditまたはWriteツールを使用してファイルを変更するたびに、Prettierでフォーマットを適用する。matcherは正規表現で指定でき、Edit|Writeはいずれかのツール名にマッチする。MCP(Model Context Protocol)ツールもmcp__server__tool形式で指定可能であり、mcp__memory__.*のようなワイルドカードパターンも使える。

hookスクリプトには、以下の環境変数が利用できる:

  • $CLAUDE_PROJECT_DIR: プロジェクトルートの絶対パス
  • $CLAUDE_CODE_REMOTE: リモートWeb環境では"true"が設定される
  • $CLAUDE_ENV_FILE: SessionStartフックでのみ利用可能。環境変数の永続化用ファイルパス

stdinから受け取るJSON入力には、全イベント共通のフィールドとしてsession_idtranscript_path(会話ログのパス)、cwd(カレントディレクトリ)、permission_modehook_event_nameが含まれる。イベント固有のフィールド(例:PreToolUseのtool_nametool_input)はこれに加えて提供される。

/hooksコマンドで対話的なhooks管理メニューを開くこともできる。ここからhookの追加・削除・確認が可能である。

コード品質ゲートの実装 ── PreToolUseとPostToolUseの実践パターン

hooksの最も価値の高いユースケースは、AIが生成するコードの品質を自動的に担保する「品質ゲート」の構築である。筆者の経験では、AIコーディングツールの生産性向上の恩恵を最大化するには、品質チェックの自動化が不可欠であった。手動レビューのボトルネックを解消し、かつ品質を落とさないためのパターンを解説する。

パターン1: 危険なコマンドのブロック(PreToolUse)

PreToolUseフックは、ツール実行前に介入できる唯一のイベントであり、hookSpecificOutputを通じてallow(許可)、deny(拒否)、ask(ユーザー確認)の3段階の制御が可能である:

#!/bin/bash
# .claude/hooks/block-dangerous-commands.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# rm -rf、git push --force、DROP TABLEを検出
if echo "$COMMAND" | grep -qE 'rm\s+-rf|git\s+push\s+--force|DROP\s+TABLE'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by safety hook"
    }
  }'
else
  exit 0
fi

設定ファイルでの登録方法:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous-commands.sh"
          }
        ]
      }
    ]
  }
}

パターン2: ファイル変更後の自動リント(PostToolUse)

PostToolUseフックはツール実行後に発火する。ツール呼び出し自体はブロックできないが、decision: "block"でClaudeにフィードバックを返し、修正を促すことができる:

#!/bin/bash
# .claude/hooks/lint-after-edit.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# TypeScript/JavaScriptファイルのみ対象
case "$FILE_PATH" in
  *.ts|*.tsx|*.js|*.jsx)
    RESULT=$(npx eslint --no-eslintrc --config .eslintrc.json "$FILE_PATH" 2>&1)
    if [ $? -ne 0 ]; then
      jq -n --arg reason "ESLint errors found: $RESULT" '{
        decision: "block",
        reason: $reason
      }'
    else
      exit 0
    fi
    ;;
  *)
    exit 0
    ;;
esac

パターン3: LLMベースのコードレビュー(prompt型フック)

prompt型フックは、LLMを使った高度な品質チェックを実現する。Claude Haikuなどの高速モデルがJSON判定を返す:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Review the following code change for security vulnerabilities (XSS, SQL injection, command injection, path traversal). Context: $ARGUMENTS. Respond with {\"ok\": true} if safe, or {\"ok\": false, \"reason\": \"description of issue\"} if there are concerns.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

さらに高度なケースでは、agent型フックがファイルシステムを読み取りながら多段検証を行える。Read、Grep、Globツールにアクセスでき、最大50ターンの検証ループを実行する。

CI/CDパイプラインとの統合設計 ── SessionStart・Stop・非同期フック

Agentic DevOpsの自律的CI/CDパイプラインの実現において、Claude Code hooksは重要な統合ポイントとなる。ここでは、セッションライフサイクル全体を通じたCI/CD統合の設計パターンを解説する。

SessionStartフック: 開発コンテキストの自動注入

SessionStartフックは、セッション開始時に開発コンテキスト(未解決Issue、最近の変更、ブランチ情報など)を自動ロードする:

#!/bin/bash
# .claude/hooks/inject-dev-context.sh
INPUT=$(cat)
SOURCE=$(echo "$INPUT" | jq -r '.source')

# 新規セッションのみ
if [ "$SOURCE" != "startup" ]; then
  exit 0
fi

BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
RECENT_COMMITS=$(git log --oneline -5 2>/dev/null || echo "no commits")
PENDING_ISSUES=$(gh issue list --limit 5 --json title,number --jq '.[].title' 2>/dev/null || echo "no issues")

jq -n --arg ctx "Current branch: $BRANCH
Recent commits:
$RECENT_COMMITS
Open issues:
$PENDING_ISSUES" '{
  hookSpecificOutput: {
    hookEventName: "SessionStart",
    additionalContext: $ctx
  }
}'

加えて、SessionStartフックではCLAUDE_ENV_FILE環境変数を利用して、セッション全体で有効な環境変数を永続化できる:

#!/bin/bash
# .claude/hooks/setup-env.sh
if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
  echo 'export CI=true' >> "$CLAUDE_ENV_FILE"
fi
exit 0

Stopフック: タスク完了前の品質ゲート

Stopフックは、Claudeが応答を終了しようとするタイミングで発火し、decision: "block"で継続を強制できる。これをCI/CDの最終品質ゲートとして活用する:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Before allowing Claude to stop, verify: 1) All modified files pass linting. 2) No TODO comments were left in new code. 3) Test coverage did not decrease. Use Read and Grep tools to check the workspace. Context: $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

ただし、Stopフックには無限ループのリスクがある。入力JSONのstop_hook_activeフィールドがtrueの場合、すでにStopフックによる継続中であることを示すため、この値を確認して無限ループを防止する設計が必須である。

非同期フック: テストスイートの並行実行

長時間のテスト実行には非同期フック(async: true)が適している。Claudeの作業をブロックせずバックグラウンドでテストを実行し、完了後に結果をフィードバックする:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/async-test-runner.sh",
            "async": true,
            "timeout": 300
          }
        ]
      }
    ]
  }
}

非同期フックの結果は次の会話ターンで配信されるため、リアルタイムのブロッキングには使えない点に注意が必要である。品質ゲート(ブロッキング)と情報フィードバック(非同期)を適切に使い分けることが、ワークフロー設計の鍵となる。

実運用のベストプラクティスと注意点

筆者がhooksを実運用に導入する中で得た知見を、ベストプラクティスとしてまとめる。150人月規模のプロジェクト技術リードを務めた経験から言えるのは、自動化の成否は技術よりも運用設計で決まるということである。

設定のスコープ管理

hooksはプロジェクトの.claude/settings.jsonに定義すればgit管理下に置けるため、チーム全体で統一した品質ゲートを適用できる。一方、個人的な拡張(通知設定、エディタ連携等)は.claude/settings.local.jsonに分離する。エンタープライズ管理者は、マネージドポリシー設定を通じてhooksを組織全体に強制適用し、allowManagedHooksOnlyでユーザー定義フックを無効化できる。

セキュリティ上の考慮事項

hooksはシステムユーザーの完全な権限で実行される点は要注意である。エージェントオーケストレーションにおいても同様だが、以下のセキュリティプラクティスを厳守すべきである:

  • シェル変数は必ずダブルクオートで囲む("$VAR"
  • 入力データのバリデーションとサニタイズを行う
  • ファイルパスに..が含まれるパストラバーサルをチェックする
  • スクリプトは$CLAUDE_PROJECT_DIRからの絶対パスで参照する
  • .env.git/、秘密鍵などの機密ファイルへのアクセスを回避する

デバッグとトラブルシューティング

claude --debugでhookのマッチング状況、終了コード、出力を確認できる。Ctrl+Oで詳細モードに切り替えると、フック実行の進捗がトランスクリプトに表示される。hooksの設定変更はセッション開始時にスナップショットされるため、実行中の変更は即座に反映されない。変更後は/hooksメニューでレビューが必要となる。

パフォーマンスへの配慮

全てのマッチングフックは並列実行されるが、同期フックはClaude本体の実行をブロックする。SessionStartフックは毎回のセッションで発火するため、高速に保つことが重要である。長時間処理は非同期フックに分離し、品質ゲートとして必須のチェックのみを同期フックに配置する設計が推奨される。

HTTPフックとWebhook連携

HTTP型フック(type: "http")を使えば、外部CI/CDサービス(GitHub Actions、GitLab CI、Jenkins等)との連携が可能である。ヘッダーに環境変数を埋め込むことで認証にも対応する:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/code-quality",
            "timeout": 30,
            "headers": {
              "Authorization": "Bearer $CI_TOKEN"
            },
            "allowedEnvVars": ["CI_TOKEN"]
          }
        ]
      }
    ]
  }
}

HTTPフックはコネクション失敗やタイムアウト時にも実行を継続する(非ブロッキングエラー)ため、外部サービス障害に対する耐性がある。ツール呼び出しをブロックするには、2xxレスポンスのボディにdecision: "block"を含むJSONを返す必要がある。

FAQ

Claude Code hooksはどのバージョンから使えるか?

hooks機能は2025年中盤のClaude Codeアップデートで導入された。初期実装ではPreToolUse、PostToolUse、Notification、Stopの基本イベントがサポートされ、2026年にかけてSessionStart、PermissionRequest、SubagentStart/Stop、TaskCompleted、ConfigChange、WorktreeCreate/Remove、PreCompact、prompt型・agent型フックなどが段階的に追加されている。最新の公式ドキュメントで対応イベントを確認されたい。

hooksの設定ファイルをチームで共有するには?

プロジェクトルートの.claude/settings.jsonにhooksを定義し、gitリポジトリにコミットすることでチーム全体に適用できる。個人的なカスタマイズは.claude/settings.local.json(gitignore対象)に分離する。エンタープライズ環境では、マネージドポリシー設定で組織全体のhooksを管理できる。

hookが発火しない場合のデバッグ方法は?

claude --debugでデバッグモードを有効にし、hookのマッチング状況と実行結果を確認する。よくある原因は、matcherの正規表現がツール名と一致していない、スクリプトに実行権限がない(chmod +xが必要)、またはJSON出力にシェルプロファイルの出力が混入している(.bashrc等のprint文を確認)ケースである。

非同期フックとprompt型フックの使い分けは?

非同期フック(async: trueのcommand型)は、テスト実行やデプロイなど時間のかかるタスクをバックグラウンドで実行する場合に適している。prompt型フックは、LLMベースの判定(セキュリティチェック、コード品質評価等)に適しており、高速モデルによる30秒以内の単一ターン判定を行う。複雑な検証にはagent型フック(Read/Grep/Globツール使用可、最大50ターン)を検討されたい。

PreToolUseのpermissionDecisionでaskを返すとどうなるか?

permissionDecision: "ask"は、ユーザーに確認ダイアログを表示する。allowはパーミッションシステムをバイパスして即座に許可、denyはClaude本体に拒否理由を返す。askは「自動判定は難しいがユーザーに判断を委ねたい」場合に有用である。なお、updatedInputフィールドでツール入力を変更してからaskすることも可能である。

参考文献