Skip to content

archstrict

archstrictv0.4.0MIT

TypeScript module boundary checker: default-private module surfaces, layers, cycles, and a todo freeze that can only shrink.

Install

npm install archstrict

archstrict checks TypeScript module boundaries, in the sense of ArchUnit. tsc and type checkers examine types, and ESLint examines style. archstrict examines the boundary between modules. A module shows the rest of the codebase one public-surface file, and an import that reaches past it is a violation. Each report carries a rule id, a location, the reason, and the command that fixes it, so an agent can repair its own mistake.

  • Declared modules. archstrict.config.ts is one TypeScript value. declaredModules names each module as a directory or a single file, and surface names its public file. A file that matches no module and no exclude pattern is an uncovered-module violation. An unknown key in the config is an error that names the accepted keys.
  • Reports an agent can act on. archstrict check prints each violation with a rule id, path:line:col, the evidence, a because reason, and a do: command. --json gives the same data, and --rule and --module narrow it.
  • Layer and domain rules. classify puts tags on files, and edges checks the imports between tags with allowDeny, order, and point rules. guard: true on an allowDeny or point rule bans an import that no file has yet. archstrict diagram draws the config as a Mermaid flowchart.
  • A todo file that only shrinks. The first archstrict todo freezes the current violations into archstrict.todo.json, and later runs only remove entries. A module listed in strict can hold no entry at all.
  • Questions before an edit. archstrict rules <path> reports the module, tags, and constraints that govern a path, even one that does not exist yet. archstrict simulate previews a source or config change without writing it.
  • Next steps for the map. archstrict recommend proposes edges rules and surfaces from the real import graph. archstrict hotspots scores modules by commits and fan-in, and lists module pairs that change together.
  1. Before it creates a file or adds an import, the agent runs npx archstrict rules <path>.
  2. After the edit, it runs npx archstrict check and does what each do: line says.
  3. In Claude Code, the archstrict plugin’s PostToolUse hook runs archstrict check <file> after each edit and returns any violation into the agent’s context. Its MCP server offers check, rules, search, and simulate as tools.
  4. On other hosts, npx archstrict agents adds the pre-edit instructions to AGENTS.md, and CI runs archstrict check on every change.

archstrict needs Node.js 22 or newer.

Terminal window
npm install -D archstrict
npx archstrict init
npx archstrict check

init declares one module per top-level source directory and one per loose top-level source file, so the first check covers every analyzed file. That first map is an inventory. Run npx archstrict recommend next, and add an edges rule before you treat the check as a finished architecture.

The repository keeps two small projects in fixtures/. In the first, one file imports billing/rounding.ts directly, past the public surface of the billing module.

archstrict reports that import as public-surface-bypass, with the file, line, and column. checked with archstrict 0.3.0
$ archstrict checkran in fixtures/archstrict-bypass · exit 1 · archstrict 0.3.0
[public-surface-bypass] ./fixtures/archstrict-bypass/src/app/main.ts:1:28
  '../billing/rounding.ts' resolved to a file inside module 'billing' other than its index.ts, index.tsx, index.mts, index.cts
  because: a module's public surface is its only public surface; everything else is private
  config: ./fixtures/archstrict-bypass/archstrict.config.ts:33:5 declaredModules[1] (governs)
  do: import from src/billing/index.ts instead of src/billing/rounding.ts, adding the export there first if it is missing
modules: 2
modules without a public surface: 1
edges: 1
not covered by any declared module: 0
unresolved specifiers: 0
unsupported syntax: 0
type leaks: 0
todo: 0
summary: this config freezes today's import graph, not a target architecture; no edges rule is configured yet
do: archstrict recommend
do: archstrict hotspots
do: read node_modules/archstrict/skills/archstrict/references/rearchitect.md
do: archstrict todo

In the second project, the same code imports through billing/index.ts.

The check passes once the import goes through the public surface. checked with archstrict 0.3.0

Before you create a file, ask which module governs it.

archstrict rules <path> names the module that governs a path and says whether the file is a public surface. checked with archstrict 0.3.0
$ archstrict rules src/billing/rounding.tsran in fixtures/archstrict-bypass · exit 0 · archstrict 0.3.0
path: ./fixtures/archstrict-bypass/src/billing/rounding.ts
exists: true
excluded: no
module: billing
tags: (none)
surface file: no
must-be-empty: ok
importable from: (none)
friend access: (none)
allowDeny constraints: (none)
order constraints: (none)
point constraints: (none)