archprint 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +74 -51
  3. package/dist/cli/adoption-readme.d.ts +7 -0
  4. package/dist/cli/adoption-readme.d.ts.map +1 -0
  5. package/dist/cli/adoption-readme.js +76 -0
  6. package/dist/cli/adoption-readme.js.map +1 -0
  7. package/dist/cli/archprint-config.d.ts +33 -0
  8. package/dist/cli/archprint-config.d.ts.map +1 -0
  9. package/dist/cli/archprint-config.js +83 -0
  10. package/dist/cli/archprint-config.js.map +1 -0
  11. package/dist/cli/generate.d.ts +44 -37
  12. package/dist/cli/generate.d.ts.map +1 -1
  13. package/dist/cli/generate.js +167 -258
  14. package/dist/cli/generate.js.map +1 -1
  15. package/dist/cli/ignore-file.d.ts +8 -0
  16. package/dist/cli/ignore-file.d.ts.map +1 -0
  17. package/dist/cli/ignore-file.js +37 -0
  18. package/dist/cli/ignore-file.js.map +1 -0
  19. package/dist/cli/layout.d.ts +24 -0
  20. package/dist/cli/layout.d.ts.map +1 -0
  21. package/dist/cli/layout.js +74 -0
  22. package/dist/cli/layout.js.map +1 -0
  23. package/dist/cli/migrate.d.ts +27 -0
  24. package/dist/cli/migrate.d.ts.map +1 -0
  25. package/dist/cli/migrate.js +177 -0
  26. package/dist/cli/migrate.js.map +1 -0
  27. package/dist/cli/outputs-manifest.d.ts +0 -6
  28. package/dist/cli/outputs-manifest.d.ts.map +1 -1
  29. package/dist/cli/outputs-manifest.js +2 -29
  30. package/dist/cli/outputs-manifest.js.map +1 -1
  31. package/dist/cli/program.d.ts +1 -0
  32. package/dist/cli/program.d.ts.map +1 -1
  33. package/dist/cli/program.js +237 -51
  34. package/dist/cli/program.js.map +1 -1
  35. package/dist/cli/recommend.d.ts +5 -1
  36. package/dist/cli/recommend.d.ts.map +1 -1
  37. package/dist/cli/recommend.js +42 -2
  38. package/dist/cli/recommend.js.map +1 -1
  39. package/dist/cli/report.d.ts +4 -2
  40. package/dist/cli/report.d.ts.map +1 -1
  41. package/dist/cli/report.js +31 -5
  42. package/dist/cli/report.js.map +1 -1
  43. package/dist/cli/wiring.d.ts +2 -13
  44. package/dist/cli/wiring.d.ts.map +1 -1
  45. package/dist/cli/wiring.js +63 -110
  46. package/dist/cli/wiring.js.map +1 -1
  47. package/dist/detector/family-maturity.d.ts.map +1 -1
  48. package/dist/detector/family-maturity.js +0 -2
  49. package/dist/detector/family-maturity.js.map +1 -1
  50. package/dist/generator/console-isolation-emitters.d.ts +3 -0
  51. package/dist/generator/console-isolation-emitters.d.ts.map +1 -1
  52. package/dist/generator/console-isolation-emitters.js +5 -1
  53. package/dist/generator/console-isolation-emitters.js.map +1 -1
  54. package/dist/generator/deep-relative-emitters.d.ts +4 -0
  55. package/dist/generator/deep-relative-emitters.d.ts.map +1 -1
  56. package/dist/generator/deep-relative-emitters.js +4 -0
  57. package/dist/generator/deep-relative-emitters.js.map +1 -1
  58. package/dist/generator/depcruise-exempt.d.ts +5 -0
  59. package/dist/generator/depcruise-exempt.d.ts.map +1 -0
  60. package/dist/generator/depcruise-exempt.js +8 -0
  61. package/dist/generator/depcruise-exempt.js.map +1 -0
  62. package/dist/generator/dependency-internals-emitters.d.ts +1 -1
  63. package/dist/generator/dependency-internals-emitters.d.ts.map +1 -1
  64. package/dist/generator/dependency-internals-emitters.js +5 -2
  65. package/dist/generator/dependency-internals-emitters.js.map +1 -1
  66. package/dist/generator/env-access-emitters.d.ts +1 -1
  67. package/dist/generator/env-access-emitters.d.ts.map +1 -1
  68. package/dist/generator/env-access-emitters.js +3 -1
  69. package/dist/generator/env-access-emitters.js.map +1 -1
  70. package/dist/generator/eslint-preset-emitter.d.ts +4 -1
  71. package/dist/generator/eslint-preset-emitter.d.ts.map +1 -1
  72. package/dist/generator/eslint-preset-emitter.js +6 -4
  73. package/dist/generator/eslint-preset-emitter.js.map +1 -1
  74. package/dist/generator/eslint-scope.d.ts +10 -0
  75. package/dist/generator/eslint-scope.d.ts.map +1 -0
  76. package/dist/generator/eslint-scope.js +37 -0
  77. package/dist/generator/eslint-scope.js.map +1 -0
  78. package/dist/generator/phantom-dependency-emitters.d.ts +1 -1
  79. package/dist/generator/phantom-dependency-emitters.d.ts.map +1 -1
  80. package/dist/generator/phantom-dependency-emitters.js +4 -1
  81. package/dist/generator/phantom-dependency-emitters.js.map +1 -1
  82. package/dist/generator/public-api-emitters.d.ts +1 -1
  83. package/dist/generator/public-api-emitters.d.ts.map +1 -1
  84. package/dist/generator/public-api-emitters.js +2 -1
  85. package/dist/generator/public-api-emitters.js.map +1 -1
  86. package/dist/generator/test-isolation-emitters.d.ts +3 -1
  87. package/dist/generator/test-isolation-emitters.d.ts.map +1 -1
  88. package/dist/generator/test-isolation-emitters.js +26 -2
  89. package/dist/generator/test-isolation-emitters.js.map +1 -1
  90. package/dist/generator/workspace-package-emitters.d.ts +1 -1
  91. package/dist/generator/workspace-package-emitters.d.ts.map +1 -1
  92. package/dist/generator/workspace-package-emitters.js +2 -0
  93. package/dist/generator/workspace-package-emitters.js.map +1 -1
  94. package/dist/index.d.ts +2 -1
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +2 -1
  97. package/dist/index.js.map +1 -1
  98. package/dist/scanner/enforcers.d.ts +8 -0
  99. package/dist/scanner/enforcers.d.ts.map +1 -0
  100. package/dist/scanner/enforcers.js +74 -0
  101. package/dist/scanner/enforcers.js.map +1 -0
  102. package/package.json +8 -8
  103. package/dist/cli/init.d.ts +0 -21
  104. package/dist/cli/init.d.ts.map +0 -1
  105. package/dist/cli/init.js +0 -19
  106. package/dist/cli/init.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # archprint
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Archprint now writes a minimal `.archprint/` directory instead of a folder of per-family files. `generate` and `init` emit one self-contained rules file per linter you actually use (`.archprint/eslint.mjs` and/or `.archprint/dependency-cruiser.json`) plus a `.archprint/config.json` that records what is enforced and what Archprint manages. The plain-language summary that used to be `ADOPTION.md` is now a managed section in your `README.md`, and a managed `.prettierignore` entry keeps the generated files out of your formatter.
8
+
9
+ - `--expand` writes the granular artifacts (per-family configs, per-rule cards and fixtures, the eslint-plugin, ts-arch tests, and the layer graph) inside `.archprint/` for anyone who wants to inspect or vendor individual rules.
10
+ - New `archprint migrate` (alias `upgrade`) moves an older `archprint-rules/` setup to the new layout and rewrites the reference in your linter config in place, writing the new files before removing the old ones so the config never points at a deleted file. `generate` and `init` stop and point you to it if they detect the old layout.
11
+ - `wire` imports the single rules file with an ESM-safe relative specifier, and `eject` removes the files, the config, the managed README section, and every wired reference.
12
+
13
+ ## 0.5.0
14
+
15
+ ### Minor Changes
16
+
17
+ - archprint now generates rules for the enforcers your repo actually uses. It detects whether you have
18
+ ESLint or dependency-cruiser and emits each rule for a tool you already run, instead of writing
19
+ dependency-cruiser configs you have no way to enforce. On an ESLint-only repo it expresses test
20
+ isolation as an ESLint `no-restricted-imports` rule; dependency-cruiser configs are only written when
21
+ dependency-cruiser is present. `archprint recommend` now names, per rule, the installed tool that will
22
+ enforce it (or what you would need to install).
23
+ - `archprint generate --readme` writes an `ADOPTION.md` in the output directory summarizing what is
24
+ enforced now, held for review, and worth adopting, each with the tool that enforces it. It is tracked
25
+ in the manifest, so `archprint eject` removes it too.
26
+ - `archprint generate` gains two output controls: `--emit <eslint|dependency-cruiser|all>` forces the
27
+ output format regardless of the tooling archprint detects, and `--no-graph` skips the layer dependency
28
+ graph (Mermaid and Graphviz).
29
+ - `archprint generate` gains finer output control: `--only <family>` emits a single rule family,
30
+ `--rules <ids>` emits only the named forbidden-import rule ids, and `--check` runs the generated
31
+ ESLint rules against your repo and reports whether they pass, so you can confirm before wiring.
32
+
33
+ ### Patch Changes
34
+
35
+ - Generated rules are now green-by-construction: a rule archprint says you "already follow" passes clean
36
+ when you wire it, instead of flagging code that was there all along. Emitted rules are scoped to exactly
37
+ the files the detector measured (test files and cli/config files are excluded, matching what the analysis
38
+ skips) and exempt the specific exceptions the confidence gate accepted. This covers the console,
39
+ environment-access, deep-relative-import, workspace-package, test-isolation, phantom-dependency, and
40
+ dependency-internals rules across both ESLint and dependency-cruiser. The ESLint import rules
41
+ (deep-relative, workspace, test) are also emitted as a single merged block so they no longer override one
42
+ another in flat config.
43
+ - More fixes so an "enforce now" rule cannot flag code it was mined from:
44
+
45
+ - The dependency-cruiser test-isolation, phantom-deps, no-internals, and public-API deep-import rules now
46
+ exclude test files (`__tests__`, `e2e`, `cypress`, `.test`/`.spec`/`e2e` suffixes), matching what the
47
+ analysis excludes.
48
+ - The public-API deep-import rule also exempts the tolerated deep importers the confidence gate already
49
+ accepted, so an "enforce now" rule cannot flag code it was mined from.
50
+ - no-internals matches only the internals directories the analysis measures (`src`, `internal`,
51
+ `internals`), not a package's normal `dist`/`lib` entry.
52
+ - no-internals and phantom-deps are now held for review (emitted with `--include-structural`) rather than
53
+ auto-enforced, because their rules match resolved paths or dependency types and can flag a package whose
54
+ public entry resolves through `src/`, or a workspace package with no local `package.json` entry.
55
+ - Phantom (undeclared) dependency checking is emitted only for dependency-cruiser; the ESLint form is
56
+ dropped because it flags test files and first-party path aliases the analysis excludes.
57
+ - `wire` only splices into a recognized flat-config array or factory (`defineConfig`, `tseslint.config`);
58
+ an unrecognized call such as `export default loadConfig()` bails instead of reporting a false success.
59
+ - `generate --check` reports files it could not parse instead of calling them clean.
60
+
61
+ - - `wire` parses your ESLint config with the TypeScript AST, so it reliably edits the common
62
+ `const config = [...]; export default config;` shape and is not fooled by `export default` appearing
63
+ inside a string or comment.
64
+ - archprint no longer writes rule files for a tool your repo does not use; pass `--emit all` to force
65
+ every format.
66
+ - The wired ESLint config and the shareable preset ignore archprint's own generated files, so linting
67
+ your repo never flags them.
68
+ - `archprint generate --check` reports only violations of the rules archprint generated.
69
+ - `archprint wire` now edits the common config shape where the ESLint flat config is bound to a name
70
+ and exported by reference (`const config = [...]; export default config;`, the default for Next.js and
71
+ many TypeScript projects) instead of falling back to a manual edit. It resolves the exported name to
72
+ its declaration, including a `defineConfig([...])` wrapper, and `archprint eject` reverses it as before.
73
+
3
74
  ## 0.4.1
4
75
 
5
76
  ### Patch Changes
package/README.md CHANGED
@@ -17,25 +17,27 @@ Your `CLAUDE.md` is guidance. Your lint rules are enforcement. Archprint closes
17
17
  enforcement from patterns your codebase already demonstrates, so you adopt rules you can trust instead of
18
18
  authoring them by hand.
19
19
 
20
- Validated at scale: the full CLI workflow (`scan`, `recommend`, `generate`, `wire`, `eject`) was run across
21
- 92,861 real public TypeScript repositories with zero crashes, and the reversible install/uninstall round-trip
22
- held. A companion benchmark, [AgentRuleBench](https://github.com/Tommkruix/agentrulebench), measures the
23
- guidance-vs-enforcement question directly (a pre-registered, honest null result on the boundary it tested).
20
+ Validated at scale: `scan` and `recommend` ran across all 92,861 real public TypeScript repositories with zero
21
+ crashes, and the full `init`/`wire`/`eject` round-trip ran clean on a 2,000-repo stratified sample. A companion
22
+ benchmark, [AgentRuleBench](https://github.com/Tommkruix/agentrulebench), measures the guidance-vs-enforcement
23
+ question directly (a pre-registered, honest null result on the boundary it tested).
24
24
 
25
25
  **What auto-enforces vs. what you review.** Archprint is honest about which of its inferences it will stand
26
26
  behind unattended. An adversarial correctness audit (three rounds over four real repositories) found that the
27
27
  _mechanical_ families, ones grounded in unambiguous signals (no cycles, production must not import tests, no
28
- `console` in library code, no undeclared dependencies, deep-relative import style, public-API barrels, no
29
- reaching into a dependency's internals, and the DB/UI-in-server-entry rule), had zero false positives every
30
- round. So those auto-generate as enforcement. The _structural-inference_ families (layer and role boundaries,
31
- UI/data separation, entry purity, server/client, feature-slice and app isolation) infer a "layer" or "role"
32
- from paths, which can be wrong, so Archprint holds them for human review by default rather than silently
33
- enforcing them. Nothing whose inferred layer or role could be wrong is written as enforcement without you
34
- opting in.
35
-
36
- > Status: published on npm, pre-stable (0.x may break between minor versions). Production-ready today: the
37
- > insight commands (`scan`, `recommend`) and the auto-enforcement of the mechanical families above. The
38
- > structural families are review-only while they are hardened.
28
+ `console` in library code, no undeclared dependencies, deep-relative import style, public-API barrels, and the
29
+ DB/UI-in-server-entry rule), had zero false positives every round. So those auto-generate as enforcement. The
30
+ families held for human review by default, emitted only with `--include-structural`, are the ones that infer a
31
+ "layer" or "role" from paths, which can be wrong (layer and role boundaries, UI/data separation, entry purity,
32
+ server/client, feature-slice and app isolation), plus dependency hygiene, whose enforcement can over-flag.
33
+ Nothing that could be wrong is written as enforcement without you opting in.
34
+
35
+ > Status: published on npm and safe to run on your real repo. Every rule is review-gated by default,
36
+ > reversible in one command (`archprint eject`), and deterministic, and a rule archprint marks
37
+ > "enforce now" is checked to pass on your code before it says so. `scan` and `recommend` are stable;
38
+ > the structural families stay review-only while they are hardened. Versioning is still 0.x, so the CLI
39
+ > surface and rule format can refine between minor versions (the 0.6.0 release moved output to the
40
+ > `.archprint/` layout; run `archprint migrate` to upgrade), but the analysis is not experimental.
39
41
 
40
42
  ## What makes it different
41
43
 
@@ -74,15 +76,18 @@ package such as `apps/web`; a monorepo root is fine too, Archprint discovers the
74
76
 
75
77
  ```bash
76
78
  # One-shot setup: detect the stack, enforce the rules your code already follows,
77
- # and record what to adopt next in archprint.json
79
+ # and record what to adopt next in .archprint/config.json
78
80
  archprint init apps/web
79
81
 
80
82
  # See the rules your repo already follows, with the evidence
81
83
  archprint scan apps/web
82
84
 
83
- # Write the auto-trusted (mechanical) rules to disk (rule files + tool configs).
85
+ # Write the auto-trusted (mechanical) rules to .archprint/, only for the linters your repo uses.
84
86
  # Structural-inference rules are held for review; add --include-structural to emit them too.
85
- archprint generate apps/web --out archprint-rules
87
+ archprint generate apps/web
88
+
89
+ # Confirm the generated rules pass on your repo before wiring
90
+ archprint generate apps/web --check
86
91
 
87
92
  # Inspect the gate evidence behind one rule
88
93
  archprint explain AP-002 apps/web
@@ -98,9 +103,12 @@ archprint wire
98
103
 
99
104
  # Remove archprint's files and any wired references (clean uninstall)
100
105
  archprint eject
106
+
107
+ # Upgrading from 0.5.x? move an older archprint-rules/ setup to the .archprint layout
108
+ archprint migrate
101
109
  ```
102
110
 
103
- Re-running `generate` (or `init`) refreshes the files in `archprint-rules/` and removes any rule the
111
+ Re-running `generate` (or `init`) refreshes the files in `.archprint/` and removes any rule the
104
112
  evidence no longer supports, so the output never drifts from the current codebase. `wire` detects the
105
113
  enforcement tools your repo already uses (a flat eslint config, a `.dependency-cruiser.json`) and inserts a
106
114
  single managed reference into each, one that survives those regenerations; `eject` removes archprint's files
@@ -162,8 +170,8 @@ regardless of framework.
162
170
  | Forbidden imports (marker based) | A role (route handler, server entry) must not import a target (the DB client, the UI layer) | Auto |
163
171
  | Circular dependencies | The module graph should stay acyclic (gated on how cycle free it already is) | Auto |
164
172
  | Test isolation | Production (non-test) code must not import test or spec files | Auto |
165
- | Dependency hygiene | Import third-party packages by their public entry, not a dependency's `src`/`internal` internals | Auto |
166
- | Dependency declaration | Every imported third-party package must be declared in `package.json` (no phantom/transitive deps) | Auto |
173
+ | Dependency hygiene | Import third-party packages by their public entry, not a dependency's `src`/`internal` internals | Review |
174
+ | Dependency declaration | Every imported third-party package must be declared in `package.json` (no phantom/transitive deps) | Review |
167
175
  | Import style | Prefer workspace aliases over deep relative imports (`../../../`) | Auto |
168
176
  | Console isolation | Library (non-CLI) code must not call `console.*` | Auto |
169
177
  | Public API (barrel) boundaries | Files outside a feature or package must import it through its `index` barrel, not deep import its internals | Auto |
@@ -202,22 +210,32 @@ zero rules.
202
210
 
203
211
  ## Output formats
204
212
 
205
- `archprint generate` writes into the formats your existing tools already read:
206
-
207
- - **dependency-cruiser** `forbidden` rulesets: by default the mechanical boundaries (public-API deep-import,
208
- test-isolation, dependency-internals); the structural ones (layer, role-layering, feature-slice,
209
- app-isolation, entry-purity) are written only with `--include-structural`, after you review them
210
- - **eslint-plugin-boundaries** element-types config, and **ESLint core** rules (`no-restricted-imports`) for
211
- import-style boundaries
212
- - **ESLint rule files** for marker based patterns: a rule card (`.md`), the rule (`.ts`), and a passing and a
213
- failing fixture
214
- - **A shareable ESLint preset**: one self-contained `eslint-preset.archprint.mjs` that inlines the inferred
215
- rules and needs only eslint, so you can commit it, publish it, or hand it to another repo and adopt the rules
216
- in one line
217
- - **ts-arch tests** for the first-party boundaries (layer, role, UI/data), so the inferred architecture can run
218
- inside your existing Vitest or Jest suite
219
- - **Mermaid** and **Graphviz DOT** of the layer dependency graph, so the inferred architecture is visible and
220
- its violations are marked
213
+ `archprint generate` writes a minimal `.archprint/` directory: one self-contained rules file per linter your
214
+ repo actually uses, plus a `config.json` that records what is enforced and what Archprint manages. It detects
215
+ ESLint and dependency-cruiser and emits each rule for a tool you already run, so you are not left with config
216
+ for a tool you do not have. `--emit <eslint|dependency-cruiser|all>` forces the format. The default output:
217
+
218
+ - **`.archprint/eslint.mjs`**: one self-contained ESLint flat-config file that inlines every inferred ESLint
219
+ rule (marker-based forbidden imports, `no-restricted-imports` import-style boundaries, console isolation) and
220
+ needs only eslint, so you can commit it, publish it, or hand it to another repo and adopt it in one line
221
+ (`import archprint from './.archprint/eslint.mjs'`). It self-ignores `**/.archprint/**`.
222
+ - **`.archprint/dependency-cruiser.json`** (when dependency-cruiser is present): one `forbidden` ruleset with
223
+ the mechanical boundaries (public-API deep-import, test-isolation); the review-held ones (layer, role-layering,
224
+ feature-slice, app-isolation, entry-purity, dependency-internals, phantom deps) are added only with
225
+ `--include-structural`, after you review them.
226
+ - **`.archprint/config.json`**: the system file, what is enforced / held / worth adopting, and the managed
227
+ outputs list `eject` uses.
228
+ - **A managed README section** summarizing what is enforced now, held for review, and worth adopting (written
229
+ by `init`, or `generate --readme`), plus a managed `.prettierignore` entry so the generated files stay out of
230
+ your formatter.
231
+
232
+ `--expand` additionally writes the granular artifacts inside `.archprint/`: the per-family ESLint and
233
+ dependency-cruiser JSON, per-rule cards (`.md`) with passing and failing fixtures, the
234
+ eslint-plugin-boundaries element-types config, ts-arch tests, and the Mermaid and Graphviz DOT layer graph.
235
+
236
+ `generate --check` runs the generated ESLint rules against your repo and reports whether they pass, so you
237
+ can confirm before wiring. Upgrading from 0.5.x? `archprint migrate` moves an older `archprint-rules/` setup to
238
+ this layout and rewires your configs in place.
221
239
 
222
240
  ## How it compares
223
241
 
@@ -241,15 +259,16 @@ orphans, reachability) and knip (dead code); rather than compete, it emits into
241
259
 
242
260
  ## Commands
243
261
 
244
- | Command | What it does |
245
- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
246
- | `archprint init [path]` | Zero-config setup: detect the stack, enforce the rules the code already follows, and write an `archprint.json` with the adopt tiers. `--include-structural`, `--out <dir>`, `--fast`, `--force`. |
247
- | `archprint scan [path]` | Report the rules the repo already follows, with evidence. `--deep` resolves through barrels and aliases. |
248
- | `archprint generate [path]` | Write the auto-trusted mechanical rules + tool configs; structural rules held for review. `--rule <id>` emits one reviewed rule (including a SUGGEST rule). `--include-structural`, `--out <dir>`, `--fast`. |
249
- | `archprint explain <id> [path]` | Show the gate breakdown for one rule, with a codeframe per exception plus how-to-fix, when-not-to-use, and how-to-enforce. |
250
- | `archprint recommend [path]` | Recommend a rule set from the repo's evidence and detected stack (works on a fresh repo too). |
251
- | `archprint wire` | Reference the generated rules from the enforcement tools your repo uses (flat eslint config, `.dependency-cruiser.json`) via a managed, reversible reference. `--out <dir>`, `--dry-run`. |
252
- | `archprint eject` | Remove archprint's generated files, its manifests, and any wired references. `--out <dir>`, `--dry-run`. |
262
+ | Command | What it does |
263
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
264
+ | `archprint init [path]` | Zero-config setup: detect the stack, enforce the rules the code already follows, and write `.archprint/` plus a managed README section. `--expand`, `--include-structural`, `--out <dir>`, `--fast`, `--force`. |
265
+ | `archprint scan [path]` | Report the rules the repo already follows, with evidence. `--deep` resolves through barrels and aliases. |
266
+ | `archprint generate [path]` | Write the auto-trusted mechanical rules to `.archprint/` for the linters your repo uses; structural rules held for review. `--emit <eslint\|dependency-cruiser\|all>` forces the format, `--only <family>` and `--rules <ids>` narrow the output, `--check` runs the generated rules against your repo, `--readme` adds the README section, `--expand` also writes the per-family files/cards/fixtures/graph, `--rule <id>` emits one reviewed rule. Also `--include-structural`, `--no-graph`, `--out <dir>`, `--fast`. |
267
+ | `archprint migrate` (`upgrade`) | Move an older `archprint-rules/` setup to the `.archprint/` layout and rewire your configs in place. `--dry-run`. |
268
+ | `archprint explain <id> [path]` | Show the gate breakdown for one rule, with a codeframe per exception plus how-to-fix, when-not-to-use, and how-to-enforce. |
269
+ | `archprint recommend [path]` | Recommend a rule set from the repo's evidence and detected stack (works on a fresh repo too); names the installed tool that will enforce each rule. |
270
+ | `archprint wire` | Reference the generated rules from the enforcement tools your repo uses (flat eslint config, `.dependency-cruiser.json`) via a managed, reversible reference. `--out <dir>`, `--dry-run`. |
271
+ | `archprint eject` | Remove archprint's generated files, its config, the managed README section, and any wired references. `--out <dir>`, `--dry-run`. |
253
272
 
254
273
  ## Documentation
255
274
 
@@ -272,11 +291,15 @@ Same repo plus same version produces the same output. Analysis is pure and sorte
272
291
 
273
292
  ## Status and roadmap
274
293
 
275
- Pre-stable (`0.x`). The engine (twenty detectors, the confidence gate, and emitters for ESLint, a shareable
276
- preset, dependency-cruiser, ts-arch, and the layer graph) is in place and tested, and an adversarial
277
- correctness audit (three rounds, four real repositories) drove the false-positive rate on auto-generated rules
278
- to zero for the mechanical families, which is why those auto-enforce while the structural-inference families
279
- are held for review.
294
+ Archprint is safe to adopt today: every rule is review-gated and reversible via `archprint eject`, the analysis
295
+ is deterministic, and `scan`/`recommend` are battle-tested at census scale. The engine (twenty detectors, the
296
+ confidence gate, and emitters for a self-contained ESLint file, dependency-cruiser, ts-arch, and the layer
297
+ graph) is in place and tested, and an adversarial correctness audit (three rounds, four real repositories) drove
298
+ the false-positive rate on auto-generated rules to zero for the mechanical families, which is why those
299
+ auto-enforce while the structural-inference families are held for review. Versioning is still 0.x, so the CLI
300
+ surface and rule format can refine between minor versions, that is a maturing surface, not experimental
301
+ analysis; the 0.6.0 release moved the output to the compact `.archprint/` layout, and `archprint migrate`
302
+ upgrades an older setup in place.
280
303
 
281
304
  Production-ready today: `scan` and `recommend` (insight), and auto-enforcement of the mechanical families,
282
305
  with a self-consistency check at generate time, an `init` scaffolder for fresh repos, and framework coverage
@@ -0,0 +1,7 @@
1
+ export interface ReadmeResult {
2
+ status: 'created' | 'updated' | 'skipped';
3
+ reason?: string;
4
+ }
5
+ export declare function injectAdoptionSection(readmePath: string, body: string): ReadmeResult;
6
+ export declare function stripAdoptionSection(readmePath: string, createdByArchprint: boolean): ReadmeResult;
7
+ //# sourceMappingURL=adoption-readme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adoption-readme.d.ts","sourceRoot":"","sources":["../../src/cli/adoption-readme.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAsBD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CA6BpF;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,kBAAkB,EAAE,OAAO,GAC1B,YAAY,CAwBd"}
@@ -0,0 +1,76 @@
1
+ import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
+ const START = '<!-- archprint:start -->';
3
+ const END = '<!-- archprint:end -->';
4
+ function detectLayout(content) {
5
+ const bom = content.startsWith('') ? '' : '';
6
+ const eol = /\r\n/.test(content) ? '\r\n' : '\n';
7
+ return { eol, bom };
8
+ }
9
+ function block(body, eol) {
10
+ const lines = [
11
+ '## Architecture rules (managed by archprint, do not edit between the markers)',
12
+ '',
13
+ body.trim(),
14
+ ];
15
+ return [START, ...lines, END].join(eol);
16
+ }
17
+ export function injectAdoptionSection(readmePath, body) {
18
+ try {
19
+ if (!existsSync(readmePath)) {
20
+ writeFileSync(readmePath, `${block(body, '\n')}\n`);
21
+ return { status: 'created' };
22
+ }
23
+ const raw = readFileSync(readmePath, 'utf8');
24
+ const { eol, bom } = detectLayout(raw);
25
+ const content = bom ? raw.slice(bom.length) : raw;
26
+ const start = content.indexOf(START);
27
+ const end = content.indexOf(END);
28
+ if ((start !== -1) !== (end !== -1) || (start !== -1 && end < start)) {
29
+ return { status: 'skipped', reason: 'unpaired archprint markers in README; left untouched' };
30
+ }
31
+ const normalized = content.replace(/\r\n/g, '\n');
32
+ const fresh = block(body, '\n');
33
+ let next;
34
+ if (start !== -1) {
35
+ const s = normalized.indexOf(START);
36
+ const e = normalized.indexOf(END) + END.length;
37
+ next = normalized.slice(0, s) + fresh + normalized.slice(e);
38
+ }
39
+ else {
40
+ next = `${normalized.replace(/\n+$/, '')}\n\n${fresh}\n`;
41
+ }
42
+ writeFileSync(readmePath, bom + (eol === '\r\n' ? next.replace(/\n/g, '\r\n') : next));
43
+ return { status: 'updated' };
44
+ }
45
+ catch (error) {
46
+ return { status: 'skipped', reason: `could not write README (${error.message})` };
47
+ }
48
+ }
49
+ export function stripAdoptionSection(readmePath, createdByArchprint) {
50
+ try {
51
+ if (!existsSync(readmePath))
52
+ return { status: 'skipped', reason: 'no README' };
53
+ const raw = readFileSync(readmePath, 'utf8');
54
+ const { eol, bom } = detectLayout(raw);
55
+ const content = (bom ? raw.slice(bom.length) : raw).replace(/\r\n/g, '\n');
56
+ const start = content.indexOf(START);
57
+ const end = content.indexOf(END);
58
+ if (start === -1 || end === -1 || end < start) {
59
+ return { status: 'skipped', reason: 'no archprint section' };
60
+ }
61
+ const head = content.slice(0, start).replace(/\n+$/, '');
62
+ const tail = content.slice(end + END.length).replace(/^\n+/, '');
63
+ const without = head === '' ? tail : tail === '' ? head : `${head}\n\n${tail}`;
64
+ if (createdByArchprint && without.trim() === '') {
65
+ rmSync(readmePath, { force: true });
66
+ return { status: 'updated', reason: 'removed archprint-created README' };
67
+ }
68
+ const out = without.trim() === '' ? '' : `${without.replace(/\n+$/, '')}\n`;
69
+ writeFileSync(readmePath, bom + (eol === '\r\n' ? out.replace(/\n/g, '\r\n') : out));
70
+ return { status: 'updated' };
71
+ }
72
+ catch (error) {
73
+ return { status: 'skipped', reason: `could not update README (${error.message})` };
74
+ }
75
+ }
76
+ //# sourceMappingURL=adoption-readme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adoption-readme.js","sourceRoot":"","sources":["../../src/cli/adoption-readme.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { Recommendation, Recommendations } from './recommend.js';
2
+ export declare const CONFIG_FILE = "config.json";
3
+ export interface ManagedOutputs {
4
+ files: string[];
5
+ readme: boolean;
6
+ readmeCreated: boolean;
7
+ prettierignore: boolean;
8
+ prettierignoreCreated: boolean;
9
+ npmignore: boolean;
10
+ npmignoreCreated: boolean;
11
+ }
12
+ export interface ArchprintConfig {
13
+ archprintVersion: string;
14
+ app: string;
15
+ stack: string[];
16
+ rulesDir: string;
17
+ enforced: Recommendation[];
18
+ review: Recommendation[];
19
+ adopt: Recommendation[];
20
+ evidence: {
21
+ apps: number;
22
+ asOf: string;
23
+ };
24
+ managed: ManagedOutputs;
25
+ }
26
+ export declare function buildConfig(recommendations: Recommendations, version: string, location: {
27
+ app: string;
28
+ rulesDir: string;
29
+ }, managed: ManagedOutputs): ArchprintConfig;
30
+ export declare function writeConfig(outDir: string, config: ArchprintConfig): string;
31
+ export declare function recordManagedFiles(outDir: string, cwd: string, version: string, absoluteFiles: readonly string[]): void;
32
+ export declare function readConfig(outDir: string): ArchprintConfig | null;
33
+ //# sourceMappingURL=archprint-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archprint-config.d.ts","sourceRoot":"","sources":["../../src/cli/archprint-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtE,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,OAAO,EAAE,cAAc,CAAC;CACzB;AAgBD,wBAAgB,WAAW,CACzB,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC3C,OAAO,EAAE,cAAc,GACtB,eAAe,CAYjB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,CAK3E;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,IAAI,CAmBN;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAwBjE"}
@@ -0,0 +1,83 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import * as path from 'node:path';
3
+ export const CONFIG_FILE = 'config.json';
4
+ const EMPTY_MANAGED = {
5
+ files: [],
6
+ readme: false,
7
+ readmeCreated: false,
8
+ prettierignore: false,
9
+ prettierignoreCreated: false,
10
+ npmignore: false,
11
+ npmignoreCreated: false,
12
+ };
13
+ function configPath(outDir) {
14
+ return path.join(outDir, CONFIG_FILE);
15
+ }
16
+ export function buildConfig(recommendations, version, location, managed) {
17
+ return {
18
+ archprintVersion: version,
19
+ app: location.app,
20
+ stack: recommendations.stack,
21
+ rulesDir: location.rulesDir,
22
+ enforced: recommendations.enforceNow,
23
+ review: recommendations.review,
24
+ adopt: recommendations.adopt,
25
+ evidence: recommendations.evidence,
26
+ managed,
27
+ };
28
+ }
29
+ export function writeConfig(outDir, config) {
30
+ mkdirSync(outDir, { recursive: true });
31
+ const file = configPath(outDir);
32
+ writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`);
33
+ return file;
34
+ }
35
+ export function recordManagedFiles(outDir, cwd, version, absoluteFiles) {
36
+ const relative = absoluteFiles.map((file) => path.relative(cwd, file));
37
+ const existing = readConfig(outDir);
38
+ if (existing) {
39
+ const files = [...new Set([...existing.managed.files, ...relative])];
40
+ writeConfig(outDir, { ...existing, managed: { ...existing.managed, files } });
41
+ return;
42
+ }
43
+ writeConfig(outDir, {
44
+ archprintVersion: version,
45
+ app: '.',
46
+ stack: [],
47
+ rulesDir: path.relative(cwd, outDir) || outDir,
48
+ enforced: [],
49
+ review: [],
50
+ adopt: [],
51
+ evidence: { apps: 0, asOf: '' },
52
+ managed: { ...EMPTY_MANAGED, files: relative },
53
+ });
54
+ }
55
+ export function readConfig(outDir) {
56
+ const file = configPath(outDir);
57
+ if (!existsSync(file))
58
+ return null;
59
+ try {
60
+ const parsed = JSON.parse(readFileSync(file, 'utf8'));
61
+ if (typeof parsed.archprintVersion !== 'string')
62
+ return null;
63
+ return {
64
+ archprintVersion: parsed.archprintVersion,
65
+ app: parsed.app ?? '.',
66
+ stack: parsed.stack ?? [],
67
+ rulesDir: parsed.rulesDir ?? '.archprint',
68
+ enforced: parsed.enforced ?? [],
69
+ review: parsed.review ?? [],
70
+ adopt: parsed.adopt ?? [],
71
+ evidence: parsed.evidence ?? { apps: 0, asOf: '' },
72
+ managed: {
73
+ ...EMPTY_MANAGED,
74
+ ...(parsed.managed ?? {}),
75
+ files: Array.isArray(parsed.managed?.files) ? parsed.managed.files : [],
76
+ },
77
+ };
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ //# sourceMappingURL=archprint-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archprint-config.js","sourceRoot":"","sources":["../../src/cli/archprint-config.ts"],"names":[],"mappings":"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"}
@@ -1,41 +1,48 @@
1
- import type { GenerationStatus } from '../detector/confidence-gate.js';
1
+ import type { InstalledEnforcers } from '../scanner/enforcers.js';
2
+ import { type BoundaryRule } from '../generator/tsarch-emitter.js';
3
+ import { type ForbiddenImportSpec } from '../generator/eslint-plugin-emitter.js';
2
4
  import type { ScannedPattern, ScanResult } from './scan.js';
3
- export declare function writeRules(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
4
- export declare function emitOne(pattern: ScannedPattern, appDir: string, outDir: string): string;
5
- export declare function writeLayerConfig(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
6
- export declare function writePublicApiConfig(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
7
- export declare function writeFeatureSliceConfig(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
8
- export declare function writeAppIsolationConfig(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
9
- export declare function writeTestIsolationConfig(scan: ScanResult, outDir: string): string[];
10
- export declare function writeRoleLayeringConfig(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
11
- export declare function writeDeepRelativeConfig(scan: ScanResult, outDir: string): string[];
12
- export declare function writeConsoleIsolationConfig(scan: ScanResult, outDir: string): string[];
13
- export declare function writeServerClientConfig(scan: ScanResult, outDir: string): string[];
14
- export declare function writeUiDataConfig(scan: ScanResult, outDir: string): string[];
15
- export declare function writeStoriesIsolationConfig(scan: ScanResult, outDir: string): string[];
16
- export declare function writeWorkspacePackageConfig(scan: ScanResult, outDir: string): string[];
17
- export declare function writeEnvAccessConfig(scan: ScanResult, outDir: string): string[];
18
- export declare function writePhantomDependencyConfig(scan: ScanResult, outDir: string): string[];
19
- export declare function writeEntryPurityConfig(scan: ScanResult, outDir: string): string[];
20
- export declare function writeDependencyInternalsConfig(scan: ScanResult, outDir: string): string[];
21
- export declare function writeEslintPreset(scan: ScanResult, outDir: string, options?: {
22
- structural?: boolean;
23
- }): string[];
24
- export declare function writeEslintPlugin(scan: ScanResult, outDir: string): string[];
25
- export declare function writeTsArchTests(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
26
- export declare function writeGraph(scan: ScanResult, outDir: string): string[];
27
- export interface WrittenConfig {
28
- files: string[];
29
- label: string | null;
5
+ export declare const ESLINT_FILE = "eslint.mjs";
6
+ export declare const DEPCRUISE_FILE = "dependency-cruiser.json";
7
+ export declare const ARCHPRINT_DIR = ".archprint";
8
+ export declare const FAMILY_NAMES: readonly ["forbidden-imports", "layer", "role-layering", "public-api", "feature-slice", "app-isolation", "test-isolation", "dependency-hygiene", "entry-purity", "phantom-deps", "import-style", "console", "env-access", "workspace-package", "stories-isolation", "ui-data", "server-client"];
9
+ interface DependencyCruiserRule {
10
+ name: string;
30
11
  }
31
- export declare function writeEnforcementConfigs(scan: ScanResult, outDir: string, options?: {
32
- structural?: boolean;
33
- }): WrittenConfig[];
34
- export declare function regenerateConfigs(scan: ScanResult, outDir: string, options: {
12
+ interface EslintFamilyBlock {
13
+ family: string;
14
+ block: unknown;
15
+ }
16
+ interface DependencyCruiserFamily {
17
+ family: string;
18
+ forbidden: DependencyCruiserRule[];
19
+ }
20
+ export interface CollectedEnforcement {
21
+ eslintSpecs: ForbiddenImportSpec[];
22
+ eslintBlocks: EslintFamilyBlock[];
23
+ depcruise: DependencyCruiserFamily[];
24
+ boundaries: unknown | null;
25
+ tsArchRules: BoundaryRule[];
26
+ hasGraph: boolean;
27
+ }
28
+ export interface CollectOptions {
35
29
  structural?: boolean;
36
- version: string;
37
- }): {
38
- configs: WrittenConfig[];
39
- removed: string[];
40
- };
30
+ enforcers: InstalledEnforcers;
31
+ only?: string;
32
+ ruleIds?: readonly string[];
33
+ }
34
+ export declare function collectEnforcement(scan: ScanResult, options: CollectOptions): CollectedEnforcement;
35
+ export declare function writeArchprintEslint(collected: CollectedEnforcement, outDir: string): string | null;
36
+ export declare function writeArchprintDepcruise(collected: CollectedEnforcement, outDir: string): string | null;
37
+ export interface EmitResult {
38
+ eslint: string | null;
39
+ depcruise: string | null;
40
+ expanded: string[];
41
+ }
42
+ export declare function emitLayout(scan: ScanResult, outDir: string, options: CollectOptions & {
43
+ expand?: boolean;
44
+ graph?: boolean;
45
+ }): EmitResult;
46
+ export declare function emitOne(pattern: ScannedPattern, appDir: string, outDir: string): string;
47
+ export {};
41
48
  //# sourceMappingURL=generate.d.ts.map