コンテンツへスキップ

media AI活用の最前線

Claude Code Hooks完全ガイド2026|PreToolUse実装

Claude Code Hooks完全ガイド2026|PreToolUse実装

結論: Claude Code Hooksは、AIエージェントの動作を「手動の承認待ち」から「自動・決定的な制御」に変える仕組みです。PreToolUseでコマンドをブロックし、PostToolUseでフォーマッタを自動実行し、Stopでセッション終了処理を確実に走らせることができます。

この記事の要点:

  • 要点1: Hooksは2026年現在20種類以上のライフサイクルイベントをカバーし、settings.jsonに数行書くだけで有効化できる
  • 要点2: PreToolUseはexit 2を返すだけでコマンドをブロックできる唯一の「拒否ゲート」として機能する
  • 要点3: PostToolUse + Prettier/ESLintの組み合わせで、Claude Codeが編集するたびにコードが自動整形される

対象読者: Claude Codeを導入済みで、反復作業の自動化・セキュリティ強化を検討しているエンジニア・開発チームリーダー

読了後にできること: 今日すぐsettings.jsonにHookを1本追加し、Claude Codeが編集するたびにPrettierが自動実行される環境を構築できる


「Claude Codeに.envファイルを触られてヒヤリとしたことはありませんか。」

企業向けのAI研修で、こんな話をよく聞きます。Claude Codeは優秀なのですが、たまに「あっ、そこは触ってほしくなかった」という操作をする。エンジニアが常に画面をにらんでいればいいのですが、それでは結局AIに仕事を任せる意味が薄れてしまいます。

そこで2026年、Anthropicが投入したのがHooks(フック)という仕組みです。「PreToolUse」「PostToolUse」「Stop」といったライフサイクルイベントにシェルコマンドを紐づけることで、Claude Codeの動作を「手動承認待ち」から「自動・決定的な制御」に変えられます。

この記事では、Hooksの全体像から各イベントの実装例、実用パターン10選、デバッグのコツまで、コピペ可能な設定例つきで徹底解説します。Claude Codeを使いこなしているチームと、まだ恐る恐る使っているチームの差は、Hooksを知っているかどうかにあると言っても過言ではありません。

なお、Claude Codeの基本的な機能や全体像についてはClaude Code完全ガイドでまとめています。こちらと合わせて読むと、全体像がより明確になります。


まず試したい「今すぐ使えるHook」3選

難しい話の前に、今すぐ試せる設定を3つ出します。すべて~/.claude/settings.jsonに貼るだけで動きます。

即効Hook 1:Prettierの自動実行(PostToolUse)

Claude Codeがファイルを編集するたびにPrettierが自動実行されます。「AIが書いたコードなのにフォーマットが崩れている」という状況が完全になくなります。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

設定の意味: matcherEdit|Writeと書くと、ファイル編集・作成ツールの実行後だけ動作します。jqでstdinのJSONからファイルパスを取り出し、Prettierに渡しています。

即効Hook 2:.envファイルの保護(PreToolUse)

Claude Codeが.envpackage-lock.jsonを書き換えようとした瞬間にブロックします。

#!/bin/bash
# .claude/hooks/protect-sensitive-files.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

PROTECTED_PATTERNS=(".env" ".env.local" ".env.production" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "ブロック: '$pattern' は保護対象ファイルです" >&2
    exit 2
  fi
done

exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/protect-sensitive-files.sh"
          }
        ]
      }
    ]
  }
}

exit 2を返すと、Claude Codeはその操作をキャンセルします。stderrに書いたメッセージがClaudeに返り、「なぜブロックされたか」を理解した上で別の方法を探してくれます。

即効Hook 3:タスク完了通知(Notification)

Claude Codeが承認を求めて止まったとき、macOSの通知センターに表示されます。「待ってたのに実は止まってた」を防げます。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification "Claude Codeが入力を待っています" with title "Claude Code" sound name "Ping"'"
          }
        ]
      }
    ]
  }
}

Linuxならnotify-send 'Claude Code' '入力待ちです'、Windowsならpowershell.exe -Command "[System.Windows.Forms.MessageBox]::Show(...)"で同様のことができます。


Hooksとは何か — なぜ手動操作の自動化が重要なのか

Claude Codeは優秀ですが、「LLMとして動いている」という本質的な性質があります。同じ命令をしても、毎回まったく同じ手順を踏む保証はありません。「コード修正後は必ずPrettierを実行して」と言っても、タスクの流れによってはスキップされることがあります。

Hooksはこの問題を根本から解決します。公式ドキュメントには次のように書かれています。

Hooks provide deterministic control over Claude Code’s behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them.

Anthropic公式 Hooks Guide

「LLMに任せるのではなく、必ず実行されることを保証する」——これがHooksの本質です。

Hooksが解決する3つの問題

問題HooksなしHooksあり
コードフォーマットClaudeが忘れることがあるPostToolUseで100%自動実行
機密ファイル保護プロンプトで都度指示が必要PreToolUseで完全ブロック
セッション終了確認見落としが発生するStopで確実に実行

研修でClaude Codeを教えていると、「Hooksを知った前と後で使い方が全然変わった」という声を非常によく聞きます。AIの動作を「お願いベース」から「仕組みベース」に移行できるのが、Hooksの最大の価値なんです。


AI活用、何から始めればいい?

100社以上の研修実績をもとに、30分の無料相談で貴社の課題を整理します。

無料相談はこちら 資料ダウンロード(無料)

Hookの種類 — 2026年現在の全ライフサイクルイベント

2026年現在、Claude Code HooksはAnthropicが公開しているリファレンスで20種類以上のイベントをサポートしています。大きく5カテゴリに分類できます。

カテゴリ1: セッション系

イベント発火タイミング主な用途
SessionStartセッション開始・再開時コンテキスト注入、環境変数ロード
Setup--init-only起動時CI/CDの初期化処理
SessionEndセッション終了時後処理、ログ保存

カテゴリ2: 会話フロー系(最重要)

イベント発火タイミング主な用途
UserPromptSubmitプロンプト送信直後(Claude処理前)コンテキスト追加、プロンプト監査
StopClaude応答終了時通知、セッション終了検証
NotificationClaude通知送信時デスクトップ通知、Slack連携

カテゴリ3: ツール実行系(セキュリティの要)

イベント発火タイミングブロック可否主な用途
PreToolUseツール実行直前可(exit 2)セキュリティゲート、ファイル保護
PostToolUseツール実行成功後不可自動フォーマット、テスト実行
PostToolUseFailureツール実行失敗後不可エラーログ、リトライ処理
PermissionRequest権限ダイアログ表示時可(deny/allow)特定操作の自動承認

カテゴリ4: ファイル・環境変化系

イベント発火タイミング主な用途
CwdChanged作業ディレクトリ変更時direnvによる環境変数再ロード
FileChanged監視ファイル変更時設定ファイル変更の即時反映
ConfigChange設定ファイル変更時変更監査ログ
InstructionsLoadedCLAUDE.mdロード時指示の整合性チェック

カテゴリ5: Sub-Agent・コンパクション系

イベント発火タイミング主な用途
SubagentStartSub-Agent起動時ログ、リソース管理
SubagentStopSub-Agent終了時結果集約、通知
PreCompactコンテキスト圧縮前重要情報の保全
PostCompactコンテキスト圧縮後コンテキスト再注入

settings.jsonへの登録方法

設定ファイルの場所と優先順位

Hooksの設定は2か所に置けます。

場所パススコープ用途
ユーザーレベル~/.claude/settings.json全プロジェクト共通個人の作業スタイル(通知、フォーマット等)
プロジェクトレベル.claude/settings.jsonそのプロジェクトのみチーム共有ルール(ファイル保護、必須チェック等)

プロジェクトレベルの設定はGitにコミットしてチームで共有するのが基本的な使い方です。「チーム全員のClaude Codeが同じルールで動く」という状態を作れます。

基本構造

{
  "hooks": {
    "イベント名": [
      {
        "matcher": "ツール名(正規表現)",
        "hooks": [
          {
            "type": "command",
            "command": "実行するシェルコマンド"
          }
        ]
      }
    ]
  }
}

複数のイベントを一つのファイルに並べて書けます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/protect-files.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification "Claude Codeが入力を待っています" with title "Claude Code"'"
          }
        ]
      }
    ]
  }
}

hookのtype(種類)

hookのtypeフィールドには4種類を指定できます。

type動作用途
commandシェルコマンドを実行最も一般的。スクリプト呼び出し等
httpURLにJSONをPOST外部APIへの通知、Webhook連携
mcp_tool接続済みMCPサーバーのツールを呼ぶMCP統合フロー
promptLLMによる単発評価判断が必要な条件チェック

/hooksコマンドで確認する

設定後は/hooksと入力すると、登録済みのHookの一覧と詳細(イベント・matcher・type・ソースファイル)を確認できます。設定ミスの検出にも使えます。


PreToolUse実装例 — コマンドブロック・パラメータ修正

PreToolUseは「ツール実行直前に割り込む」最強のHookです。exit 2を返すとツール実行がキャンセルされ、stderrに書いたメッセージがClaudeに返ります。

実装例1: 危険コマンドの検知・ブロック

#!/bin/bash
# .claude/hooks/security-guard.sh

INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Bashツールの場合のみチェック
if [[ "$TOOL_NAME" == "Bash" ]]; then
  # 危険なパターンのリスト
  DANGEROUS_PATTERNS=(
    "rm -rf /"
    "rm -rf ~"
    "git push --force.*main"
    "git push --force.*master"
    "DROP TABLE"
    "DROP DATABASE"
    "> /dev/sda"
    "chmod -R 777 /"
  )

  for pattern in "${DANGEROUS_PATTERNS[@]}"; do
    if echo "$COMMAND" | grep -qE "$pattern"; then
      echo "セキュリティエラー: '$pattern' は実行できません" >&2
      echo "安全な代替案を使用してください" >&2
      exit 2
    fi
  done
fi

exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/security-guard.sh"
          }
        ]
      }
    ]
  }
}

BashのmatcherにはBash(git push *)のようにサブコマンドまで絞り込む記法も使えます。Bash(git push --force*)と書けばforce pushだけをターゲットにできます。

実装例2: .mdファイルの新規作成ブロック

Claude Codeがドキュメントファイルを勝手に作りすぎるのを防ぎたい場合に有効です。

#!/bin/bash
# .claude/hooks/block-md-creation.sh

INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$TOOL_NAME" == "Write" ]] && [[ "$FILE_PATH" == *.md ]]; then
  echo "ブロック: .mdファイルの新規作成は禁止されています" >&2
  echo "ドキュメントを作成する場合は明示的に指示してください" >&2
  exit 2
fi

exit 0

実装例3: git pushの前に確認を要求する

#!/bin/bash
# .claude/hooks/git-push-review.sh

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if echo "$COMMAND" | grep -q "git push"; then
  # 差分サマリーをClaudeへフィードバック
  echo "git pushを実行しようとしています。" >&2
  echo "=== 現在のgit diff HEAD~1..HEAD ===" >&2
  git diff HEAD~1..HEAD --stat 2>&1 >&2
  echo "" >&2
  echo "問題がある場合はexit 2で止めてください。続行する場合はexit 0を返します" >&2
  # 今回は止めずに警告のみ (exit 2なら完全ブロック)
  exit 0
fi

exit 0

PreToolUseの入力JSON構造

PreToolUseイベントが発火すると、stdinに以下のJSONが渡されます。

{
  "session_id": "abc123",
  "cwd": "/Users/user/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}

Writeツールの場合はtool_input.file_pathtool_input.contentが、Editツールの場合はtool_input.file_pathと変更内容が含まれます。


PostToolUse実装例 — 自動フォーマット・Lint・型チェック

PostToolUseはツール実行成功後に走るHookです。「Claudeが書いたコードを即座に品質チェックする」という使い方に最適です。ツール実行をブロックすることはできませんが、stdoutに書いた内容がClaudeにフィードバックされるため、問題があれば次のターンで自動修正できます。

実装例4: TypeScript型チェックの自動実行

#!/bin/bash
# .claude/hooks/typescript-check.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# .tsまたは.tsxファイルの編集時のみ実行
if [[ "$FILE_PATH" == *.ts ]] || [[ "$FILE_PATH" == *.tsx ]]; then
  echo "TypeScript型チェックを実行しています..."

  # tsconfig.jsonがあれば型チェック
  if [[ -f "tsconfig.json" ]]; then
    TSC_OUTPUT=$(npx tsc --noEmit 2>&1)
    TSC_EXIT=$?

    if [[ $TSC_EXIT -ne 0 ]]; then
      echo "型エラーが検出されました:"
      echo "$TSC_OUTPUT"
    else
      echo "型チェック: OK"
    fi
  fi
fi
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/typescript-check.sh"
          }
        ]
      }
    ]
  }
}

実装例5: ESLintの自動実行

#!/bin/bash
# .claude/hooks/eslint-check.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# JS/TSファイルの編集時のみ実行
if [[ "$FILE_PATH" == *.js ]] || [[ "$FILE_PATH" == *.jsx ]] || 
   [[ "$FILE_PATH" == *.ts ]] || [[ "$FILE_PATH" == *.tsx ]]; then

  if command -v npx &>/dev/null && [[ -f ".eslintrc.js" || -f ".eslintrc.json" || -f "eslint.config.js" ]]; then
    LINT_OUTPUT=$(npx eslint "$FILE_PATH" 2>&1)
    LINT_EXIT=$?

    if [[ $LINT_EXIT -ne 0 ]]; then
      echo "ESLintエラー:"
      echo "$LINT_OUTPUT"
    fi
  fi
fi

実装例6: Pythonのblack + ruffの自動実行

#!/bin/bash
# .claude/hooks/python-format.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$FILE_PATH" == *.py ]]; then
  # blackでフォーマット
  if command -v black &>/dev/null; then
    black "$FILE_PATH" 2>&1
  fi

  # ruffでLint
  if command -v ruff &>/dev/null; then
    RUFF_OUTPUT=$(ruff check "$FILE_PATH" 2>&1)
    if [[ -n "$RUFF_OUTPUT" ]]; then
      echo "Ruff警告:"
      echo "$RUFF_OUTPUT"
    fi
  fi
fi

非同期Hook(async: true)

Hookの実行時間が長くなりそうな場合はasync: trueを追加することで、バックグラウンド実行にできます。Claudeの次の処理をブロックしません。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/run-all-tests.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

テスト実行のような時間がかかる処理にはasync: trueが有効です。結果をファイルに書き出しておき、次のセッションで確認するフローにするとスムーズです。


Stop実装例 — セッション終了時の検証

Stopイベントは「Claudeが応答を終えた直後」に発火します。セッション全体を通じた検証、後処理、ログ記録に使います。

実装例7: console.logの残留チェック

「本番環境にconsole.logが混入した」というよくある事故を防ぎます。

#!/bin/bash
# ~/.claude/hooks/check-console-logs.sh
# Stop hookとして登録(セッション終了ごとに実行)

FOUND=$(grep -rn "console.log" --include="*.ts" --include="*.tsx" --include="*.js" 
  --exclude-dir=node_modules --exclude-dir=".git" . 2>/dev/null)

if [[ -n "$FOUND" ]]; then
  echo "⚠️  console.logが残っています:"
  echo "$FOUND"
  echo ""
  echo "本番デプロイ前に削除することを推奨します"
fi
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/check-console-logs.sh"
          }
        ]
      }
    ]
  }
}

実装例8: セッション終了時のSlack通知

#!/bin/bash
# ~/.claude/hooks/slack-notify.sh

WEBHOOK_URL="${SLACK_WEBHOOK_URL}"
SESSION_ID=$(cat | jq -r '.session_id // "unknown"')

if [[ -n "$WEBHOOK_URL" ]]; then
  curl -s -X POST "$WEBHOOK_URL" 
    -H "Content-Type: application/json" 
    -d "{"text": "Claude Codeセッション完了 (ID: $SESSION_ID)"}" 
    > /dev/null
fi

実用パターン10選

Hooksを使う上で実際に価値が高い10のパターンをまとめました。すべて公式サポートされた方法です。

パターン1: git push前の差分確認

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(git push*)",
        "hooks": [
          {
            "type": "command",
            "command": "git log --oneline origin/main..HEAD && echo 'pushを確認しました'"
          }
        ]
      }
    ]
  }
}

パターン2: .mdファイル作成ブロック

Claude Codeが不要なドキュメントを自動生成し続けるのを防ぎます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "INPUT=$(cat); FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty'); if [[ "$FILE" == *.md ]]; then echo 'Markdownファイルの新規作成は禁止' >&2; exit 2; fi"
          }
        ]
      }
    ]
  }
}

パターン3: TypeScript自動チェック

(前述の実装例4参照。settings.jsonに追加するだけで動作します)

パターン4: Prettierの自動実行

(前述の即効Hook 1参照)

パターン5: ESLintの自動実行

(前述の実装例5参照)

パターン6: デスクトップ通知

(前述の即効Hook 3参照)

パターン7: SecretGuard(シークレット漏洩防止)

#!/bin/bash
# .claude/hooks/secret-guard.sh

INPUT=$(cat)
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // empty')

# APIキーパターンの検出
PATTERNS=(
  "sk-[a-zA-Z0-9]{20,}"        # OpenAI API Key
  "AIza[0-9A-Za-z_-]{35}"      # Google API Key
  "AKIA[0-9A-Z]{16}"           # AWS Access Key
  "ghp_[a-zA-Z0-9]{36}"        # GitHub Personal Access Token
  "xoxb-[0-9]+-[0-9A-Za-z]+"  # Slack Bot Token
)

for pattern in "${PATTERNS[@]}"; do
  if echo "$CONTENT" | grep -qE "$pattern"; then
    echo "セキュリティアラート: APIキーやシークレットが含まれている可能性があります" >&2
    echo "ファイルへの書き込みをブロックしました" >&2
    exit 2
  fi
done

exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/secret-guard.sh"
          }
        ]
      }
    ]
  }
}

パターン8: コンテキスト圧縮後の重要情報再注入

Claude Codeのコンテキストが満杯になると自動圧縮されますが、重要な情報が失われることがあります。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo '重要: このプロジェクトはBunを使用。npmは使わないこと。コミット前にbun testを必ず実行。現在のスプリント: 認証リファクタリング'"
          }
        ]
      }
    ]
  }
}

パターン9: direnv連携(ディレクトリ別環境変数)

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > "$CLAUDE_ENV_FILE""
          }
        ]
      }
    ],
    "CwdChanged": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > "$CLAUDE_ENV_FILE""
          }
        ]
      }
    ]
  }
}

パターン10: 変更監査ログ

{
  "hooks": {
    "ConfigChange": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
          }
        ]
      }
    ]
  }
}

エラーハンドリングとデバッグ

exit codeの意味を正確に理解する

Hooksで最も重要な概念がexit codeです。間違えると「止めたつもりが止まっていない」という事故が起きます。

exit code動作stderrの扱い
0成功。ツール実行は続行表示されない
1エラー発生。ただしツール実行は続行Claudeへのフィードバックとして渡される
2ブロック。ツール実行をキャンセルClaudeへのフィードバックとして渡される(なぜ止まったかを説明できる)

セキュリティ重要なHookは必ずexit 2を使います。exit 1は「エラーがあったが続行する」という意味なので、セキュリティゲートとしては機能しません。これが実際によくある誤解のひとつです。

デバッグの基本手順

Hookが期待通りに動かない場合の確認手順です。

# 1. hookスクリプトを単体で実行してみる
echo '{"tool_name":"Write","tool_input":{"file_path":"test.env"}}' | bash .claude/hooks/protect-sensitive-files.sh

# 2. exit codeを確認
echo $?

# 3. /hooksコマンドで登録状態を確認
# (Claude Code CLIで) /hooks

# 4. jqが正しく動くか確認
echo '{"tool_input":{"file_path":"test.ts"}}' | jq -r '.tool_input.file_path'

スクリプトの実行権限を忘れずに

# 必須: スクリプトに実行権限を付与
chmod +x .claude/hooks/protect-sensitive-files.sh
chmod +x .claude/hooks/security-guard.sh

実行権限がないとHookが無音でスキップされます。設定したのに動かない場合はまずここを確認してください。

標準入出力の流れ

#!/bin/bash
# デバッグ用:受け取ったJSONを全部ログに残す

INPUT=$(cat)
echo "[$(date)] Hook input: $INPUT" >> /tmp/claude-hook-debug.log

# 通常の処理へ続く...
exit 0

Hookに何が渡ってきているかを確認したい場合は、このように一時的にログを残す方法が手軽です。


パフォーマンス考慮 — Hookの実行時間制約

Hookはツール実行の直前・直後に走るため、処理時間が長いとClaude Codeの全体的なレスポンスが遅くなります。

推奨される実行時間の目安

Hook種類推奨実行時間対応策
PreToolUse1秒以内単純なパターンマッチのみに絞る
PostToolUse(同期)3秒以内重い処理はasync: trueで非同期化
Stop5秒以内セッション終了後なので比較的余裕あり

重い処理の非同期化

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write "$(cat | jq -r '.tool_input.file_path')"",
            "comment": "Prettierは速いので同期OK"
          }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/run-full-test-suite.sh",
            "async": true,
            "comment": "テストスイートは非同期で実行"
          }
        ]
      }
    ]
  }
}

matcher を絞り込んで不要な実行を減らす

matcherを""(全マッチ)にすると、あらゆるツール実行でHookが走ります。TypeScriptチェックをBash実行時にも走らせる必要はありません。

# 悪い例: 全ツールで型チェックが走る
"matcher": ""

# 良い例: Edit/Writeのみで型チェックが走る
"matcher": "Edit|Write"

# さらに良い例: スクリプト内でファイル拡張子も確認する
if [[ "$FILE_PATH" == *.ts ]] || [[ "$FILE_PATH" == *.tsx ]]; then
  # チェック実行
fi

【要注意】失敗パターン4選

Hooksを使い始めた開発者がはまりやすい失敗を4つまとめました。これを知っているだけで、多くの試行錯誤を避けられます。

失敗1: exit 1で止めようとする

❌ よくある間違い:

if [[ "$FILE_PATH" == *.env ]]; then
  echo "保護ファイルです" >&2
  exit 1  # これではブロックできない!
fi

⭕ 正しいアプローチ:

if [[ "$FILE_PATH" == *.env ]]; then
  echo "保護ファイルです" >&2
  exit 2  # exit 2でブロック
fi

exit 1はエラーを報告しつつツール実行を続行させる意味です。完全にブロックするには必ずexit 2を使います。企業のセキュリティポリシーをHookで実装する場合、この誤解が致命的になり得るので要注意です。

失敗2: スクリプトに実行権限を付与し忘れる

❌ よくある間違い: スクリプトを作成しただけで動かないと思い込む

⭕ 正しいアプローチ:

chmod +x .claude/hooks/my-hook.sh

実行権限がないとHookは無音でスキップされます。設定したのに動かない場合の最初の確認ポイントはここです。

失敗3: matcherを広げすぎてパフォーマンスが悪化する

❌ よくある間違い:

{
  "matcher": "",
  "hooks": [{"type": "command", "command": "npx tsc --noEmit"}]
}

⭕ 正しいアプローチ:

{
  "matcher": "Edit|Write",
  "hooks": [{"type": "command", "command": ".claude/hooks/typescript-check.sh"}]
}

さらにスクリプト内でファイル拡張子もチェックし、.ts/.tsxファイルのみで実行するようにしてください。

失敗4: stdin入力を読み取らずにパスをハードコードする

❌ よくある間違い:

npx prettier --write ./src/  # どのファイルを編集したか関係なく全部フォーマット

⭕ 正しいアプローチ:

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ -n "$FILE_PATH" ]]; then
  npx prettier --write "$FILE_PATH"
fi

stdinから渡されるJSONに「どのファイルを編集したか」の情報が入っています。これを使うことで、関係のないファイルまで処理してしまう問題を防げます。


まとめ:今日から始める3つのアクション

Claude Code HooksはAIエージェントの動作を「お願いベース」から「仕組みベース」に変える、実運用に欠かせない機能です。最初は小さく始めて、徐々に拡張していくのが定着のコツです。

  1. 今日やること: ~/.claude/settings.jsonに通知Hook(Notification)を1本追加する。Claude Codeが止まったときに気づける環境を作るのが最初の一歩
  2. 今週中: プロジェクトの.claude/settings.json.env保護のPreToolUseを追加してチームでGit共有する。チーム全員のClaude Codeが同じセキュリティルールで動くようになる
  3. 今月中: PostToolUseにPrettier+ESLint+型チェックのセットを組み込んで「AIが書いたコードが即座に品質チェックされる」パイプラインを完成させる

あわせて読みたい:


参考・出典


著者: 佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。早稲田大学法学部在学中に生成AIの可能性に魅了され、X(旧Twitter)で活用法を発信(@SuguruKun_ai、フォロワー約10万人)。100社以上の企業向けAI研修・導入支援を展開。著書『AIエージェント仕事術』(SBクリエイティブ)。SoftBank IT連載7回執筆(NewsPicks最大1,125ピックス)。

あわせて読みたい: HooksをPlugin内にバンドルしてチームに配布する方法は、Claude Code Plugin開発完全ガイド2026で解説しています。

ご質問・ご相談はお問い合わせフォームからお気軽にどうぞ。

佐藤傑
この記事を書いた人 佐藤傑

株式会社Uravation代表取締役。早稲田大学法学部在学中に生成AIの可能性に魅了され、X(旧Twitter)で活用法を発信(@SuguruKun_ai、フォロワー10万人超)。100社以上の企業向けAI研修・導入支援を展開。著書累計3万部突破。SoftBank IT連載7回執筆(NewsPicks最大1,125ピックス)。

この記事をシェア

Claude Codeを本格的に使いこなしたい方へ

週1回・1時間のマンツーマン指導で、3ヶ月後にはClaude Codeで自走できる実力が身につきます。
現役エンジニアが貴方の業務に合わせてカリキュラムをカスタマイズ。

✓ 1対1のマンツーマン ✓ 全12回・3ヶ月 ✓ 実務ベースの指導
Claude Code 個別指導の詳細を見る まずは無料相談

contact お問い合わせ

生成AI研修や開発のご依頼、お見積りなど、
お気軽にご相談ください。

Claude Code 個別指導(1対1・12セッション)をご希望の方はこちらから別途お申し込みください

Claude Code 個別指導 無料相談