1. Paste your instructions file
2. What drifted
| Kind | Reference | Detail |
|---|
Prefer a CLI, or want this as a CI gate?
Same logic, zero dependencies, one file:
node cli.mjs check AGENTS.md # resolves refs from the current directory node cli.mjs check AGENTS.md --root . # explicit root node cli.mjs check CLAUDE.md --package package.json # explicit package.json node cli.mjs check AGENTS.md --json # machine-readable
Get cli.mjs — Node 18+, nothing to install. It reads only the instructions file, an optional package.json, and the local paths the instructions file itself names: no network call, ever. Exits 1 if anything is found, so it works as a CI gate on the same review that renames a file or a script.
What's checked
| Check | How |
|---|---|
| Broken file reference | A markdown link target or an inline `code span` that looks like a path (has a slash and a letter-led extension, or is a well-known bare filename like README.md) is resolved against your repo root and flagged if it doesn't exist |
| Undefined npm script | npm run <name>, npm test, and npm start references are checked against your package.json's scripts map, if you supply one |
| Interpreter-mismatch footgun | A bare pytest or mypy inside a fenced code block, without a python3 -m (or python -m) prefix on the same line |
Restricted to markdown links and inline code spans on purpose — that is how instruction files actually cite a path when they mean it literally, and scanning ordinary prose for anything path-shaped would flag far more false positives than real drift. A shorthand reference that relies on nearby prose for its directory ("the same validate.py as above") reports as not found even when a longer form of the same path exists elsewhere — a known, disclosed limitation, not a bug. See why these files rot, and where this checker's own edges are, or this checker vs. checking it by hand.
Common questions
Why do AI agent instruction files go stale?
AGENTS.md and CLAUDE.md are prose, so nothing mechanical keeps them in sync with the repo the way a compiler keeps a type signature in sync with a call site. A script gets renamed, a doc gets moved, a package.json script gets renamed in a refactor -- and the instruction file still points an agent (or a new human contributor) at the old name. The agent then either fails on a command that no longer exists or, worse, silently succeeds against the wrong tool.
What is an interpreter-mismatch footgun?
A bare tool invocation like pytest or mypy that, on a machine where that tool was installed in isolation (a uv tool install, a pipx install), resolves to a different Python interpreter than the one the repository's own dependencies were installed into. The command runs, produces a plausible-looking result (often "module not found"), and is easy to misread as a real failure in the code rather than a wrong interpreter. python3 -m pytest always resolves against the same interpreter as the rest of the repo's tooling.
Does this check whether the instructions are good advice?
No. It never judges tone, completeness, or whether a rule makes sense -- only whether a thing the file points at (a file path, an npm script, a known-bad shell pattern) still matches what the repo actually has today. That is a narrower, purely mechanical question, and it is the one a stale reference actually breaks on.
Is anything uploaded?
No. The browser page parses the text you paste and the file names it reads from a folder you pick entirely in your own browser, and never sends any of it anywhere. It reads only file names from the folder picker, never file contents. The CLI reads only local files -- no network call, ever.