
1行のrm -rfで全消失|9割が知らないHooks19連ガード
あなたが許可プロンプトを1回ずつ目で追っている間に、同じClaude Codeを使う側は19個の禁止パターンを物理的に塞いで席を離れている。
差はスキルではない。設定ファイル1枚だ。
この記事は、その1枚を今日埋めるための全文を渡す。
事故は「判断ミス」ではなく「設定の不在」で起きる
Claude Codeは確率的に動く。だから`rm -rf`や`git reset --hard`を提案する確率は0にならない。
permissionsのallow/denyは強力だが、人間の確認を前提にした仕組みだ。autoモードや`-p`の無人実行では、確認する人間が席にいない。
ここで起きるのは「Claudeが暴走した」ではない。止める経路を誰も作らなかったという結果だ。
失うものは具体的だ。`git reset --hard`は未コミットの作業を丸ごと消す。`git push --force`は共同作業者の履歴を書き換える。`.env`の上書きは本番の接続情報を消す。
どれも「あとで直す」ができない操作だ。そして3つとも、設定2行で経路を塞げる。
止めるべきはClaudeの判断ではなく、操作そのものの経路。
これを担うのがHooksだ。ユーザーが定義したコマンドを、Claude Codeがライフサイクルの特定点で必ず実行する。
LLMが「実行しようと思うか」には依存しない。公式リファレンスはこれを決定論的な制御(deterministic control)と明記している。
フックイベントは33種類。日本語記事で見るのは5つだけ
2026年10月8日時点の公式リファレンスに載るフックイベントは33種類ある。
PreToolUse / PostToolUse / UserPromptSubmit / Stop / SessionStart。解説記事で見かけるのはこの5つで止まる。残り28個に、事故を止める本体が入っている。
`PermissionRequest`: 許可ダイアログが出る直前に割り込み、allow/denyを自動で返す
`PermissionDenied`: autoモードが拒否した瞬間を記録する
`FileChanged`: 誰が書いたかに関係なく、監視ファイルの変更で発火する
`ConfigChange`: 設定ファイル自体の書き換えをexit 2で止める
`TaskCompleted`: テストが通るまでタスクを閉じさせない
`PreCompact`: 圧縮で消える前に全文を退避する
5つしか知らないと、PreToolUseだけで全部やろうとする。そしてシェル経由のファイル書き込みを取りこぼす。ここが最大の穴だ。
なぜ誰もHooksを書かないのか
機能は公式に用意されている。それでも入れていない環境が多い理由は3つに絞れる。
1つ目は優先順位が読み取れないこと。33イベントは公式リファレンスに平等に並んでいる。「どれを最初に入れると事故が止まるか」は、そこには書かれていない。
2つ目はバージョンで挙動が変わること。exit 2とJSON出力の優先関係はv2.1.214で、PreToolUseのタイムアウト扱いはv2.1.210で変わった。半年前の解説をコピペすると、ブロックしたつもりで素通りする。
3つ目が最悪だ。設定を間違えたフックは、エラーを出さずに静かに無効化される。`if`をツール系以外のイベントに付けた場合も、matcherに括弧を書いて正規表現として解釈された場合も、画面には何も出ない。
「入れたつもり」が一番高くつく。発火の確認までやって初めて守りになる。
実際に無人運用しているパイプラインのフックディレクトリには、スクリプトが17ファイル・合計2,308行入っている。そのうち最初に書くべき1本は決まっている。
今日入れる最小の1本(.envと.gitを守る)
まずこれだけで、最頻の事故は落ちる。`.claude/hooks/protect-files.sh`に保存する。
bash
#!/bin/bash
protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
Windowsのバックスラッシュを正規化(これが無いとパターンが一致しない)
FILE_PATH="${FILE_PATH//\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == "$pattern" ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
`.claude/settings.json`に登録する。macOSとLinuxでは`chmod +x`が必須で、忘れると起動しない。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
`/hooks`と打てば、いま効いているフックがイベント別に一覧で出る。発火しない時は`claude --debug`、もしくはセッション中に`/debug`で理由が見える。
ここまでが無料で渡す範囲だ。だがこの構成には3つの穴が残っている。exit 2とJSONの優先順位、matcherの文字判定、そしてシェル経由の抜け道だ。塞がないと「入れたつもり」で終わる。