mybook

第5章 — Hooks: 見えない番人

見逃した秘密鍵

金曜の夕方、ユウキは急いで機能を仕上げていた。Claude Code に「Stripe 連携の API を作って」と指示し、生成されたコードをそのままコミットした。

月曜の朝、Slack に CTO からのメンションが飛んできた。

「ユウキ、sk_live_ で始まる文字列が Git の履歴に入ってるんだけど」

血の気が引いた。Stripe のライブ API キーがハードコードされていた。AI が .env ではなくソースコードに直接キーを埋め込んでいたのだ。

「二度とこんなことが起きないようにしたい」——ユウキはミサキに泣きついた。

「Hooks を使えばいい。AI がコードを書くにもにも、自動でチェックを走らせられる」

Hooks とは何か

Hooks は、Claude Code のツール実行の前後にシェルコマンドを自動実行する仕組みだ。

Loading diagram...

4種類の Hook がある:

Hookタイミング主な用途
PreToolUseツール実行危険な操作のブロック
PostToolUseツール実行自動 lint/format、検証
Stop応答完了時セッション記録、引き継ぎ資料生成
SessionStartセッション開始時環境チェック、前回の引き継ぎ表示

settings.json での設定

Hooks は settings.json に定義する。場所によってスコープが変わる。

Loading diagram...

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 0

WARNING

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 が生成したコードは常にプロジェクトのフォーマットルールに従うようになる。

Loading diagram...

「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": "セッション引き継ぎ資料を自動生成"
      }
    ]
  }
}

これにより、セッションがクラッシュしたり、翌日に作業を再開する場合でも、前回の作業内容を把握できる。

Loading diagram...

Hook 設計のベストプラクティス

Loading chart...

1. 高速であれ

Hook はツール実行のたびに走る。100ms 以内に完了するのが理想。重い処理は PostToolUse で非同期実行する。

2. 誤検知を許容するな

BLOCK の誤検知はユーザーの信頼を損なう。パターンマッチは保守的に、ブロックするときは理由を明確に伝える。

3. 階層を活かせ

  • グローバル Hook: 全プロジェクト共通のガードレール(秘密鍵、破壊的操作)
  • プロジェクト Hook: そのプロジェクト固有の品質ルール(lint、format、テスト)
  • ローカル Hook: 個人の実験的なチェック

4. デバッグを容易にせよ

# Hook のデバッグ: stderr に出力すると Claude のコンテキストに入る
echo "DEBUG: checking file $FILEPATH" >&2

ユウキの変化

Hook を導入して1週間。ユウキの開発フローは劇的に変わった。

Loading chart...

「Hooks は"見えない番人"だ。自分がうっかりしても、仕組みが守ってくれる。AI を使う上でのシートベルトみたいなものだ」

ユウキは日報にそう書いた。

次の章では、もう一つの強力なハーネス——Skills と Commands——に進む。定型作業を「手順書」として AI に渡す技術だ。