Architectural linter — enforces structural rules before bad code lands. Pre-commit, CI, and MCP-ready.
Mar 31, 2026
Visit Site ↗*NOTE: This image might be generated using ChatGPT.*
Arklint is an architectural linter - a tool that enforces structural rules on your codebase before bad patterns ever land in a PR. Think of it as ESLint, but for your architecture rather than your syntax. It prevents things like:
models/ directoryIt works on any language, runs 100% locally with no cloud dependency, and integrates into pre-commit hooks, GitHub Actions, and AI coding agents via an MCP server.
The trigger was working with AI coding agents. Tools like Claude Code and Cursor are great at generating code fast - but they don't inherently know your project's architectural decisions. They'll happily import SQLAlchemy in a FastAPI route or drop a Pydantic model in a utility file, because nothing stops them.
I wanted a tool that could act as a guardrail at commit time and also teach agents the rules before they write code. That's the core idea behind Arklint.
.arklint.yml.Arklint ships with five rule types:
boundary - Prevent files in a source directory from importing blocked packages:
- id: no-direct-db-in-routes type: boundary source: "routes/**" blocked_imports: ["sqlalchemy", "psycopg2"] severity: error
dependency - Enforce one HTTP client, one ORM, no conflicting packages:
- id: single-http-client type: dependency allow_only_one_of: ["requests", "httpx", "aiohttp"] severity: error
file-pattern - Keep model/schema classes in the right directories:
- id: models-in-models-dir type: file-pattern pattern: 'class\s+\w*(Model|Schema)\s*[:(]' allowed_in: ["models/**", "schemas/**"] severity: warning
pattern-ban - Ban patterns codebase-wide (e.g. print() statements, TODO markers):
- id: no-print-statements
type: pattern-ban
pattern: 'print\('
exclude: ["tests/**"]
severity: warning
layer-boundary - Enforce layered architecture direction:
- id: layered-architecture
type: layer-boundary
layers:
- { name: routes, path: "routes/**" }
- { name: services, path: "services/**" }
- { name: repositories, path: "repositories/**" }
allowed_dependencies:
routes: [services]
services: [repositories]
repositories: []
severity: error
arklint add arklint/fastapi, arklint/django, arklint/nextjs, arklint/express, arklint/clean-archarklint learn --describe "routes must not touch the DB" --provider anthropicarklint export --format claude writes a CLAUDE.md with all your rules in natural languagearklint check --diff origin/main only scans changed files (fast CI)--github-annotations emits inline review commentsarklint visualize generates a Mermaid diagram of your layer dependenciesExtracting imports correctly across Python, JS/TS, Go, Rust, Java, C#, Ruby, and PHP is harder than it sounds. Each language has different import syntax, aliasing patterns, and relative vs absolute paths. Getting this right required per-language parsers and a solid test suite.
Building the brand identity involved an SVG favicon using <mask> and <clipPath> for a keystone icon with three colour zones. Safari's favicon renderer silently ignores masks - falling back to a blank icon. The fix was computing exact polygon boundaries geometrically and dropping masks entirely for the favicon.
Most JS developers won't pip install anything. The solution was prebuilt binaries (via PyInstaller) released to GitHub, with a thin npm package and a .NET global tool that download the right binary on first run. Zero Python required for those users.
The MCP server approach (exposing list_rules, check_file, check_snippet) works well at validation time. But pairing it with arklint export --format claude to generate a CLAUDE.md teaches agents the rules at generation time - before they write a single line. The combination is much more effective than either alone.
If you're curious about the project, connect on LinkedIn: www.linkedin.com/in/kaushik13k 😊