Hook Development について
Hook Development は、Claude Code プラグインでイベント駆動の自動化を構築するガイドです。ツール呼び出しの検証、コンテキスト追加、タスク完了確認に使うフックの設定を解説します。
主な機能
- 01ツールやセッションのイベントへの対応
- 02プロンプト型またはコマンド型フックの利用
- 03イベントマッチャーと出力の設定
- 04入力検証とパス制限
- 05フック動作のテストとデバッグ
活用シーン
- 01実行前のファイル書き込みチェック
- 02セッション開始時のプロジェクトコンテキスト読み込み
- 03エージェント停止前のタスク完了確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ anthropics/ claude-code --skill hook-developmentインストール後、Claude Code で/hook-developmentを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/load-context.shvalidate-bash.shvalidate-write.shreferences/advanced.mdmigration.mdpatterns.mdscripts/hook-linter.shREADME.mdtest-hook.shvalidate-hook-schema.sh詳細
Claude Code プラグインのフック開発
概要
フックは、Claude Code のイベントに応じて実行されるイベント駆動型の自動化スクリプトです。フックを使うと、操作の検証、ポリシーの適用、コンテキストの追加、外部ツールのワークフローへの統合ができます。
主な機能:
- 実行前にツール呼び出しを検証(PreToolUse)
- ツールの結果に対応(PostToolUse)
- 完了基準を適用(Stop、SubagentStop)
- プロジェクトのコンテキストを読み込む(SessionStart)
- 開発ライフサイクル全体のワークフローを自動化
フックの種類
プロンプトベースのフック(推奨)
LLM による意思決定を使って、コンテキストを考慮した検証を行います。
{
"type": "prompt",
"prompt": "Evaluate if this tool use is appropriate: $TOOL_INPUT",
"timeout": 30
}
対応イベント: Stop、SubagentStop、UserPromptSubmit、PreToolUse
メリット:
- 自然言語による推論に基づき、コンテキストを考慮した判断ができます
- bash スクリプトを使わずに、柔軟な評価ロジックを実現できます
- エッジケースへの対応が向上します
- 保守や拡張が容易です
コマンドフック
決定的なチェックを行うために、bash コマンドを実行します。
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 60
}
用途:
- 高速で決定的な検証
- ファイルシステム操作
- 外部ツールとの統合
- パフォーマンスが重要なチェック
フック設定形式
プラグインの hooks.json 形式
hooks/hooks.json 内のプラグインフックでは、ラッパー形式を使います。
{
"description": "Brief explanation of hooks (optional)",
"hooks": {
"PreToolUse": [...],
"Stop": [...],
"SessionStart": [...]
}
}
要点:
descriptionフィールドは任意ですhooksフィールドは必須のラッパーで、実際のフックイベントを含みます- これはプラグイン固有の形式です
例:
{
"description": "Validation hooks for code quality",
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh"
}
]
}
]
}
}
設定形式(直接指定)
.claude/settings.json 内のユーザー設定では、直接指定する形式を使います。
{
"PreToolUse": [...],
"Stop": [...],
"SessionStart": [...]
}
要点:
- ラッパーはなく、イベントをトップレベルに直接記述します
- description フィールドはありません
- これは設定形式です
重要: 以下の例は、どちらの形式にも含められるフックイベントの構造を示しています。プラグインの hooks.json では、これらを {"hooks": {...}} で囲みます。
フックイベント
PreToolUse
ツールの実行前に実行されます。ツール呼び出しの承認、拒否、変更に使います。
例(プロンプトベース):
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "Validate file write safety. Check: system paths, credentials, path traversal, sensitive content. Return 'approve' or 'deny'."
}
]
}
]
}
PreToolUse の出力:
{
"hookSpecificOutput": {
"permissionDecision": "allow|deny|ask",
"updatedInput": {"field": "modified_value"}
},
"systemMessage": "Explanation for Claude"
}
PostToolUse
ツールの完了後に実行されます。結果への対応、フィードバックの提供、ログ記録に使います。
例:
{
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "prompt",
"prompt": "Analyze edit result for potential issues: syntax errors, security vulnerabilities, breaking changes. Provide feedback."
}
]
}
]
}
出力の動作:
- Exit 0:stdout がトランスクリプトに表示されます
- Exit 2:stderr が Claude にフィードバックされます
- systemMessage がコンテキストに含まれます
Stop
メインエージェントが停止を検討するときに実行されます。完了状態の検証に使います。
例:
{
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "Verify task completion: tests run, build succeeded, questions answered. Return 'approve' to stop or 'block' with reason to continue."
}
]
}
]
}
判断の出力:
{
"decision": "approve|block",
"reason": "Explanation",
"systemMessage": "Additional context"
}
SubagentStop
サブエージェントが停止を検討するときに実行されます。サブエージェントがタスクを完了したことを確認するために使います。
Stop フックと似ていますが、サブエージェント向けです。
UserPromptSubmit
ユーザーがプロンプトを送信したときに実行されます。コンテキストの追加、検証、プロンプトのブロックに使います。
例:
{
"UserPromptSubmit": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "Check if prompt requires security guidance. If discussing auth, permissions, or API security, return relevant warnings."
}
]
}
]
}
SessionStart
Claude Code のセッション開始時に実行されます。コンテキストの読み込みや環境の設定に使います。
例:
{
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh"
}
]
}
]
}
特別な機能: $CLAUDE_ENV_FILE を使って環境変数を保持できます。
echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
完全な例は examples/load-context.sh を参照してください。
SessionEnd
セッション終了時に実行されます。クリーンアップ、ログ記録、状態の保持に使います。
PreCompact
コンテキストの圧縮前に実行されます。保持する必要のある重要な情報を追加するために使います。
Notification
Claude が通知を送信したときに実行されます。ユーザーへの通知に対応するために使います。
フックの出力形式
標準出力(すべてのフック)
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Message for Claude"
}
continue:false の場合、処理を停止します(デフォルトは true)suppressOutput:出力をトランスクリプトに表示しません(デフォルトは false)systemMessage:Claude に表示されるメッセージ
終了コード
0- 成功(stdout がトランスクリプトに表示されます)2- ブロッキングエラー(stderr が Claude にフィードバックされます)- その他 - 非ブロッキングエラー
フックの入力形式
すべてのフックは、共通フィールドを含む JSON を stdin 経由で受け取ります。
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/current/working/dir",
"permission_mode": "ask|allow",
"hook_event_name": "PreToolUse"
}
イベント固有のフィールド:
- PreToolUse/PostToolUse:
tool_name、tool_input、tool_result - UserPromptSubmit:
user_prompt - Stop/SubagentStop:
reason
プロンプト内で $TOOL_INPUT、$TOOL_RESULT、$USER_PROMPT などを使ってフィールドにアクセスします。
環境変数
すべてのコマンドフックで使用できます。
$CLAUDE_PROJECT_DIR- プロジェクトのルートパス$CLAUDE_PLUGIN_ROOT- プラグインディレクトリ(移植可能なパスに使用)$CLAUDE_ENV_FILE- SessionStart のみ:環境変数をここに保存します$CLAUDE_CODE_REMOTE- リモートコンテキストで実行されている場合に設定されます
移植性を確保するため、フックコマンドでは常に ${CLAUDE_PLUGIN_ROOT} を使用してください。
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
}
プラグインのフック設定
プラグインでは、hooks/hooks.json でフックを定義します。
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "Validate file write safety"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "Verify task completion"
}
]
}
],
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
"timeout": 10
}
]
}
]
}
プラグインのフックはユーザーのフックと統合され、並列で実行されます。
マッチャー
ツール名の照合
完全一致:
"matcher": "Write"
複数のツール:
"matcher": "Read|Write|Edit"
ワイルドカード(すべてのツール):
"matcher": "*"
正規表現パターン:
"matcher": "mcp__.*__delete.*" // All MCP delete tools
注: マッチャーでは大文字と小文字が区別されます。
よく使われるパターン
// All MCP tools
"matcher": "mcp__.*"
// Specific plugin's MCP tools
"matcher": "mcp__plugin_asana_.*"
// All file operations
"matcher": "Read|Write|Edit"
// Bash commands only
"matcher": "Bash"
セキュリティのベストプラクティス
入力の検証
コマンドフックでは、必ず入力を検証してください。
#!/bin/bash
set -euo pipefail
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')
# Validate tool name format
if [[ ! "$tool_name" =~ ^[a-zA-Z0-9_]+$ ]]; then
echo '{"decision": "deny", "reason": "Invalid tool name"}' >&2
exit 2
fi
パスの安全性
パストラバーサルや機密ファイルがないか確認してください。
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
# Deny path traversal
if [[ "$file_path" == *".."* ]]; then
echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
exit 2
fi
# Deny sensitive files
if [[ "$file_path" == *".env"* ]]; then
echo '{"decision": "deny", "reason": "Sensitive file"}' >&2
exit 2
fi
完全な例については、examples/validate-write.sh と examples/validate-bash.sh を参照してください。
すべての変数を引用符で囲む
# GOOD: Quoted
echo "$file_path"
cd "$CLAUDE_PROJECT_DIR"
# BAD: Unquoted (injection risk)
echo $file_path
cd $CLAUDE_PROJECT_DIR
適切なタイムアウトを設定する
{
"type": "command",
"command": "bash script.sh",
"timeout": 10
}
デフォルト: コマンドフック(60s)、プロンプトフック(30s)
パフォーマンスに関する考慮事項
並列実行
一致するフックはすべて並列で実行されます。
{
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{"type": "command", "command": "check1.sh"}, // Parallel
{"type": "command", "command": "check2.sh"}, // Parallel
{"type": "prompt", "prompt": "Validate..."} // Parallel
]
}
]
}
設計上の留意点:
- フックは互いの出力を参照できません
- 実行順序は一定ではありません
- 独立して動作するように設計してください
最適化
- すばやく確定的なチェックにはコマンドフックを使用します
- 複雑な推論にはプロンプトフックを使用します
- 検証結果を一時ファイルにキャッシュします
- 頻繁に実行される処理では I/O を最小限に抑えます
一時的に有効化するフック
フラグファイルまたは設定を確認し、条件に応じて有効化するフックを作成します。
パターン:フラグファイルによる有効化
#!/bin/bash
# Only active when flag file exists
FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-strict-validation"
if [ ! -f "$FLAG_FILE" ]; then
# Flag not present, skip validation
exit 0
fi
# Flag present, run validation
input=$(cat)
# ... validation logic ...
パターン:設定による有効化
#!/bin/bash
# Check configuration for activation
CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/plugin-config.json"
if [ -f "$CONFIG_FILE" ]; then
enabled=$(jq -r '.strictMode // false' "$CONFIG_FILE")
if [ "$enabled" != "true" ]; then
exit 0 # Not enabled, skip
fi
fi
# Enabled, run hook logic
input=$(cat)
# ... hook logic ...
使用例:
- 必要な場合にのみ厳密な検証を有効にする
- 一時的なデバッグ用フック
- プロジェクト固有のフック動作
- フック用の機能フラグ
ベストプラクティス: 一時的なフックを有効/無効にする方法がユーザーにわかるよう、プラグインの README に有効化の仕組みを記載してください。
フックのライフサイクルと制限事項
フックはセッション開始時に読み込まれる
重要: フックは Claude Code のセッション開始時に読み込まれます。フック設定を変更するには、Claude Code を再起動する必要があります。
フックはホットスワップできません:
hooks/hooks.jsonを編集しても、現在のセッションには反映されません- 新しいフックスクリプトを追加しても認識されません
- フックコマンドやプロンプトを変更しても更新されません
- Claude Code を再起動する必要があります。終了してから、再度
claudeを実行してください
フックの変更をテストするには:
- フックの設定またはスクリプトを編集します
- Claude Code のセッションを終了します
claudeまたはccで再起動します- 新しいフック設定が読み込まれます
claude --debugでフックをテストします
起動時のフック検証
Claude Code の起動時にフックが検証されます。
- hooks.json の JSON が無効な場合、読み込みに失敗します
- スクリプトが見つからない場合、警告が表示されます
- 構文エラーはデバッグモードで報告されます
/hooks コマンドを使用して、現在のセッションで読み込まれているフックを確認してください。
フックのデバッグ
デバッグモードを有効にする
claude --debug
フックの登録、実行ログ、入出力の JSON、タイミング情報を確認してください。
フックスクリプトをテストする
コマンドフックを直接テストします。
echo '{"tool_name": "Write", "tool_input": {"file_path": "/test"}}' | \
bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh
echo "Exit code: $?"
JSON 出力を検証する
フックが有効な JSON を出力することを確認してください。
output=$(./your-hook.sh < test-input.json)
echo "$output" | jq .
クイックリファレンス
フックイベントの概要
| イベント | タイミング | 用途 |
|---|---|---|
| PreToolUse | ツールの実行前 | 検証、変更 |
| PostToolUse | ツールの実行後 | フィードバック、ログ記録 |
| UserPromptSubmit | ユーザー入力時 | コンテキスト、検証 |
| Stop | エージェントの停止時 | 完全性の確認 |
| SubagentStop | サブエージェントの完了時 | タスクの検証 |
| SessionStart | セッション開始時 | コンテキストの読み込み |
| SessionEnd | セッション終了時 | クリーンアップ、ログ記録 |
| PreCompact | コンパクト化の前 | コンテキストの保持 |
| Notification | ユーザーへの通知時 | ログ記録、リアクション |
ベストプラクティス
推奨事項:
- ✅ 複雑なロジックにはプロンプトベースのフックを使用します
- ✅ 移植性を高めるために${CLAUDE_PLUGIN_ROOT}を使用します
- ✅ コマンドフックですべての入力を検証します
- ✅ すべてのbash変数を引用符で囲みます
- ✅ 適切なタイムアウトを設定します
- ✅ 構造化されたJSON出力を返します
- ✅ フックを十分にテストします
禁止事項:
- ❌ ハードコードされたパスを使用しないでください
- ❌ 検証せずにユーザー入力を信頼しないでください
- ❌ 長時間実行されるフックを作成しないでください
- ❌ フックの実行順序に依存しないでください
- ❌ グローバル状態を予測不能な形で変更しないでください
- ❌ 機密情報をログに記録しないでください
その他のリソース
リファレンスファイル
詳しいパターンや高度なテクニックについては、以下を参照してください。
references/patterns.md- 一般的なフックパターン(実証済みのパターンを8つ以上)references/migration.md- 基本的なフックから高度なフックへの移行references/advanced.md- 高度なユースケースとテクニック
フックスクリプトの例
examples/に動作する例があります。
validate-write.sh- ファイル書き込みの検証例validate-bash.sh- Bashコマンドの検証例load-context.sh- SessionStartでコンテキストを読み込む例
ユーティリティスクリプト
"scripts/に開発ツールがあります。
validate-hook-schema.sh- hooks.jsonの構造と構文を検証しますtest-hook.sh- デプロイ前にサンプル入力でフックをテストしますhook-linter.sh- フックスクリプトにありがちな問題がないか確認し、ベストプラクティスへの準拠をチェックします
外部リソース
- 公式ドキュメント:https://docs.claude.com/en/docs/claude-code/hooks
- 例:claude-plugins-officialマーケットプレイスのsecurity-guidanceプラグインを参照してください
- テスト:詳細なログを確認するには
claude --debugを使用します - 検証:フックのJSON出力を検証するには
jqを使用します
実装ワークフロー
プラグインにフックを実装するには、次の手順に従います。
- フックするイベントを特定します(PreToolUse、Stop、SessionStartなど)
- プロンプトベース(柔軟)とコマンド(決定的)のどちらのフックを使うか決めます
hooks/hooks.jsonにフック設定を記述します- コマンドフックの場合は、フックスクリプトを作成します
- すべてのファイル参照に${CLAUDE_PLUGIN_ROOT}を使用します
scripts/validate-hook-schema.sh hooks/hooks.jsonで設定を検証します- デプロイ前に
scripts/test-hook.shでフックをテストします claude --debugを使ってClaude Codeでテストします- プラグインのREADMEにフックを記載します
ほとんどのユースケースでは、プロンプトベースのフックを優先してください。コマンドフックは、パフォーマンスが重要なチェックや決定的なチェックに限定してください。