第6章 · Claude Code Advanced

Hooks で振る舞いを制御する

Hooks
→ で次のスライド · F でフルスクリーン · N で講師ノート · Esc で終了

重要キーワード

Hook
フック
イベント発生時に実行されるシェルコマンド
PreToolUse
ツール実行前
Claude がツールを呼ぶ直前のイベント
PostToolUse
ツール実行後
ツール実行直後のイベント
Exit Code
終了コード
0=成功 / 2=ブロック / その他=警告

Hooks (フック)

特定のイベント発生時に シェルコマンドを実行 する仕組み。 メモリやプロンプトでは強制できない、決定論的な自動化 に向きます。

イベント例

  • SessionStart: セッション開始時
  • PreToolUse: ツール実行前
  • PostToolUse: ツール実行後
  • UserPromptSubmit: ユーザーがプロンプト送信した時
  • Stop: Claude の応答終了時
  • Notification: 通知イベント
  • PreCompact: コンテキスト圧縮前

設定例 (.claude/settings.json)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "ruff check $CLAUDE_PROJECT_DIR" }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "echo Done" }
        ]
      }
    ]
  }
}

Hook の役割

  • 強制ルール: 編集後に必ず lint を走らせる、フォーマッタを通す。
  • 通知: タスク完了時に音/通知を出す。
  • ログ: 全プロンプトをファイルに記録 (監査)。
  • ガード: 危険なコマンドをブロック (PreToolUse で exit code 2)。
  • 自動 git checkpoint: 編集前に commit を打つ。

exit code の意味

  • 0: 正常 (続行)。
  • 2: ブロック (Claude がそのアクションを取り消す)。stderr の文言が次のターンに渡されるので、Claude が修正を試みる。
  • それ以外: 警告として扱われる。

環境変数

Hook 実行時には便利な環境変数が渡されます: - $CLAUDE_PROJECT_DIR: プロジェクトルート - $CLAUDE_TOOL_NAME: 呼ばれたツール名 (PreToolUse / PostToolUse) - $CLAUDE_TOOL_INPUT_JSON: ツール入力 (PreToolUse)

実用例 1: 編集後フォーマット

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "prettier --write $CLAUDE_PROJECT_DIR && eslint --fix $CLAUDE_PROJECT_DIR"
      }]
    }]
  }
}

実用例 2: 危険コマンドブロック

PreToolUseBash ツールに対し、入力に rm -rf / などが含まれていたら exit 2 でブロック。

#!/bin/bash
# .claude/scripts/guard-bash.sh
input=$(echo "$CLAUDE_TOOL_INPUT_JSON" | jq -r '.command')
if [[ "$input" =~ rm\ -rf\ / ]]; then
  echo "Blocked: dangerous rm -rf detected" >&2
  exit 2
fi

実用例 3: ロギング

{
  "hooks": {
    "UserPromptSubmit": [{
      "hooks": [{ "type": "command", "command": "echo "$(date -Iseconds) $(pwd) $CLAUDE_USER_PROMPT" >> ~/claude.log" }]
    }]
  }
}

注意

  • Hook は ホスト OS 上で実行される ので、悪意あるリポジトリの settings.json に注意。
  • 重い処理を入れると体感が悪化する。lint は変更ファイルだけに絞るなど工夫を。
  • settings.local.json (gitignore 推奨) に個人設定、settings.json をチーム共有用、と分けるのが定番。

本番運用パターン集 (コピペで使える)

パターン 1: 編集後に変更ファイルだけフォーマット (高速)

「全プロジェクトを毎回フォーマット」は遅すぎる。変更ファイルだけ に絞る:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "echo \"$CLAUDE_TOOL_INPUT_JSON\" | jq -r '.file_path' | xargs -I{} ruff format {}"
      }]
    }]
  }
}

→ 編集された そのファイルだけ ruff format が走る。100 ms 以内で終わる。

パターン 2: シークレット流出をブロック (PreToolUse)

.env や鍵ファイルへの 書き込み・読み込み を未然にブロック:

.claude/scripts/guard-secrets.sh:

#!/usr/bin/env bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // empty')
cmd=$(echo "$input" | jq -r '.tool_input.command // empty')

# .env, *.pem, *.key, secrets/ への書き込みを拒否
if [[ "$file" =~ \.env(\..+)?$ ]] || [[ "$file" =~ \.(pem|key|p12)$ ]] || [[ "$file" =~ ^secrets/ ]]; then
  echo "Blocked: writes to secret files are forbidden ($file)" >&2
  exit 2
fi

# Bash で curl https://... | bash 系を拒否
if [[ "$cmd" =~ curl.*\|.*bash ]] || [[ "$cmd" =~ wget.*\|.*sh ]]; then
  echo "Blocked: piping curl/wget to shell is forbidden" >&2
  exit 2
fi

exit 0

settings.json:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Edit|Write|Bash",
      "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/scripts/guard-secrets.sh" }]
    }]
  }
}

→ Claude が .env を編集しようとした瞬間に止まり、stderr の文言を見て自己修正を試みる。

パターン 3: 全プロンプトをログに残す (監査)

{
  "hooks": {
    "UserPromptSubmit": [{
      "hooks": [{
        "type": "command",
        "command": "jq -r '\(.timestamp) \(.prompt)' >> $HOME/.claude/audit.log"
      }]
    }]
  }
}

→ 全プロンプトが ~/.claude/audit.log に記録される。コンプライアンス要件のある現場で必須。

パターン 4: セッション終了時に WIP コミット

「Claude Code を閉じる前に必ず checkpoint」を強制:

{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "cd $CLAUDE_PROJECT_DIR && git diff --quiet || (git add -A && git commit -m 'wip: claude session end')"
      }]
    }]
  }
}

→ 未コミット変更があればセッション終了時に自動 commit。作業ロスト防止

パターン 5: 危険 git コマンドの追加ガード

PreToolUseBash を Inspect:

#!/usr/bin/env bash
cmd=$(jq -r '.tool_input.command' < /dev/stdin)
case "$cmd" in
  *"git push --force"*|*"git push -f "*)
    echo "Blocked: force push is forbidden. Use --force-with-lease if necessary, manually." >&2
    exit 2
    ;;
  *"git reset --hard"*)
    echo "Blocked: hard reset can lose work. Confirm manually." >&2
    exit 2
    ;;
  *"rm -rf "*)
    echo "Blocked: rm -rf is restricted. Use a more specific pattern." >&2
    exit 2
    ;;
esac
exit 0

Hooks デバッグのコツ

  • /hooks で現在登録されている hook を確認
  • 失敗時は ~/.claude/logs/hooks.log に stderr が記録される (環境による)
  • command の中で set -x を入れると実行コマンドが見える
  • 開発中は echo$CLAUDE_TOOL_INPUT_JSON を出力し、構造を確認
Hands-on Exercise

演習: 編集後フォーマッタを Hook で強制

Python プロジェクトで Edit / Write 直後に ruff format が走る Hook を設定してください。

  1. .claude/settings.json を編集 (もしくは新規作成)
  2. PostToolUse + matcher "Edit|Write"
  3. command に ruff format $CLAUDE_PROJECT_DIR を設定
  4. Claude Code セッションで何かファイルを編集し、自動フォーマットを確認
▶ Playground を開いて実行
Hands-on Exercise

演習: 危険コマンドブロックの Hook

Bash ツール呼び出しで rm -rf が含まれていたらブロックする PreToolUse Hook を作ってください。

Hook スクリプト (bash) を .claude/scripts/guard.sh に置き、settings.json から呼び出します。

▶ Playground を開いて実行

理解度チェック

4 問のクイズで理解度を確認しましょう。

クイズを開く
🎉

まとめ

お疲れ様でした!