
【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パイプラインへの組み込みを解説する。