C
Claude Academy
Hooks

Hooks で振る舞いを制御する

約 14 分 · クイズ 4 問 · 演習 2 問
重要キーワード (4 語)
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 演習

演習 1: 編集後フォーマッタを 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 を開いて実行
💡 ヒント

ruff がインストールされていない場合は pip install ruff で入ります。設定の検証は Claude Code 側で /hooks を打つと現在有効な hook が確認できます。

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

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

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

▶ Playground を開いて実行
💡 ヒント

exit 2 のとき stderr に書いた文言が Claude に渡るので、ブロック理由をきちんと出力すること。jq が必要です。

進捗保存にはログインが必要 クイズに挑戦 (4問)

💬 このレッスンへの質問 (0)

全質問を見る →
質問の投稿には ログイン が必要です。閲覧は誰でも可能です。

まだ質問はありません。最初の 1 件を投稿してみましょう。