Claude Codeのフック(Hooks)でワークフローを自動化する
PreToolUse・PostToolUseフックで品質と安全性を自動で担保
はじめに
Claude Codeの「フック(Hooks)」は、AIがツールを実行する前後に自動的にスクリプトを実行できる機能です。コードの品質チェック、セキュリティ検証、通知などを自動化し、人間の介入なしに安全で高品質な開発を実現できます。
この記事では、フックの種類・設定方法から、実践的なユースケース5選まで、完全な設定例とともに解説します。
フックとは何か
フックは、Claude Codeの特定のイベント(ツール実行、通知など)に応じて自動的に実行されるスクリプトです。Gitのpre-commitフックやCI/CDのパイプラインに似た概念で、開発ワークフローを自動化・標準化できます。
フックの基本概念
- トリガー:フックが実行されるきっかけ(ツール使用前、ツール使用後など)
- マッチャー:どのツールに対してフックを発火するかのフィルタ条件
- コマンド:フックが発火したときに実行するシェルコマンド
- 結果処理:コマンドの終了コードに基づいて続行・中止を判断
フックの活用例
| 用途 | フックの種類 | 実行タイミング |
|---|---|---|
| コード品質チェック | PostToolUse | ファイル書き込み後にlintを実行 |
| セキュリティ検証 | PreToolUse | コマンド実行前に危険なコマンドをブロック |
| 自動テスト | PostToolUse | コード変更後にテストを自動実行 |
| 通知 | Notification | AIがユーザーの入力を待つときに通知 |
| ログ記録 | PostToolUse | ツール実行の記録を残す |
フックの種類
Claude Codeでは5種類のフックタイプが用意されています。
1. PreToolUse(ツール使用前)
AIがツール(ファイル書き込み、コマンド実行など)を使用する前に実行されます。フックが非ゼロで終了すると、ツールの実行がブロックされます。
用途:
- 危険なコマンドの実行を防止
- ファイル書き込み前のバリデーション
- 特定のディレクトリへの書き込みをブロック
2. PostToolUse(ツール使用後)
AIがツールを使用した後に実行されます。ツール実行の結果に対してアクションを実行できます。
用途:
- ファイル書き込み後の自動lint/format
- コマンド実行後の結果検証
- コード変更後の自動テスト実行
3. Notification(通知)
Claude Codeがユーザーの入力待ちになったときに実行されます。長い処理が完了したことを通知するのに便利です。
用途:
- デスクトップ通知の送信
- Slack/Discord通知
- サウンド再生
4. Stop(停止時)
Claude Codeのエージェントターンが終了したときに実行されます。
用途:
- 作業完了のログ記録
- 自動的なクリーンアップ処理
5. SubagentStop(サブエージェント完了時)
サブエージェントが完了したときに実行されるフックです。サブエージェントの結果に基づいた後処理を自動化できます。
settings.jsonでの設定方法
フックは.claude/settings.json(プロジェクト単位)または~/.claude/settings.json(グローバル)で設定します。
基本構文
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo 'ツール実行前のチェック'"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "echo 'ファイル書き込み後の処理'"
}
]
}
]
}
}
設定の要素
| 要素 | 説明 | 例 |
|---|---|---|
| matcher | フックを適用するツール名 | "Bash", "Write", "Edit", "Read" |
| hooks | 実行するフックの配列 | 複数のフックを連鎖できる |
| type | フックの種類 | "command"(シェルコマンド実行) |
| command | 実行するシェルコマンド | 任意のシェルコマンド |
マッチャーのパターン
// 特定のツールにマッチ
"matcher": "Bash" // Bashツールのみ
"matcher": "Write" // ファイル書き込みのみ
"matcher": "Edit" // ファイル編集のみ
// 正規表現でマッチ
"matcher": "Write|Edit" // 書き込みまたは編集
実例1: ファイル保存時の自動lint
ファイルが書き込まれた後に自動的にlinter/formatterを実行し、コードスタイルを常に統一します。
設定(PHP + JavaScript プロジェクト)
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path'); if [[ \"$FILE\" == *.php ]]; then php -l \"$FILE\" 2>&1; elif [[ \"$FILE\" == *.js ]]; then npx eslint --fix \"$FILE\" 2>&1; fi"
}
]
}
]
}
}
動作の流れ
- Claude CodeがPHPファイルを書き込み/編集
- フックが発火し、
php -lで構文チェック - JavaScriptファイルの場合は
eslint --fixで自動修正 - エラーがあればClaude Codeに結果が返される
実例2: コード変更後の自動テスト
特定のディレクトリのファイルが変更されたら、関連するテストを自動実行します。
設定
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path'); if [[ \"$FILE\" == */src/* ]]; then TEST_FILE=$(echo \"$FILE\" | sed 's|/src/|/tests/|' | sed 's|\\.php|Test.php|'); if [ -f \"$TEST_FILE\" ]; then php vendor/bin/phpunit \"$TEST_FILE\" --no-coverage 2>&1 | tail -5; fi; fi"
}
]
}
]
}
}
動作説明
- src/配下のファイルが変更されたとき、対応するテストファイルを探す
- テストファイルが存在すれば自動的にPHPUnitを実行
- 結果の最後の5行をClaude Codeに返す(コンテキスト節約のため)
実例3: 作業完了時の通知
長い処理が終わったときにデスクトップ通知を送ります。
設定(macOS)
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Codeが入力を待っています\" with title \"Claude Code\"'"
}
]
}
]
}
}
設定(Windows - PowerShell)
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Codeが入力を待っています')\""
}
]
}
]
}
}
設定(Linux)
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Codeが入力を待っています'"
}
]
}
]
}
}
実例4: 危険なコマンドのブロック
本番環境に影響する可能性のある危険なコマンドの実行を事前にブロックします。
設定
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "BLOCKED_PATTERNS='rm -rf /|DROP TABLE|DROP DATABASE|git push.*--force|git reset --hard|:(){ :|:& };:'; CMD=$(cat /dev/stdin | jq -r '.tool_input.command'); echo \"$BLOCKED_PATTERNS\" | tr '|' '\\n' | while read pattern; do if echo \"$CMD\" | grep -qiE \"$pattern\"; then echo \"BLOCKED: 危険なコマンドが検出されました: $pattern\"; exit 1; fi; done"
}
]
}
]
}
}
ブロックされるコマンドの例
| パターン | 危険性 |
|---|---|
rm -rf / | システム全体の削除 |
DROP TABLE / DROP DATABASE | データベースの破壊 |
git push --force | リモートの履歴を強制上書き |
git reset --hard | ローカルの変更を完全に破棄 |
実例5: カスタムバリデーション
プロジェクト固有のルールをフックで自動検証します。
設定: セキュリティチェック
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path'); if [[ \"$FILE\" == *.php ]]; then ISSUES=''; if grep -n 'mysql_query\\|mysqli_query.*\\$_' \"$FILE\" 2>/dev/null; then ISSUES=\"${ISSUES}SQLインジェクションの可能性あり\\n\"; fi; if grep -n 'echo.*\\$_GET\\|echo.*\\$_POST\\|echo.*\\$_REQUEST' \"$FILE\" 2>/dev/null; then ISSUES=\"${ISSUES}XSSの可能性あり\\n\"; fi; if [ -n \"$ISSUES\" ]; then echo \"セキュリティ警告:\"; echo -e \"$ISSUES\"; fi; fi"
}
]
}
]
}
}
検出されるパターン
- プリペアドステートメントを使っていないSQLクエリ
- エスケープなしのユーザー入力の出力(XSS)
高度なフック設定
複数フックの連鎖
1つのトリガーに複数のフックを設定できます。順番に実行され、1つでも失敗すると後続は実行されません。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path'); echo 'Step 1: 構文チェック' && php -l \"$FILE\""
},
{
"type": "command",
"command": "FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path'); echo 'Step 2: コーディング規約チェック' && phpcs --standard=PSR12 \"$FILE\""
},
{
"type": "command",
"command": "FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path'); echo 'Step 3: 静的解析' && phpstan analyse \"$FILE\" --level=5"
}
]
}
]
}
}
フックへの入力(stdin JSON)
フックのコマンドには、標準入力(stdin)を通じてJSON形式でツール情報が渡されます。環境変数ではなく、stdinからJSONをパースして必要な情報を取得します。
stdin JSONの構造
{
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.php",
"content": "..."
},
"project_dir": "/path/to/project"
}
| JSONフィールド | 内容 | 利用場面 |
|---|---|---|
| tool_name | 実行されるツール名 | 全フック |
| tool_input | ツールへの入力パラメータ | 全フック |
| tool_input.file_path | 操作対象のファイルパス | Write/Editフック |
| tool_input.command | 実行されるBashコマンド | Bashフック |
| project_dir | プロジェクトのルートディレクトリ | 全フック |
stdinからの値取得例(jq使用)
# ファイルパスの取得
FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path')
# Bashコマンドの取得
CMD=$(cat /dev/stdin | jq -r '.tool_input.command')
トラブルシューティング
よくある問題と解決策
| 問題 | 原因 | 解決策 |
|---|---|---|
| フックが実行されない | matcherの指定ミス | ツール名の大文字小文字を確認("Bash", "Write", "Edit") |
| JSONパースエラー | settings.jsonの構文ミス | JSONバリデーターで確認 |
| コマンドがエラーになる | パスの問題 | 絶対パスを使用するか、PATHを確認 |
| フックが遅い | 重い処理を同期実行 | 出力を最小限にする、タイムアウトを設定 |
| 意図せずブロックされる | PreToolUseの条件が広すぎ | matcherとコマンド内の条件を絞り込む |
まとめ
Claude Codeのフック機能を活用したワークフロー自動化のポイント:
- 5種類のフック:PreToolUse(事前チェック)、PostToolUse(事後処理)、Notification(通知)、Stop(終了時処理)
- settings.jsonで設定:プロジェクト単位(.claude/settings.json)またはグローバル(~/.claude/settings.json)
- 主要ユースケース:自動lint、自動テスト、通知、危険コマンドのブロック、セキュリティチェック
- matcherで対象を絞る:ツール名で発火条件を制御(Bash, Write, Edit等)
- 段階的に導入:まず通知フックから始め、慣れてきたらlintやセキュリティチェックを追加
フックは「手動で毎回確認していたこと」を自動化する強力なツールです。まずはNotificationフックで通知を設定することから始めてみましょう。