コンテンツにスキップ

Hooks でガードレールを作る

LLM の判断に任せず、settings.json の hooks でシェルレベルの強制をかける。

押さえるべきイベント

イベント 何ができるか
PreToolUse ツール実行を ブロックできる。exit 2 で阻止、stderr が Claude に返る
UserPromptSubmit ユーザ入力に機密語が含まれていないかチェック
PostToolUse 編集後の format/lint/test/通知
Stop 応答完了時に diff レビュー、PR コメントを書く

典型 1: 危険コマンドブロック

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.command' | grep -qE '(rm -rf|git push --force|drop database)' && { echo 'blocked: dangerous command' >&2; exit 2; } || exit 0"
      }]
    }]
  }
}

典型 2: 機密ファイル保護

{
  "permissions": {
    "deny": ["Read(./.env)", "Read(./secrets/**)", "Edit(./.git/**)"]
  }
}

permissions 評価順は deny → ask → allow。deny で明示する方が確実。

典型 3: 編集後の自動フォーマット

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.file_path' | xargs -r npx prettier --write"
      }]
    }]
  }
}

-r は xargs が空入力の時にコマンドを呼ばない安全策。

典型 4: コミット前テスト

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "node .claude/hooks/pre-bash.js"
      }]
    }]
  }
}

pre-bash.js 内で git push を intercept し npm run lint && npm test を実行、失敗時 exit 1 で push を止める。

設計原則

  • deterministic であること。LLM 判断を入れず、確実にブロック/実行する所だけ hook 化
  • exit 2 で stderr が Claude にフィードバックされる → 「なぜブロックしたか」を必ず stderr に書く
  • 重い処理は hook で同期実行せず、run_in_background 的に投げて Stop hook で待つ

アンチパターン

  • 何でも hook 化 → 編集のたびに数秒〜数十秒待たされる。重いのは Stop hook へ
  • LLM 判断系を hook の中で別 API 呼び出し → ループ・コスト爆発・遅延
  • --no-verify で hook 回避を Claude 自身に許す → permission で Bash(git commit:--no-verify*) を deny

出典

  • _research/claude-code-features/2026-05-11-claude-code-features.md
  • _research/examples/2026-05-11-awesome-templates.md
  • https://code.claude.com/docs/en/hooks-guide
  • https://adambailey.io/blog/claude-hooks-lint-tests