
Claude Code Hooks: 本番環境で使えるサンプル付き開発者向け完全ガイド
Claude Code はコードを書くのが非常に得意ですが、依然として確率的なシステムです。ファイル編集のたびに Prettier を実行するよう指示することもできますし、その指示を CLAUDE.md に記載することもできます。それでも時々、単純に…忘れてしまうことがあります。Claude Code hooks は、Claude が実行するあらゆるアクションの前・最中・後に何が起こるかを、決定的かつ確実に制御できるようにすることで、この問題を解決します。
私は過去数ヶ月にわたり、数十のプロジェクトで hooks を設定してきましたが、hooks はいつの間にか私の Claude Code セットアップにおいて最も重要な要素になっています。このガイドでは、基礎から、どのプロジェクトにも今すぐ導入できる本番環境対応のスターターキットまで、すべてを解説します。Cursor や Copilot といったツールと併用した Claude Code を使ったことがある方なら、カスタマイズの価値はすでにご存知でしょう。hooks はそれをさらに一歩進めたものです。
Claude Code Hooksとは(なぜ気にすべきなのか)?
Claude Code Hooksとは、Claude Codeのライフサイクルにおける特定のポイントで自動的に実行される、ユーザー定義のシェルコマンド、HTTPエンドポイント、またはLLMプロンプトのことです。Anthropicの公式ドキュメントによると、Claudeが無視する可能性のあるプロンプトの指示とは異なり、Hooksは毎回決定的に発火するため、フォーマット、セキュリティ、通知、ワークフローの自動化に対して確実に制御できます。
確率論的な問題
CLAUDE.md の指示について言えるのは、それは提案であって契約ではないということです。プロジェクトのコンテキストに「TypeScript ファイルを編集した後は必ず npx prettier --write を実行すること」と書いておけば、Claude はほとんどの場合それに従います。しかし「ほとんどの場合」では不十分なのです。チーム全体でコードのフォーマットを強制したいとき、本番環境へのプッシュをブロックしたいとき、セキュリティ監査のためにすべてのシェルコマンドをログに記録したいときなどは特にそうです。
これはあらゆる AI コーディングツールに共通する根本的なジレンマです。Claude は言語モデルであり、確率に基づいて動作します。コンテキストエンジニアリングで行動を促すことはできても、それを保証することはできません。
フックがこれをどう解決するか
フックはLLMを完全に迂回します。フックとは、特定のライフサイクルイベントで発火するシェルスクリプト、HTTP呼び出し、またはAI評価のことです。ツールが実行される前(PreToolUse)、完了した後(PostToolUse)、通知が表示されたとき、セッション開始時、あるいはClaudeが停止したときに発火します。Gitフックのようなものですが、それがAIコーディングアシスタント向けになったものだと考えてください。
フックには4つの種類があります。command(シェルスクリプト)、HTTP(WebhookのPOSTリクエスト)、prompt(単発のClaudeによるyes/no評価)、そしてagent(ツールアクセス権を持つサブエージェントを起動する)です。それぞれについて後で詳しく説明しますが、commandフックで必要なことの約90%はカバーできます。
Claude Code フックの仕組み:ライフサイクルフロー
Claude Code のフックは、定義されたライフサイクルに沿って実行されます。イベントが発生し(例:PreToolUse)、マッチャーがそのフックを適用するかどうかを判定し、フックスクリプトが実行されて標準入力(stdin)で JSON を受け取り、終了コードがその後の動作を決定します。終了コード 0 は処理の続行を、終了コード 2 はアクションのブロックを意味します。このフローは、使用するフックの種類に関係なく共通しています。
イベント -> マッチャー -> フック -> 終了コード(4ステップの流れ)
すべてのフック実行は次のように動作します。
1. EVENT FIRES e.g., PreToolUse(Write)
|
2. MATCHER CHECKS Does "Write" match the hook's matcher pattern?
|
3. HOOK EXECUTES Shell script runs, receives JSON via stdin
|
4. EXIT CODE DECIDES 0 = proceed | 2 = block | other = error標準入力(stdin)に届く JSON には、イベントに関するすべての情報が含まれています。tool_name、tool_input(ファイルパス、コンテンツ、コマンド)、そしてセッションのメタデータです。スクリプトはこの JSON を読み取り、必要なロジックを実行して、適切なコードで終了します。
PreToolUse フックでは、終了コード 2 が強力です。このコードはアクションを完全にブロックし、stdout に出力したメッセージをフィードバックとして Claude に送り返します。Claude はそのメッセージを確認し、アプローチを調整できます。
設定のスコープ:ユーザー、プロジェクト、ローカル
フックは 3 つのレベルで settings.json に記述します:
| スコープ | ファイル | Git にコミットする? | ユースケース |
|---|---|---|---|
| ユーザー | ~/.claude/settings.json | いいえ | 個人のデフォルト設定(通知、フォーマット設定) |
| プロジェクト | .claude/settings.json | はい | チームで共有するフック(ファイル保護、テストランナー、リンティング) |
| ローカル | .claude/settings.local.json | いいえ(gitignore 対象) | このプロジェクト向けの個人用オーバーライド |
プロジェクト設定はチームにとって最も有用です。フックを .claude/settings.json に記述してコミットすれば、チームのすべての開発者が同じガードレールを自動的に適用できます。
if フィールド:きめ細かなフィルタリング
Claude Code v2.1.85 以降、フックは if フィールドをサポートしており、ツール名だけでなくツールの引数でもフィルタリングできるようになりました。Anthropic フックリファレンスに記載されている通り、これにより、すべての Bash 呼び出しで発火するのではなく、git push に一致する Bash コマンドでのみトリガーされるフックを書くことができます。
{
"matcher": "Bash",
"if": "tool_input.command matches 'git push'",
"hooks": [{ "type": "command", "command": "./scripts/check-branch.sh" }]
}これは大きな改善でした。if が登場する前は、広くマッチしすぎる(すべての Bash コマンドに反応する)か、スクリプト内部でフィルタリングを行う(煩雑になる)かのどちらかでした。
Claude Code の全フックイベント:クイックリファレンステーブル
Claude Code は、公式フックリファレンスや Claude Code の変更履歴に記載されているように、ライフサイクル全体で 20 以上のフックイベントを提供しています。最もよく使われるのは PreToolUse、PostToolUse、Notification、Stop ですが、ConfigChange や FileChanged といった新しいイベントによって、より高度な自動化パターンが可能になります。
以下が完全なリファレンスです:
| イベント | 発火タイミング | ブロック可能? | 一般的なユースケース |
|---|---|---|---|
| PreToolUse | ツール実行前 | 可能(exit 2) | 危険なコマンドのブロック、ファイルの保護 |
| PostToolUse | ツール完了後 | 不可 | 自動フォーマット、テスト実行、アクションのログ記録 |
| Notification | Claude が通知を送信したとき | 不可 | デスクトップアラート、Slack メッセージ |
| Stop | Claude が応答を完了したとき | 不可 | クリーンアップ、サマリーの生成 |
| SessionStart | セッション初期化時 | 不可 | コンテキストの注入、環境の設定 |
| UserPromptSubmit | ユーザーがプロンプトを送信したとき | 可能(exit 2) | 入力検証、コンテンツフィルタリング |
| PreCompact | コンテキスト圧縮前 | 不可 | メモリが削減される前の状態保存 |
| PostCompact | コンテキスト圧縮後 | 不可 | 重要なコンテキストの再注入 |
| ConfigChange | 設定が変更されたとき | 不可 | 環境変数のホットリロード |
| FileChanged | 監視対象のファイルが変更されたとき | 不可 | リビルドのトリガー、キャッシュの無効化 |
| TaskCreated | 新しいタスクが生成されたとき | 不可 | タスクの追跡、リソースの割り当て |
| PermissionDenied | 権限チェックが失敗したとき | 不可 | 監査ログ、ブロックされたアクションのアラート |
| WorktreeCreate | 新しい Git worktree が作成されたとき | 不可 | worktree 固有の設定の初期化 |
| SubagentStart | サブエージェントが起動したとき | 不可 | サブエージェントのアクティビティ監視 |
| SubagentStop | サブエージェントが完了したとき | 不可 | サブエージェントの出力検証 |
プロのヒント: フックの 80% は PreToolUse と PostToolUse で占められます。次に便利なのが SessionStart で、毎回のセッション開始時に Claude が必要とするプロジェクトのコンテキストを注入するのに最適です。
Claude Code の4種類のフックタイプを解説
Claude Code は4種類のフックハンドラをサポートしています。コマンドフックはシェルスクリプトを実行し、HTTP フックは URL に POST し、プロンプトフックは Claude に yes/no の質問を投げかけ、エージェントフックはツールアクセス権を持つサブエージェントを起動します。私たちの経験では、ユースケースの90%はコマンドフックで対応できます。外部連携には HTTP を、AI の判断が必要な繊細な決定にはプロンプトフックとエージェントフックを使用してください。
| タイプ | 速度 | 複雑度 | 最適な用途 | 例 |
|---|---|---|---|---|
| コマンド | 高速 | 低 | 整形、ブロック、ログ記録 | ファイル編集後に Prettier を実行 |
| HTTP | 中程度 | 中程度 | 外部サービス、Webhook | 完了時に Slack に POST |
| プロンプト | 低速 | 中程度 | 主観的な判断 | "このコードは実行しても安全ですか?" |
| エージェント | 最も低速 | 高 | ファイルを認識する複雑な検証 | 新しいコードがプロジェクトのパターンに従っているか確認 |
コマンドフック(主力)
コマンドフックはシェルコマンドを実行し、その終了コードを使って結果を判定します。イベントの JSON データは標準入力(stdin)経由で受け取ります。
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.command' | grep -q 'rm -rf /' && exit 2 || exit 0"
}]
}]
}
}フォーマット、ファイル保護、通知、そしてほとんどの自動化で使うのがこれです。高速で、シンプルで、挙動が予測しやすいのが特徴です。
HTTPフック(外部連携)
HTTPフックは、イベントのJSONをボディとしてURLにPOSTリクエストを送信します。レスポンスのステータスコードによって結果が決まります(200 = 続行、403 = ブロック)。
{
"hooks": {
"Stop": [{
"matcher": "",
"hooks": [{
"type": "http",
"url": "https://your-api.com/claude-webhook"
}]
}]
}
}Slack、Discord、PagerDuty、またはカスタムダッシュボードへのイベント送信に最適です。また、ツール実行を許可する前に外部ポリシーエンジンに問い合わせる用途にも利用できます。
Prompt Hooks(AIによる意思決定)
Prompt Hooks はイベントデータを Claude 自身に渡し、1 ターンの yes/no 評価を行います。Claude は "decision": "allow" または "decision": "block" と、その理由を含む JSON レスポンスを返します。
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "prompt",
"prompt": "Is this bash command safe to run in a production environment? Consider: does it modify system files, delete data, or access sensitive credentials?"
}]
}]
}
}これらは控えめに使用してください。フックの実行ごとに完全な LLM コールが 1 回発生するため、レイテンシとコストが増加します。しかし、「このデータベース移行は破壊的ではないか?」といった真に主観的な安全性チェックには、これに勝るものはありません。Claude Code のモデル切り替えに興味がある方のために補足すると、Prompt Hooks で使用されるモデルは現在のセッションのモデルに従います。
エージェントフック(ツールによる検証支援)
エージェントフックは、Read、Grep、Glob ツールへのアクセス権を持つサブエージェントを起動します。サブエージェントは判断を下す前にファイルを検査できます。
{
"hooks": {
"PreToolUse": [{
"matcher": "Write",
"hooks": [{
"type": "agent",
"prompt": "Check if the file being written follows the project's naming conventions and import patterns. Read .claude/CONVENTIONS.md for the rules."
}]
}]
}
}これは最も強力なフックタイプですが、最も遅くもあります。適切な判断のためにファイルのコンテキストが必要な、重要なチェックに限定して使用してください。
7つの本番環境対応 Claude Code フック例(コピー&ペーストですぐ使える)
最も有用な Claude Code フックには、ファイル編集後の Prettier や Black による自動フォーマット、保護対象ファイルへの書き込みブロック、タスク完了時のデスクトップ通知、セッション開始時のプロジェクトコンテキスト注入、コード変更後のテスト実行、ブランチ保護の強制、すべてのツール使用状況の監査が含まれます。私は過去3ヶ月間、あらゆるプロジェクトでこれらのバリエーションを運用してきました。
以下の各例は、.claude/settings.json にそのまま配置できる完全な settings.json スニペットです。awesome-claude-code のようなコミュニティコレクションには、さらに多くのパターンが揃っています。
1. 保存時の自動フォーマット
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path // .tool_input.file' /dev/stdin); case \"$FILE\" in *.ts|*.tsx|*.js|*.jsx) npx prettier --write \"$FILE\" 2>/dev/null;; *.py) black \"$FILE\" 2>/dev/null;; esac; exit 0"
}]
}]
}
}これは Write または Edit のたびに発火し、stdin の JSON からファイルパスを抽出して、適切なフォーマッターを実行します。末尾の exit 0 により、このフックが処理をブロックしないことが保証されます。フォーマットの失敗で Claude を止めるべきではないからです。
上級者向けのヒント: 複数の言語を扱う場合は、*.go には gofmt、*.rs には rustfmt を追加してください。
2. 保護されたファイルへの書き込みをブロックする
{
"hooks": {
"PreToolUse": [{
"matcher": "Write|Edit",
"if": "tool_input.file_path matches '(\\.env|\\.env\\.local|package-lock\\.json|yarn\\.lock|pnpm-lock\\.yaml)'",
"hooks": [{
"type": "command",
"command": "echo '{\"message\": \"BLOCKED: This file is protected. Edit it manually.\"}' && exit 2"
}]
}]
}
}終了コード 2 はアクションをブロックし、JSON メッセージを Claude に送り返します。Claude はフィードバックを確認して挙動を調整します。通常、ファイルを変更しようとしたことを伝え、手動で対応するよう求めてきます。if フィールドにより、すべての Write でこれが発動するのを防ぎます。
3. 完了時のデスクトップ通知
{
"hooks": {
"Notification": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "MSG=$(jq -r '.message // \"Claude Code task finished\"' /dev/stdin); if [ \"$(uname)\" = 'Darwin' ]; then osascript -e \"display notification \\\"$MSG\\\" with title \\\"Claude Code\\\"\"; else notify-send 'Claude Code' \"$MSG\"; fi; exit 0"
}]
}]
}
}macOS(osascript)と Linux(notify-send)で動作します。空のマッチャーはすべての通知で発火することを意味します。時間のかかるタスクを開始して別のウィンドウに切り替えたときに、これは本当に便利です。
4. セッション開始時のコンテキスト注入
{
"hooks": {
"SessionStart": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "echo '{\"message\": \"Project: '\"$(basename $(pwd))\"' | Branch: '\"$(git branch --show-current 2>/dev/null || echo none)\"' | Last commit: '\"$(git log --oneline -1 2>/dev/null || echo none)\"'\"}'; exit 0"
}]
}]
}
}これにより、現在のプロジェクト名、Gitブランチ、直近のコミットがすべてのセッションに注入されます。Claudeはこのコンテキストを自動的に受け取るため、現在どのブランチにいるかを伝える必要はありません。
5. コード変更後にテストを自動実行
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"if": "tool_input.file_path matches '\\.(ts|tsx|js|jsx|py)$'",
"hooks": [{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path' /dev/stdin); TEST_FILE=$(echo \"$FILE\" | sed 's/\\.[^.]*$/.test&/'); if [ -f \"$TEST_FILE\" ]; then npx jest \"$TEST_FILE\" --no-coverage 2>&1 | tail -5; fi; exit 0",
"timeout": 30000
}]
}]
}
}対応するテストファイルが存在する場合、Claude がソースを編集した後に自動的に実行されます。tail -5 により出力が簡潔に保たれ、タイムアウトによってテストスイートの暴走を防ぎます。これは AI を活用したコードレビューのワークフローとも相性が抜群です。
6. ブランチ保護の適用(上級編)
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"if": "tool_input.command matches 'git push.*(main|master|production)'",
"hooks": [{
"type": "command",
"command": "echo '{\"message\": \"BLOCKED: Direct push to protected branch. Use a feature branch and open a PR.\"}' && exit 2"
}]
}]
}
}これにより、main、master、または本番用ブランチを対象とする git push はすべてブロックされます。Claude はそのフィードバックを受け取り、代わりにフィーチャーブランチを作成することを提案します。
7. セキュリティ監査ログ(上級編)
{
"hooks": {
"PostToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "INPUT=$(cat /dev/stdin); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command'); echo \"[$(date -u +%Y-%m-%dT%H:%M:%SZ)] BASH: $CMD\" >> .claude/audit.log; exit 0"
}]
}]
}
}Claude が実行するすべての Bash コマンドを、UTC タイムスタンプ付きで監査ファイルに記録します。セキュリティレビューや、セッション中に Claude が実際に何を行ったかを把握するのに非常に役立ちます。.claude/audit.log は .gitignore に追加しておきましょう。
Hooks vs MCP vs Skills vs CLAUDE.md:どれをいつ使うか
Hooks は、必ず実行されなければならない決定的な自動化(フォーマット、ブロック、通知)に使います。MCP は、Claude に外部のツールやデータへのアクセスを与えるために使います。Skills は、再利用可能なプロンプトのパッケージに使います。CLAUDE.md は、振る舞いに関するガイダンスやプロジェクトのコンテキストに使います。Hooks は保証されていますが、それ以外はすべて確率的です。これこそが最も重要な違いであり、チームにアドバイスする際に私が何度も立ち返るポイントです。
意思決定マトリックス
| 仕組み | 決定的か? | 実行タイミング | 最適な用途 | 例 |
|---|---|---|---|---|
| フック | はい | ライフサイクルイベント時に自動実行 | 強制、自動化、通知 | 自動フォーマット、ファイル書き込みのブロック |
| MCP | いいえ(Claudeが判断) | ClaudeがMCPツールを呼び出したとき | 新しい機能、外部データへのアクセス | データベースへのクエリ、Notionの検索 |
| スキル | いいえ(ユーザーがトリガー) | ユーザーがスラッシュコマンドを実行したとき | 再利用可能な指示セット | コードレビューワークフロー用の /review |
| CLAUDE.md | いいえ(ガイダンス) | セッション開始時に読み込まれる | プロジェクトの背景、コーディング規約 | 「Tailwindを使用し、すべての新しいコードにテストを書く」 |
MCPについて詳しくは、MCPガイドをご覧ください。Cursorから移行してきた場合、CursorのルールシステムはCLAUDE.mdにほぼ相当しますが、Cursorにはフックのような機能はありません。
重複する場合(そして選び方)
私が使っているフローチャートはこれです:
- 「これは例外なく、毎回必ず実行される必要があるか?」 → フック。コードのフォーマット、保護されたファイルのブロック、通知の送信。曖昧さはゼロ。
- 「Claude にまだ備わっていない新しい機能が必要か?」 → MCP サーバー。データベースへのアクセス、API の呼び出し、外部ドキュメントの検索。
- 「特定のワークフロー向けの再利用可能な指示が欲しいか?」 → スキル(スラッシュコマンド)。コードレビューのテンプレート、デプロイのチェックリスト。
- 「このプロジェクトでの Claude の振る舞いを形作りたいか?」 → CLAUDE.md。コーディング規約、アーキテクチャの判断、推奨ライブラリ。
境界を明確にする実際の例:
- 「常に Prettier でフォーマットする」= フック(毎回必ず実行される必要がある)
- CLAUDE.md に「フォーマットには Prettier を使う」= ガイダンス(Claude は忘れるかもしれない)
- 「社内のドキュメントを検索する」= MCP(新しい機能)
- 「コードレビュー時にスタイルガイドに従う」= スキル または CLAUDE.md
Anthropic のプラグイン発表で説明されているように、フックは MCP やスキルも含むより広範なプラグインエコシステムの一部です。これらは競合するためではなく、相互に補完し合うように設計されています。
スターターキット:あらゆるプロジェクトにそのまま使える Claude Code フック設定
Claude Code 向けのスターターフック設定には、ファイル編集時の自動フォーマット、タスク完了時の通知、機密ファイルの保護、セッションコンテキストの注入、そしてクリーンアップ用の停止フックを含めるべきです。これは私が新規プロジェクトのたびにそのまま投入している設定そのもので、スタックに合わせて調整は加えますが、構造は変わりません。
設定
{
"hooks": {
"SessionStart": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "echo '{\"message\": \"Project: '\"$(basename $(pwd))\"' | Branch: '\"$(git branch --show-current 2>/dev/null)\"' | Node: '\"$(node -v 2>/dev/null)\"'\"}'; exit 0"
}]
}],
"PreToolUse": [{
"matcher": "Write|Edit",
"if": "tool_input.file_path matches '(\\.env|\\.env\\..+|.*lock\\.json|.*lock\\.yaml)'",
"hooks": [{
"type": "command",
"command": "echo '{\"message\": \"Protected file. Edit manually.\"}' && exit 2"
}]
}],
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path // .tool_input.file' /dev/stdin); case \"$FILE\" in *.ts|*.tsx|*.js|*.jsx) npx prettier --write \"$FILE\" 2>/dev/null;; *.py) black \"$FILE\" 2>/dev/null;; *.go) gofmt -w \"$FILE\" 2>/dev/null;; esac; exit 0"
}]
}],
"Notification": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "MSG=$(jq -r '.message // \"Done\"' /dev/stdin); osascript -e \"display notification \\\"$MSG\\\" with title \\\"Claude Code\\\"\" 2>/dev/null || notify-send 'Claude Code' \"$MSG\" 2>/dev/null; exit 0"
}]
}],
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "echo '[STOP] '\"$(date +%H:%M:%S)\"'' >> .claude/session.log; exit 0"
}]
}]
}
}スタックに合わせてカスタマイズする方法
| スタック | 整形コマンド | テストコマンド | 監視対象の拡張子 |
|---|---|---|---|
| Node/TypeScript | npx prettier --write | npx jest --no-coverage | .ts, .tsx, .js, .jsx |
| Python | black | pytest -x | .py |
| Go | gofmt -w | go test ./... | .go |
| Rust | rustfmt | cargo test | .rs |
上記の設定で、整形コマンドとテストコマンドを自分のスタックに合わせて入れ替えてください。構造はそのまま変わりません。
フックが動作しているか確認する
フックが有効になっていることを確認する方法は3つあります:
/hooksコマンド — Claude Code で/hooksと入力すると、登録されているすべてのフック、そのマッチャー、ステータスを確認できます。- トランスクリプトの確認 — フックが発火した後、セッションのトランスクリプトを確認してください。フックの実行結果と終了コードが表示されます。
- クイックトグル — settings.json に
"disableAllHooks": trueを追加すると、設定を削除せずに一時的にすべてのフックを無効化できます。削除する(またはfalseに設定する)と再有効化されます。
CI/CD統合:ヘッドレスモードでのClaude Codeフック
Claude Codeフックはヘッドレスモード(claude -p)でも動作しますが、いくつかの違いがあります。Notificationフックは引き続き発火しますが、デスクトップアラートの代わりにログ出力へリダイレクトする必要があります。終了コード2を返すPreToolUseフックを使用すると、ヘッドレスセッションを一時停止して人間のレビューを求めることができます。GitHub Actionsではanthropics/claude-code-action@v1を使用し、フックと組み合わせて自動化ワークフローを構築します。
ヘッドレスモードの動作
| フックイベント | 対話モード | ヘッドレスモード (-p) | CI での推奨事項 |
|---|---|---|---|
| PreToolUse (exit 2) | ブロックしてメッセージを表示 | --resume 用に一時停止 | 必須の人間による承認に使用 |
| PostToolUse | 通常通り実行 | 通常通り実行 | フォーマッターやロガーを維持 |
| Notification | デスクトップ通知 | 引き続き発火 (UI なし) | ログファイルまたは Slack Webフックへリダイレクト |
| Stop | クリーンアップを実行 | クリーンアップを実行 | CI 成果物の収集に最適 |
| SessionStart | コンテキストを注入 | コンテキストを注入 | CI 環境変数を注入 |
ヘッドレスモードにおける大きな驚き:終了コード 2 で終了する PreToolUse フックは、単にサイレントに失敗するわけではありません。セッションを一時停止し、--resume で再開できるため、CI パイプラインにおけるヒューマンインザループのパターンを実現できます。
GitHub Actions 連携
以下は、フックを備えた Claude Code を使用する最小限の GitHub Actions ワークフローです。公式 GitHub Actions ガイドに記載されているとおりです。
- name: Run Claude Code
uses: anthropics/claude-code-action@v1
with:
prompt: "Review this PR and suggest improvements"
allowed_tools: "Read,Grep,Glob"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}.claude/settings.json のフックはリポジトリとともに持ち運ばれるため、CI 上でもローカルとまったく同じように実行されます。ただし、(osascript のような) デスクトップ固有のツールに依存するフックには、フォールバックや条件分岐を設けておいてください。
チームでのフック管理
チームでうまく機能するパターン:
.claude/settings.json(コミット済み)、チーム共有のフック: ファイル保護、フォーマッター、ブランチ保護。全員に適用されます。.claude/settings.local.json(gitignore 済み)、個人用フック: 通知設定、カスタムログ、実験的なフック。~/.claude/settings.json(ユーザーグローバル)、全プロジェクト共通のデフォルト: 通知スタイル、個人のフォーマット設定。
これは .editorconfig (コミット済み) とローカルの IDE 設定 (個人用) の仕組みと同様です。Angelo Lima の CI/CD ガイドでも指摘されているように、共有フックを標準化しているチームでは、Claude Code における「自分のマシンでは動く」という問題が少なくなります。
Claude Code フックのトラブルシューティングとよくある間違い
Claude Code フックでよくある問題には、フックが発火しない(マッチャーのスペルと settings.json の場所を確認する)、フックは実行されるがブロックしない(終了コードが誤っている。1 ではなく 2 を使う)、無限ループ(Stop フックが自分自身をトリガーしてしまう)、起動が遅い(同期フックが多すぎる)などがあります。私が最もよく見かける間違いは終了コードの混同で、開発者が exit 2 とすべきところで exit 1 を使ってしまうというものです。
フックが起動しない
症状: フックを追加したのに、イベントが発生しても何も起こらない。
対処法:
- Matcher のタイプミス、Matcher は大文字と小文字を区別します。
"write"はWriteツールにマッチしません。正確なツール名は/hooksで確認してください。 - 設定ファイルの指定ミス、
~/.claude/settings.jsonに記述したフックは、プロジェクトスコープの/hooks出力には表示されません。プロジェクトルートの.claude/settings.jsonを試してください。 - JSON の構文エラー、余分なカンマや括弧の欠落があると、フック設定全体がサイレントに無効化されます。settings.json を
jq .に通して検証してください。 disableAllHooks: true、誰か(または以前のデバッグセッション)がこのフラグをオンにしたままにしていないか確認してください。
フックは実行されるがブロックされない
症状: PreToolUse フックは実行されるのに、アクションがそのまま続行されてしまいます。
修正方法:
- 終了コードが間違っている、終了コード 1 は「エラー」(フックの失敗)を意味し、「ブロック」を意味するものではありません。アクションをブロックするには
exit 2を使用してください。公式ドキュメントにも記載されているように、これはほぼ全員がつまずくポイントです。 - stdout への JSON 出力がない、ブロック用フックでは、アクションがブロックされた理由を Claude に伝えるために JSON メッセージを出力してください:
echo '{"message": "Blocked: reason"}'
無限ループ
症状: Claudeが同じアクションを繰り返し再試行する、またはマシンが不審に熱くなる。
対処法:
- Stopフックがアクションを引き起こしている、Stopフックがファイルを書き込んだり、Claudeの応答を引き起こすコマンドを実行したりしている場合、ループが発生しています。Stopフックはログ記録、通知、クリーンアップなど、受動的な処理のみを行うべきです。
- PostToolUseフックが編集を引き起こしている、ファイルを変更するPostToolUseフックは、別のPostToolUseイベントをトリガーします。特定のマッチャーや
ifフィールドを使用してこれを防いでください。
パフォーマンスの問題
症状: Claude の起動やツール実行が明らかに遅くなる。
対処法:
- SessionStart フックが多すぎる — 各フックは起動時に同期的に実行されます。それぞれを軽量に保ってください(各 1 秒未満)。
- ホットパスに重いスクリプトがある — PreToolUse と PostToolUse のフックは頻繁に発火します。スクリプトがネットワークリクエストや重い処理を行う場合は、
timeoutフィールド(ミリ秒)を追加し、代わりに HTTP フックにすべきかどうかを検討してください。 - キャッシュがない — 同じこと(「これは保護ブランチか?」など)を繰り返しチェックしている場合は、フック呼び出しのたびに Git コマンドを実行するのではなく、結果を一時ファイルにキャッシュしてください。
よくある質問
Claude Codeフックとは?その仕組み
Claude Codeフックは、Claude Codeセッション中の特定のライフサイクルイベントで実行される、ユーザー定義の自動化スクリプトです。settings.jsonでマッチャーパターンとハンドラー(シェルコマンド、HTTPエンドポイント、プロンプト、またはエージェント)を設定します。一致するイベントが発生すると、フックが自動的に実行され、終了コードを使用して結果を制御します。
Claude Code の settings.json でフックを設定するには?
"hooks" オブジェクトを、次の3つの設定場所のいずれかに追加します。~/.claude/settings.json(ユーザー全体)、.claude/settings.json(プロジェクト共有)、または .claude/settings.local.json(プロジェクト個人)です。各イベントタイプはフック定義の配列にマップされ、そこには matcher、任意の if フィールド、そして type と command または url を持つハンドラオブジェクトを含む hooks 配列が含まれます。
PreToolUse フックと PostToolUse フックの違いは何ですか?
PreToolUse はツールの実行前に発火し、終了コード 2 を返すことでその実行をブロックできます。PostToolUse は実行完了後に発火し、フォーマットやテスト、ロギングに役立ちます。PreToolUse は防止やゲート制御のためのもの、PostToolUse は検証やクリーンアップのためのものです。どちらもツール名と入力内容を JSON 形式で標準入力(stdin)経由で受け取ります。
Claude Code のフックは危険なコマンドをブロックできますか?
はい。PreToolUse フックは終了コード 2 を返すことで、あらゆるツールの実行をブロックできます。機密ファイルへの書き込みを保護したり、rm -rf や git push main のような危険なパターンに一致するシェルコマンドをブロックしたり、本番データベースへのアクセスを防止したりできます。ブロック時のメッセージはフィードバックとして Claude に返送されるため、Claude はアプローチを調整できます。
Claude Codeで利用可能なフックイベントは?
Claude Codeは15以上のイベントを提供しています。ツール実行のためのPreToolUseとPostToolUse、アラートのためのNotification、セッション終了のためのStop、初期化のためのSessionStart、入力フィルタリングのためのUserPromptSubmit、コンテキスト管理のためのPreCompactとPostCompact、そしてConfigChange、FileChanged、TaskCreated、PermissionDeniedなどの新しいイベントがあります。リファレンステーブルの全容については、上記のフックイベントのセクションを参照してください。
フックはMCPツールやSkillsとどう違うのですか?
フックは決定的であり、Claudeの判断に関係なく、一致するイベントに対して必ず発火します。MCPツールはClaudeの機能を拡張します(データベースアクセス、API呼び出し)が、いつ使用するかはClaudeが選択します。Skillsはスラッシュコマンドで呼び出される再利用可能な指示パッケージです。CLAUDE.mdは行動上のガイダンスを提供します。毎回必ず実行する必要がある場合はフックを、Claudeに新しい機能が必要な場合はMCPを使用してください。
Claude Code のフックはヘッドレスモードで動作しますか?
はい。ただし注意点があります。フックはヘッドレスモード(claude -p)でも通常どおり発火しますが、macOS の通知のようなデスクトップ固有のフックにはフォールバックが必要です。重要なのは、終了コード 2 で終了する PreToolUse フックは、--resume を介して人間の承認のためにヘッドレスセッションを一時停止できるという点です。これにより、特定の操作に手動での承認を必要とするヒューマンインザループの CI/CD パイプラインが実現します。
フックは何個までなら多すぎない?フックはClaude Codeを遅くする?
厳密な上限はありませんが、同期型のフックはそれぞれレイテンシを増加させます。SessionStartフックは起動時に実行されるため、高速に保ってください(各1秒未満が目安です)。PreToolUseフックとPostToolUseフックは、該当するツール呼び出しのたびに発火するため、ここで重いスクリプトを実行すると遅延が急速に累積します。フックの合計を10〜15個以内に抑え、ifフィールドでスコープを絞り、timeout値を設定してスクリプトの暴走を防ぐことをおすすめします。
フックを使って Prettier や Black でコードを自動フォーマットできますか?
はい、これは最も人気のあるフックのユースケースです。Write|Edit にマッチする PostToolUse フックを作成し、stdin の JSON からファイルパスを抽出して、ファイルの拡張子に応じて適切なフォーマッターを実行します。TypeScript、JavaScript、Python ファイルに対応した、コピー&ペーストですぐに使える完全な設定については、本番環境の例セクションの例1を参照してください。
Claude Codeのフックは安全ですか?セキュリティリスクは何ですか?
フックはユーザーの完全な権限で実行され、サンドボックスはありません。悪意のあるフックは、SSH鍵の読み取り、ファイルの削除、データの外部送信を行う可能性があります。信頼できるソースのフックのみを使用し、共有された.claude/settings.jsonはプロジェクトに組み込む前に必ず内容を確認してください。共有すべきでない個人的なフックには.claude/settings.local.jsonを使用してください。より広範なAI安全性のパターンについては、LLMガードレールガイドをご覧ください。