CLI tool
dadbodgeoff/drift avatar
dadbodgeoff/drift

Drift: a layering gate for Next.js API routes, built for AI agents

Codebase intelligence for AI. Detects patterns & conventions + remembers decisions across sessions. MCP server for any IDE. Offline CLI.

789 stars63 forksTypeScriptMIT

At a glance

What is it?
Drift scans a repository, infers one convention (API routes must not import data-access clients directly), and blocks diffs that break it. It is a narrow, local tool aimed at teams whose coding agents keep reaching for the database in the wrong layer.
Who is it for?
Adopt Drift if your repository is a TypeScript or JavaScript Next.js app whose API routes import a data client named prisma, database, db or data-access, and you want an agent to be told before it merges. Do not adopt it if your data layer is called store or supabase and you are unwilling to pass --data-modules, if you need languages beyond JavaScript and TypeScript, or if you expect a published npm install.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Drift picks: an agent that ignores the layering your repo already follows

Coding agents write plausible code, and plausible code in a Next.js repository often means an API route that imports the database client directly. That is not a syntax error and not a failing test. It is a convention breach that a reviewer catches by memory or not at all. Drift's stated goal is to stop an agent from writing code that violates conventions the repository already follows, and it runs on the local machine rather than as a hosted service. The intended user is a team already running an agent inside an IDE, on a Next.js codebase, that wants a machine-checkable answer to one question: does this diff put data access in a place this repository does not put it. The scope section is unusually direct about the limit. Drift enforces one convention family and says so rather than implying more. It does not review code generally, does not support other languages, does not modify your source, and does not sync anything to a server. If you want a general linter with opinions about naming, complexity or dead code, this is not that tool.

Facts, contract, baseline, check: how Drift turns a scan into a gate

The pipeline has four stages, named in the documentation as facts, contract, baseline, check. Drift scans the repository and collects facts about imports and routes. From those facts it infers a convention, and the inference is the interesting part: it derives the convention from the violations themselves. A repository where every route touches the database produces the same statement as one where a single route does. The contract is then materialized into the repository, and existing findings are baselined so legacy drift does not block the first check. The check stage compares a diff against the contract and reports the file, the line and the convention that was broken. Data-layer recognition is a plain substring test on the import specifier: it matches when the specifier contains prisma, database, db or data-access. That is cheap and predictable, and it also means a repository that names its data layer store or supabase must declare it explicitly with --data-modules. Drift bootstraps and enforces a declared layering contract. It does not learn conventions in general, and the project's claims manifest blocks that claim explicitly, which is a level of self-restraint that most tools in this category skip.

Warn versus block, and why a majority-violating repo will not gate

The mode follows the evidence rather than a config default. If a minority of routes violate the convention, new violations block. If a majority violate, Drift treats the convention as a refactor goal and warns until a human decides. The README gives two concrete examples from the external evaluation: formbricks, with 1 violating route out of 83, blocks, while dub, with roughly 323 of 494, warns. This is a defensible design choice and also the most likely source of confusion on first contact. A team installs Drift, runs a check, sees findings, and notices the exit code is 0. Nothing is broken. The tool has decided that enforcing a rule most of the codebase already breaks would reject new code written exactly like its neighbours. The cost is that the tool's value is inversely related to how much cleanup you have already done. On a clean repo it gates immediately. On a messy one it is a report generator until someone runs a refactor. Drift does print the command to promote an accepted convention to a gate when it will not block on its own, so the path out of warn mode is at least stated rather than hidden.

Installing from source, because nothing is on npm

There is no published package. The README states plainly that npm install -g @drift/cli does not work, and that the driftdetect package on npm is the unrelated v1 from January. Installation is a source build, and the scan engine is Rust, so a Rust toolchain via rustup is required alongside pnpm. The documented sequence is git clone of the repository, pnpm install --frozen-lockfile, then pnpm build and pnpm build:engine. There is no drift binary yet, so the README suggests an alias pointing at the built entry point, packages/cli/dist/main.js. The first command to run is drift doctor --repo-root ., described as a zero-write readiness check that fails loudly if the toolchain is missing. In the target repository, drift start --repo-root . --accept-defaults accepts the deterministic default convention, materializes the contract, and baselines existing findings. Omitting --accept-defaults lets you review candidates manually first. Start prints the local SQLite path, which you export as DRIFT_DB, and a repo id used by the review loop: drift baseline status, drift version --json, drift capabilities --json, drift conventions list with filters such as --status candidate, --kind api_route_no_direct_data_access and --capability deterministic_check, plus drift conventions show, drift ask, drift prepare, drift repo map, drift checks list and drift checks run. A second doctor run after start acts as the ongoing local health gate, validating SQLite migration compatibility, repo registration, contract compatibility, scan freshness, audit-chain integrity and tracked backup artifacts.

The empty-diff refusal, and other places Drift says no

Checking a range that contains no changes returns exit 3 rather than a pass. The README walks through the trap: --diff main...HEAD only works once your branch has commits that main does not, and on a freshly cloned repository you are on main, so the range is empty. Drift refuses instead of reporting a pass it cannot support. This is the correct behaviour and it will still surprise someone wiring the check into CI for the first time, because a refusal looks like a failure. The same instinct shows up in coverage reporting. Every check reports its own coverage so a clean result is not mistaken for full coverage, and where Drift cannot resolve something it says so with the offending specifiers, surfaced through drift doctor and summary.import_coverage. The README cites local import resolution between 96.1% and 99.9% across the evaluated repositories, which means the remainder is a known, reported gap rather than a silent one. The experimental security heuristics behind --experimental-security are explicitly not proofs, and their own audit lives in docs/internal/architecture/security-heuristic-audit.md. Treat that flag as a research surface, not a second product.

What the external evaluation does and does not establish

The project publishes three scripts: pnpm eval:external over seven open-source Next.js repositories, pnpm eval:evasion across an evasion matrix, and pnpm eval:bench over ordinary edits. The reported results are 7 of 7 repos onboarding, learning the real data layer and catching an injected violation, with correct file:line evidence in all seven and zero properly layered routes falsely flagged. The dub false-positive rate is given as 3.1% across 494 routes. The evasion matrix reports 66 of 66 testable cells caught at the contract's mode. The bench reports 0 of 56 ordinary edits refused rather than answered, which the README frames as the number that decides whether a first session is tolerable. These are the project's own numbers, produced by scripts in its own repository, on repositories it selected. They are more specific than most beta tools offer and they are not independent verification. The honest reading is that the approach works on the sample the authors chose, and that the 3.1% false-positive figure on dub is the number to watch, since a gate that flags correct code is the failure mode that gets a tool uninstalled.

Where Drift sits next to ESLint and dependency-cruiser

The obvious comparison is ESLint with no-restricted-imports, which can already forbid an import path inside a directory. The difference is where the rule comes from and what it understands. An ESLint rule is written by hand and matches on the specifier or the file path. Drift derives the rule from a scan of the repository, decides whether to enforce or warn based on how widespread the violation already is, baselines the existing findings, and exposes the whole thing over MCP so an agent can query it before writing code rather than after. Dependency-cruiser is closer in spirit, since it models allowed and forbidden dependencies between modules. It is configured by a written rule file and reports on the codebase; it does not infer the rule, does not distinguish a minority from a majority violation, and has no notion of answering an agent's question about a specific route. The trade is flexibility for inference. ESLint and dependency-cruiser will express rules Drift cannot, in languages Drift does not parse. Drift will tell you what your repository already does without you writing the rule, and will refuse to enforce it if the evidence says the repository does not actually do it.

Maintenance cost, licence, and the rebuild history

Drift is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission text are retained. That is a statement about the licence text, not legal advice; if you redistribute a modified build, read the file yourself. The maintenance picture is shaped by two facts in the README. First, the project has been rebuilt three times, and docs/HISTORY.md documents why and what changed each time. A tool on its third architecture is a tool whose interfaces have moved before, so pinning to a commit rather than tracking main is the safer posture while it is in beta. Second, installation is a source build with a Rust engine, which means every upgrade is a toolchain operation: pull, reinstall with a frozen lockfile, rebuild both the TypeScript packages and the Rust engine. There is no published artifact to version-pin against, so the upgrade path is git plus a build. The second doctor run is the intended check after any upgrade, since it validates SQLite migration compatibility and contract compatibility, both of which are the things that break when a tool stores state on disk and then changes its schema.

Editorial conclusion

Adopt Drift if your repository is a TypeScript or JavaScript Next.js app whose API routes import a data client named prisma, database, db or data-access, and you want an agent to be told before it merges. Do not adopt it if your data layer is called store or supabase and you are unwilling to pass --data-modules, if you need languages beyond JavaScript and TypeScript, or if you expect a published npm install. Verify first with drift doctor --repo-root ., then drift start --repo-root . --accept-defaults, then check whether the accepted convention lands in block or warn mode, because on a repo where most routes already query the database directly Drift will only warn.

Official sources

  1. dadbodgeoff/drift on GitHub
  2. Issues
  3. License: MIT
  4. README
Community notes

Community notes