Claude Code でどの CLAUDE.md がいつ読まれたかを記録する InstructionsLoaded フックの使い方
CLAUDE.md や .claude/rules/ のルールが読み込まれた瞬間に発火する InstructionsLoaded フックで、読み込まれたファイル・理由・トリガーになったファイルをログに残す方法を解説します。matcher の値、入力 JSON の各フィールド、ブロックできない制約もまとめます。
「このルール、本当に読み込まれているのか」「サブディレクトリの CLAUDE.md はいつ読まれたのか」を調べるとき、/context の一覧だけでは時系列が分かりません。Claude Code には、指示ファイルが読み込まれるたびに発火する InstructionsLoaded フックがあり、ファイル名・理由・トリガーになったファイルを外部に記録できます。
結論として、hooks.InstructionsLoaded に load_reason を matcher にしたコマンドを登録し、標準入力の JSON を jq で取り出してログに追記すれば、どの指示がいつ効き始めたかを追えます。
KEY POINT
この記事で分かること
- InstructionsLoaded が発火するタイミングと、matcher に使える
load_reasonの値 - 入力 JSON のフィールド(
file_path、memory_type、globs、trigger_file_pathなど) - ログを残すフックの設定例と、このフックでは「できないこと」
発火するタイミングと matcher
InstructionsLoaded は、CLAUDE.md または .claude/rules/*.md がコンテキストに読み込まれたときに発火します。セッション開始時に読まれるファイルで一度、その後 Claude がサブディレクトリにアクセスしてネストした CLAUDE.md が読まれたときや、paths: 付きのルールが一致したときにも再び発火します。
matcher は load_reason に対して評価されます。
load_reason | 意味 |
|---|---|
session_start | セッション開始時に読み込まれた |
nested_traversal | サブディレクトリの走査で読み込まれた |
path_glob_match | paths: フロントマターのグロブに一致して読み込まれた |
include | @path の取り込みで読み込まれた |
compact | コンパクト後に指示ファイルが再読み込みされた |
"matcher": "session_start" なら起動時だけ、"matcher": "path_glob_match|nested_traversal" なら遅延読み込みだけに絞れます。
用語解説
非同期イベント: InstructionsLoaded は Claude の処理を止めずにバックグラウンドで実行されます。終了コード 2 も、systemMessage や continue のような JSON 出力も無視されるため、読み込みをブロックしたり内容を書き換えたりはできません。
入力 JSON のフィールド
共通フィールド(session_id、transcript_path、cwd、hook_event_name など)に加えて、次のフィールドが渡されます。
| フィールド | 内容 |
|---|---|
file_path | 読み込まれた指示ファイルの絶対パス |
memory_type | スコープ。"User"、"Project"、"Local"、"Managed" のいずれか |
load_reason | 上の表の値 |
globs | paths: のグロブ一覧。path_glob_match のときだけ存在 |
trigger_file_path | 遅延読み込みのきっかけになったファイルのパス |
parent_file_path | include のとき、取り込み元の指示ファイルのパス |
公式の入力例は次のとおりです。
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "InstructionsLoaded",
"file_path": "/Users/my-project/CLAUDE.md",
"memory_type": "Project",
"load_reason": "session_start"
}
ログを残す設定例
すべての読み込みを 1 行ずつ追記する例です。.claude/settings.json に書けばチームで共有でき、自分だけなら ~/.claude/settings.json に書きます。
{
"hooks": {
"InstructionsLoaded": [
{
"matcher": "session_start|nested_traversal|path_glob_match|include|compact",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/log-instructions.sh"
}
]
}
]
}
}
#!/bin/bash
# .claude/hooks/log-instructions.sh
input=$(cat)
file_path=$(jq -r '.file_path' <<<"$input")
reason=$(jq -r '.load_reason' <<<"$input")
scope=$(jq -r '.memory_type' <<<"$input")
trigger=$(jq -r '.trigger_file_path // ""' <<<"$input")
echo "$(date -Is) [$reason/$scope] $file_path ${trigger:+<- $trigger}" >> ~/.claude/instructions.log
exit 0
スクリプトには実行権限(chmod +x)が必要です。フックの基本的な仕組みや ${CLAUDE_PROJECT_DIR} の意味は、親記事の hooks で lint と format を自動実行する と hooks の終了コードの意味 を参照してください。
ログには絶対パスが残る
file_path や trigger_file_path は絶対パスです。ホームディレクトリ名やプロジェクトの場所がそのままログに残るので、ログを共有する際は注意してください。また、このフックで読み込みを止めることはできないため、「他チームの CLAUDE.md を読ませない」目的には claudeMdExcludes を使います。
何に使うか
- パス限定ルールが期待どおりのファイルで発火しているかの確認(
globsとtrigger_file_pathを見る) - サブディレクトリの CLAUDE.md がいつ読み込まれたかの確認(
nested_traversal) - コンパクト後に指示が再注入されたことの確認(
compact) - 組織のコンプライアンス用に、どの指示ファイルがセッションに入ったかの監査
読み込みの仕組みそのものは サブディレクトリの CLAUDE.md はいつ読み込まれるか と .claude/rules/ のパス限定ルール で解説しています。
まとめ
- InstructionsLoaded は CLAUDE.md と
.claude/rules/*.mdが読み込まれるたびに発火する非同期フック - matcher は
load_reason(session_start/nested_traversal/path_glob_match/include/compact)に対して評価される - 入力には
file_path、memory_type、globs、trigger_file_path、parent_file_pathが含まれる - ブロックも書き換えもできない。用途はログ・監査・可観測性
- 読み込みを止めたいなら
claudeMdExcludesを使う
よくある質問
- InstructionsLoaded フックで CLAUDE.md の読み込みを止められますか?
- 止められません。このフックは決定制御を持たず、終了コードも JSON 出力も無視されます。監査ログや可観測性のための非同期イベントです。
- サブディレクトリの CLAUDE.md が読まれたときだけ発火させるには?
- matcher に nested_traversal を指定します。パス限定ルールも含めるなら path_glob_match|nested_traversal です。
- コンパクト後に CLAUDE.md が再読み込みされたことも分かりますか?
- はい。load_reason が compact のイベントとして発火します。
参考にした一次情報
この記事は公式ドキュメントを基に AI が下書きを作成し、運営者が内容を確認して公開しています。誤りを見つけた場合は お問い合わせからお知らせください。