はじめに
Claude Code などの AI コーディングエージェントは生産性を大幅に向上させますが、誤った操作で未コミットの変更を吹き飛ばしたり、直接 main ブランチへ push してしまったりするリスクも伴います。
Claude Code には hooks.json でカスタムフックを定義する機能があり、ツール実行前後に任意のスクリプトを割り込ませることができます。このフック機構を活用すると、AI エージェントの行動にガードレールを設けることができます。
本記事では、実際のプロジェクトで運用されているフック設計パターンを解説します。すべての実装例はそのまま自分のプロジェクトに適用できます。
Claude Code カスタムフックの設定方法と基本構造
Claude Code は .claude/hooks.json(または settings.json 内の hooks)でフックを定義します。
{
"$schema": "https://json.schemastore.org/claude-code-hooks.json",
"hooks": {
"PreToolUse": [
{
"id": "block-dangerous-operation",
"description": "危険な操作をブロックする",
"matcher": "Bash",
"command": "node .claude/scripts/block-dangerous-operation.js \"$TOOL_INPUT\""
}
],
"PostToolUse": [
{
"id": "auto-format",
"description": "編集後に自動フォーマット",
"matcher": "Write|Edit",
"command": "node .claude/scripts/auto-format.js \"$TOOL_INPUT\""
}
]
}
}
フックが exit code 2 を返すとツール実行がブロックされます。exit code 0 は許可、それ以外は警告扱いです。
フックイベント一覧
| イベント | タイミング |
|---|---|
UserPromptSubmit | ユーザーがプロンプトを送信する直前 |
PreToolUse | ツールを実行する直前 |
PostToolUse | ツール実行後 |
PreCompact | コンテキスト圧縮前 |
SessionStart | セッション開始時 |
Claude Code フックの実践パターン:破壊的操作の防止
実装例1: 破壊的 git 操作のブロック(pre-destructive-git)
最も重要なガードレールがこれです。git checkout --、git clean、git reset --hard は未コミットの変更を消去する操作です。AI エージェントがこれらを実行すると、手元の作業が失われることがあります。
// .claude/scripts/pre-destructive-git.js
const DESTRUCTIVE_PATTERNS = [
/\bgit\s+checkout\s+--\s/,
/\bgit\s+checkout\s+--$/,
/\bgit\s+clean\b/,
/\bgit\s+reset\s+--hard\b/,
];
const input = process.argv[2] || "";
let command = "";
try {
command = JSON.parse(input).command || "";
} catch {
command = input;
}
const isDestructive = DESTRUCTIVE_PATTERNS.some((p) => p.test(command));
if (!isDestructive) process.exit(0);
// 未コミット変更を確認
const status = execSync("git status --porcelain").trim();
if (!status) process.exit(0); // クリーンなら許可
// 未コミット変更があればブロック
process.stderr.write(
`❌ ブロック: 未コミット変更が ${count} 件あります\n` +
`先に git add -A && git commit -m "chore: WIP save" してください\n`
);
process.exit(2); // exit 2 でブロック
このフックにより、未コミット変更がある状態では破壊的操作が自動的にブロックされます。AI エージェントが誤ってリセットしようとしても、コミットが先に強制されます。
実装例2: コミットリマインダー(commit-reminder)
ファイルを大量に変更しても AI エージェントはコミットを後回しにしがちです。PostToolUse フックで変更ファイル数を監視し、閾値を超えたらリマインドします。
// .claude/scripts/commit-reminder.js
const THRESHOLD = parseInt(process.env.COMMIT_REMINDER_THRESHOLD || "5", 10);
const status = execSync("git status --porcelain").trim();
if (!status) process.exit(0);
const count = status.split("\n").filter(Boolean).length;
if (count < THRESHOLD) process.exit(0);
// しきい値に達したときのみリマインド(毎回は出さない)
process.stdout.write(
`💾 コミット推奨: 未コミット変更が ${count} 件あります\n` +
`論理的な区切りでコミットしてください。\n`
);
process.exit(0); // 警告のみ、ブロックはしない
ポイントはブロックではなく警告のみにしていること。強制力は pre-destructive-git が担い、リマインダーは気づきを促す役割に限定しています。
実装例3: main/develop への直接 push ブロック
# .claude/hooks/pretool-guard.sh(Shell 版の実装例)
# main/develop への直接 push をブロック
if echo "${command_text}" | grep -Eq 'git push.* origin (main|develop)'; then
echo "[BLOCK] main/develop への直接 push は禁止です。" >&2
exit 2
fi
# --no-verify によるフックバイパスをブロック
if echo "${command_text}" | grep -Eq 'git (commit|push).*--no-verify'; then
echo "[BLOCK] --no-verify は禁止です。" >&2
exit 2
fi
このガードにより、AI エージェントが feature/xxx ブランチで作業しているつもりで main に直接 push してしまう事故を防ぎます。
実装例4: プロンプト内の秘密情報検出(detect-secrets-in-prompt)
UserPromptSubmit フックを使うと、ユーザーがプロンプトに API キーや秘密情報を貼り付けた場合に警告できます。
{
"UserPromptSubmit": [
{
"id": "detect-secrets-in-prompt",
"description": "ユーザープロンプト内の機密情報パターンを検出して警告する",
"command": "node .claude/scripts/detect-secrets-in-prompt.js"
}
]
}
AI にシステムプロンプトや会話履歴として秘密情報が流れるリスクを軽減できます。
実装例5: CI 自動確認(check-ci)
PostToolUse で git push または gh pr create の後に CI 結果を自動確認するフックです。
{
"PostToolUse": [
{
"id": "check-ci",
"description": "git push / gh pr create 後に CI チェック結果を報告する",
"matcher": "Bash",
"command": "bash .claude/scripts/check-ci.sh",
"timeout": 190
}
]
}
AI エージェントが PR を作成したあと、人間が確認しなくても CI の pass/fail が自動的に会話に返ってきます。
フック設計の原則
実際に運用して分かったフック設計の原則をまとめます。
ブロックと警告を使い分ける
| 操作リスク | 対応 | exit code |
|---|---|---|
| 復元不可能な破壊操作 | ブロック | 2 |
| 好ましくないが回復可能 | 警告 | 0 |
| 検知のみ(情報提供) | 通知 | 0 |
フック有効/無効の切り替え
フックを環境によって無効化できるように hook-gate.js パターンを使います。
// .claude/scripts/hook-gate.js
function shouldRun(hookId) {
const disabled = process.env.CLAUDE_DISABLE_HOOKS || "";
return !disabled.split(",").includes(hookId);
}
module.exports = { shouldRun };
各スクリプトの冒頭で確認します:
const { shouldRun } = require("./hook-gate");
if (!shouldRun("commit-reminder")) process.exit(0);
これにより、特定の環境やデバッグ時だけフックを外せます。
コミット規律との組み合わせ
フックはルール単体では機能しません。コミット規律のルールと組み合わせることで真価を発揮します。
# .claude/rules/commit-discipline.md
## コミットタイミング
- 機能の論理的な区切り
- テストを追加・修正した後
- 設定ファイルを変更した後
- 大きな作業の前(リファクタ・削除の前に現状保存)
## ハーネスによる強制
pre-destructive-git フックが以下の操作の前に未コミット変更を検出:
- git checkout -- <file>
- git clean -fd
- git reset --hard HEAD
ルールでコミットタイミングを定め、フックでルール遵守を技術的に強制する ── この二段構えが重要です。
まとめ
Claude Code のカスタムフックを活用することで、AI エージェントの行動に対して以下のガードレールを設けられます。
- 未コミット変更を保護: 破壊的 git 操作の前に強制コミット
- ブランチ保護: main/develop への直接 push をブロック
- セキュリティ: —no-verify バイパス禁止、プロンプト内秘密情報検出
- 品質担保: 編集後の自動フォーマット、CI 自動確認
フックは「うっかり防止」のための仕組みです。AI エージェントを完全に縛るためではなく、人間でも AI でも踏み外しやすい操作だけをガードする設計が運用しやすくなります。
関連記事
- Claude Code の承認ログを SQLite に保存して AI の行動パターンを分析する — フックで収集したツール呼び出しログを SQLite に記録し、allowedTools の最適化に活用する方法
- Claude Code コミット規律フックの実装 — 本記事で紹介した pre-destructive-git フックの詳細実装と運用ノウハウ
- Claude Code Hooks・カスタムコマンドで開発品質を自動化する — フック・コマンド・スキルを組み合わせた包括的な開発ワークフロー強化