driftlint

agent
Guvenlik Denetimi
Uyari
Health Uyari
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 9 GitHub stars
Code Gecti
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Finds the claims in your CLAUDE.md / AGENTS.md / skills that your code no longer supports. Zero-config, no API key.

README.md

driftlint

npm ci license

Your CLAUDE.md is lying to your agent. driftlint keeps agent context files true, two ways: a drift linter that finds the claims in CLAUDE.md, AGENTS.md, skills, subagents and cursor rules that your code no longer supports — and Reviewed Memory, the missing approval layer between what your agents learn and what your team ships into CLAUDE.md.

npx @alifurkangokce/driftlint          # lint this repo
npx @alifurkangokce/driftlint twins    # keep CLAUDE.md and AGENTS.md in sync

driftlint demo

Zero config. No API key. Zero runtime dependencies. Works on any repo.

The two-file problem: Claude Code reads CLAUDE.md; Codex, Cursor, Amp and 30+ other tools read AGENTS.md. Asking for both is the most-upvoted request on the Claude Code tracker — 5,200+ reactions, marked not planned — so teams keep two copies, and the copies drift silently. driftlint twins --check is the CI gate for that; the twin-drift rule catches pairs that already diverged.

Why

Coding agents trust context files completely. But code moves and context files don't: the file you renamed in March is still "the entry point" in CLAUDE.md, the deploy:prod script you deleted is still the documented release path, and the skill you wrote last month is silently invisible because your skill descriptions overflowed the system-prompt budget.

Agent knowledge decays like code documentation always has — except now the reader can't tell something is off. It just follows the instructions.

The research backs this up: an ETH Zurich evaluation measured that auto-generated context files reduce agent success while minimal human-written ones help — so the winning move is keeping the human-written file true, not generating a new one. And a 2026 study of 247k instruction lifetimes ("Why Does CLAUDE.md Keep Growing?") found context files gain ~5 instructions per commit and almost never shrink, because once a rule's rationale is lost nobody dares delete it — which is why every Reviewed Memory entry carries its evidence and provenance.

Reviewed Memory (beta)

Agents keep relearning the same repo facts, and pasting them into CLAUDE.md by hand doesn't scale to a team. Reviewed Memory closes the loop:

driftlint memory propose --text "Auth goes through the BFF." --evidence src/auth.ts:42   # the AGENT does this
driftlint memory review    # the HUMAN approves/rejects, one entry at a time
driftlint memory sync      # approved set → a marked block in CLAUDE.md / AGENTS.md / GEMINI.md

Why it works: the synced block lives in the files every agent CLI already reads (no hooks, no daemon), git distributes it via ordinary PRs, and driftlint scans .agent-memory/ and the block itself — so when the code moves, the memory that references it gets flagged like any other drift. Claude Code users get a /memory-propose command with the plugin. The propose → human-review → commit flow aligns with the governance channel of the memorywire vendor-neutral memory wire format.

Auto-memory audit

Claude Code also keeps its own auto memory per project (~/.claude/projects/<project>/memory/) — and those memories decay exactly like context files, except they live outside the repo where no repo-scoped linter ever looks:

driftlint memory audit        # finds the memory dir for the current repo automatically

It verifies every memory against the repo it describes: dead paths and removed commands referenced in memories, broken [[wiki-links]] between memories (resolved via filenames and frontmatter name: slugs), and a MEMORY.md past the 200-line / 25KB fold — everything below it silently never loads into a session. Memories that record facts about other repos are recognized and collapsed into one info line instead of a flood.

Twins: CLAUDE.md ↔ AGENTS.md

The most-upvoted request on the Claude Code tracker — support AGENTS.md, 5,200+ 👍 — is marked not planned. So teams using Claude Code next to Codex/Amp/Cursor keep both files, and the copies drift: someone fixes the test command in CLAUDE.md, AGENTS.md goes stale, and a week later half the team's agents follow the outdated copy. driftlint attacks this twice:

driftlint                  # the twin-drift rule flags pairs that already diverged
driftlint twins            # mirror AGENTS.md into CLAUDE.md as a marked, idempotent block
driftlint twins --check    # CI mode: fail when the mirror is stale

The twin-drift rule stays quiet for intentionally different files — it fires only on evidence: near-identical files with divergent lines, command claims that exist in one file but not the other, or a stale driftlint twins mirror. Pairs bridged with an @AGENTS.md import are recognized and skipped.

What it checks

Rule What it catches
dead-path Referenced files/dirs that no longer exist — with "did you mean src/util.ts?" hints when the file moved
dead-command npm run scripts and make targets that were removed or renamed
skill-budget Skill descriptions overflowing the ~15k-char system-prompt budget — skills past it are silently invisible to the agent
stale-knowledge Context files untouched for months while the code they describe churned heavily
foreign-context A file whose references mostly don't resolve — probably describes another repo; findings collapse into one warning instead of a flood
narrative-claim (only with --llm) Narrative claims ("auth goes through the BFF") that the code contradicts — verified with your own Anthropic API credentials
template-context Workflow files that describe a project this repo generates — collapsed into one warning instead of a flood
load-budget Content that silently never reaches the model: AGENTS.md past Codex's 32 KB truncation limit, files past the ~150-instruction adherence ceiling
missing-rationale Directive walls (never/always/must) with no stated reason — the rules nobody dares delete
twin-drift CLAUDE.md and AGENTS.md that carry the same instructions but diverged — differing command claims, drifted near-copies, stale driftlint twins mirrors
untracked-context Context files git doesn't track — your agent follows them, your teammates' agents never see them (CLAUDE.local.md is exempt by convention)
dead-link Markdown links whose target file moved, or whose #anchor heading was renamed — with the closest heading offered as a fix
silent-config Config in a shape or place the tool ignores: a plain .md under .cursor/rules (Cursor needs .mdc), a bare .md where a <name>/SKILL.md belongs
dead-config-ref Hooks, MCP servers, plugin manifests and skill allowed-tools pointing at scripts that don't exist — valid JSON, missing file

dead-command is workspace-aware: a script that exists in another monorepo package is reported as a location warning ("defined in packages/client/package.json"), not a dead command.

Config that never loads

Schema validators check that your JSON is well-formed. driftlint checks whether the thing it points at is actually there — and whether the file will be read at all:

  • a hook whose script was moved ($CLAUDE_PROJECT_DIR/.claude/hooks/guard.sh) fails the first time it fires, silently
  • an MCP server whose local entry file is gone never starts (remote npx/docker servers are left alone)
  • a plugin manifest listing a command that isn't there installs fine and does nothing
  • a plain .md under .cursor/rules is ignored by Cursor — no error, no rule
  • a skill whose description + when_to_use runs past 1,536 characters loses the tail: that's where the skill listing truncates

Skills are discovered wherever the Agent Skills standard puts them — .claude/skills/ and .cursor/skills/ alike.

Usage

npx @alifurkangokce/driftlint                 # scan the current repo
npx @alifurkangokce/driftlint path/to/repo    # scan another repo
npx @alifurkangokce/driftlint --fix           # interactively apply safe fixes (--yes: all)
npx @alifurkangokce/driftlint --json          # machine-readable output (CI-friendly)
npx @alifurkangokce/driftlint --sarif         # SARIF 2.1.0 for GitHub code scanning
npx @alifurkangokce/driftlint --no-fail       # report but always exit 0
npx @alifurkangokce/driftlint --diff          # only drift THIS change caused (vs origin/main)

--diff is what you want on pull requests: it scans the merge-base in a temporary worktree, reports only findings that are new, and attributes them to the change — "this PR renames src/auth.tssrc/authn.ts; CLAUDE.md still references the old path" — with the fix derived from the rename. Pre-existing drift stays out of your PR.

Installed globally (npm i -g @alifurkangokce/driftlint) the command is just driftlint.

Exit code is 1 when errors are found, so it drops straight into CI. Or use the action:

# .github/workflows/driftlint.yml
on: [pull_request]
permissions:
  security-events: write   # only needed when sarif-file is set
jobs:
  driftlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }   # full history enables the staleness check
      - uses: alifurkangokce/driftlint@main
        with:
          diff: "true"                  # PRs: only report drift this PR caused
          sarif-file: driftlint.sarif   # optional: findings become PR annotations

Or as a pre-commit hook:

repos:
  - repo: https://github.com/alifurkangokce/driftlint
    rev: v0.7.0
    hooks:
      - id: driftlint

Optional LLM pass

Deterministic checks can't see narrative claims. --llm extracts them from your context files, greps the repo for evidence, and asks Claude whether the code contradicts them:

npm install @anthropic-ai/sdk        # optional peer dependency, only needed for --llm
export ANTHROPIC_API_KEY=sk-ant-...  # or `ant auth login`
npx @alifurkangokce/driftlint --llm
npx @alifurkangokce/driftlint --llm --llm-model claude-haiku-4-5   # budget option

Your key, your bill (default model claude-opus-5; capped at 10 files / 8 claims per file, token usage is printed). Findings are warnings marked needs review — the verifier is conservative: missing evidence is "unverifiable", never "contradicted". Without --llm, driftlint never touches the network.

reviewdog: one-click "Apply suggestion" on PRs

      - run: npx -y @alifurkangokce/driftlint --rdjsonl --no-fail | reviewdog -f=rdjsonl -reporter=github-pr-review -filter-mode=nofilter

--rdjsonl emits reviewdog RDFormat where every did-you-mean fix becomes a committable GitHub suggestion. -filter-mode=nofilter matters: drift findings live on lines the diff never touched.

MCP server: agents lint their own context

claude mcp add driftlint -- npx -y @alifurkangokce/driftlint-mcp

Two tools from @alifurkangokce/driftlint-mcp: drift_scan (full report, optional diff_range) and drift_check — an agent about to edit CLAUDE.md verifies the reference before writing it, so it never writes a dead one.

Freshness badge

Every scan computes a deterministic context-freshness score (the share of path references that resolve; collapsed template/foreign files excluded). Put it in your README:

      - uses: alifurkangokce/driftlint@main
        with: { badge-json: badge.json, fail: "false" }
      - uses: Schneegans/[email protected]
        with:
          auth: ${{ secrets.GIST_SECRET }}
          gistID: <your-gist-id>
          filename: driftlint.json
          contentFile: badge.json
![context freshness](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/<user>/<gist-id>/raw/driftlint.json)

Adopting on a legacy repo

npx @alifurkangokce/driftlint --update-baseline   # record today's findings

This writes .driftlint-baseline.json; from then on only new drift is reported, so CI stays green while you pay down the backlog.

Config

Optional .driftlintrc.json at the repo root:

{
  "skillBudget": 15000,
  "ignore": ["docs/archive/**"],
  "templates": [".claude/skills/**"],
  "rules": { "dead-command": "off", "stale-knowledge": "info" }
}

Template repos (scaffolds, methodology kits)

If your repo generates other projects, its skills legitimately reference files that will exist in the generated project — driftlint would report those as dead. Three escapes: put a driftlint-template comment in the file, list globs under "templates" in .driftlintrc.json (both skip path/command checks with one info note), or let the auto-heuristic handle it — a skill/agent/command file with ≥2 unresolved references and generator vocabulary ("scaffolds", "will create", "your project") collapses into a single template-context warning. Root CLAUDE.md/AGENTS.md are never auto-suppressed: they describe this repo.

Suppress a single false positive with a comment on the same line or the line above:

<!-- driftlint-ignore -->
This mentions `hypothetical/example.ts` on purpose.

Scanned files

CLAUDE.md, CLAUDE.local.md, AGENTS.md, GEMINI.md (anywhere in the tree), .claude/skills/*/SKILL.md, .claude/agents/*.md, .claude/commands/*.md, .cursor/rules/*, .github/copilot-instructions.md, .windsurfrules, .clinerules (file or directory), .opencode/{agent,command,knowledge}/**.md.

Claude Code plugin

driftlint also ships as a Claude Code plugin: a /driftlint command that runs the scan and then fixes the drift it finds (with your approval).

/plugin marketplace add alifurkangokce/driftlint
/plugin install driftlint@driftlint

How driftlint compares

driftlint agnix ctxlint / agents-lint claude-mem etc.
Checks context files against the actual codebase (dead paths, dead commands, staleness) ❌ structural rules only
PR-first: baseline mode, SARIF annotations, workspace-aware commands partial partial
Template-repo awareness (scaffold kits don't drown you in noise)
Reviewed team memory (agent proposes → human approves → synced & re-verified) ❌ auto-capture, no review
Structural/spec rules, LSP, IDE plugins ❌ by design ✅ 448 rules partial

driftlint and agnix are complements, not rivals — agnix checks that your context files are well-formed; driftlint checks that they're still true. Run both, like eslint and tsc.

Roadmap

See ROADMAP.md — next up: an optional LLM pass for narrative claims, then Reviewed Memory: agents propose knowledge at session end, humans approve via PR, git distributes it, and driftlint keeps it honest.

License

MIT

Yorumlar (0)

Sonuc bulunamadi