claude-code-hooks について
claude-code-hooksは、Claude Codeのフックでイベント駆動の自動化を構築する開発者向けのリファレンススキルです。イベント、設定、入出力、安全対策を解説し、コマンドフックとプロンプトフックの例を掲載しています。
主な機能
- 01Claude Codeのイベントと実行条件の参照
- 02ツール実行前後のフック設定
- 03権限制御とツール入力の変更
- 04コマンドフックとプロンプトフックの併用
- 05セキュリティ・デバッグ・フック用テンプレート
活用シーン
- 01危険なコマンドの実行前ブロック
- 02ツール呼び出し後のフォーマットや監査
- 03Claudeのタスク完了時にテストを実行
- 04Claude CodeとCI/CDワークフローの連携
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vasilyu1983/ ai-agents-public --skill claude-code-hooksインストール後、Claude Code で/claude-code-hooksを入力して読み込まれたか確認します。
詳細
Claude Code Hooks — メタリファレンス
このスキルは、Claude Code のフックを作成するための決定版リファレンスです。Claude Code のイベントをトリガーにして自動化を構築する際に使用してください。
このスキルを使用する場面
- Claude Code 向けのイベント駆動型自動化の構築
- 危険なコマンドをブロックする PreToolUse ガードの作成
- PostToolUse フォーマッター、リンター、監査ツールの実装
- テストや通知向けの Stop フックの追加
- 環境管理向けの SessionStart/SessionEnd の設定
- Claude Code と CI/CD パイプラインの統合(ヘッドレスモード)
クイックリファレンス
| イベント | トリガー | 使用例 |
|---|---|---|
SessionStart | セッションの開始/再開時 | 環境の初期化 |
UserPromptSubmit | ユーザーがプロンプトを送信したとき | 入力の前処理/検証 |
PreToolUse | ツールの実行前 | 検証、危険なコマンドのブロック |
PermissionRequest | 権限ダイアログの表示時 | 権限の自動許可/拒否 |
PostToolUse | ツールの実行成功後 | フォーマット、監査、通知 |
PostToolUseFailure | ツールの実行失敗後 | 失敗の記録、ガイダンスの追加 |
SubagentStart | サブエージェントの起動時 | サブエージェントのメタデータの検査 |
Stop | Claude の処理完了時 | テストの実行、要約 |
SubagentStop | サブエージェントの処理完了時 | サブエージェントの完了確認 |
Notification | 通知時 | 連携先へのアラート |
PreCompact | コンテキストの圧縮前 | 重要なコンテキストの保持 |
Setup | --init/--maintenance | リポジトリ/環境の初期化 |
SessionEnd | セッションの終了時 | クリーンアップ、状態の保存 |
フックの構造
.claude/hooks/
├── pre-tool-validate.sh
├── post-tool-format.sh
├── post-tool-audit.sh
├── stop-run-tests.sh
└── session-start-init.sh
設定
settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-format.sh"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-validate.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-run-tests.sh"
}
]
}
]
}
}
実行モデル(2026年1月)
- フックは stdin 経由で JSON ペイロードを受け取ります(信頼できない入力として扱ってください)。また、ユーザーの権限で実行されます(Bash ツールのサンドボックス外)。
- フックコマンドごとのデフォルトのタイムアウトは 60 秒です。一致するフックはすべて並列で実行され、同一のコマンドは重複排除されます。
フック入力(stdin)
{
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "ls -la"
}
}
環境変数(シェル)
| 変数 | 説明 |
|---|---|
CLAUDE_PROJECT_DIR | Claude Code が起動したプロジェクトルートの絶対パス |
CLAUDE_PLUGIN_ROOT | プラグインのルート(プラグインフックのみ) |
CLAUDE_CODE_REMOTE | リモート/ウェブ環境では "true"、それ以外のローカル環境では空 |
CLAUDE_ENV_FILE | export ... 行を永続化するファイルパス(SessionStart で利用可能。Setup のサポートについてはドキュメントを確認してください) |
終了コード
| コード | 意味 | 備考 |
|---|---|---|
0 | 成功 | stdout に書き込まれた JSON は、構造化された制御のために解析されます |
2 | ブロッキングエラー | stderr がメッセージになり、stdout の JSON は無視されます |
| その他 | 非ブロッキングエラー | 実行は続行され、stderr は詳細モードで表示されます |
stdout の挿入に関する注意:UserPromptSubmit、SessionStart、Setup では、JSON 以外の stdout(終了コードが 0 の場合)が Claude のコンテキストに挿入されます。その他のイベントの多くでは、stdout は詳細モードでのみ表示されます。
判定制御と入力の変更(v2.0.10 以降)
PreToolUse フックは許可/拒否/確認を行い、必要に応じて updatedInput を使ってツール入力を変更できます。
フック出力スキーマ
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Reason shown to user (and to Claude on deny)",
"updatedInput": { "command": "echo 'modified'" },
"additionalContext": "Extra context added before tool runs"
}
}
注:古い decision/reason フィールドは非推奨です。hookSpecificOutput.* フィールドを優先してください。
例:機密ファイルの編集先を変更する
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
# Redirect package-lock.json edits to /dev/null
if [[ "$FILE_PATH" == *"package-lock.json" ]]; then
UPDATED_INPUT="$(echo "$INPUT" | jq -c '.tool_input | .file_path = "/dev/null"')"
jq -cn --argjson updatedInput "$UPDATED_INPUT" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "allow",
permissionDecisionReason: "Redirected write to /dev/null",
updatedInput: $updatedInput
}
}'
exit 0
fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
例:git add から機密ファイルを除外する
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"
if [[ "$TOOL_NAME" == "Bash" && "$CMD" =~ ^git[[:space:]]+add ]]; then
# Remove .env files from staging
SAFE_CMD="$(echo "$CMD" | sed 's/\.env[^ ]*//g')"
if [[ "$SAFE_CMD" != "$CMD" ]]; then
echo '{}' | jq -cn --arg cmd "$SAFE_CMD" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "allow",
permissionDecisionReason: "Removed .env from git add",
updatedInput: { command: $cmd }
}
}'
exit 0
fi
fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
プロンプトベースのフック
複雑な判定には、bash スクリプトではなく、LLM が評価するフック(type: "prompt")を使用してください。これは Stop や SubagentStop の判定に特に有用です。
設定
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate whether Claude should stop. Context JSON: $ARGUMENTS. Return {\"ok\": true} if all tasks are complete, otherwise {\"ok\": false, \"reason\": \"what remains\"}.",
"timeout": 30
}
]
}
]
}
}
応答スキーマ
- 許可:
{"ok": true} - ブロック:
{"ok": false, "reason": "Claude に表示される説明"}
コマンドフックとプロンプトフックの組み合わせ
高速で決定論的なチェックにはコマンドフックを使用してください。微妙な判断にはプロンプトフックを使用してください。
{
"Stop": [
{
"hooks": [
{ "type": "command", "command": ".claude/hooks/quick-check.sh" },
{ "type": "prompt", "prompt": "Verify code quality meets standards" }
]
}
]
}
フックのテンプレート
ツール実行前の検証
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"
if [[ "$TOOL_NAME" == "Bash" ]]; then
# Block rm -rf /
if echo "$CMD" | grep -qE 'rm\s+-rf\s+/'; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Dangerous rm command detected"
}
}'
exit 0
fi
# Block force push to main
if echo "$CMD" | grep -qE 'git\s+push.*--force.*(main|master)'; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Force push to main/master not allowed"
}
}'
exit 0
fi
# Soft-warning: possible credential exposure
if echo "$CMD" | grep -qE '(password|secret|api_key)\s*='; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "Possible credential exposure in command",
additionalContext: "Command may include a secret. Confirm intent and avoid committing secrets."
}
}'
exit 0
fi
fi
exit 0
ツール実行後のフォーマット
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
case "$FILE_PATH" in
*.js|*.ts|*.jsx|*.tsx|*.json|*.md)
npx prettier --write "$FILE_PATH" 2>/dev/null || true
;;
*.py)
ruff format "$FILE_PATH" 2>/dev/null || true
;;
*.go)
gofmt -w "$FILE_PATH" 2>/dev/null || true
;;
*.rs)
rustfmt "$FILE_PATH" 2>/dev/null || true
;;
esac
fi
exit 0
ツール実行後のセキュリティ監査
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
# Check for hardcoded secrets
if grep -qE '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' "$FILE_PATH"; then
echo "WARNING: Possible hardcoded secret in $FILE_PATH" >&2
fi
# Check for console.log in production code
if [[ "$FILE_PATH" =~ \.(ts|js|tsx|jsx)$ ]] && grep -q 'console.log' "$FILE_PATH"; then
echo "NOTE: console.log found in $FILE_PATH" >&2
fi
fi
exit 0
Stop フック(テストの実行)
#!/bin/bash
set -euo pipefail
# Run tests after Claude finishes
cd "$CLAUDE_PROJECT_DIR"
# Detect test framework
if [[ -f "package.json" ]]; then
if grep -q '"vitest"' package.json; then
npm run test 2>&1 | head -50
elif grep -q '"jest"' package.json; then
npm test 2>&1 | head -50
fi
elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then
pytest --tb=short 2>&1 | head -50
fi
exit 0
セッション開始
#!/bin/bash
set -euo pipefail
cd "$CLAUDE_PROJECT_DIR"
# Check git status
echo "=== Git Status ==="
git status --short
# Check for uncommitted changes
if ! git diff --quiet; then
echo "WARNING: Uncommitted changes detected"
fi
# Verify dependencies
if [[ -f "package.json" ]]; then
if [[ ! -d "node_modules" ]]; then
echo "NOTE: node_modules missing, run npm install"
fi
fi
exit 0
マッチャー
マッチャーは、どのツールがフックをトリガーするかを絞り込みます。
- 完全一致:
Writeは Write ツールのみに一致します - 正規表現:
Edit|WriteまたはNotebook.* - すべてに一致:
*(""またはマッチャーの省略でも機能します)
セキュリティのベストプラクティス
HOOK SECURITY CHECKLIST
[ ] Validate all inputs with regex
[ ] Quote all variables: "$VAR" not $VAR
[ ] Use absolute paths
[ ] No eval with untrusted input
[ ] Set -euo pipefail at top
[ ] Keep hooks fast (<1 second)
[ ] Log actions for audit
[ ] Test manually before deploying
フックの構成
同じイベントでの複数のフック
{
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": ".claude/hooks/format.sh" },
{ "type": "command", "command": ".claude/hooks/audit.sh" },
{ "type": "command", "command": ".claude/hooks/notify.sh" }
]
}
]
}
一致するフックはすべて並行して実行されます。厳密な順序(フォーマット → リント → テスト)が必要な場合は、順番に実行するラッパースクリプトを1つ作成してください。
フックのデバッグ
# Test a PostToolUse hook manually (stdin JSON)
export CLAUDE_PROJECT_DIR="$(pwd)"
echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"'"$(pwd)"'/src/app.ts"}}' \
| bash .claude/hooks/post-tool-format.sh
# Check exit code
echo $?
ナビゲーション
リソース
- references/hook-patterns.md — よく使われるパターン
- references/hook-security.md — セキュリティガイド
- data/sources.json — ドキュメントへのリンク
関連スキル
- ../claude-code-commands/SKILL.md — コマンドの作成
- ../claude-code-agents/SKILL.md — エージェントの作成
- ../ops-devops-platform/SKILL.md — CI/CD 統合