CLI tool
dadbodgeoff/drift avatar
dadbodgeoff/drift

Drift: A Convention Enforcement Gate for AI Agents Working in Next.js Codebases

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

789 stars65 forksTypeScriptMIT

At a glance

What is it?
Drift is a beta CLI and MCP server that detects when an AI agent or a developer writes code that violates the layering conventions already established in a TypeScript Next.js repository. It runs entirely on your machine, uses a Rust scan engine, and enforces a single convention: API routes must not import data-access clients directly.
Who is it for?
Drift is a focused tool for teams using AI coding agents on TypeScript Next.js applications who have a data-access layer worth protecting. If your codebase names its data layer prisma, database, db, or data-access, Drift can start enforcing the boundary without configuration.
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 20 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 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Drift Solves and Who It Is For

When an AI coding agent is asked to add a new API route, it may write the handler by reaching directly into the database using Prisma or any other data-access client. This produces code that bypasses whatever service or repository layer the rest of the codebase uses. The agent was not wrong about the task; it was not aware of the convention. The result is a pull request that a code reviewer will need to catch and reject.

Drift automates that catch. It inspects the diff produced by the agent, checks whether any new API route imports a data-access client directly, and exits with code 2 if the convention has been violated. In MCP server mode, a compatible IDE receives the finding before the change is even committed. In CI mode, the check runs on each push and fails the build for any violation that slips through the IDE.

The target audience is engineering teams working on TypeScript or JavaScript Next.js projects that have an existing layering convention and are increasingly using AI agents to write code. The project is less useful for teams without a convention to enforce, for projects that are not Next.js, or for teams whose codebase is already so inconsistent that the convention cannot be detected automatically.

The Single Convention Drift Enforces

Drift enforces one convention family: API routes in a Next.js project must not import data-access clients directly. This covers both the App Router style routes under app/api/ and the Pages Router style routes under pages/api/.

The data layer is identified by scanning import specifiers. If an import path contains prisma, database, db, or data-access, Drift treats it as a data-access import. Repositories that name their layer differently must declare the correct names via the --data-modules flag. A repo using store or supabase as the module name will produce false negatives unless those names are declared.

The README is explicit about what Drift does not do: it does not review code generally, does not support languages other than TypeScript and JavaScript, does not modify source files, and does not sync data to any remote service. Security heuristics exist behind an --experimental-security flag, and the README notes that they are not proofs; the audit of those heuristics is documented in a separate internal architecture file.

The convention has two enforcement modes. When violations exist in a minority of the codebase (the example given is formbricks, where 1 route of 83 violates the convention), Drift treats the violation as an exception and blocks new ones. When violations are in the majority (the example is dub, where roughly 323 of 494 routes import data-access clients directly), Drift infers that this is a refactor goal, not an active convention, and switches to warn mode. The mode is printed after onboarding so the team knows whether their checks will actually block.

Building and Running Drift from Source

No npm package is published yet. Building requires both a Node.js toolchain and a Rust toolchain because the scan engine is written in Rust and compiled to a binary. Install Rust via rustup before proceeding.

Clone the repository and build everything:

bash
git clone https://github.com/dadbodgeoff/drift.git && cd drift
pnpm install --frozen-lockfile
pnpm build && pnpm build:engine

There is no drift binary in PATH after the build. The README provides a shell alias to invoke the CLI from the build output:

bash
alias drift="node $PWD/packages/cli/dist/main.js"
drift doctor --repo-root .

The doctor command is a zero-write readiness check that verifies the Rust toolchain is present and working. It prints a clear error if anything is missing.

Switch to the repository you want Drift to protect and run the onboarding command:

bash
drift start --repo-root . --accept-defaults

This accepts the deterministic default convention, creates a local SQLite database, and baselines existing violations so that legacy drift does not block the first check. The --accept-defaults flag skips the manual review of each candidate convention; omitting it lets you review candidates before accepting.

To check a specific set of changes:

bash
drift check --diff HEAD~1...HEAD --scope changed-hunks

This scans only the changed lines, not the entire codebase. The --scope changed-hunks option is important for CI performance. If the check exits 2, at least one changed hunk violated the convention. The output names the file, the line number, and the convention that was broken.

Evidence from Evaluation on Real Repositories

The README documents the results of running Drift against seven open-source Next.js repositories. In all seven cases, the onboarding process correctly learned the real data layer, injected an artificial violation, and caught it. In all seven cases, the reported file and line number were correct. No properly layered route was falsely flagged in any of the seven repos.

The false-positive rate on the dub repository, which has 494 routes, was 3.1%. Every check reports its own coverage, expressed as the percentage of local imports that the resolver successfully placed. For the seven evaluation repositories, this ranged from 96.1% to 99.9%. Where Drift cannot resolve an import specifier, it names the offending import rather than reporting a pass it cannot support.

The evaluation also tested how Drift handles ordinary, non-violating edits: eight edits per repository across seven repositories. Of those 56 ordinary edits, zero were refused or answered incorrectly. This is the number that matters for day-to-day use: a gate that false-positives on routine work will be bypassed.

The evasion matrix tests whether the convention can be satisfied in form while being violated in substance. The README reports 66 of 66 testable cells caught across several evasion shapes. This figure is bounded by what the tool explicitly commits to checking; the README states that Drift's contract is deterministic within its declared scope.

The SQLite State Model and Backup Commands

Drift stores its local state in a SQLite database. After onboarding, the start command prints the path to this file as an environment variable:

bash
export DRIFT_DB=/path/to/drift.sqlite

This database holds the repo registration, the accepted and candidate conventions, the baseline of existing violations, and the audit chain. The doctor command performs an ongoing health check of this state on each run, including validating SQLite migration compatibility, scan freshness, and audit-chain integrity.

The backup commands allow exporting and restoring the database. Verified backups include a sha256 checksum and compact summaries that setup scripts can parse without reading prose. The restore dry-run command prints the expected outcome before any write happens.

For teams keeping Drift across multiple machines or CI runners, the SQLite file is the only artefact that needs to be transferred. The file does not contain model weights or large data; it is a small operational record of what the tool has agreed to enforce and what violations existed before onboarding.

Limitations and Cases Where Drift Is the Wrong Tool

The scope is narrow by design, and the README says so. Drift enforces one convention for one framework in two languages. A project using a different backend framework, a non-TypeScript server, or a different architecture pattern cannot use it.

The data-layer detection heuristic is pattern-based. A repository that stores Prisma queries in a file named repository.ts rather than prisma.ts will be invisible to Drift until the name is declared with --data-modules. This is a configuration burden for projects with non-standard naming.

Beta status means no stable API and no versioned releases. The scan engine binary and the TypeScript CLI will change as the project evolves. A team pinning Drift in CI needs a strategy for rebuilding from a specific commit rather than relying on a published package version.

Drift does not modify code. It only reports violations. Teams who want automated fixes, not just detection, need to pair it with a separate code-modification tool or handle fixes in the IDE.

Drift Compared to ESLint Import Rules

ESLint's no-restricted-imports rule and the eslint-plugin-import package can both enforce import boundaries in TypeScript projects. The difference is the configuration model. ESLint rules require a human to write the rule that specifies what is forbidden and why. The team decides up front that no file in src/pages/api may import from src/db, and the rule encodes that decision.

Drift inverts this: it observes the codebase, infers what the convention appears to be from the distribution of violations, and then enforces the inferred convention. This is useful when the convention was never written down and exists only as a pattern in the code. It is less useful when the team wants to declare a new convention before any code exists, since there is nothing to infer from.

ESLint is a mature, stable tool with broad language support beyond TypeScript. It integrates with every major editor and CI system. Drift is a younger, narrower tool that targets a specific failure mode in AI-generated code. The two are not mutually exclusive: teams using both can have ESLint for broad import control and Drift for the specific case of AI agents bypassing the data layer in Next.js routes.

Editorial conclusion

Drift is a focused tool for teams using AI coding agents on TypeScript Next.js applications who have a data-access layer worth protecting. If your codebase names its data layer prisma, database, db, or data-access, Drift can start enforcing the boundary without configuration. If your naming is different, pass the correct names via --data-modules. Before adopting it, verify that the codebase is in warn mode rather than block mode by running drift baseline status after onboarding; a legacy codebase where violations are the majority will warn rather than block until a human decides to refactor. The project has no stable release and must be built from source, so plan for maintenance work when Rust or Node toolchains change.

Frequently asked questions

Does Drift work with the Next.js App Router or only Pages Router?

According to the README, Drift covers both App Router and Pages Router API routes. The enforced convention applies to any file the scanner identifies as a Next.js API route handler in either structure.

What happens if the majority of routes in my repository already violate the convention?

Drift detects this and switches to warn mode rather than block mode. The README gives the example of dub, where approximately 323 of 494 routes import data-access clients directly; Drift treats this as a refactor goal and warns on new violations rather than blocking them, until a human decides to enforce the boundary.

Is there an npm package for Drift?

No. The README states that npm install -g @drift/cli does not work and that the driftdetect package on npm is an unrelated project from January. As of the last push on 2026-09-11, installation requires cloning the repository and building from source with both a Node.js and a Rust toolchain.

Official sources

  1. dadbodgeoff/drift on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dadbodgeoff-drift.svg)](https://hysenlabs.com/projects/dadbodgeoff-drift)