ankuro.dev
← ブログ一覧に戻る
【CCA Foundations対策 / Claude Code実践編 #4】HooksでClaude Codeを自動化する——PreToolUse・PostToolUseの設計と実装
2026-04-03#Claude Code#AI#Hooks#自動化#Claude Certified Architect

【CCA Foundations対策 / Claude Code実践編 #4】HooksでClaude Codeを自動化する——PreToolUse・PostToolUseの設計と実装

Anthropic Academyの「Claude Code in Action」コースをもとに解説しています。

Claude CodeのHooksはツール実行の前後に独自の処理を割り込ませる仕組みだ。プロンプトで「こうしてほしい」と伝えるより確実にルールを強制できる。今回はHooksの設計・実装・実用例を解説する。

この記事でわかること:

  • PreToolUse / PostToolUseの役割と制御フロー
  • exit code 0 / 2 によるブロック制御とstderrフィードバック
  • .envファイル保護フックの実装例
  • 絶対パス推奨の理由とセキュリティ考慮
  • TypeScript型チェックフック・クエリ重複防止フックの設計
  • PostToolUseフックがprompt instructionより確実な理由

Hooksの仕組み

通常のClaude Codeのフローは次の通り。

ユーザーの入力 → Claude(ツール決定) → ツール実行 → 結果 → Claude(最終回答)

Hooksはこの流れに割り込む。

ユーザーの入力 → Claude(ツール決定)
  → [PreToolUse Hook] → ツール実行 → [PostToolUse Hook]
  → 結果 → Claude(最終回答)

PreToolUse:ツール実行前に動く。ブロックが可能。
PostToolUse:ツール実行後に動く。ブロックはできないが、フィードバックをClaudeに渡せる。


設定ファイルの場所

Hooksは3箇所の設定ファイルに書ける。

ファイル スコープ
~/.claude/settings.json 全プロジェクト共通
.claude/settings.json プロジェクト共有(チームに適用)
.claude/settings.local.json 個人専用(コミットしない)

/hooks コマンドをClaude Code内で実行すると、インタラクティブにHooksを設定できる。


exit codeによる制御

HookスクリプトはJSONを標準入力で受け取り、exit codeで結果をClaude Codeに伝える。

exit code 意味
0 問題なし。ツール実行を許可(PostToolUseでは続行)
2 ブロック(PreToolUseのみ有効)

exit code 2で終了すると、stderrに書き出したメッセージがClaudeへのフィードバックとして送られる。Claudeはそのメッセージをもとになぜブロックされたかを理解して対処する。


.envファイル保護フックの実装

PreToolUseフックの実践例。ClaudeがReadやGrepで .env を読もうとしたときにブロックする。

設定(.claude/settings.local.json)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Grep",
        "hooks": [
          {
            "type": "command",
            "command": "node /absolute/path/to/hooks/read_hook.js"
          }
        ]
      }
    ]
  }
}

マッチャーの | はOR。ReadとGrepの両方で発火する。

フックスクリプト(hooks/read_hook.js)

async function main() {
  const chunks = [];
  for await (const chunk of process.stdin) {
    chunks.push(chunk);
  }

  const toolArgs = JSON.parse(Buffer.concat(chunks).toString());

  // Readはfile_path、GrepはpathでパスをJSONに含む
  const targetPath =
    toolArgs.tool_input?.file_path || toolArgs.tool_input?.path || "";

  if (targetPath.includes(".env")) {
    console.error(".envファイルは読み取れません");
    process.exit(2);
  }
}

main();

ClaudeCodeが Read: .env を試みると、このスクリプトが起動し、パスに .env が含まれていればexit 2でブロックする。Claudeはstderrのメッセージを受け取り「フックによってブロックされた」と認識する。


セキュリティの注意点:絶対パスを使う

コマンドには相対パスではなく絶対パスを指定するのが推奨。

// NG
"command": "node ./hooks/read_hook.js"

// OK
"command": "node /Users/yourname/project/hooks/read_hook.js"

相対パスだとパスインターセプションやバイナリプランティング攻撃のリスクがある。

チームで共有するときの課題:絶対パスを使うとプロジェクトを異なるディレクトリに置く開発者間でパスが変わる。

解決策として、settings.example.json$PWD プレースホルダーを書いておき、セットアップスクリプト(init-claude.js など)が実行時に絶対パスに置換してローカルの settings.local.json を生成する方法がある。


実用的なフック2選

TypeScript型チェックフック

Claudeが関数シグネチャを変更したとき、呼び出し元を更新し忘れてもすぐに気づける。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "node /path/to/hooks/typecheck_hook.js"
          }
        ]
      }
    ]
  }
}

フックスクリプトは tsc --noEmit を実行し、エラーがあればその内容をstdoutに出力してClaudeに渡す。Claudeはエラーメッセージをもとにほかのファイルの修正も自動で行う。

型がある言語ならどれにも同様のフックを作れる。型がない言語では自動テストの実行で代替できる。

クエリ重複防止フック

データベースクエリのディレクトリ(./queries)にファイルが変更されると、別のClaudeインスタンスを起動して重複クエリがないかレビューさせる。

メインのClaude → queriesディレクトリを変更
→ [PostToolUse] フックが発火
→ 別のClaudeインスタンスを起動
→ 「このクエリと同じ機能のものが既存にないか確認して」
→ 重複あり → メインのClaudeにフィードバック
→ メインのClaude → 既存クエリを再利用して重複を削除

このフックはClaude Code SDK(TypeScript)を使って別インスタンスを起動する。重複レビューのたびにAPIコールが発生するためコストに注意が必要。./queries など重要度の高いディレクトリのみ監視対象にするのが現実的。


その他のフックタイプ

PreToolUseとPostToolUse以外にも複数のフックタイプがある。

フックタイプ 発火タイミング
Notification ツール許可を求めるとき・アイドル60秒後
Stop Claude Codeが応答を完了したとき
SubagentStop サブエージェント(Task)が完了したとき
PreCompact コンパクト操作の前
UserPromptSubmit ユーザーがプロンプトを送信したとき(Claude処理前)
SessionStart セッション開始・再開時
SessionEnd セッション終了時

各フックタイプとマッチャーによって、標準入力に渡されるJSONの構造が異なる。

stdinの構造を確認するデバッグ手法

フックを書き始めるとき、まずどんなデータが渡されるかを確認する。

{
  "PostToolUse": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "jq . > /tmp/post-log.json"
        }
      ]
    }
  ]
}

jq . で標準入力をそのままJSONとして整形し、ファイルに書き出す。実際に操作してから post-log.json を確認すると、フックスクリプトが受け取るデータ構造を把握できる。

📋 試験ガイドより
公式試験ガイドのIn-Scope Topicsに「Agentic loop implementation: Control flow based on stop_reason, tool result handling, loop termination conditions」が含まれる。またDomain 1のHooksとして「PostToolUse hook — tool call interception」が明記されている。PostToolUseフックはprompt instructionと違い、Claudeが従うかどうかに依存しない。コード実行として確実にビジネスルールを強制できる点が設計上の重要な違い。


よくある誤解まとめ

誤解 実際
PostToolUseフックでもツール実行をブロックできる ブロックできるのはPreToolUseのみ。PostToolUseはフィードバックのみ
フックスクリプトはどこに置いてもいい セキュリティ上、絶対パスで指定するのが推奨
prompt instructionと同じ効果がある フックはコード実行なので、Claudeが指示を無視するという問題が起きない
exit code 1でもブロックできる ブロックはexit code 2のみ。exit code 1は異常終了として扱われる
stdinの構造はどのフックでも同じ フックタイプとマッチャーによって異なる。jqでデバッグして確認する

設計の判断基準

場面 やりがちな選択 正しい選択 判断の根拠
.envを読ませたくない システムプロンプトに「.envを読まないで」と書く PreToolUseフックでブロックする Claudeが指示を無視する可能性がある。フックはコード実行なので確実
ファイル編集後に型チェックしたい 毎回手動でtscを実行する PostToolUseフックでtscを自動実行してClaudeにフィードバック フックがエラーをClaudeに渡すと、Claudeが自動で修正まで行う
重複コードを防ぎたい コードレビューで後から指摘する PostToolUseで別Claudeインスタンスを起動してリアルタイムレビュー 変更時点で即フィードバックが入るため、重複が蓄積しない

まとめ

  • PreToolUse(実行前・ブロック可)とPostToolUse(実行後・フィードバック可)の2種類
  • exit code 0で許可、exit code 2でブロック。stderrのメッセージがClaudeへのフィードバックになる
  • コマンドには絶対パスを使う。チーム共有は$PWDプレースホルダーとセットアップスクリプトで対応
  • TypeScript型チェックフックは編集後即エラーをフィードバック、クエリ重複防止フックは別インスタンスでレビュー
  • PostToolUseフックはprompt instructionより確実にルールを強制できる——Claudeの判断に依存しないコード実行だから
  • jq . > ファイル でstdinの構造を確認してからフックを実装する

次回(#5)はClaude Code SDK——プログラムからClaude Codeを操作する方法と、CI/CDパイプラインへの組み込みを解説する。


#3:カスタムコマンドとMCP・GitHub連携#5:Claude Code SDK