はじめに
CLAUDE.mdに「rmは使わないでください」と書いても、Claudeがうっかり実行してしまうことがあります。CLAUDE.mdは所詮「お願い」だからです。確実に止めたい処理には、Hooks(フック)という別の仕組みを使います。
この記事で分かること
- Hooksとは何か、CLAUDE.mdとの決定的な違い
- フックイベントの種類(代表的な6つ)
-
settings.jsonでの基本的な書き方 -
PreToolUseでrmや環境変数ファイルへの書き込みをブロックする具体例 - マッチャーパターンと環境変数の基本
① Hooksとは何か——CLAUDE.mdとの決定的な違い
Hooksは、Claude Codeが特定のタイミングで必ず実行する、あなたが書いたシェルコマンドです。
CLAUDE.mdとの最大の違いは、この「必ず実行される」という保証の有無です。
| CLAUDE.md | Hooks | |
|---|---|---|
| 役割 | Claudeへの指示・文脈情報を伝える | 毎回確実に実行したい処理を行う |
| 実行の保証 | 確率的(読んで従おうとするが保証はない) | 決定論的(設定した通り必ず実行される) |
| 実体 | マークダウンの文章 | シェルコマンド |
公式ドキュメントも、CLAUDE.mdは強制される設定ではなくコンテキストとして扱われるとし、動作を確実にブロックしたい場合はPreToolUseフックを使うよう案内しています。
② フックイベントの種類とHooksの基本構造
Hooksが発火するタイミング(イベント)は、2026年7月時点の公式ドキュメントで30種類あります。ここではセッションの流れに沿った代表的な6つを紹介します(一部です)。
| イベント | 発火タイミング | ブロック可能か |
|---|---|---|
SessionStart |
セッションの開始・再開時 | - |
UserPromptSubmit |
プロンプト送信直後、Claudeが処理する前 | ✅ |
PreToolUse |
ツールの実行前 | ✅ |
PostToolUse |
ツールの実行後 | ❌(すでに実行済みのため) |
Stop |
Claudeの応答が完了した時 | ✅ |
SessionEnd |
セッションの終了時 | - |
Hooksは.claude/settings.jsonに、次のような入れ子構造で書きます。
{
"hooks": {
"イベント名": [
{
"matcher": "マッチャーパターン",
"hooks": [
{
"type": "command",
"command": "実行するコマンド",
"timeout": 30
}
]
}
]
}
}
設定ファイルとスクリプトは、次のように分けて置くのが基本形です。
my-project/
└── .claude/
├── settings.json # Hooksの設定
└── hooks/
└── protect-files.sh # 実行されるスクリプト(③で作成)
③ 設定方法:rm・環境変数ファイルへの書き込みをブロックする
環境変数ファイル(.envなど)への書き込みをブロック
公式ドキュメントの実例です。Claude CodeはPreToolUse実行時、ツール名や引数をJSON形式でstdinに渡します。jq(brew install jq等で導入)でファイルパスを取り出します。スクリプトを.claude/hooks/protect-files.shに保存してください。
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
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
保存したら実行権限を付与します(忘れるとフックが動きません)。
chmod +x .claude/hooks/protect-files.sh
.claude/settings.jsonにEdit・Write実行前にこのスクリプトを呼ぶよう登録します。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
危険なコマンド(rmなど)をブロック
公式ドキュメントには、Bashコマンドにdrop tableという文字列が含まれる場合にブロックする例があります。同じ仕組みでrm -rfをブロックする完成版が次のスクリプトです。
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q "rm -rf"; then
echo "Blocked: rm -rf is not allowed" >&2
exit 2
fi
exit 0
ポイントは終了コード2です。返すとClaude Codeは実行をブロックし、stderrの内容がClaudeへのフィードバックになります。
④ マッチャーパターンと環境変数
matcherには次の書き方ができます。
| 書き方 | 意味 | 例 |
|---|---|---|
| ツール名そのまま | 完全一致 | Bash |
|または,区切り |
複数ツールのいずれか | Edit|Write |
| それ以外の記号を含む文字列 | 正規表現として評価 | mcp__github__.* |
空文字または*
|
すべてにマッチ | "" |
フックのコマンド内では次の環境変数が使えます。
| 変数名 | 内容 |
|---|---|
$CLAUDE_PROJECT_DIR |
プロジェクトのルートディレクトリ |
$CLAUDE_PLUGIN_ROOT |
プラグインのインストール先ディレクトリ |
$CLAUDE_CODE_REMOTE |
リモートのWeb環境では"true"
|
$CLAUDE_EFFORT |
現在のエフォートレベル |
$CLAUDE_ENV_FILE |
direnv等と連携する際、各Bashコマンドの前に読み込まれるファイル |
まとめ:まずPreToolUseフックを1つ試す
Hooksは、CLAUDE.mdの「お願い」だけでは心配な処理——危険なコマンドや、書き換えたくないファイルへの書き込み——を機械的に必ず止める仕組みです。まずは.claude/settings.jsonに次の最小構成を試してみてください。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo 'This command was checked by a hook' >&2"
}
]
}
]
}
}