第5章 — Hooks: 見えない番人
見逃した秘密鍵
金曜の夕方、ユウキは急いで機能を仕上げていた。Claude Code に「Stripe 連携の API を作って」と指示し、生成されたコードをそのままコミットした。
月曜の朝、Slack に CTO からのメンションが飛んできた。
「ユウキ、sk_live_ で始まる文字列が Git の履歴に入ってるんだけど」
血の気が引いた。Stripe のライブ API キーがハードコードされていた。AI が .env ではなくソースコードに直接キーを埋め込んでいたのだ。
「二度とこんなことが起きないようにしたい」——ユウキはミサキに泣きついた。
「Hooks を使えばいい。AI がコードを書く前にも後にも、自動でチェックを走らせられる」
Hooks とは何か
Hooks は、Claude Code のツール実行の前後にシェルコマンドを自動実行する仕組みだ。
4種類の Hook がある:
| Hook | タイミング | 主な用途 |
|---|---|---|
PreToolUse | ツール実行前 | 危険な操作のブロック |
PostToolUse | ツール実行後 | 自動 lint/format、検証 |
Stop | 応答完了時 | セッション記録、引き継ぎ資料生成 |
SessionStart | セッション開始時 | 環境チェック、前回の引き継ぎ表示 |
settings.json での設定
Hooks は settings.json に定義する。場所によってスコープが変わる。
Hook の設定例:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"command": "bash ~/.claude/guard-secrets.sh \"$TOOL_INPUT\"",
"description": "秘密鍵の混入をブロック"
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "npx biome format --write \"$FILEPATH\"",
"description": "編集後に自動フォーマット"
}
]
}
}実践: 秘密鍵ガード
ユウキが最初に作った Hook は、あの事件を二度と起こさないための「秘密鍵ガード」だった。
#!/bin/bash
# guard-secrets.sh
# ステージされた差分に秘密情報がないかチェック
PATTERNS=(
'sk_live_'
'sk_test_'
'AKIA[0-9A-Z]{16}'
'ghp_[a-zA-Z0-9]{36}'
'-----BEGIN.*PRIVATE KEY-----'
'password\s*=\s*["\x27][^"\x27]+'
)
INPUT="$1"
for pattern in "${PATTERNS[@]}"; do
if echo "$INPUT" | grep -qE "$pattern"; then
echo "BLOCK: 秘密情報のパターンを検出: $pattern"
exit 1
fi
done
exit 0WARNING
Hook がゼロ以外の終了コードを返すと、ツールの実行がブロックされる。echo "BLOCK: ..." のメッセージは Claude に渡され、代替案を考えるよう促される。
実践: 自動フォーマット&リント
PostToolUse Hook で、ファイル編集のたびに自動で lint と format を走らせる。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "biome format --write \"$FILEPATH\" && oxlint --fix \"$FILEPATH\"",
"description": "編集後に自動 lint/format"
}
]
}
}これにより、AI が生成したコードは常にプロジェクトのフォーマットルールに従うようになる。
「AI は Hook の出力も読む。一度フォーマットされると、そのスタイルを学習して次から合わせてくる。好循環が生まれるんだ」とミサキ。
実践: 破壊的操作のブロック
ユウキのチームでは、以下の操作を Hook でブロックしている。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "bash ~/.claude/block-destructive.sh \"$TOOL_INPUT\"",
"description": "破壊的コマンドをブロック"
}
]
}
}#!/bin/bash
# block-destructive.sh
INPUT="$1"
BLOCKED_PATTERNS=(
'git push --force'
'git reset --hard'
'--no-verify'
'rm -rf /'
'DROP TABLE'
'DROP DATABASE'
)
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$INPUT" | grep -qF "$pattern"; then
echo "BLOCK: 破壊的操作を検出: $pattern"
echo "この操作はユーザーの明示的な承認が必要です"
exit 1
fi
done
exit 0実践: Stop Hook でセッション引き継ぎ
Stop Hook は、Claude の応答が完了するたびに実行される。セッションの引き継ぎ資料を自動生成するのに使える。
{
"hooks": {
"Stop": [
{
"command": "bash ~/.claude/handoff-on-stop.sh",
"description": "セッション引き継ぎ資料を自動生成"
}
]
}
}これにより、セッションがクラッシュしたり、翌日に作業を再開する場合でも、前回の作業内容を把握できる。
Hook 設計のベストプラクティス
1. 高速であれ
Hook はツール実行のたびに走る。100ms 以内に完了するのが理想。重い処理は PostToolUse で非同期実行する。
2. 誤検知を許容するな
BLOCK の誤検知はユーザーの信頼を損なう。パターンマッチは保守的に、ブロックするときは理由を明確に伝える。
3. 階層を活かせ
- グローバル Hook: 全プロジェクト共通のガードレール(秘密鍵、破壊的操作)
- プロジェクト Hook: そのプロジェクト固有の品質ルール(lint、format、テスト)
- ローカル Hook: 個人の実験的なチェック
4. デバッグを容易にせよ
# Hook のデバッグ: stderr に出力すると Claude のコンテキストに入る
echo "DEBUG: checking file $FILEPATH" >&2ユウキの変化
Hook を導入して1週間。ユウキの開発フローは劇的に変わった。
「Hooks は"見えない番人"だ。自分がうっかりしても、仕組みが守ってくれる。AI を使う上でのシートベルトみたいなものだ」
ユウキは日報にそう書いた。
次の章では、もう一つの強力なハーネス——Skills と Commands——に進む。定型作業を「手順書」として AI に渡す技術だ。