Claude Code の SessionStart フックを全パターン検証してみた (Windows 11 + MINGW64)

Claude Code の SessionStart フックを全パターン検証してみた (Windows 11 + MINGW64)

Claude Code の SessionStart フックについて、Windows 11 + MINGW64 環境で 4 種類のマッチャーと 3 つの設定ファイル配置先の組合せを網羅的に検証しました。ドキュメント未記載の挙動も含めた検証結果を共有します。
2026.03.10

はじめに

Claude Code を使って長時間の開発作業をしていると、コンテキストウィンドウが上限に近づき auto-compact が走ります。auto-compact は過去の会話を要約して圧縮する機能ですが、この圧縮によって作業中の重要な文脈が失われることがあります。

auto-compact message

CLAUDE.md にあらかじめ書いておける静的な情報であれば、コンパクション後も保持されます。しかし、以下のような動的な情報は CLAUDE.md では対処できません。

  • 現在のブランチや直近のコミット履歴
  • 進行中のタスクや未解決の issue の状態
  • セッション中に対話で伝えたプロジェクト固有のルールや方針

SessionStart フックはこの問題に対する仕組みです。セッション開始時だけでなく、/compact 後や /clear 後にもフックを発火させ、必要な情報を Claude のコンテキストに再注入できます。 本記事では、SessionStart フックの 4 種類のマッチャーと 3 つの設定ファイル配置先の全組合せを実際に動かし、その結果を報告します。

検証環境

項目 バージョン
OS Windows 11
シェル GNU bash 5.2.37 (MINGW64)
Claude Code 2.1.72
モデル claude-opus-4-6

対象読者

  • Claude Code を業務や個人開発で使用しているエンジニア
  • Claude Code のフック機能に興味があるが、まだ触ったことがない方
  • SessionStart フックの具体的な挙動を知りたい方

参考

SessionStart フックの仕組み

SessionStart フックは、セッションの開始や再開といったイベントをトリガーにシェルコマンドを実行する仕組みです。settings.json に設定を記述します。

マッチャー

SessionStart フックには 4 種類のマッチャーがあり、セッションの開始方法に応じて異なるフックを発火させられます。

マッチャー トリガー条件
startup claude コマンドで新しいセッションを開始したとき
resume claude --resumeclaude --continue でセッションを再開したとき
clear セッション内で /clear コマンドを実行したとき
compact 手動で /compact を実行、または auto-compact が走ったとき

設定ファイルの配置先

設定ファイルは 3 箇所に配置できます。

配置先 適用範囲 Git 管理
~/.claude/settings.json 全プロジェクト共通 不可
.claude/settings.json プロジェクト単位 可能
.claude/settings.local.json プロジェクト単位 不可 (ローカルのみ)

主な機能

SessionStart フックでは主に以下の機能が利用できます。

  • stdout によるコンテキスト注入
    フックスクリプトの stdout 出力が Claude のコンテキストに注入されます。Claude はこの内容を認識し、応答に活用できます。

  • CLAUDE_ENV_FILE による環境変数の永続化
    SessionStart フックでのみ利用可能な環境変数 CLAUDE_ENV_FILE を通じて、セッション全体で有効な環境変数を設定できます。

  • マッチャーによる処理分岐
    マッチャーごとに異なるフックスクリプトやコマンドを指定することで、セッション開始方法に応じた処理の分岐が可能です。

検証

検証にあたり、3 つの機能を同時にテストするフックスクリプトを作成しました。

#!/bin/bash
MATCHER="${1:-unknown}"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
LOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.logs"
mkdir -p "$LOG_DIR"

# stdin からフック入力 JSON を読み取る
INPUT=$(cat)

# ログ記録
{
  echo "[$TIMESTAMP] matcher=$MATCHER"
  echo "$INPUT"
} >> "$LOG_DIR/session-start.log"

# stdout コンテキスト注入テスト
cat <<CONTEXT
[SessionStart Hook]
matcher: $MATCHER
timestamp: $TIMESTAMP
project_dir: $CLAUDE_PROJECT_DIR
message: This text was injected by the SessionStart hook.
CONTEXT

# CLAUDE_ENV_FILE 環境変数永続化テスト
if [ -n "$CLAUDE_ENV_FILE" ]; then
  {
    echo "export TEST_HOOK_MATCHER='$MATCHER'"
    echo "export TEST_HOOK_TIMESTAMP='$TIMESTAMP'"
    echo "export TEST_HOOK_VAR='hello_from_session_start'"
  } >> "$CLAUDE_ENV_FILE"
  echo "env_file: variables written to CLAUDE_ENV_FILE"
else
  echo "env_file: CLAUDE_ENV_FILE is not set"
fi

exit 0

このスクリプトは 3 つの検証を同時に行います。

  1. stdout に情報を出力し、Claude のコンテキストに注入されるかを確認する
  2. CLAUDE_ENV_FILE に環境変数を書き込み、Claude Code からシェルコマンドを実行して参照できるかを確認する
  3. 入力 JSON とマッチャー名をログファイルに記録し、発火タイミングを確認する

設定ファイルには、4 種類のマッチャーそれぞれに対してこのスクリプトを実行する設定を記述しました。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh\" startup"
          }
        ]
      },
      {
        "matcher": "resume",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh\" resume"
          }
        ]
      },
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh\" clear"
          }
        ]
      },
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh\" compact"
          }
        ]
      }
    ]
  }
}

検証手順

設定ファイルの配置先ごとに独立したプロジェクトディレクトリを用意し、以下の手順を繰り返しました。

  1. claude コマンドで新しいセッションを開始する
  2. Claude に「SessionStart フックから注入されたメッセージは見えますか?」と質問する
  3. Claude に「echo $TEST_HOOK_VAR を実行してください」と依頼し、環境変数の反映を確認する
  4. /clear を実行する
  5. /compact を実行する
  6. セッションを終了し、claude --continue で再開する

各ステップでログファイルとトランスクリプトを確認し、フックの発火状況と入力 JSON の内容を記録しました。

結果

マッチャーごとの挙動

4 種類のマッチャーすべてが、想定通りのタイミングで発火することを確認しました。ただし、いくつかのマッチャーでは入力 JSON の構造に差異がありました。

以下は startup マッチャー発火時にフックスクリプトが stdin から受け取った JSON です。

{
  "session_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "transcript_path": "C:\\Users\\<user>\\.claude\\projects\\...\\<session>.jsonl",
  "cwd": "D:\\work\\<project-root>\\projects\\test-local",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "model": "claude-opus-4-6"
}

マッチャーごとの入力 JSON の差異を以下にまとめます。

フィールド startup clear compact resume
session_id 新規 新規 変更なし 継続元
source "startup" "clear" "compact" "resume"
model あり なし あり なし
hook_event_name あり あり あり あり
cwd あり あり あり あり
transcript_path あり あり あり あり

model フィールドが clearresume で欠落している点は、公式ドキュメントの記載と異なります。また、ドキュメントに記載されている permission_mode フィールドは、いずれのマッチャーでも含まれていませんでした。

stdout によるコンテキスト注入

フックの stdout 出力は Claude のコンテキストに取り込まれ、応答時に参照されることを確認しました。Claude に「SessionStart フックから注入されたメッセージは見えますか?」と質問したところ、フックが出力した matcher、timestamp、project_dir、message の各値を正しく認識していました。

なお、トランスクリプト上では system-reminder 相当の形で記録されていました。

CLAUDE_ENV_FILE による環境変数の永続化

フックスクリプトから CLAUDE_ENV_FILE への書き込み自体は成功しました。ログにも「CLAUDE_ENV_FILE written (matcher=startup)」のように記録されています。

しかし、セッション内で echo $TEST_HOOK_VAR を実行したところ、結果は空でした。この挙動は 3 つの設定ファイル配置先すべてで同一であり、少なくとも設定ファイル配置先の違いは原因ではなさそうです。一方で、OS やシェル、あるいは Claude Code の実行経路の違いによる可能性は残ります。なお、macOS や Linux 環境での動作は未検証です。

設定ファイル配置先による差異

3 つの配置先すべてで同一の挙動でした。

観点 settings.local.json settings.json ~/.claude/settings.json
フック実行 全マッチャー OK 全マッチャー OK 全マッチャー OK
コンテキスト注入 OK OK OK
環境変数永続化 NG NG NG
マッチャー動作 全 4 種とも発火 全 4 種とも発火 全 4 種とも発火

設定ファイルの配置先は、フックの発火タイミング、入力 JSON の構造、stdout によるコンテキスト注入の挙動に影響しません。

ドキュメント未記載の挙動

検証環境では、公式ドキュメントの記載と異なる以下 4 点の挙動を確認しました。 Windows + MINGW64 環境固有の挙動である可能性があります。

1. --continue 実行時の二重発火

claude --continue でセッションを再開すると、resume マッチャーに加えて startup マッチャーも発火することを確認しました。ログ上では同一時刻に記録されており、少なくとも実運用上は二重発火として扱う必要があります。

startup 側は新規セッション ID を持ち、resume 側は継続元のセッション ID を持ちます。resume マッチャーのみに処理を限定したい場合は、この二重発火を考慮した設計が必要です。

2. model フィールドの不一致

入力 JSON の model フィールドは startup と compact には含まれますが、clear と resume には含まれません。フックスクリプト内でモデル名を参照する場合は、このフィールドが存在しないケースを考慮する必要があります。

3. permission_mode フィールドの不在

公式ドキュメントでは入力 JSON に permission_mode フィールドが含まれると記載されていますが、今回の検証ではいずれのマッチャーでも含まれていませんでした。

4. /clear によるセッション ID の更新

/clear を実行すると新しいセッション ID が発行され、トランスクリプトファイルも新たに作成されます。一方、/compact ではセッション ID は変化しません。以下の図は、一連の操作におけるセッション ID の変遷を示しています。

まとめ

SessionStart フックは、auto-compact やセッション再開によるコンテキスト喪失に対する有効な仕組みです。4 種類のマッチャーはすべて正常に発火し、stdout によるコンテキスト注入も期待通りに動作しました。設定ファイルの配置先による動作の差異はありません。

一方で、--continue 時の startup・resume 二重発火や model フィールドの欠落など、公式ドキュメントの記載と異なる挙動が複数確認されました。 SessionStart フックを活用する際は、これらの挙動を考慮した設計をおすすめします。

この記事をシェアする

FacebookHatena blogX

関連記事