
Claude Code の SessionStart フックを全パターン検証してみた (Windows 11 + MINGW64)
はじめに
Claude Code を使って長時間の開発作業をしていると、コンテキストウィンドウが上限に近づき auto-compact が走ります。auto-compact は過去の会話を要約して圧縮する機能ですが、この圧縮によって作業中の重要な文脈が失われることがあります。

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 --resume や claude --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 つの検証を同時に行います。
- stdout に情報を出力し、Claude のコンテキストに注入されるかを確認する
CLAUDE_ENV_FILEに環境変数を書き込み、Claude Code からシェルコマンドを実行して参照できるかを確認する- 入力 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"
}
]
}
]
}
}
検証手順
設定ファイルの配置先ごとに独立したプロジェクトディレクトリを用意し、以下の手順を繰り返しました。
claudeコマンドで新しいセッションを開始する- Claude に「SessionStart フックから注入されたメッセージは見えますか?」と質問する
- Claude に「
echo $TEST_HOOK_VARを実行してください」と依頼し、環境変数の反映を確認する /clearを実行する/compactを実行する- セッションを終了し、
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 フィールドが clear と resume で欠落している点は、公式ドキュメントの記載と異なります。また、ドキュメントに記載されている 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 フックを活用する際は、これらの挙動を考慮した設計をおすすめします。






