archprint 0.2.0 → 0.4.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 (53) hide show
  1. package/CHANGELOG.md +48 -399
  2. package/README.md +40 -20
  3. package/dist/cli/generate.d.ts +4 -0
  4. package/dist/cli/generate.d.ts.map +1 -1
  5. package/dist/cli/generate.js +38 -0
  6. package/dist/cli/generate.js.map +1 -1
  7. package/dist/cli/program.d.ts.map +1 -1
  8. package/dist/cli/program.js +25 -8
  9. package/dist/cli/program.js.map +1 -1
  10. package/dist/cli/recommend.d.ts.map +1 -1
  11. package/dist/cli/recommend.js +2 -0
  12. package/dist/cli/recommend.js.map +1 -1
  13. package/dist/cli/wiring.d.ts.map +1 -1
  14. package/dist/cli/wiring.js +0 -3
  15. package/dist/cli/wiring.js.map +1 -1
  16. package/dist/detector/layer-detector.d.ts.map +1 -1
  17. package/dist/detector/layer-detector.js +4 -3
  18. package/dist/detector/layer-detector.js.map +1 -1
  19. package/dist/detector/role-layering-detector.d.ts.map +1 -1
  20. package/dist/detector/role-layering-detector.js +4 -2
  21. package/dist/detector/role-layering-detector.js.map +1 -1
  22. package/dist/generator/eslint-plugin-emitter.d.ts +2 -0
  23. package/dist/generator/eslint-plugin-emitter.d.ts.map +1 -1
  24. package/dist/generator/eslint-plugin-emitter.js +13 -13
  25. package/dist/generator/eslint-plugin-emitter.js.map +1 -1
  26. package/dist/generator/eslint-preset-emitter.d.ts +3 -0
  27. package/dist/generator/eslint-preset-emitter.d.ts.map +1 -0
  28. package/dist/generator/eslint-preset-emitter.js +17 -0
  29. package/dist/generator/eslint-preset-emitter.js.map +1 -0
  30. package/dist/generator/tsarch-emitter.d.ts +12 -0
  31. package/dist/generator/tsarch-emitter.d.ts.map +1 -0
  32. package/dist/generator/tsarch-emitter.js +29 -0
  33. package/dist/generator/tsarch-emitter.js.map +1 -0
  34. package/dist/generator/ui-data-isolation-emitters.d.ts.map +1 -1
  35. package/dist/generator/ui-data-isolation-emitters.js +4 -1
  36. package/dist/generator/ui-data-isolation-emitters.js.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/scanner/file-walker.d.ts.map +1 -1
  42. package/dist/scanner/file-walker.js +23 -13
  43. package/dist/scanner/file-walker.js.map +1 -1
  44. package/dist/scanner/ignore-filter.d.ts.map +1 -1
  45. package/dist/scanner/ignore-filter.js +0 -3
  46. package/dist/scanner/ignore-filter.js.map +1 -1
  47. package/dist/scanner/resolve-import.d.ts.map +1 -1
  48. package/dist/scanner/resolve-import.js +3 -4
  49. package/dist/scanner/resolve-import.js.map +1 -1
  50. package/dist/scanner/role-classifier.d.ts.map +1 -1
  51. package/dist/scanner/role-classifier.js +4 -0
  52. package/dist/scanner/role-classifier.js.map +1 -1
  53. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,412 +1,61 @@
1
1
  # archprint
2
2
 
3
- ## 0.2.0
3
+ ## 0.4.0
4
4
 
5
5
  ### Minor Changes
6
6
 
7
- - 621dd8f: Add a self-consistency guardrail (roadmap A2). Before `generate` writes anything, it verifies every AUTO rule
8
- it would emit is internally coherent: it governs a non-empty population, and the exception files its gate
9
- recorded exactly match the violations it lists (and stay within the exception budget). If any rule fails,
10
- generate refuses to write and names the rule, so a detector regression can never ship an incoherent rule. The
11
- check is exported as `checkSelfConsistency` and covered by a test that both confirms real repos are clean and
12
- that an injected inconsistency is caught.
13
- - b6cebc9: Separate rule applicability from role-classification confidence in the confidence gate (roadmap A4). The gate
14
- previously overloaded `roleConfidence`: detectors passed `0` both to mean "this rule governs nothing" (vacuous)
15
- and paid the price when a role was real but only moderately identifiable, so a genuinely-followed rule over a
16
- low-confidence role was silently REJECTed. The gate now takes an explicit `applicable` flag for the vacuous
17
- case, and grades role confidence in two tiers: `>= 0.8` to auto-enforce, `>= 0.5` (moderate) to suggest. A real
18
- but moderately-classified role (for example a UI component matched only by its `.tsx` extension) now surfaces as
19
- a SUGGEST instead of vanishing, while a role we are more-unsure-than-not about is still rejected. Consequently
20
- ui-data isolation, whose COMPONENT role is a 0.5-confidence catch-all, can no longer auto-enforce at the gate
21
- level (not merely by family tiering); it is review-only by construction. The four vacuous-guard detectors
22
- (ui-data, env-access, server-client, test-isolation) were migrated to the new `applicable` flag with no change
23
- to their deterministic-role outcomes.
24
- - fccc369: `archprint scan` now discovers app directories automatically. Pointed at a repo root, including a monorepo,
25
- it finds each app or package that has its own tsconfig.json and enough of its own source, and scans each one,
26
- rather than requiring you to point at a single app directory. Sub-packages below the size threshold are
27
- skipped; single-app repos are unchanged.
28
- - 4a36220: Add monorepo app isolation, a new rule family: sibling apps under an `apps`/`services` container must not
29
- import each other directly (they should communicate through shared packages). `detectAppIsolation` gates
30
- "apps under `<container>` must not import each other" with the Wilson floor, and `archprint generate` writes
31
- `dependency-cruiser.app-isolation.archprint.json`, a `$1`-back-reference cross-app rule. The sibling-isolation
32
- traversal and gating are now factored into a shared `detectSiblingIsolation` core used by both the
33
- feature-slice and app-isolation detectors.
34
- - 864fff5: Add the CLI: `archprint scan | generate | explain | approve` (commander), wiring the full pipeline so a
35
- repo is scanned, its markers inferred, patterns gated, and rule artifacts emitted end to end.
36
-
37
- `scan` and `explain` are specifier-level by default (about a second on a few-thousand-file repo), with
38
- `--deep` to resolve imports through barrels and aliases. `generate` and `approve` resolve the graph by
39
- default (the commitment point should not mint an AUTO rule the full graph would reject), with a `--fast`
40
- opt-out that warns to confirm with a deep pass before enforcing.
41
-
42
- Internally, `detectForbiddenImports` runs several patterns in one shared pass, and `createImportAnalyzer`
43
- takes `{ resolve: false }` to skip module resolution and the type checker. The fast path matches only at
44
- the import-specifier level, so barrel/alias-hidden imports are not caught in that mode (disclosed in the
45
- fast-scan report footer); the emitted rule enforces at the specifier level regardless, which its rule card
46
- documents.
47
-
48
- - 6f27c1a: Add circular-dependency detection (`detectCycles`). Archprint builds the first-party import graph for an app
49
- and finds every strongly connected component (Tarjan), reports the cycles, and gates a "no circular
50
- dependencies" rule by how cycle-free the repo already is. The graph is built fast (specifier-level) by
51
- default, with a deep type-resolved mode available; both agree on the cycles. A cycle-free repo yields an AUTO
52
- no-cycles rule; a cyclic one surfaces the cycles for review.
53
- - 25040e3: Add import-style detection: prefer workspace aliases over deep relative imports (`../../../` and deeper).
54
- `detectDeepRelativeImports` gates the rule with the Wilson floor over files that use relative imports, and
55
- `archprint generate` writes `eslint.deep-relative.archprint.json`, an ESLint `no-restricted-imports` config,
56
- Archprint's first ESLint-core output format.
57
- - 258777b: Add dependency hygiene, a new rule family covering external packages (the first detector that is not
58
- first-party only): a file should import a third-party package by its public entry or a documented subpath, not
59
- by reaching into its build/impl directories (`dist`, `src`, `lib`, `esm`, `cjs`, `build`, `out`, `internal`).
60
- `detectDependencyInternals` counts files importing external packages and those reaching into internals, gates
61
- with the Wilson floor, and `archprint generate` writes `dependency-cruiser.dependency-internals.archprint.json`.
62
- - 8440ef3: Add the pattern detector and confidence gate: `detectForbiddenImport` / `detectNoDbInRequestEntry`
63
- infer a "role A must not import target B" boundary from the resolved import graph, and `evaluateGate`
64
- decides AUTO / SUGGEST / REJECT against four conditions (ratio >= 90%, evidence >= 20 files,
65
- exceptions <= 3, role confidence >= 80%). Markers match the import specifier and first-party leaves
66
- only (never a dependency's internal `node_modules` folders). The file walker now classifies `.ts`
67
- modules with a top-level `"use server"` directive as server actions.
68
- - 5d4af81: The import analyzer now captures dynamic imports (`import('...')`) as value edges, resolving the target in
69
- deep mode (barrel-aware, like a namespace import) and by specifier in fast mode. Layer / dependency-direction
70
- and cycle detection now see dependencies that flow through dynamic imports; on real codebases this surfaces
71
- cycles and edges a static-only scan would miss.
72
- - 629468e: `archprint generate` now emits the inferred layer / dependency-direction boundaries as a dependency-cruiser
73
- config (`dependency-cruiser.archprint.json`), one `forbidden` rule per AUTO boundary. This is the first of the
74
- ecosystem output formats: the rules Archprint infers can be enforced directly by dependency-cruiser in CI,
75
- rather than being hand-written.
76
- - 5726159: `archprint generate` now also emits the inferred layer boundaries as an eslint-plugin-boundaries config
77
- (`eslint-boundaries.archprint.json`): each layer becomes an element type, and each layer disallows the layers
78
- it must not import. Alongside the dependency-cruiser output, the inferred rules can be enforced by whichever
79
- tool a TypeScript team already uses.
80
- - e92d273: Add entry-point purity, a new rule family: framework file-convention entries (pages, routes, layouts, API
81
- handlers) are loaded by the framework and should not be imported by other first-party code. `detectEntryPurity`
82
- counts entries with a non-zero first-party in-degree and gates the rule with the Wilson floor; `archprint
83
- generate` writes `dependency-cruiser.entry-purity.archprint.json`.
84
- - 667de54: Make `archprint explain` actionable (roadmap B2). Each explanation now shows, below the gate breakdown, a
85
- codeframe for every exception (the offending import line with its line number), a "How to fix" line, a "When
86
- not to use this" caveat, and a "How to enforce" line that names the exact next command for the rule's gate
87
- status. Per-rule guidance lives in a single `rule-guidance` source and the codeframe reader is a small,
88
- self-contained module.
89
- - b5598e4: Introduce family-maturity tiering so only audit-trusted rules auto-enforce. The Phase A1 adversarial audit
90
- found that every false-AUTO came from the structural-inference families (layer, role-layering, entry-purity,
91
- ui/data, server/client, feature-slice, app-isolation, stories, plus env-access and workspace-package-api),
92
- while the mechanical families had zero false-AUTO across 148 audited rules. `archprint generate` now emits only
93
- the mechanical families as AUTO by default and holds the structural families for review (pass
94
- `--include-structural` to emit them anyway); `archprint recommend` correspondingly lists structural AUTO under
95
- "review and adopt" rather than "enforce now". This makes the tool honest and shippable today: nothing whose
96
- inferred layer/role can be wrong is ever silently written as enforcement, while the structural families are
97
- hardened toward AUTO over time.
98
- - 87ad55d: Add feature-slice isolation, a new rule family. A container directory (`features`, `modules`, `slices`,
99
- `domains`) holds sibling slices that should not import one another; `detectFeatureSliceIsolation` counts each
100
- slice file that imports a different sibling slice and runs it through the Wilson gate, so "slices under
101
- `<container>` must not import each other" becomes enforceable (AUTO), provisional (SUGGEST), or unsupported.
102
- Surfaced in the scan report, and `archprint generate` writes
103
- `dependency-cruiser.feature-slice.archprint.json`, a cross-slice `forbidden` rule that captures the source
104
- slice and forbids the others with a `$1` back-reference.
105
- - 84b9da9: Broaden framework role coverage in the classifier so the existing rules apply beyond Next.js and NestJS:
106
- SvelteKit (`+server.ts` endpoints, `+page.server.ts`/`+layout.server.ts` server loads, `hooks.server.ts`,
107
- and `+page.ts`/`+layout.ts` universal loads), Nuxt Nitro handlers (`server/api|routes|middleware|plugins`),
108
- and Remix / React Router file-based routes (`app/routes/**`, `root`, `entry.server`/`entry.client`).
109
-
110
- `ROLE_PATTERNS` now maps each role to all of its path patterns rather than one, and the rule generator embeds
111
- every pattern per role. Previously a role matched by more than one rule would have collapsed to a single
112
- pattern in generated rules; with the new multi-framework rules this would have dropped variants.
113
-
114
- - bbae98a: Grandfather known exceptions in the generated AP- eslint rules (roadmap B3). An AUTO rule is inferred because the
115
- code already follows it apart from at most a few exception files; the generated plugin rule now skips exactly
116
- those files, so wiring the rule and running the linter is green on the current codebase (no red wall on day one)
117
- while any new violation elsewhere is still caught, the standard ratchet. The exceptions remain visible in `scan`
118
- and `explain`; only enforcement grandfathers them. Proven end-to-end: the exception file passes and the same
119
- violation in a new file is flagged by the real eslint engine.
120
- - 293be92: Emit the inferred layer dependency graph as Mermaid and Graphviz DOT (the visualization formats
121
- dependency-cruiser and madge produce). `archprint generate` now also writes `layer-graph.archprint.mmd` and
122
- `layer-graph.archprint.dot` whenever layers are present. Each interacting layer pair renders as a weighted
123
- directed edge: the dominant dependency is a solid arrow, and a leak that runs against an inferred boundary is
124
- dotted (Mermaid) or dashed red (DOT), so a reader sees the architecture and where it is violated.
125
- - 0671bf9: Add `archprint init` (roadmap B1): a single zero-config onboarding command. It detects the repo's stack, scans
126
- the import graph, writes enforcement configs for the rules the code already follows (mechanical families by
127
- default, `--include-structural` to add the review-tier families), and records an `archprint.json` manifest with
128
- the three census-backed recommendation tiers (enforce now / review / adopt) so the setup is reproducible and
129
- inspectable. It refuses to overwrite an existing manifest without `--force`, runs the self-consistency guardrail
130
- before writing, and prints a friendly summary with next steps. The writer orchestration shared with `generate`
131
- was extracted into a single `writeEnforcementConfigs` helper so the mechanical/structural partition lives in one
132
- place.
133
- - f4943d8: Add machine-readable output (roadmap B4). `scan --json` emits a stable, serializable summary (per app: file and
134
- alias counts, plus each non-rejected rule with its family, status, observed conformance, confidence floor,
135
- observations, and violating-file count), and `recommend --json` emits the three recommendation tiers as JSON.
136
- Both are keyed by `archprintVersion` for forward compatibility, making archprint scriptable in CI. Exit codes are
137
- the documented contract: 0 on success, 1 on error or refusal. SARIF is intentionally left to the tools archprint
138
- emits into (eslint and dependency-cruiser already produce it) rather than reimplemented here.
139
- - fccc369: Add layer / dependency-direction inference (`detectLayerBoundaries`). Archprint now infers a repo's layers
140
- from its directory structure and, for each interacting layer pair, the observed dependency direction, then
141
- flags the minority ("upward") direction as a candidate forbidden boundary through the same Wilson confidence
142
- gate used for the other rules. The scan is fast (specifier-level) by default, with a deep type-resolved mode
143
- available; both agree on the enforced (AUTO) boundaries. This is the first of the broader rule families that
144
- bring Archprint level with hand-written architecture-conformance tools, with the difference that the rules
145
- are inferred from the real import graph and evidence-gated rather than hand-written.
146
- - 0f41d6f: Add the generated-output lifecycle (roadmap B6, part 1). Archprint now records everything it writes into an
147
- `.archprint-outputs.json` manifest in the output directory, so it owns a precise, safe list of its own files.
148
- `generate` and `init` now clean their previous outputs before writing fresh ones, so a rule the evidence no
149
- longer supports stops being enforced instead of lingering as a stale file (upholds the conservative-bias
150
- principle). A new `archprint eject` command removes archprint's generated files and its config manifests cleanly
151
- (`--dry-run` to preview), giving a clean uninstall. The clean step only ever touches paths archprint recorded
152
- as its own, and refuses to delete anything outside the output directory.
153
- - 38789b8: Make the flagship forbidden-import rules (AP-) loadable and enforceable (roadmap B7). `generate`/`init` now emit
154
- a self-contained local eslint plugin (`eslint-plugin.archprint.mjs`) that implements each AUTO forbidden-import
155
- rule faithfully to the detector (matches the import specifier, skips type-only imports, and only applies to files
156
- whose path matches the rule's role). The eslint aggregator loads the plugin so a single wired reference activates
157
- the rules, and they are removed cleanly on `eject`. An end-to-end test runs the real eslint engine and confirms a
158
- server-entry route importing the UI layer is flagged while a clean route passes. Also fixes a wiring bug where a
159
- single-line `export default []` config had its array closer swallowed by the inserted managed comment.
160
- - 1faa4aa: Infer forbidden-import markers per repo instead of using one repo's hardcoded conventions.
161
- `inferUiLayerMarkers` derives the UI-layer specifier from where a repo's components cluster,
162
- disambiguated by import fan-in so a shared component library beats a feature directory.
163
- `inferDbClientMarkers` combines a curated list of known db libraries with first-party wrappers
164
- discovered by client instantiation (unambiguous ORM constructors, or generic pool/driver
165
- constructors paired with a known-library import), and emits a leaf-path marker per wrapper so
166
- barrel re-exports are caught via the detector's barrel resolution. `detectUiLayerInServerEntry` and
167
- `detectDbClientInRequestEntry` use them, and `DEFAULT_DB_MARKERS` drops its repo-specific first-party
168
- entries.
169
-
170
- This reduces hardcoding of first-party conventions; it does not eliminate hardcoding: the known-db
171
- library list, the ORM constructor set, and the structural-segment exclusions are curated framework/ORM
172
- vocabulary by design. Inference is heuristic: an exotic ORM whose constructor is not listed is missed,
173
- and UI fan-in only helps when the shared layer is actually imported, so a fully colocated app whose
174
- routes never import the shared library can still pick a feature directory.
175
-
176
- - 0fbca1c: Merge `approve` into `generate --rule <id>`. Emitting a single reviewed rule (including a SUGGEST rule) was a
177
- special case of generate, and having two commands that both write rule artifacts was the one genuinely ambiguous
178
- pair in the CLI. Now `archprint generate --rule AP-001` does what `archprint approve AP-001` did; the standalone
179
- `approve` command is removed. Naming the rule is the explicit consent, so the AUTO-is-automatic /
180
- SUGGEST-needs-review distinction is preserved. This drops the command surface from 8 to 7. Done pre-1.0, before
181
- the surface is public.
182
- - 2e7d4bc: Add orphan-module detection: `detectOrphans` finds first-party source files that no other file imports and
183
- that are not framework or tooling entry points (routes, pages, layouts, middleware, config). These dead-code
184
- candidates are surfaced in the scan report as an informational SUGGEST (never enforced), since dynamic or
185
- string-based loading can make a live file look unreferenced. The cycle and orphan detectors now share a
186
- single `buildImportGraph` builder so the first-party value-import graph is constructed one way.
187
- - 5b57f3c: Add phantom-dependency detection: every imported third-party package should be declared in package.json rather
188
- than relied on transitively. `detectPhantomDependencies` collects the declared dependencies (the app's
189
- package.json, the workspace root's, and all workspace package names) and flags imports of anything else, gated
190
- by the Wilson floor. `archprint generate` writes `dependency-cruiser.phantom-deps.archprint.json` keyed on
191
- dependency-cruiser's `npm-no-pkg` / `npm-unknown` dependency types.
192
- - e94ac18: Add public-API (barrel) boundary detection. A directory holding an `index.ts`/`index.tsx` exposes a public
193
- API; `detectPublicApiBoundaries` classifies every external consumer against the nearest enclosing barrel (an
194
- import of the barrel conforms, an import of any other file in the group is a deep-import violation) and runs
195
- the count through the Wilson gate, so "files outside `<dir>` must import it through its barrel" becomes
196
- enforceable (AUTO), provisional (SUGGEST), or unsupported. Surfaced in the scan report. Uses the fast import
197
- graph deliberately: deep resolution would resolve through the barrel and erase the barrel-vs-deep signal.
198
- - 7d6a325: Generate an enforceable rule from public-API boundaries. `archprint generate` now writes
199
- `dependency-cruiser.public-api.archprint.json`: one `forbidden` deep-import rule per AUTO barrel group, where
200
- a module outside `<dir>` may not import any file inside it except the barrel. Completes the public-API rule
201
- family (detect plus emit), alongside the existing dependency-cruiser and eslint-plugin-boundaries layer
202
- outputs.
203
- - d25caf9: Back `archprint recommend` with real adoption evidence. The command now ships a catalog of per-family
204
- AUTO-adoption rates, overall and per detected stack, mined from a 92,861-repo census, and attaches to every
205
- recommendation the share of comparable repos that already enforce that rule. The "adopt from day one" tier is
206
- now decided by that census evidence (a family is recommended when a meaningful share of comparable repos
207
- already enforce it) instead of hand-picked stack flags, so the guidance reflects what the TypeScript ecosystem
208
- actually does.
209
- - dcc2b66: Add `archprint recommend`, which sorts every rule family into three tiers from the repo's
210
- evidence and detected stack: enforce now (already followed), review and adopt (thin evidence),
211
- and adopt from day one (suits the stack but not yet evidenced). The last tier gives fresh repos a
212
- stack-aware baseline to enforce from day one, where there is little code to infer from.
213
- - 803839d: Add role-based layering, a new rule family that uses the classifier's semantic roles rather than directory
214
- names, so it catches the classic backend tier hierarchy even when the tiers are named suffixes rather than
215
- folders (NestJS-style). `detectRoleLayering` counts value edges between roles (CONTROLLER, SERVICE, REPOSITORY,
216
- DATA_ACCESS, DB_MODULE), takes the minority direction of each interacting pair as the candidate boundary, and
217
- gates it with the Wilson floor (using the classifier's confidence as roleConfidence). `archprint generate`
218
- writes `dependency-cruiser.role-layering.archprint.json`.
219
- - ded3544: Add the rule generator: from an AUTO-gated detected pattern, emit four artifacts (ESLint rule `.ts`,
220
- rule card `.md` with the evidence attached, plus passing and failing fixtures). The emitted rule is
221
- self-contained plain ESLint (no `@typescript-eslint` runtime dependency) and enforces the detector's
222
- direct-import semantics. Validated by RuleTester and an independent scratch-project run.
223
- - 2d372bd: Add the scanner core: a workspace tsconfig alias resolver, a convention-based role classifier, a
224
- barrel resolver, and a symbol-level, type-aware, workspace-package-aware import analyzer
225
- (`analyzeImports`). This is the deterministic substrate the rule generator builds on.
226
- - 0751a34: Fix two scanning bugs found by running archprint on itself. (1) Respect `.gitignore`: the walker and app-dir
227
- discovery now skip git-ignored paths (via the standard `ignore` matcher) in addition to the always-noise
228
- directories, so a scan no longer descends into vendored repos or other large ignored trees and hang. (2) Resolve
229
- ESM `.js` specifiers to their `.ts` source: `import './x.js'` where the file is `x.ts` (the NodeNext/ESM
230
- convention, which archprint's own code uses) now resolves, so first-party edges are found on ESM TypeScript
231
- projects instead of every file looking like an orphan. The three duplicated import resolvers were unified into
232
- one `resolveFirstPartyImport`. Verified against the five-repo regression corpus (unchanged) and by dogfooding
233
- this repo (false orphans dropped from 66 to 1, the real CLI entry).
234
- - 6e758fa: Add the server/client boundary rule family (the twentieth detector): a Next.js `"use client"` module must not
235
- import a `server-only` module. `detectServerClientBoundary` finds `"use client"` modules (via a ReDoS-safe
236
- leading-directive scan) and modules importing the `server-only` package, then flags client modules that reach a
237
- server-only module, gated by the Wilson floor. `archprint generate` writes
238
- `dependency-cruiser.server-client.archprint.json`. The directive scanner is generalized to expose both
239
- `hasUseServerDirective` and `hasUseClientDirective`.
240
- - 949d583: Add stories isolation: Storybook `.stories` files are loaded by Storybook and should not be imported by other
241
- code. `detectStoriesIsolation` flags stories with a non-story importer (over the shared import graph), gated by
242
- the Wilson floor, and `archprint generate` writes `dependency-cruiser.stories-isolation.archprint.json`.
243
- - 3408ba6: Add test isolation, a new rule family: production (non-test) code must not import test or spec files.
244
- `detectTestIsolation` builds the import graph with test files kept as nodes (via a new `includeTests` option on
245
- `buildImportGraph`), counts production files that import a test file, and runs the count through the Wilson
246
- gate. Surfaced in the scan report (only when the app has test files), and `archprint generate` writes
247
- `dependency-cruiser.test-isolation.archprint.json`, a `not-to-test` `forbidden` rule.
248
- - d28f41a: Add transitive layer reachability. `computeLayerReachability` condenses the first-party import graph by
249
- strongly-connected component into a DAG and computes, for every layer, the set of layers a file in it can
250
- reach through a chain of value imports. The scan now flags an AUTO layer boundary that a plain import rule
251
- would pass but that still leaks through an intermediary layer (`from` reaches `to` transitively), pointing at
252
- the stronger dependency-cruiser `reachable` form. The cycle, orphan, and reachability passes now share one
253
- prebuilt import graph and one strongly-connected-components routine, so a full scan builds the graph once.
254
- - ea29d9c: Add UI/data separation: reusable UI components (COMPONENT role) should reach the data layer through services,
255
- not import the DB/data layer (`DB_MODULE` / `DATA_ACCESS`) directly. `detectUiDataIsolation` gates it with the
256
- Wilson floor over the component population, and `archprint generate` writes
257
- `dependency-cruiser.ui-data.archprint.json`.
258
- - 628c96e: Add two AST usage-based rule families over a new shared usage-scanner: console isolation (library / non-CLI
259
- code must not call `console.*`) and env access (read `process.env` only in the config/env layer). Both are
260
- Wilson-gated and emit scoped ESLint flat-config blocks (`no-console`, `no-restricted-properties`) via
261
- `archprint generate`. This is Archprint's first analysis beyond the import graph.
262
- - 756c800: Gate rules on a statistical confidence bound instead of a fixed file-count threshold.
263
-
264
- The confidence gate previously required `ratio >= 90%` AND `evidence >= 20` files as two separate checks.
265
- These are now fused into one principled test: the Wilson score 95% lower bound on the true conformance rate
266
- must be at least 90%. This accounts for sample size and observed rate together, a pattern followed in 5/5
267
- files is not evidence of a real rule (low bound), while 40/40 or 216/217 is. A pattern that looks like a
268
- rule (observed >= 80%) but lacks the evidence to be confident is now surfaced as a provisional `SUGGEST`
269
- rather than a silent `REJECT`, so a thin or simple codebase is not locked out. The `exceptions <= 3` and
270
- `roleConfidence >= 0.80` guards are unchanged. `GATE_THRESHOLDS` now exposes `{ confidence, exceptions,
271
- roleConfidence }`.
272
-
273
- - d39460b: `wire` now edits the common `export default tseslint.config(...)` and `export default defineConfig([...])`
274
- flat-config shapes, not just a bare `export default [` array (dogfood finding). It inserts the managed spread as
275
- the first config, keeping the file's formatting, and `eject` restores it exactly. Config shapes it still cannot
276
- parse fall back to the printed snippet as before. Proven end-to-end: wiring archprint's own `tseslint.config()`
277
- eslint config and running the real eslint engine fires the generated `no-console` rule.
278
- - 1ddb697: Add `archprint wire` to reference the generated eslint rules from your flat eslint config (roadmap B6, part 2).
279
- `generate`/`init` now also emit an aggregator (`eslint.archprint.mjs`) that globs archprint's eslint rule blocks,
280
- so the reference is one stable line that survives regeneration (new rules are picked up, dropped ones disappear,
281
- without re-wiring). `wire` inserts a MANAGED, reversible block (a marked import + one spread) into a flat
282
- `eslint.config.{js,mjs,cjs}` when it can detect the array-form export, is idempotent, supports `--dry-run`, and
283
- prints the exact snippet to paste for any other shape. `eject` now also removes that managed reference, restoring
284
- the config exactly. Scope note: this wires the eslint built-in-rule blocks; the layer rules (eslint-plugin-
285
- boundaries), the AP- custom rules, and the dependency-cruiser configs still target their own tools.
286
- - 23fba83: Generalize `wire`/`eject` across the enforcement tools a repo uses, not just eslint. Wiring is now a tool
287
- registry: `wire` detects each supported tool's config and inserts a managed, reversible reference into every one
288
- it finds, and `eject` removes them all. Dependency-cruiser is now supported: `generate`/`init` emit an aggregated
289
- `dependency-cruiser.all.archprint.json` (merging the individual forbidden-rule sets, refreshed on every
290
- regeneration), and `wire` adds a managed `extends` to a `.dependency-cruiser.json`, preserving the rest of the
291
- config and removing exactly that entry on eject. A tool config that cannot be edited safely (a JS
292
- dependency-cruiser config) gets the exact snippet printed instead. Adding a future tool is now a single registry
293
- entry.
294
- - e4c7307: Add workspace-package public-API detection: in a monorepo, a workspace package should be imported by its name
295
- (which resolves to its entry), not by a deep path into its source. `detectWorkspacePackageApi` matches import
296
- specifiers against the workspace package names, gates deep imports with the Wilson floor, and `archprint
297
- generate` writes `eslint.workspace-package.archprint.json` (a `no-restricted-imports` rule over the package
298
- names).
7
+ - Monorepo-friendly `recommend`, `generate`, and `init`. `recommend` now discovers every app directory under a
8
+ monorepo root and reports per app (its `--json` output gains an `apps` array, matching `scan`). `generate` and
9
+ `init` automatically use the single app directory they find under a root, or, when a monorepo has several, list
10
+ the directories to point at instead of failing with a generic message.
299
11
 
300
12
  ### Patch Changes
301
13
 
302
- - 6188b91: Fix `detectLayerBoundaries` and `detectCycles` returning empty or incorrect results when called with a
303
- relative `appDir`. They now normalize the directory to an absolute path, so import edges resolve consistently
304
- whether an absolute or relative directory is passed. (The CLI already passed absolute paths and was
305
- unaffected.)
306
- - bd70f41: Identify UI components by rendered JSX, not the `.tsx` extension.
14
+ - More accurate layer-boundary inference. A layer boundary is now trusted for auto-enforcement only when the
15
+ evidence for its direction is strong (many imports consistently flow the dominant way). Boundaries inferred from
16
+ just a handful of cross-layer imports, where the direction could be noise, are held for review instead, which
17
+ removes spurious layer rules while keeping the well-evidenced ones.
18
+ - More accurate role-layering inference. A role boundary (for example, a repository must not import a service) is
19
+ now trusted for auto-enforcement only when both the file roles are confidently classified and the direction is
20
+ strongly evidenced. Boundaries whose direction rests on just a couple of imports, where it could be a coin flip,
21
+ are held for review instead.
307
22
 
308
- A component is now recognized when it actually renders JSX (a JSX element/fragment, or a
309
- `React.createElement`/`cloneElement` call), matching how react-docgen and eslint-plugin-react define a
310
- component, instead of trusting the `.tsx` file extension. A non-rendering `.tsx` file (types, constants,
311
- re-exports) no longer inflates a directory's component count, and a `.ts` file that renders via
312
- `createElement` is correctly counted. Detection uses a syntactic parse only (no type checker), so it stays
313
- fast. No marker change on inbox-zero, dub, formbricks, or cal.com.
23
+ ## 0.3.0
314
24
 
315
- - 96fb249: Detector accuracy fixes:
316
-
317
- - Type-only imports (`import type { X } from '...'` and inline `import { type X } from '...'`) are no longer
318
- counted as a runtime dependency on a forbidden target. They are erased at compile time, so a request entry
319
- that imports a marker only as a type is no longer a false violation, including in fast (specifier-only)
320
- mode. Implemented as a syntactic `hasValueBinding` signal on every analyzed import.
321
- - Database-client marker inference now recognizes first-party files that re-export a known database library
322
- (`export * from '@prisma/client'`, for example `@/lib/db` or a workspace-scoped
323
- `@scope/core/prisma-client`), not only files that instantiate a client. This closes a false-AUTO gap where
324
- direct database access through a re-export surface was invisible.
325
-
326
- - 4d2a0fc: Speed up fast-mode scanning about 2.6x (roadmap C1/C2). Fast mode now extracts imports with the raw TypeScript
327
- parser instead of ts-morph's heavier wrapper layer (same AST fidelity, no regex parsing), and caches each file's
328
- parse by path + mtime + size so the many detectors that scan the same files reuse one parse instead of
329
- re-parsing. On inbox-zero (2,232 files) a fast scan drops from ~13.5s to ~5.2s. Output is unchanged: the
330
- per-family gate distribution is identical across the five-repo regression corpus, and all tests pass. Deep mode
331
- (full barrel/alias resolution via ts-morph) is unaffected.
332
- - fcd8010: Fix four more Phase A1 re-audit findings in the structural families: server-client is REJECTed as vacuous when
333
- no `server-only` module exists anywhere; `.e2e-spec` files and `__tests__`/`test`/`e2e`/`cypress`/`playwright`
334
- directories are classified as tests (so test scaffolding no longer forms bogus layers); generated code
335
- (`generated/**`) is neutral so a component importing generated enums is not a false data-layer violation; and
336
- an entry re-exporting another entry (idiomatic route aliasing) no longer breaks entry-purity.
337
- - f70c105: Fix four correctness bugs the Phase A1 audit found in the structural detectors, all of which produced
338
- false "enforceable" rules:
339
-
340
- - **Workspace self-reference imports** (`@scope/pkg/sub` where that is the app's own package name, an npm
341
- workspaces self-reference, not a tsconfig alias) are now resolved as first-party. They were silently
342
- dropped, undercounting violations (a false-AUTO source) and leaving a live enforcement gap.
343
- - **Vacuous rules** no longer reach AUTO: UI/data-separation when the app has no data layer, and env-access
344
- when nothing reads `process.env` at all, govern nothing, so they can no longer be "enforceable".
345
- - **Data-access classification** now recognizes a flat client file named after a known ORM
346
- (`utils/prisma.ts`, `lib/drizzle.ts`), not only files under a `db/`/`database/`/`prisma/` directory, so
347
- UI-imports-the-database violations are detected instead of a false "clean".
348
- - **Dependency-hygiene** no longer flags a package's published `dist/lib/esm/cjs/build/out` subpaths as
349
- internal reaches (they are commonly the public entry point, e.g. `react-syntax-highlighter/dist/esm/...`);
350
- it flags only the unambiguous `/src/` and `/internal(s)/` reaches.
351
-
352
- - 9ab170d: Resolve bare `baseUrl`-relative imports as first-party. With an explicit tsconfig `baseUrl`, TypeScript
353
- resolves a bare specifier like `import x from "app/foo"` or `"test/fixtures/x"` against baseUrl before
354
- node_modules, but the resolver treated these as external and dropped them, undercounting real violations (a
355
- false-AUTO source) and misreporting them as phantom dependencies. Each top-level directory under an explicit
356
- baseUrl is now a first-party prefix. Surfaced by the Phase A1 re-audit on three monorepos.
357
- - 596df8f: Fix the env-access detector's confidence gate, which could never reach AUTO. It gated on the count of files
358
- that read `process.env`, but a repo that centralizes env access reads it in only a handful of config files, so
359
- the population was always far below the Wilson floor. It now gates on the non-config files the rule governs
360
- (the population), with violations being non-config files that read `process.env` directly, mirroring the
361
- console-isolation detector. A full 92,861-repo census surfaced the bug: env-access was AUTO on 0% of apps.
362
- - 1266ebd: Fix the layer detector treating file-router route directories as architectural layers. `layerOfPath` skipped
363
- `app`/`pages` and promoted the first route segment beneath them to a "layer", so a file deep in
364
- `app/(group)/sub.domain/(group)/<segment>/` became layer `<segment>`, and same-named route directories across
365
- disjoint route groups collapsed into one fake, non-cohesive layer (e.g. a "programs" layer spanning nine
366
- unrelated `app/**/programs` directories). This produced many false "enforceable" rules on Next.js app-router
367
- repos. The router tree is now a single layer (`app`/`pages`), so only genuine top-level source directories
368
- become layers. Surfaced by the Phase A1 false-AUTO audit; on the audited repos this removed 131 of 222 layer
369
- AUTO rules, nearly all of them false positives, while preserving the real "shared layer must not import the
370
- router" boundaries.
371
- - 3740ff8: Make the test-isolation gate REJECT when an app has no test files at all, instead of vacuously passing as
372
- AUTO. `generate`/`recommend` already suppressed this case at the surface, but the detector's own gate now
373
- reports it honestly, so every consumer of the gate status is consistent. Surfaced by the Phase A1 round-3 audit.
374
- - 3cc1b6b: Make the scan report label consistent with what actually ships. The structural-inference families (layer,
375
- role-layering, entry-purity, ui/data, server/client, feature-slice, app-isolation, env-access,
376
- workspace-package, stories) now render as "(review before enforcing)" instead of "(enforceable)", matching the
377
- fact that `generate` holds them for review by default; the mechanical families keep "(enforceable)".
378
- - 96e3ae4: Stop test files diluting UI-layer inference, and select the UI layer by coverage.
379
-
380
- Colocated test files were counted as counter-evidence against a component directory, sinking a real UI
381
- layer below the confidence gate (a false negative on repos that colocate tests next to components).
382
- Inference now excludes tests and stories from the file set, and selects the UI layer by component coverage
383
- (where components concentrate) instead of import fan-in, which mis-selected heavily-imported primitive
384
- sub-libraries such as `components/ui`. Validated with no marker change on inbox-zero, dub, formbricks, and
385
- cal.com.
386
-
387
- - 0f5617d: Fix catastrophic backtracking (ReDoS) in the "use server" directive check.
388
-
389
- `hasUseServerDirective` used a regex with nested quantifiers over overlapping whitespace/comments, which
390
- backtracks exponentially: a file whose first bytes contain ~20+ comment tokens took ~6s, and more hung the
391
- scan indefinitely (a real hang on `archprint scan` for repos with heavily-commented or generated leading
392
- files). It now scans the head linearly (skipping whitespace, line comments, and block comments) with only a
393
- fixed-literal final check, so the worst case is linear. Behavior is unchanged for real inputs; a
394
- comment-heavy head that took seconds now takes microseconds.
395
-
396
- - f2d0572: Classify Next.js App Router entry files as route entries, not reusable components.
25
+ ### Minor Changes
397
26
 
398
- `page.tsx`, `layout.tsx`, `template.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`, `default.tsx`, and
399
- `global-error.tsx` under `app/` render UI but are framework entry points, not part of the shared UI layer.
400
- Classifying them as a distinct `ROUTE_ENTRY` role keeps them out of the component count during UI-layer
401
- inference, so a page-heavy feature area is no longer mistaken for the UI layer. No marker change on
402
- inbox-zero, dub, formbricks, or cal.com.
27
+ - Add Angular support: Archprint now detects Angular in your stack and recognizes Angular components, so the
28
+ component-aware rules (such as UI/data separation) apply to Angular projects.
29
+ - Generate a shareable ESLint preset. `archprint generate` now writes a single, self-contained
30
+ `eslint-preset.archprint.mjs` that inlines the inferred rules and needs only eslint, so you can commit it,
31
+ publish it, or hand it to another repository and adopt the rules in one line:
32
+ `import archprint from './eslint-preset.archprint.mjs'`.
33
+ - Scan Vue and Svelte single-file components. Archprint now reads the `<script>` block of `.vue` and `.svelte`
34
+ files, treats them as UI components, and follows their imports, so the component-aware rules (such as UI/data
35
+ separation) apply to Vue and Svelte projects.
36
+ - Emit architecture boundary tests for ts-arch. Alongside the ESLint and dependency-cruiser outputs, Archprint
37
+ now writes a ts-arch test file for the inferred first-party boundaries (layer, role, and UI/data), so you can
38
+ run them inside your existing Vitest or Jest suite.
403
39
 
404
- - d706e63: `archprint scan` now surfaces circular-dependency detection: it lists the import cycles it finds, or, when
405
- the repo is cycle-free, reports that the "no circular dependencies" rule is enforceable. Cycle detection runs
406
- in fast mode (the structural graph is faithful without the type checker), so it does not slow a `--deep` scan.
407
- - d94fa88: Classify `.tsx` route handlers and `pages/api` files as request entries. Route handlers can legitimately use
408
- JSX (for example `next/og` `ImageResponse` routes), and matching only `.ts` misclassified those as UI
409
- components, hiding request-entry boundary violations in them.
40
+ ## 0.2.0
410
41
 
411
- Release notes are generated by [Changesets](https://github.com/changesets/changesets) on each release and
412
- appear here starting with the first published version.
42
+ First public release. Archprint mines the architecture rules your repository already follows from its real
43
+ import graph, gates each on statistical evidence, and emits them into the tools you already use.
44
+
45
+ ### Highlights
46
+
47
+ - **Evidence-gated rule inference across 20 families**, including forbidden imports (DB client / UI in a server
48
+ entry), import cycles, test isolation, console isolation, import style, public-API barrels, dependency hygiene
49
+ and declaration, layer and role boundaries, UI/data separation, entry purity, server/client boundaries,
50
+ feature-slice and app isolation, env access, workspace-package API, and stories isolation. Mechanical families
51
+ auto-enforce; inferred structural families are held for review.
52
+ - **CLI**: `init` (zero-config setup), `scan` (the rules your code already follows, with the evidence),
53
+ `recommend` (adoption tiers backed by a census of tens of thousands of public repositories, works on a fresh
54
+ repo too), `explain` (per-rule evidence, codeframes, and how-to-fix), `generate` (write the rule configs), and
55
+ `wire` / `eject` (reference the generated rules from your config, reversibly).
56
+ - **Emits into your stack**: ESLint, including a generated plugin for the forbidden-import rules, and
57
+ dependency-cruiser, wired in with a single managed reference that survives regeneration.
58
+ - **Clean lifecycle**: re-running refreshes the output and drops any rule the evidence no longer supports; the
59
+ few known exceptions are grandfathered so adoption is green on day one.
60
+ - **Framework awareness**: Next.js, Nest, SvelteKit, Nuxt, Remix, and React / Vue / Svelte stacks.
61
+ - **Machine-readable output** (`scan --json`, `recommend --json`) and stable exit codes for CI.
package/README.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Archprint
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/archprint.svg)](https://www.npmjs.com/package/archprint)
4
+ [![CI](https://github.com/Tommkruix/archprint/actions/workflows/ci.yml/badge.svg)](https://github.com/Tommkruix/archprint/actions/workflows/ci.yml)
5
+ [![npm downloads](https://img.shields.io/npm/dm/archprint.svg)](https://www.npmjs.com/package/archprint)
6
+ [![license](https://img.shields.io/npm/l/archprint.svg)](./LICENSE)
7
+ [![docs](https://img.shields.io/badge/docs-live-blue)](https://tommkruix.github.io/archprint/)
8
+
3
9
  **Mine the architecture rules your repo already enforces, with the evidence attached.**
4
10
 
5
11
  Archprint scans a TypeScript repository's real import graph, finds the architectural boundaries the code
@@ -22,9 +28,9 @@ from paths, which can be wrong, so Archprint holds them for human review by defa
22
28
  enforcing them. Nothing whose inferred layer or role could be wrong is written as enforcement without you
23
29
  opting in.
24
30
 
25
- > Status: `0.1.0`, pre-stable (0.x may break between minor versions). Not yet published to npm. Production-ready
26
- > today: the insight commands (`scan`, `recommend`) and the auto-enforcement of the mechanical families above.
27
- > The structural families are review-only while they are hardened.
31
+ > Status: published on npm, pre-stable (0.x may break between minor versions). Production-ready today: the
32
+ > insight commands (`scan`, `recommend`) and the auto-enforcement of the mechanical families above. The
33
+ > structural families are review-only while they are hardened.
28
34
 
29
35
  ## What makes it different
30
36
 
@@ -36,7 +42,17 @@ your stack rather than replacing it.
36
42
 
37
43
  ## Install
38
44
 
39
- Not yet on npm. For now, build from source:
45
+ ```bash
46
+ npm install --save-dev archprint
47
+ ```
48
+
49
+ Then run it (or use `npx archprint …` without installing):
50
+
51
+ ```bash
52
+ npx archprint scan .
53
+ ```
54
+
55
+ Or build from source:
40
56
 
41
57
  ```bash
42
58
  git clone https://github.com/Tommkruix/archprint
@@ -46,13 +62,6 @@ npm run build
46
62
  node dist/cli.js scan <path-to-your-app>
47
63
  ```
48
64
 
49
- Once published, it will install as a normal dev dependency:
50
-
51
- ```bash
52
- npm install --save-dev archprint
53
- npx archprint scan .
54
- ```
55
-
56
65
  Requires Node >= 20. Point Archprint at an app directory that has a `tsconfig.json` (for a monorepo, a
57
66
  package such as `apps/web`; a monorepo root is fine too, Archprint discovers the app directories).
58
67
 
@@ -111,7 +120,7 @@ A real scan of [inbox-zero](https://github.com/elie222/inbox-zero) (`apps/web`,
111
120
  trimmed:
112
121
 
113
122
  ```
114
- Archprint v0.1.0
123
+ Archprint v0.2.0
115
124
  Scanned 2,232 TypeScript files
116
125
  Workspace aliases: 18 resolved
117
126
 
@@ -138,6 +147,11 @@ Ships as: **Auto** = auto-generated as enforcement (mechanical families, 0 false
138
147
  correctness audit). **Review** = held for human review by default; emit with `--include-structural` (the
139
148
  inferred layer/role can be wrong, so it is not enforced silently). **Report** = surfaced only, never enforced.
140
149
 
150
+ Framework aware: Archprint recognizes the stack (Next.js, Nest, SvelteKit, Nuxt, Remix) and classifies UI
151
+ components across React (`.tsx`), Angular (`.component.ts`, `.directive.ts`), and Vue and Svelte single-file
152
+ components (it reads the `<script>` block of `.vue`/`.svelte` files), so the component-aware rules apply
153
+ regardless of framework.
154
+
141
155
  | Detector | Rule it can infer | Ships as |
142
156
  | -------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------- |
143
157
  | Forbidden imports (marker based) | A role (route handler, server entry) must not import a target (the DB client, the UI layer) | Auto |
@@ -192,6 +206,11 @@ zero rules.
192
206
  import-style boundaries
193
207
  - **ESLint rule files** for marker based patterns: a rule card (`.md`), the rule (`.ts`), and a passing and a
194
208
  failing fixture
209
+ - **A shareable ESLint preset**: one self-contained `eslint-preset.archprint.mjs` that inlines the inferred
210
+ rules and needs only eslint, so you can commit it, publish it, or hand it to another repo and adopt the rules
211
+ in one line
212
+ - **ts-arch tests** for the first-party boundaries (layer, role, UI/data), so the inferred architecture can run
213
+ inside your existing Vitest or Jest suite
195
214
  - **Mermaid** and **Graphviz DOT** of the layer dependency graph, so the inferred architecture is visible and
196
215
  its violations are marked
197
216
 
@@ -248,15 +267,16 @@ Same repo plus same version produces the same output. Analysis is pure and sorte
248
267
 
249
268
  ## Status and roadmap
250
269
 
251
- `0.1.0`, pre-stable. The engine (twenty detectors, five output formats, the confidence gate) is in place and
252
- tested, and an adversarial correctness audit (three rounds, four real repositories) drove the false-positive
253
- rate on auto-generated rules to zero for the mechanical families, which is why those auto-enforce while the
254
- structural-inference families are held for review.
270
+ Pre-stable (`0.x`). The engine (twenty detectors, the confidence gate, and emitters for ESLint, a shareable
271
+ preset, dependency-cruiser, ts-arch, and the layer graph) is in place and tested, and an adversarial
272
+ correctness audit (three rounds, four real repositories) drove the false-positive rate on auto-generated rules
273
+ to zero for the mechanical families, which is why those auto-enforce while the structural-inference families
274
+ are held for review.
255
275
 
256
- Production-ready today: `scan` and `recommend` (insight), and auto-enforcement of the mechanical families.
257
- Still ahead: hardening the structural families toward auto-enforcement (a real per-file role-confidence measure,
258
- layer-cohesion, role-classifier ordering), a self-consistency check at generate time, a pinned regression
259
- corpus, an `init` scaffolder for fresh repos, and broader framework role coverage.
276
+ Production-ready today: `scan` and `recommend` (insight), and auto-enforcement of the mechanical families,
277
+ with a self-consistency check at generate time, an `init` scaffolder for fresh repos, and framework coverage
278
+ across React, Angular, Vue, and Svelte. Still ahead: hardening the structural families toward auto-enforcement
279
+ (a real per-file role-confidence measure, layer-cohesion, role-classifier ordering).
260
280
 
261
281
  ## Contributing
262
282
 
@@ -18,7 +18,11 @@ export declare function writeEnvAccessConfig(scan: ScanResult, outDir: string):
18
18
  export declare function writePhantomDependencyConfig(scan: ScanResult, outDir: string): string[];
19
19
  export declare function writeEntryPurityConfig(scan: ScanResult, outDir: string): string[];
20
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[];
21
24
  export declare function writeEslintPlugin(scan: ScanResult, outDir: string): string[];
25
+ export declare function writeTsArchTests(scan: ScanResult, outDir: string, statuses?: readonly GenerationStatus[]): string[];
22
26
  export declare function writeGraph(scan: ScanResult, outDir: string): string[];
23
27
  export interface WrittenConfig {
24
28
  files: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"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"}