mcpField
C
claude-code-hooks

claude-code-hooks

github.com/vasilyu1983エージェントワークフロー51 回インストール
リポジトリ

claude-code-hooks について

claude-code-hooksは、Claude Codeのフックでイベント駆動の自動化を構築する開発者向けのリファレンススキルです。イベント、設定、入出力、安全対策を解説し、コマンドフックとプロンプトフックの例を掲載しています。

主な機能

  1. 01Claude Codeのイベントと実行条件の参照
  2. 02ツール実行前後のフック設定
  3. 03権限制御とツール入力の変更
  4. 04コマンドフックとプロンプトフックの併用
  5. 05セキュリティ・デバッグ・フック用テンプレート

活用シーン

  1. 01危険なコマンドの実行前ブロック
  2. 02ツール呼び出し後のフォーマットや監査
  3. 03Claudeのタスク完了時にテストを実行
  4. 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サブエージェントの起動時サブエージェントのメタデータの検査
StopClaude の処理完了時テストの実行、要約
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_DIRClaude Code が起動したプロジェクトルートの絶対パス
CLAUDE_PLUGIN_ROOTプラグインのルート(プラグインフックのみ)
CLAUDE_CODE_REMOTEリモート/ウェブ環境では "true"、それ以外のローカル環境では空
CLAUDE_ENV_FILEexport ... 行を永続化するファイルパス(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 $?

ナビゲーション

リソース
関連スキル

コメント