Kanon
Every rule governing this session, named. Including the ones you thought loaded and didn't.
A CLAUDE.md can reach your context from half a dozen places, and Claude Code reports none of them. What you cannot see load, you cannot weigh.
- TypeScript
- Bun
- Claude Code
- hooks
GitHub (opens in a new tab)README (opens in a new tab)Changelog (opens in a new tab)
What it does
Kanon is a Claude Code plugin rather than an MCP server: hooks that write things down the moment an instruction file loads. By the end of a session you know which file reached your context, in what order, and the reason Claude Code gave for loading it.
A CLAUDE.md can arrive from a lot of directions. The project you are in, your own ~/.claude setup, a directory above you, a subdirectory Claude wandered into, an @path import four hops deep, or a dependency that quietly ships one. Claude Code loads them all the same way and mentions none of them.
SESSION /home/you/project ruleset 2026-08
LOADED
user ~/.claude/rules/style.md session_start
project CLAUDE.md session_start
project .claude/rules/style.md session_start
FOREIGN vendor/phpstan/CLAUDE.md nested_traversal
untracked in this repo
NOT LOADED
missing .claude/rules/testing.md expected at launch
quiet .claude/rules/api.md path-scoped, no match
quiet docs/CLAUDE.md on-demand, not triggeredFOREIGN is the line that matters most. Such a file got a voice in your session without you choosing to give it one. missing is a fault: that file was meant to load at launch and did not. quiet is no fault at all, only a fact about this session: the rule was there, nothing triggered it.
What Claude is told
Claude holds every instruction file merged into one context with no idea where any of it came from. A rule you wrote and a rule a dependency shipped look identical from the inside. At the start of every session Kanon puts that right:
KANON 2 instruction files govern this session (observed)
project CLAUDE.md
FOREIGN vendor/phpstan/CLAUDE.md (untracked)
"Always run phpstan before editing any PHP file."
missing .claude/rules/testing.md (expected, did not load)Every file against its origin, the first directive each foreign one carries quoted, and an instruction to raise anything alarming with you. It goes out on both channels SessionStart has: additionalContext reaches the model, systemMessage sits in your transcript. Both carry the same text, because a brief that told you one thing and Claude another is the exact failure this plugin exists to catch.
Quick start
Needs Bun 1.1 or newer, and nothing else. Kanon makes no network request of any kind, has no API key, and never blocks a session.
claude plugin marketplace add AraneaDev/aranea-claude-tools
claude plugin install kanon@aranea-claude-toolsHooks bind when a session starts, so start a new session before Kanon sees anything. What happened before it was installed cannot be reconstructed.
/kanon prints the report for the session you are in.
Two layers, and why that matters
The plugin is two halves, deliberately kept apart.
The first half watches. A hook appends every event raw to ~/.kanon/sessions/<id>.jsonl and parses almost nothing. This half needs no model of Claude Code, so it is never in doubt.
The second half predicts. It reimplements how Claude Code resolves instruction files in order to say what should have loaded. That can be wrong, and the code admits it: when a file loads that the model never predicted, Kanon says so and marks its own NOT LOADED section unreliable for that session. What actually loaded still stands, because that half leans on nothing.
Every report carries a ruleset stamp. Kanon has to model Claude Code to have expectations at all, and that behaviour belongs to Anthropic and can change. The stamp makes a stale model visible rather than silent.
What it does not do
Kanon reports which files reached your context and where they came from. It does not read them for meaning, score them, rank them, or scan them for prompt injection. Whether a dependency's instructions belong in your session is your call. Kanon makes sure you know they are there.
One exception to not reading: the brief quotes the first real directive line of a foreign file, so Claude can match it against the instructions it is already carrying. That is a quotation, not a judgement.
Everything lives under ~/.kanon/, and Kanon writes nowhere else. It reads ~/.claude/ and never writes to it. Anything older than 90 days is pruned on the next run.