hook-development について
Claude Code プラグインのイベント駆動型フックを開発するためのリファレンスです。イベント、設定形式、プロンプト型・コマンド型フック、入出力、セキュリティ、デバッグを説明します。
主な機能
- 01フックイベントと用途
- 02プラグインと設定の形式
- 03プロンプト型・コマンド型フック
- 04フックの入出力スキーマ
- 05セキュリティとデバッグのガイド
活用シーン
- 01ツール操作の検証や制限
- 02ファイル変更後のテスト実行
- 03セッション開始時のプロジェクト情報読み込み
- 04フック設定と出力の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ josiahsiegel/ claude-plugin-marketplace --skill hook-developmentインストール後、Claude Code で/hook-developmentを入力して読み込まれたか確認します。
詳細
Claude Code プラグイン向けフック開発
概要
フックは、Claude Code のイベントに応じて実行されるイベント駆動型の自動化機能です。フックを使って、操作の検証、ポリシーの適用、コンテキストの読み込み、外部ツールとの統合を行えます。
フックの種類:
- プロンプトベース(推奨):LLM 主導の、コンテキストを考慮した判断
- コマンドベース:高速で決定論的なチェックを行うシェルコマンド
フックイベントのリファレンス
| イベント | タイミング | 主な用途 |
|---|---|---|
| PreToolUse | ツールの実行前 | 検証、承認/拒否、入力の変更 |
| PostToolUse | ツールの完了後 | テスト、Lint、ログ記録、フィードバックの提供 |
| Stop | メインエージェントの停止時 | タスクの完了度を確認 |
| SubagentStop | サブエージェントの停止時 | サブエージェントの作業を検証 |
| UserPromptSubmit | ユーザーがプロンプトを送信した時 | コンテキストの追加、検証、前処理 |
| SessionStart | セッションの開始時 | コンテキストの読み込み、環境の設定 |
| SessionEnd | セッションの終了時 | クリーンアップ、ログ記録 |
| PreCompact | コンテキストの圧縮前 | 重要な情報の保持 |
| Notification | 通知の表示時 | カスタム通知への応答 |
設定形式
プラグインの hooks.json(hooks/hooks.json 内)
hooks フィールドを使うラッパー形式です:
{
"description": "What these hooks do (optional)",
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 10
}
]
}
]
}
}
ユーザー設定形式(.claude/settings.json 内)
ラッパーを使わない直接形式です:
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "script.sh" }]
}
]
}
重要な違い: プラグインの hooks.json では、イベントを {"hooks": {...}} 内にラップします。設定形式では、イベントをトップレベルに配置します。
プロンプトベースのフック(推奨)
コンテキストを考慮した判断には、LLM の推論を使います:
{
"type": "prompt",
"prompt": "Evaluate if this tool use is appropriate. Check for: system paths, credentials, path traversal. Return 'approve' or 'deny'.",
"timeout": 30
}
対応イベント: PreToolUse、PostToolUse、Stop、SubagentStop、UserPromptSubmit
利点: コンテキストを考慮でき、柔軟で、エッジケースへの対応に優れ、保守も容易です。
コマンドフック
決定論的なチェックには、シェルコマンドを実行します:
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 60
}
移植可能なパスには、常に ${CLAUDE_PLUGIN_ROOT} を使ってください。
終了コード
| コード | 意味 |
|---|---|
| 0 | 成功(標準出力がトランスクリプトに表示されます) |
| 2 | ブロッキングエラー(標準エラーが Claude にフィードバックされます) |
| その他 | 非ブロッキングエラー |
マッチャー
フックをトリガーするツールを制御します:
"matcher": "Write" // Exact match
"matcher": "Write|Edit|Bash" // Multiple tools
"matcher": "mcp__.*__delete.*" // Regex (all MCP delete tools)
"matcher": "*" // All tools (use sparingly)
マッチャーでは大文字と小文字が区別されます。
フックの入出力
入力(すべてのフックが標準入力から受け取ります)
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/current/working/dir",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/path/to/file" }
}
イベント固有のフィールド:tool_name、tool_input、tool_result、user_prompt、reason
プロンプトでのアクセス方法:$TOOL_INPUT、$TOOL_RESULT、$USER_PROMPT
出力
標準(すべてのフック):
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Message for Claude"
}
PreToolUse の判断:
{
"hookSpecificOutput": {
"permissionDecision": "allow|deny|ask",
"updatedInput": { "field": "modified_value" }
}
}
Stop/SubagentStop の判断:
{
"decision": "approve|block",
"reason": "Explanation"
}
環境変数
| 変数 | 使用可能な範囲 | 用途 |
|---|---|---|
$CLAUDE_PLUGIN_ROOT | すべてのフック | プラグインディレクトリ(移植可能なパス) |
$CLAUDE_PROJECT_DIR | すべてのフック | プロジェクトのルートパス |
$CLAUDE_ENV_FILE | SessionStart のみ | セッション用の環境変数を永続化 |
SessionStart では変数を永続化できます:
echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
一般的なパターン
ファイル書き込みの検証(PreToolUse)
{
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "prompt",
"prompt": "Check if this file write is safe. Deny writes to: .env, credentials, system paths, or files with path traversal (..). Return 'approve' or 'deny' with reason."
}]
}]
}
変更後の自動テスト(PostToolUse)
{
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "npm test -- --bail",
"timeout": 60
}]
}]
}
タスク完了の確認(Stop)
{
"Stop": [{
"matcher": "*",
"hooks": [{
"type": "prompt",
"prompt": "Verify: tests run, build succeeded, all questions answered. Return 'approve' to stop or 'block' with reason to continue."
}]
}]
}
プロジェクトコンテキストの読み込み(SessionStart)
{
"SessionStart": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
"timeout": 10
}]
}]
}
セキュリティのベストプラクティス
コマンドフックのスクリプト内:
#!/bin/bash
set -euo pipefail
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
# Always validate inputs
if [[ ! "$file_path" =~ ^[a-zA-Z0-9_./-]+$ ]]; then
echo '{"decision": "deny", "reason": "Invalid path"}' >&2
exit 2
fi
# Block path traversal
if [[ "$file_path" == *".."* ]]; then
echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
exit 2
fi
# Block sensitive files
if [[ "$file_path" == *".env"* ]]; then
echo '{"decision": "deny", "reason": "Sensitive file"}' >&2
exit 2
fi
# Always quote variables
echo "$file_path"
ライフサイクルと制限事項
フックはセッション開始時に読み込まれます。 フック設定を変更した場合は、Claude Code を再起動する必要があります。
hooks/hooks.jsonを編集しても、現在のセッションには反映されません- 新しいフックスクリプトを追加しても、再起動するまでは認識されません
- 条件に一致するすべてのフックは並列で実行されます(順次ではありません)
- フック同士は互いの出力を参照できません。独立して動作するように設計してください
変更をテストするには: Claude Code を終了し、claude または claude --debug で再起動します。
デバッグ
# Enable debug mode to see hook execution
claude --debug
# Test hook scripts directly
echo '{"tool_name": "Write", "tool_input": {"file_path": "/test"}}' | \
bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh
# Validate hook JSON output
output=$(./hook-script.sh < test-input.json)
echo "$output" | jq .
# View loaded hooks in session
# Use /hooks command
検証チェックリスト
- hooks.json が正しい形式を使用している(プラグインラッパーまたは設定を直接指定)
- すべてのスクリプトパスで
${CLAUDE_PLUGIN_ROOT}を使用している(ハードコードされたパスがない) - スクリプトに実行権限があり、エラーを処理する(
set -euo pipefail) - スクリプトですべての入力を検証し、すべての変数を引用符で囲んでいる
- マッチャーが具体的である(必要な場合を除き
*を避ける) - タイムアウトが適切に設定されている(デフォルト:コマンドは60秒、プロンプトは30秒)
- フックの出力が有効なJSONである
-
claude --debugでテスト済み