Install, declare your architecture, and let the guardrail catch drift while your agent writes code. One Python file, standard library only — no daemon, no service, no LLM in the hot path.
Archlint is a guardrail that catches architectural drift while your AI coding agent writes code — not hours later in CI. It reads the layer rules you already keep in your project's CLAUDE.md (or a dedicated ARCHITECTURE.md) and checks every edited file's imports against them, in the agent's loop, while there's still a cheap chance to course-correct. It's the seatbelt, not the steering wheel: it surfaces drift, the agent decides what to do.
Unzip the bundle and run the installer — one command, no JSON to edit.
This installs Archlint as a Claude Code plugin under ~/.claude/skills/archlint/, so the hooks and the /archlint:* commands are available everywhere. It's a safe no-op in any project that hasn't declared rules. Start a new Claude Code session to activate the hooks.
This writes the hook and the settings.json block into .claude/, so the whole team and CI share the same guardrail. If your rules live in a dedicated ARCHITECTURE.md rather than inline in CLAUDE.md, the installer also adds the line that loads them into the agent's context — @ARCHITECTURE.md — idempotently, never duplicating it.
Cursor's per-edit hook is observational, so in Cursor Archlint surfaces the turn's drift at end-of-turn, which Cursor feeds back as the next message. Cursor reads AGENTS.md natively, so keep your rules block there.
.claude/hooks/archlint.py, mark it executable, and wire the PostToolUse and Stop hooks in .claude/settings.json (an example ships as settings.example.json).Archlint does nothing until a project declares its architecture. Add an ## Architecture section to your CLAUDE.md — or to AGENTS.md, ARCHITECTURE.md, SPEC.md, or ROADMAP.md. Inside it, a fenced archlint block declares your layers and rules:
| Statement | Meaning |
|---|---|
layer NAME = glob, ... | Define a layer by one or more file globs. |
X -> Y | X is allowed to depend on Y. |
forbid X -> Y | Explicit prohibition (overrides allow). |
forbid! X -> Y | Hard prohibition — always blocks, even in lenient mode. |
forbid * -> path/** | Anything depending on path/... is forbidden. |
set max-file-lines N | Oversized-source-file smell threshold (default 500). |
set detect-cycles on|off | Layer-cycle detection (default on). |
One statement per line; # starts a comment. Imports of third-party packages (npm, pip, stdlib) are ignored — no false positives for dependencies you don't own. A line that doesn't parse is never silently dropped: Archlint surfaces it, so a typo'd forbid can't quietly disable itself.
The first host that actually carries a rule-bearing archlint block wins, with a dedicated ARCHITECTURE.md preferred over CLAUDE.md. Keeping rules in a dedicated file keeps a large CLAUDE.md lean — just add @ARCHITECTURE.md to CLAUDE.md (the installer does this) so the agent loads them. If more than one file declares a block, Archlint warns rather than guessing silently. Override the location with the ARCHLINT_BLUEPRINT environment variable.
Let Archlint draft a starting blueprint from your existing tree:
init infers layers from your directory structure and edges from imports that actually exist. It emits no forbid rules — those are intent, and only you know them. Run it, then review: prune edges that shouldn't exist and add the prohibitions that encode your real boundaries. --write refuses to clobber an existing block.
While your agent edits the project, Archlint runs at two moments:
Write/Edit — it parses the edited file, extracts its imports, and checks them against your layer rules. If a forbidden boundary is crossed, it surfaces a short, actionable note.In lenient mode (the default), drift is reported as context and the agent tends to self-correct on the next step. In strict mode, drift becomes a hard block — the agent must keep working until the violation is gone. A forbid! rule always blocks, regardless of mode, for the boundaries that are non-negotiable.
Archlint is also a CLI — the same engine and rules as the live hook.
| Command | What it does |
|---|---|
check | Scan the whole project. --format sarif emits SARIF 2.1.0; default text mode exits 1 on drift (a CI / pre-commit gate). |
init | Draft a blueprint from the tree's layers and observed edges. --write writes it. |
baseline | Record current findings as accepted, so only new drift is reported. |
graph | Render the layer graph — Mermaid (default), --format dot, or --out layers.svg. |
report | The self-correction rate from the drift log. --json for machine output. |
install | Wire up the hooks — global, --project, or --harness cursor. |
The graph output is colored to the brand — allowed grey, observed-but-undeclared amber, forbidden red, cycles magenta — and Mermaid ships inside a fence, so it drops straight into a README or PR comment and renders. report turns the drift history into the one number nobody else has:
a -> b -> a) from your real imports — the structural smell a layered blueprint is uniquely positioned to catch — and names a concrete import realizing each edge, so it's fixable without grep.forbid! X -> Y always blocks, even in lenient mode._helpers, an internal/ package) is flagged as a smell.max-file-lines threshold are flagged. Only source code is checked — logs and docs are not.| Variable | Values | Effect |
|---|---|---|
ARCHLINT_MODE | lenient (default), strict | strict makes drift block the agent; lenient just notifies. |
ARCHLINT_BLUEPRINT | path | Override the blueprint location. |
ARCHLINT_QUIET | 1 | Silence non-blocking feedback; hard/strict blocks still surface. |
check speaks SARIF 2.1.0, the format GitHub code scanning consumes — so a violation shows up as an inline annotation on the exact line of the PR diff, not a buried log line. Drop this workflow in at .github/workflows/archlint.yml (it ships in the bundle); it runs the archlint.py you already committed, so there's no marketplace Action to trust:
For a plain pre-commit gate, python3 .claude/hooks/archlint.py check exits 1 on drift — wire it straight into .git/hooks/pre-commit.
A legacy project lights up with historical violations on day one — and nobody enables a linter that starts 50 findings in the red. Grandfather them:
From then on, the hook and check report only new drift. Commit the baseline so the whole team and CI share the same starting line. Re-run baseline to re-accept the current state after an intentional restructure; delete it to see everything again.
Import extraction works for Python, TypeScript/JavaScript, Go, Rust, Java, and C#. Archlint understands the import shapes real projects use — TypeScript path aliases (@/services/x), Python src/ layouts, Go module prefixes, Rust crate/self/super paths, Java fully-qualified names, and C# namespaces. Third-party and unresolved imports are simply skipped, so you never get a false positive for a package you don't own.
By design, Archlint is honest about what it isn't:
Archlint is open core. The free lite tier — layers, ->/forbid rules, blueprint diagnostics, the oversized-file check, the Claude Code hook (lenient + strict), real-world import resolution, the four core languages (Python, JS/TS, Go, Rust), and check — is MIT and public at github.com/MKanhan/archlint.
The Pro bundle adds everything that turns the guardrail into a product: cycle detection with named witness edges, forbid! hard rules, public-API leakage, init / baseline / report / graph, multi-harness (Cursor), Java and C#, SARIF for GitHub code scanning, the dedicated-blueprint discovery + @import pointer, and one-command install. USD 20, one-time.
LICENSE inside the Pro bundle.No license keys, no DRM, no phone-home. The terms define permitted use; honoring them is the deal.
Purchase includes one year of updates; your purchased version is yours perpetually. For questions, updates, or licensing: hello@archlint.pro.
Does it phone home or send my code anywhere?
No. Archlint runs entirely locally — standard-library Python, no network calls, no telemetry. Your code never leaves your machine.
Is there a license key to manage?
No keys, no activation, no DRM. The bundle just works; the license terms govern use.
Will it slow down my agent?
No. There's no LLM in the hot path — Archlint parses imports and matches globs, effectively instantaneous on a single edited file.
What if my project has no CLAUDE.md?
Archlint stays a silent no-op until you declare an architecture. Put the block in a supported host, or run init to draft one.
Does it work outside Claude Code?
Yes — check runs standalone for pre-commit and CI (including GitHub code scanning via SARIF), and there's a Cursor adapter. Same engine, same rules.
Can I get a refund?
Refunds are handled manually on a case-by-case basis — reach out.
Windows?
Yes. Python 3, standard library, cross-platform path handling.