Find stale commands, paths and versions in AGENTS.md and CLAUDE.md

AGENTS.md & CLAUDE.md Linter (Context Drift), a VS Code extension by Zovo

For teams whose coding agents read AGENTS.md or CLAUDE.md, Context Drift flags the commands, paths and versions in those files that no longer match the repo.

Install in VS CodeOpen the Marketplace page

Free, MIT license. Needs VS Code 1.90.0 or newer. Extension ID zovo.context-drift, version 0.2.3.

AGENTS.md open in VS Code with squiggles under Node 18, pnpm, buidl, deploy and src/server/main.ts, and the Problems panel listing the 5 findings

A real VS Code 1.142 window running Context Drift on test/fixtures/readme-example, a 4 line AGENTS.md with 5 stale claims.

CLAUDE.md in VS Code with two @imports underlined. Cmd+. on @docs/stlye.md offers Replace with "@docs/style.md". The Problems panel also lists a missing npm script in .claude/rules/e2e.md and a 43.5 KiB AGENTS.md over the Codex limit

Real VS Code 1.142 window on a generated sample repo: two broken @imports in CLAUDE.md (CD007), a missing script in a .claude/rules file (CD001) and an AGENTS.md over Codex's 32 KiB limit (CD008).

Quick start

  1. Install Context Drift and open a folder that has an AGENTS.md, CLAUDE.md or other agent instruction file. Findings show up as squiggles and in the Problems panel.
  2. Put the cursor on a finding and press Ctrl+. (Cmd+. on macOS) to fix it.
Cmd+. on the misspelled script buidl opens the Quick Fix menu. A click on Apply all 3 Context Drift fixes in this file fixes buidl, pnpm and the moved path, and the Problems count drops from 5 to 2

Recorded in the same window. The white arrow is drawn in to show where each click lands. The 2 findings left (a missing Makefile target and the Node version) have no one-click fix.

For a short tour, run "Context Drift: Get Started" from the Command Palette.

Questions people ask

Is there a linter for AGENTS.md?

Context Drift checks whether AGENTS.md is still true for your repo. It flags npm scripts and make targets that no longer exist, paths that moved, the wrong package manager and outdated Node, Python or Go versions. For the file format itself (frontmatter, rule schemas), use agnix. The two fit together.

Which instruction files does it read?

AGENTS.md, CLAUDE.md, CLAUDE.local.md, GEMINI.md, .cursorrules, Cursor .mdc rules, .github/copilot-instructions.md, Copilot .instructions.md files, Windsurf rules, .clinerules and SKILL.md.

How do I fix a finding?

Put the cursor on the squiggle and press Ctrl+. (Cmd+. on macOS). A quick fix swaps in the right script, path or package manager. When a file has two or more fixes, "Apply all" does them in one edit.

Can I run it in CI?

Yes. The same engine runs as a CLI with npx -y github:theluckystrike/context-drift. Use --format github to get inline annotations on the pull request. Pin a tag so a later change can't alter your CI.

How many findings are false alarms?

It's built for precision. On the repositories we measured, about 9 in 10 reported findings were true. It skips negated, historical and example text, so it misses some real drift.

Does it send my code anywhere?

No. It runs locally, makes no network requests and has no telemetry. It's free and MIT licensed.

Why

Agent instruction files state facts about your repo, like which commands to run, where files live, and which package manager and runtime to use. Code moves on and these files don't. Then the agent runs a script that no longer exists, opens a file that moved, or installs with the wrong package manager.

Context Drift compares what your instruction files say with what is actually in the repo. It shows each mismatch in the editor, with the exact range and a quick fix where one is cheap.

It reads AGENTS.md, AGENTS.override.md (Codex), AGENTS.local.md and AGENT.md, CLAUDE.md, CLAUDE.local.md, .claude/rules/**/*.md, GEMINI.md, .cursorrules, .cursor/rules/**/*.mdc, .github/copilot-instructions.md, .github/instructions/**/*.instructions.md, .windsurfrules, .windsurf/rules/**/*.md, .devin/rules/**/*.md, .kiro/steering/**/*.md, .clinerules (file or folder), and SKILL.md.

It's built for precision over recall. It skips text that reads as negated, historical, hypothetical or an example, and paths it can't check, so it misses some real drift. On the repositories we measured, about 9 in 10 reported findings were true (see Precision). We haven't measured recall on real files.

Before / after

AGENTS.md in a repo that moved from pnpm to npm, renamed its deploy target, moved main.ts, and upgraded Node. Below is the real output on test/fixtures/readme-example.

A drawing of AGENTS.md with five findings underlined, and the same five findings printed by the CLI

Drawn from real output on test/fixtures/readme-example. The editor half is a drawing made from the CLI's JSON output. The CLI half is its real text output.

- Requires Node 18.
- Install with `pnpm install`.
- Build: `npm run buidl`. Deploy: `make deploy`.
- Entry point: `src/server/main.ts`
$ npx -y github:theluckystrike/context-drift
AGENTS.md:1:12: warning CD004 Version claim "Node 18" does not match the repo: .nvmrc says 20.11.0.
AGENTS.md:2:17: warning CD003 `pnpm` is used here, but the repo uses npm (package-lock.json).
AGENTS.md:3:19: error CD001 Script "buidl" is not defined in package.json. Did you mean "build"?
AGENTS.md:3:41: error CD001 `make deploy`: no target "deploy" in Makefile.
AGENTS.md:4:17: warning CD002 Path "src/server/main.ts" does not exist in the repo. Did you mean "src/main.ts"?
context-drift: 1 instruction file(s) checked in 9 ms: 2 error(s), 3 warning(s), 0 info

Here's the file after the quick fixes (and picking deploy-prod from the Makefile).

- Requires Node 20.
- Install with `npm install`.
- Build: `npm run build`. Deploy: `make deploy-prod`.
- Entry point: `src/main.ts`

What it checks

Each finding has a stable rule id, a severity, and an exact range.

CD001 command drift

It's an error for npm run X, pnpm run X and make X checked against the manifest in the right folder. Every other form is a warning (npm test, bare pnpm X / yarn X / bun run X, workspace-wide --filter/-r, just, task, cargo, uv run, a Makefile found only in a parent folder).

Commands it checks

How it reads commands

Negated and historical text

It skips negated or historical text. The words no, not, never, don't, avoid, instead of, deprecated, removed, old (and similar) in the clause before the command, or on the previous line of the same paragraph, turn the check off, as in "There is no root pnpm dev" or "don't run npm run eject". A new clause starts in four places.

A negation followed by without, forget, fail to, skip, omit, ignore is a requirement only when that word governs the command directly. So "Never commit without running npm run lint", "Don't forget to run npm run typecheck" and "Do not skip npm run typecheck" are checked. In "Do not skip tests by calling npm run skip-tests" the command is the forbidden means, so it stays skipped. Words after the command ("is deprecated", "(removed)", "no longer works") count only when the command is their subject.

These are word rules, not a grammar. Unusual sentences can still be misread, in either direction.

It also skips commands that the text says run somewhere else ("inside its package directory", "from the workspace root", "from the library"). In a sparse checkout it skips workspace-wide forms, because the packages may not be checked out. If the instruction file is a skill (SKILL.md), this rule is off, since skills usually describe other projects.

CD002 path drift

Reported as a warning (globs as info).

What it ignores

It ignores URLs, ~/ and absolute paths, and paths right after an external URL or an owner/repo name (they live in that other repo). It also skips ../ paths that climb above the opened folder (../shared/config.ts in a monorepo package opened on its own). They may exist outside it, so they aren't checked. Placeholders (<name>, path/to/, my-app, your_tool.py, mycommand.go) are skipped too.

Gitignored paths and globs are skipped. Each .gitignore in the repo and .git/info/exclude are matched against the whole path, wildcards included, and when git is allowed, git check-ignore has the last word. .gitignore negation (!folder/) isn't modeled, so a path under a re-included folder is treated as ignored and not reported.

It skips build output and scratch folders (dist, build, out, cjs, generated, …), tool state folders such as .scratchpad/, paths inside git submodules, code fences, and anything under an "Example" heading.

Text that names, creates, generates or stores a file rather than claiming it exists is skipped too ("a file named X", "make sure X/ exists", "regenerate X", "stored in X"). Generic words such as new, like and into count only within the 4 words before the path, so "New API handlers go in src/api/handlers.ts" is checked.

Special folders

CD003 package-manager drift

Reported as a warning.

CD004 version drift

Reported as a warning.

"Node 18", "Python 3.11+", "Go 1.22", nvm use 18 and pyenv local 3.10 are checked against .nvmrc and .node-version, engines.node and volta.node, .python-version and runtime.txt, requires-python and Poetry's python, go.mod (go and toolchain), and .tool-versions.

CD005 cross-file conflict

Reported as info.

CD006 stale file

Reported as info, and off by default.

CD007 import drift

Reported as a warning.

CD008 Codex size limit

Reported as a warning.

A 43.5 KiB services/billing/AGENTS.md open at line 314, where the CD008 finding in the Problems panel says Codex stops reading

Suppressing a finding

<!-- context-drift-disable-next-line CD002 -->
- `generated/schema.ts` appears after `npm run codegen`.

<!-- context-drift-disable-next-line CD001 -->   (above a code fence: the whole block)
```bash
npm run legacy-task
```

<!-- context-drift-disable CD006 -->   (whole file)

Directives count only as real HTML comments. Text in a code span that shows the syntax doesn't turn anything off. In YAML frontmatter (Cursor .mdc), use a YAML comment such as # context-drift-disable-next-line CD002. The quick fix picks the right form.

Format linting is agnix's job

Context Drift doesn't lint the format of instruction files (frontmatter, rule schemas, hook and MCP config, and so on). Use agnix for that. It has hundreds of lint rules for Claude Code, Codex, Cursor, Copilot and more. The two tools fit together. agnix checks that the file is well-formed, and Context Drift checks that it's still true. Run both.

Editor

Quick fixes can replace a script, target or path with the suggestion, or replace the package manager. When a file has two or more of these fixes, "Apply all N Context Drift fixes in this file" applies them in one edit. It shows only while the file is unchanged since the last check, so it never applies a fix at an old position. After you type, it comes back when the re-check finishes (about half a second). It leaves out a fix that overlaps another one. They can also show scripts in package.json, open the Makefile, or reveal the referenced file's folder. And they can disable a check for this line (or code block) or ignore a rule in this workspace.

Settings

SettingDefaultDescription
contextDrift.enabletrueTurn the checks on or off.
contextDrift.ignoreRules[]Rule ids to skip, for example ["CD005", "CD006"].
contextDrift.useGittrueUse read-only git check-ignore and git ls-files to skip ignored paths and files a sparse checkout leaves out. 3 s timeout per call. Always off in untrusted workspaces.
contextDrift.gitStalenessfalseTurn on CD006 (git log, read-only). Always off in untrusted workspaces.
contextDrift.staleDays180CD006 age threshold, in days.
contextDrift.staleCommits50CD006 commit threshold.
contextDrift.askForRatingtrueAsk one time for a Marketplace rating, after 5 checks and 2 days. Turn it off with false

CLI

The CLI isn't published to the npm registry. Run it from GitHub with npx, or from a clone.

npx -y github:theluckystrike/context-drift                    # check the current repo
npx -y github:theluckystrike/context-drift path/to/repo       # or a repo / a single instruction file
npx -y github:theluckystrike/context-drift --json             # machine-readable
npx -y github:theluckystrike/context-drift --format github    # GitHub Actions annotations
npx -y github:theluckystrike/context-drift --fail-on warning --ignore CD005

# or: git clone https://github.com/theluckystrike/context-drift && node context-drift/bin/context-drift.js .

Don't run npx context-drift without the github: prefix. There's no npm package by that name, and someone else could publish one.

The options are --json, --format text|json|github, --fail-on error|warning|info|never, --ignore <ids>, --stale (turn on CD006), --no-git (never run git), --stale-days, --stale-commits, and -q. Files that aren't checked (over 1 MB, a symlink that leaves the folder, lines over 4,000 characters) are listed on stderr.

The exit code is 0 when there are no findings at the --fail-on level (default error), 1 when there are findings at that level, and 2 on a usage error.

GitHub Action

Pin a tag, so a later change in the repo can't change your CI.

name: context-drift
on:
  pull_request:
    paths: ["**/*.md", "**/.cursorrules", "**/*.mdc", "package.json", "Makefile", "justfile", "pyproject.toml", "go.mod", ".nvmrc"]
concurrency: { group: context-drift-${{ github.ref }}, cancel-in-progress: true }
jobs:
  drift:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npx -y github:theluckystrike/context-drift#v0.1.3 --format github

--format github prints ::error, ::warning and ::notice workflow commands, so findings show up inline on the PR diff. Add --stale and fetch-depth: 0 on the checkout step if you want CD006.

Speed and safety

It has zero runtime dependencies. One small CommonJS engine is shared by the extension and the CLI. A 5,000-file repo takes about 20 ms on an M-series Mac. It never reads node_modules, .git, dist, build, vendor, target or .venv, and it skips nested git repos and worktrees.

Hostile repos

Globs from the repo are never turned into regular expressions. A linear-time matcher handles them instead, and every line regex is linear. Inputs are capped at 1 MB per file, 4,000 characters per line, 200 globs, 20,000 code spans, 2,000 findings per file and 32 targets per make command. Work stops at the finding cap, and a run stops after 20 s.

Tests time the adversarial inputs from both independent reviews at about 1 MB. The first review's must take under 250 ms of CPU each (they take 1–50 ms, and the quadratic versions took 5 s and more). The second review's must take under 300 ms (the slowest takes about 70 ms on an M-series Mac). Other inputs can be slower, and the 20 s budget bounds them.

Precision

Measured for 0.1.1 on the same material as the independent review of 0.1.0 (details in NOTES.md in the repository). 0.1.2 gives exactly the same warnings and errors on the 184 local folders below (re-run on 2026-11-05, none added, none removed). We compared 0.1.3 with 0.1.2 on the same 184 folders (2026-11-05) and got the same 264 findings, none added, none removed.

Privacy

Context Drift runs locally. It makes no network requests and has no telemetry. It reads files inside your workspace only, and a symlinked instruction file or manifest that points outside the workspace is skipped. In trusted workspaces it may run read-only git (check-ignore, ls-files, and log when CD006 is on). Nothing leaves your machine.

License

MIT © 2026 Zovo

Install AGENTS.md & CLAUDE.md Linter (Context Drift)

Click the button to open VS Code and install it, or press Ctrl+P (Cmd+P on macOS) in VS Code and run ext install zovo.context-drift.

Free, MIT license. Needs VS Code 1.90.0 or newer. Extension ID zovo.context-drift, version 0.2.3.

Source code and issues are on GitHub.

All our VS Code extensions

Support this work

If this extension saves you time, you can support the work on GitHub Sponsors. Monthly tiers start at $5, and one-time tiers start at $15. Sponsorship is optional, and the tool works the same without it.