ts-reviewer 2.1.0 → 3.0.1

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.
package/README.md CHANGED
@@ -10,7 +10,7 @@ Three modes, one skill:
10
10
 
11
11
  | Mode | What happens |
12
12
  |---|---|
13
- | **scan** | Analyzes the codebase and writes a prioritized report to `code-smells.md` |
13
+ | **scan** | Analyzes the codebase and writes a prioritized report to `code-smells/report.md` |
14
14
  | **fix** | Reads the report and applies fixes file-by-file with tsc/lint/test verification |
15
15
  | **auto** | Runs scan, asks you to confirm, fixes everything, deletes the report if clean |
16
16
 
@@ -81,7 +81,8 @@ Find issues in this project
81
81
  Audit the codebase for security and type safety problems
82
82
  ```
83
83
 
84
- Claude will analyze the project and write a report to `code-smells.md` in the project root.
84
+ Claude will analyze the project and write a report to `code-smells/report.md` in the project root.
85
+ With Architecture active, the same directory also holds project discovery, Knip, graph, metric, co-change, rule, and Mermaid artifacts.
85
86
 
86
87
  #### Domain flags
87
88
 
@@ -114,7 +115,7 @@ After reviewing the scan report, ask Claude to fix the issues:
114
115
  Fix the issues from the report
115
116
  ```
116
117
  ```
117
- Apply fixes from code-smells.md
118
+ Apply fixes from code-smells/report.md
118
119
  ```
119
120
 
120
121
  The fix workflow:
@@ -124,7 +125,7 @@ The fix workflow:
124
125
  4. Runs linter, fixes lint errors
125
126
  5. Runs full test suite, compares with baseline, fixes any regressions it caused
126
127
  6. Repeats verification up to 5 iterations
127
- 7. Updates the report: if all fixed → deletes `code-smells.md`; if some remain → keeps it as an audit trail with BEFORE/AFTER diffs for every fix
128
+ 7. Updates the report: if all fixed → deletes `code-smells/report.md`; if some remain → keeps it as an audit trail with BEFORE/AFTER diffs for every fix
128
129
 
129
130
  **Important:** fix never commits or stages anything. You review the changes and decide what to keep.
130
131
 
@@ -227,7 +228,7 @@ A check line looks like this:
227
228
  The format is enforced, not merely recommended:
228
229
 
229
230
  ```bash
230
- npm test # typecheck + 5 conformance tests
231
+ npm test # typecheck + conformance and tool tests
231
232
  ```
232
233
 
233
234
  The test catches a check line that lost its severity, a block the profile does not declare, blocks out of order, a banned vague word, and a line over the limit — each reported with its file and line number.
@@ -238,29 +239,33 @@ The test catches a check line that lost its severity, a block the profile does n
238
239
 
239
240
  ### Scan mode
240
241
 
241
- 1. **Discovery** — detects domain flags, maps the project, reads tsconfig.json, detects linter and test runner. If architecture is active, also maps module relationships and checks for `docs/adr/`.
242
+ 1. **Discovery** — detects domain flags, maps the project, reads tsconfig.json, detects linter and test runner, and asks once before downloading a missing architecture tool.
242
243
  2. **Diagnostics** — runs `tsc --noEmit`, linter, and LSP diagnostics (if available)
243
- 3. **Analysis** — specialized passes for each active domain (sub-agents in Claude Code, sequential in Claude.ai), each with its own checklist. Every finding passes an Evidence Protocol: verified against the actual file content, confidence-gated, version-gated against the project's TS/runtime.
244
- 4. **Report** — deduplicates, applies severity boost (scoped modes), consolidates recurring patterns, enforces a noise budget, writes `code-smells.md`. Architecture findings appear in a separate `## Architecture Opportunities` section at the end.
244
+ 3. **Architecture pre-pass** — when active, writes bounded Knip, graph, metric, co-change, rule, and Mermaid artifacts under `code-smells/`, with project coverage and bounded failure diagnostics.
245
+ 4. **Analysis** — specialized passes judge the candidates against the active checklists; tool output is never a finding by itself.
246
+ 5. **Report** — deduplicates, applies severity boost (scoped modes), consolidates recurring patterns, enforces a noise budget, writes `code-smells/report.md`, and validates its contract before the scan succeeds. Architecture findings appear in a separate `## Architecture Opportunities` section at the end.
247
+
248
+ Validate a report directly with `node ts-reviewer/tools/validate-report.mjs --repo . --report code-smells/report.md`. It checks headings, counts, finding anchors, architecture fields, and linked artifacts without adding a dependency. An **error** is a defect of the report that rewriting it fixes; a **warning** names an outcome of the mechanical pre-pass — a graph with no diagram, say — that the report cannot fix, and warnings do not fail the run.
245
249
 
246
250
  ### Fix mode
247
251
 
248
- 1. Parses `code-smells.md` as the work plan
249
- 2. Captures test baseline (runs tests before changes)
250
- 3. Applies fixes bottom-to-top within each file (so line numbers don't shift)
251
- 4. Writes regression tests for each testable fix
252
- 5. Runs `tsc --noEmit` after each file
253
- 6. Full verification loop: tsc + linter + test suite (max 5 iterations)
254
- 7. Compares test results with baseline — only fixes regressions it caused
255
- 8. Updates or deletes the report
252
+ 1. Validates `code-smells/report.md` and stops before changing code when the report is invalid
253
+ 2. Parses the report as the work plan
254
+ 3. Captures test baseline (runs tests before changes)
255
+ 4. Applies fixes bottom-to-top within each file (so line numbers don't shift)
256
+ 5. Writes regression tests for each testable fix
257
+ 6. Runs `tsc --noEmit` after each file
258
+ 7. Runs the full verification loop: tsc + linter + test suite (max 5 iterations)
259
+ 8. Compares test results with baseline — only fixes regressions it caused
260
+ 9. Updates or deletes the report, keeps the remaining `code-smells/` artifacts, and asks before removing them
256
261
 
257
262
  ## Tips
258
263
 
259
- - **Add `code-smells.md` to `.gitignore`** — it's a review artifact, not part of your source code.
264
+ - **Add `code-smells/` to `.gitignore`** — it contains review artifacts, not source code.
260
265
 
261
266
  - **Commit before running fix** — so you can `git diff` to review changes and `git checkout -- .` to revert if needed.
262
267
 
263
- - **Edit the report before fix** — since fix uses `code-smells.md` as its work plan, you can delete issues you don't want fixed, change severities, or add notes before running fix.
268
+ - **Edit the report before fix** — since fix uses `code-smells/report.md` as its work plan, you can delete issues you don't want fixed, change severities, or add notes before running fix.
264
269
 
265
270
  - **Scoped review for PRs** — `"review my branch against main"` is the most practical mode for day-to-day use. Full codebase audits are better suited for periodic health checks.
266
271
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-reviewer",
3
- "version": "2.1.0",
3
+ "version": "3.0.1",
4
4
  "description": "Install the TypeScript Code Reviewer skill for Claude Code, Codex, or Antigravity",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -22,7 +22,7 @@
22
22
  "build": "tsc -p tsconfig.json",
23
23
  "prepublishOnly": "npm run build && npm test",
24
24
  "typecheck": "tsc -p tsconfig.json --noEmit",
25
- "test": "npm run typecheck && node --test cnlp/skill-format.test.js",
25
+ "test": "npm run typecheck && node --test cnlp/skill-format.test.js tools.test.mjs",
26
26
  "dev": "npm run build && node --enable-source-maps dist/cli.js"
27
27
  },
28
28
  "keywords": [
@@ -28,13 +28,15 @@ inputs:
28
28
  - the reference checklists under `references/`
29
29
 
30
30
  preconditions:
31
- - `code-smells.md` exists before fix mode runs: it is the work plan, and fix stops with an error when it is absent
31
+ - `code-smells/report.md` exists before fix mode runs: it is the work plan, and fix stops with an error when it is absent
32
32
 
33
33
  scope:
34
34
  - `.ts`, `.mts`, and `.cts` files are reviewed alike
35
35
  - `.d.ts` files are reviewed by the Type Safety and Config domains only: a declaration has no runtime behavior
36
36
  - `.tsx` is out of scope
37
37
  - the analysis scope in a scoped mode is the diff file list, and the reading scope is wider
38
+ - `--affected` widens Architecture evidence to modules reaching a changed module, and does not widen the analysis scope
39
+ - anchor each finding to 1 file, and set `in_diff: true` only when that file is in the diff list
38
40
  - read as read-only context: `tsconfig.json`, the configs it extends, and `package.json`
39
41
  - read as read-only context: the files the scoped files import 1 level deep, and the shared types in `types.ts`, `*.d.ts`, `interfaces/`, `shared/`
40
42
 
@@ -57,11 +59,16 @@ forbidden_behaviors:
57
59
  - do not emit the same file and line twice
58
60
  - do not boost severity in `full` scope mode: all code is treated alike
59
61
  - do not flag a config issue in a scoped mode unless `tsconfig.json` is in the diff
62
+ - do not download and execute a missing analysis tool before the operator approves it once at discovery
63
+ - do not run `npm install` or `npm uninstall` for analysis: use an approved pinned-major `npx -y` command, or record the pre-pass as skipped
64
+ - do not rename a report section, field, severity, confidence, or domain: `report_format` and `domains` hold exact identifiers
60
65
 
61
66
  outputs:
62
- - `code-smells.md` in the project root: the scan report, and the work plan fix reads
63
- - the `## Architecture Opportunities` section of that report, only when Architecture is active and at least 1 candidate is found
64
- - the audit trail in `code-smells.md` when any issue remains: BEFORE/AFTER for each fixed issue, a status tag for each failed, reverted, or skipped issue, the original entry for each untouched issue
67
+ - `code-smells/report.md` in the project root: the scan report, and the work plan fix reads
68
+ - `code-smells/knip.json`, `projects.json`, `co-change.md`, `cruise-summary.md`, `metrics.md`, and graph and diagram directories when Architecture is active
69
+ - `code-smells/suggested.dependency-cruiser.cjs` when prose declares dependency rules and no machine-readable declaration owns them
70
+ - the `## Architecture Opportunities` section of the report only when Architecture is active and at least 1 confirmed finding exists
71
+ - the audit trail in `code-smells/report.md` when any issue remains: BEFORE/AFTER for each fixed issue, a status tag for each failed, reverted, or skipped issue, the original entry for each untouched issue
65
72
  - a regression test for each fix that is testable
66
73
 
67
74
  run_modes:
@@ -119,40 +126,52 @@ workflow:
119
126
  9. audit the config that governs the files in scope, and name that config in the summary
120
127
  10. read the linter config: `eslint.config.*`, `.eslintrc.*`, `biome.json`, `deno.json`
121
128
  11. read `package.json` for the dependencies and the module type, and verify the TypeScript version, `engines.node`, and `@types/node` against `target_stack`
122
- 12. identify the entry points: `index.ts`, `main.ts`, the `exports` of `package.json`
123
- 13. collect the context files named in `scope:` when the scope mode is scoped
124
- 14. map the module relationships when Architecture is active: circular imports, deep relative imports, barrel-file cycles, feature slices, public entry points
125
- 15. scan `docs/adr/`, `doc/adr/`, `adr/`, and `docs/decisions/` for architectural decisions before proposing a change, and skip it silently when none of them exists
126
- 16. report the discovery summary in the shape of `discovery_summary`
129
+ 12. identify declared entry points from `package.json#exports`, `main`, `bin`, and the `start`, `dev`, and `serve` scripts
130
+ 13. when Architecture is active, inspect local Knip and dependency-cruiser binaries and ask once before running either missing tool through pinned-major `npx -y`
131
+ 14. collect the context files named in `scope:` when the scope mode is scoped
132
+ 15. identify feature slices and public entry points when Architecture is active, leaving graph discovery to its mechanical pre-pass
133
+ 16. collect machine-readable dependency rules and prose from ADR directories, `ARCHITECTURE.md`, README, and `CONTRIBUTING.md`
127
134
  17. run `npx tsc --noEmit 2>&1 | head -200` over the full project, and report only the errors in the scoped files
128
135
  18. run the linter: `npx eslint [files] --format json` or `npx biome check [files] --reporter json`
129
136
  19. query the TypeScript LSP over MCP when it is reachable, then merge and deduplicate against the compiler output
130
137
  20. triage every compiler and linter diagnostic through `severity_mapping`
131
138
  21. read the reference file named in `domains` before each analysis pass
132
- 22. run only the passes whose domain is in the active domain set, as sub-agents shaped by `subagent_template` or one domain at a time
133
- 23. give every agent all the scoped files when scoped files <= 20, and split by directory above that, with the shared types visible to every agent
134
- 24. re-read the exact lines in the current file state before a finding enters the report
135
- 25. read the callers to verify a data flow a finding rests on, or mark its problem statement with "if <condition>" and cap its severity at Medium
136
- 26. downgrade a flagged non-High pattern that appears 5+ times across the codebase by 1 level, and report it once as a Recurring Pattern
137
- 27. boost a finding carrying `in_diff: true` by 1 level in a scoped mode, and mark it `High [boosted, was Medium — new code]`
138
- 28. deduplicate the findings on the same file, line, and issue, keeping 1
139
- 29. merge the findings 2 domains raise on the same file and line into 1 entry attributing both categories, at the higher severity
140
- 30. consolidate 3+ identical issues into 1 Recurring Pattern entry
141
- 31. keep the top 15 by severity and impact when a single domain produces more than 25 Medium or Low findings, and consolidate the rest into Recurring Pattern entries with their counts
142
- 32. write `code-smells.md` in the shape of `report_format`
143
- 33. sort by severity group, then category, then file path, and place `in_diff: true` before pre-existing in a scoped mode
144
- 34. show the top 10 and summarize the rest in a table when Medium and Low together hold more than 15 issues
145
- 35. recommend that the operator adds `code-smells.md` to `.gitignore`: it is a review artifact
146
- 36. read `references/fix-workflow.md` before fix mode executes: it holds the complete protocol
147
- 37. detect the test runner and run the baseline tests
148
- 38. fix the issues file by file, and run `tsc --noEmit` after each file
149
- 39. run the linter and fix the lint errors it reports
150
- 40. run the full test suite, compare it against the baseline, and fix the regressions
151
- 41. repeat the compiler, linter, and test verification with verification iterations <= 5
152
- 42. update `code-smells.md`: remove what is fixed, mark what failed
153
- 43. show the scan summary in auto mode, and ask the operator whether to proceed with the fix
154
- 44. re-scan after the fix in auto mode with full scan-fix cycles <= 2, and stop when issues persist after the second
155
- 45. delete `code-smells.md` and report success when every issue is fixed
139
+ 22. run the mechanical pre-pass in `references/architecture.md` when Architecture is active, passing the approved tool decision and scoped base
140
+ 23. report the discovery summary in the shape of `discovery_summary`, including skipped and clean mechanical results
141
+ 24. run only the passes whose domain is in the active domain set, as sub-agents shaped by `subagent_template` or one domain at a time
142
+ 25. give every agent all the scoped files when scoped files <= 20, and split by directory above that, with the shared types visible to every agent
143
+ 26. re-read the exact lines in the current file state before a finding enters the report
144
+ 27. read the callers to verify a data flow a finding rests on, or mark its problem statement with "if <condition>" and cap its severity at Medium
145
+ 28. downgrade a flagged non-High pattern that appears 5+ times across the codebase by 1 level, and report it once as a Recurring Pattern
146
+ 29. boost a finding carrying `in_diff: true` by 1 level in a scoped mode, and mark it `High [boosted, was Medium — new code]`
147
+ 30. deduplicate the findings on the same file, line, and issue, keeping 1
148
+ 31. merge the findings 2 domains raise on the same file and line into 1 entry attributing both categories, at the higher severity
149
+ 32. consolidate 3+ identical issues into 1 Recurring Pattern entry
150
+ 33. keep the top 15 by severity and impact when a single domain produces more than 25 Medium or Low findings, and consolidate the rest into Recurring Pattern entries with their counts
151
+ 34. write `code-smells/report.md` in the shape of `report_format`
152
+ 35. validate the report against `report_format` after writing it, and treat a `warning:` line as a pre-pass outcome the report cannot correct
153
+ ```bash
154
+ # SKILL is the directory this file was loaded from.
155
+ SKILL=<the directory this file was loaded from>
156
+ node "$SKILL/tools/validate-report.mjs" --repo . --report code-smells/report.md
157
+ ```
158
+ 36. correct every named error and retry with report validation iterations <= 2
159
+ 37. keep `code-smells/report.md` when the second validation fails, write `> unvalidated: <the first error>` under its title, and name the errors to the operator
160
+ 38. sort by severity group, then category, then file path, and place `in_diff: true` before pre-existing in a scoped mode
161
+ 39. show the top 10 and summarize the rest in a table when Medium and Low together hold more than 15 issues
162
+ 40. recommend that the operator adds `code-smells/` to `.gitignore`: it holds review artifacts
163
+ 41. read `references/fix-workflow.md` before fix mode executes: it holds the complete protocol
164
+ 42. detect the test runner and run the baseline tests
165
+ 43. fix the issues file by file, and run `tsc --noEmit` after each file
166
+ 44. run the linter and fix the lint errors it reports
167
+ 45. run the full test suite, compare it against the baseline, and fix the regressions
168
+ 46. repeat the compiler, linter, and test verification with verification iterations <= 5
169
+ 47. rerun the Architecture mechanical pre-pass on the fixed tree when Architecture is active
170
+ 48. update `code-smells/report.md`: remove what is fixed, mark what failed
171
+ 49. show the scan summary in auto mode, and ask the operator whether to proceed with the fix
172
+ 50. re-scan after the fix in auto mode with full scan-fix cycles <= 2, and stop when issues persist after the second
173
+ 51. delete `code-smells/report.md` and report success when every issue is fixed
174
+ 52. retain the remaining `code-smells/` artifacts, state what they contain, and remove them only after the operator confirms
156
175
 
157
176
  scope_commands:
158
177
  ```bash
@@ -161,6 +180,7 @@ npx glob '**/*.{ts,mts,cts}' --ignore '**/node_modules/**'
161
180
  # or: git ls-files '*.ts' '*.mts' '*.cts'
162
181
 
163
182
  # uncommitted — staged, unstaged, and untracked
183
+ BASE=HEAD
164
184
  git diff --name-only HEAD -- '*.ts' '*.mts' '*.cts'
165
185
  git ls-files --others --exclude-standard -- '*.ts' '*.mts' '*.cts'
166
186
 
@@ -170,6 +190,7 @@ git diff --name-only "$BASE"...HEAD -- '*.ts' '*.mts' '*.cts'
170
190
  git diff --name-only HEAD -- '*.ts' '*.mts' '*.cts'
171
191
 
172
192
  # commits:N — the last N commits
193
+ BASE=HEAD~N
173
194
  git diff --name-only HEAD~N..HEAD -- '*.ts' '*.mts' '*.cts'
174
195
 
175
196
  # the changed hunks, for the severity boost in a scoped mode
@@ -211,6 +232,16 @@ Strict mode: yes / partial / no
211
232
  Linter: eslint / biome / none
212
233
  Test runner: vitest / jest / mocha / node:test / none
213
234
  Files in scope: <N> .ts files (+ <M> context files)
235
+ Architecture projects: <config and source roots, when active>
236
+ Architecture tools: <local or approved npx versions, when active>
237
+ Architecture coverage: <successful>/<selected>, when active
238
+ Mechanical results: <module and edge counts, cycles, orphans, and co-change pairs; name clean zeros>
239
+ Skipped pre-passes: <tool and reason, or none>
240
+ Speculative candidates: <names only, or none>
241
+ Declared dependency rules:
242
+ | Rule | Source file:line | Directories |
243
+ |---|---|---|
244
+ | <rule, or none> | <path:line> | <mapping> |
214
245
  ```
215
246
 
216
247
  subagent_template:
@@ -239,6 +270,11 @@ Output JSONL, one object per line:
239
270
  ```
240
271
 
241
272
  report_format:
273
+ - the `##` sections of the block below are the whole set, in that order, and a heading outside it is a renamed section
274
+ - Discovery, Pre-existing Issues, Architecture Opportunities, Verification, and Generated artifacts are optional, and the other 5 are always present
275
+ - `Total issues` counts the `###` findings, the summary-table rows, and the Architecture Opportunities entries, and the severity breakdown counts the same 3
276
+ - a `Recurring Patterns` row is a pattern rather than an issue, and no row of that table is counted
277
+ - a summary table is read by its `Category` and `Location` columns, and a pattern table by its `Pattern` and `Occurrences` columns
242
278
  ````markdown
243
279
  # TypeScript Code Review Report
244
280
 
@@ -247,6 +283,7 @@ report_format:
247
283
  **Stack:** TypeScript 5.9.x / ES2024 / Node 24 — matches / <deviation>
248
284
  **Scope:** Full / Uncommitted / Branch `x` vs `y` / Last N commits
249
285
  **Files analyzed:** N (+ M context)
286
+ **Architecture coverage:** N/M (when active)
250
287
  **Total issues:** N (X highest, Y high, Z medium, W low)
251
288
  **Severity-boosted:** N (scoped modes only)
252
289
 
@@ -254,6 +291,10 @@ report_format:
254
291
 
255
292
  <2-3 sentences on codebase health and key patterns>
256
293
 
294
+ ## Discovery
295
+
296
+ <optional; the `discovery_summary` block as it was reported>
297
+
257
298
  ## Highest + High Issues
258
299
 
259
300
  ### TITLE — Severity [boosted info if applicable]
@@ -261,7 +302,7 @@ report_format:
261
302
  **Category:** cat | **File:** `path` | **Line:** N | **Auto-fixable:** Yes/No | **New code:** Yes/No
262
303
 
263
304
  ```typescript
264
- // snippet
305
+ // snippet: 3-7 lines copied from the file, within its own length of the stated line
265
306
  ```
266
307
 
267
308
  **Problem:** explanation
@@ -272,13 +313,36 @@ report_format:
272
313
 
273
314
  ## Medium Issues
274
315
  ## Low Issues
316
+
317
+ | Issue | Category | Location | Fix |
318
+ |---|---|---|---|
319
+ | title | cat | `path:N` | recommendation |
320
+
275
321
  ## Recurring Patterns
322
+
323
+ | Pattern | Occurrences | Severity treatment |
324
+ |---|---|---|
325
+ | name | N | what happened to its members |
326
+
276
327
  ## Config Issues
328
+
329
+ <findings in the shape above, or prose naming where the config findings already sit>
330
+
277
331
  ## Pre-existing Issues (scoped modes only)
278
332
 
279
333
  ## Architecture Opportunities
280
334
 
281
- <1 entry per candidate, in the shape the `report_format` block of references/architecture.md gives>
335
+ <optional; 1 entry per candidate, in the shape the `report_format` block of references/architecture.md gives>
336
+
337
+ ## Verification
338
+
339
+ | Check | Result |
340
+ |---|---|
341
+ | the command that ran | what it reported |
342
+
343
+ ## Generated artifacts
344
+
345
+ - `<file under code-smells/>` — what it holds
282
346
 
283
347
  ---
284
348
  ````
@@ -23,8 +23,8 @@ confidence_scale:
23
23
 
24
24
  | Confidence | Criteria |
25
25
  |---|---|
26
- | strong | the pain repeats, the owning module is named, and the fix path is safe |
27
- | worth-exploring | the problem is confirmed, and the interface or the migration needs its own design |
26
+ | strong | the pain repeats and the owner is named, or a quoted dependency rule maps 1:1 onto directories |
27
+ | worth-exploring | the problem is confirmed but the interface, migration, inferred rule, or layer mapping needs its own design |
28
28
  | speculative | a signal with no call, history, or test evidence behind it |
29
29
 
30
30
  read_first:
@@ -35,23 +35,57 @@ read_first:
35
35
  - dependency direction matters more than layer count: the domain owns the logic, and transport and storage are injected or isolated at the edge
36
36
  - 1 adapter is a hypothetical seam and 2 adapters are a real seam: do not introduce a port until at least 2 adapters are justified, production and test at the minimum
37
37
  - an architecture finding rests on evidence: named files and symbols, an import or call direction, a repeated change path in `git log`, a duplicated rule, or a test no correct seam can reach
38
- - state the observation apart from the inference drawn from it, and mark a candidate with no evidence behind it as speculative rather than reporting it as a defect
38
+ - state the observation apart from the inference, and list an evidence-free candidate as speculative in `discovery_summary` rather than reporting it as a defect
39
39
  - a defect no test can reach through a correct seam is an architecture finding, and not a reason to write a test past the interface
40
40
  - before proposing a module, a port, or a container, check in order: drop the need, an existing module in the repository, the standard library, a platform feature, an installed dependency
41
41
  - design a hard-to-reverse proposal twice: compare 2 different interfaces on caller knowledge, seam placement, and migration cost, then recommend 1
42
42
  - a cycle is unclear ownership and not a bad graph: move the type or the rule to its owner, and `import type` or a lazy import hides the direction rather than fixing it
43
43
  - a path alias shortens an import path and is not an architecture boundary: ownership and a controlled export list are
44
+ - under `--arch`, Knip narrows the observed interface and confirms orphans, while dead-code findings remain owned by Dependency Hygiene
45
+ - apply an exception stated with a dependency rule before reporting it, and cap a path the declaration marks legacy or deprecated at Low
44
46
  - pick the simplest form a deepened module can take: a pure function for a transformation, a reducer for events over 1 state, a state machine when a command is valid only in some states, a class only when it owns long-lived state
45
47
 
46
48
  workflow:
47
- 1. trace 1 typical scenario end to end before naming a finding: entry point, orchestration, domain rule, storage or external call, response
48
- 2. grep for feature-module imports inside the `shared`, `common`, `core`, and `utils` directories, which is the inverted direction
49
- 3. list the importers of each module, and treat a module whose every export has exactly 1 importer as a pass-through candidate for the deletion test
50
- 4. treat an interface or type re-exported unchanged through 3+ files as a pass-through chain
51
- 5. check whether the exports of a utility file imported by most modules share 1 domain concept
52
- 6. read `git log --name-only` and use it twice: a feature whose commits consistently touch 4+ directories is scattered logic, and the directories changing most often are where to start
53
- 7. name the module owning the rule and the module owning the state for each candidate, and mark the candidate speculative when neither is identifiable
54
- 8. run these checks rather than guessing from file names
49
+ 1. locate machine-readable dependency rules first, then prose in ADRs, `ARCHITECTURE.md`, README, and `CONTRIBUTING.md`, in that order
50
+ 2. resolve conflicting ADRs only through an explicit supersede, and ask when the conflict would produce a High finding
51
+ 3. list each extracted rule, its source line, and its directory mapping in `discovery_summary` before using it
52
+ 4. write prose-derived rules to `code-smells/suggested.dependency-cruiser.cjs`, and do not add them to the project during scan
53
+ 5. when no declaration exists, report observed structure and offer a declaration rather than inventing layers
54
+ 6. run the mechanical pre-pass with the commands below against 1 unchanged tree before the semantic pass
55
+ 7. classify Knip and dependency-cruiser by parseable output rather than exit code, since both tools use non-zero and zero exits for reportable candidates
56
+ 8. record a project with source files and `totalCruised == 0` as a skipped pre-pass, and do not report its mechanical zeros
57
+ 9. state Architecture coverage as successful projects over selected projects, and list each failed config with its bounded diagnostic
58
+ 10. give the semantic pass `code-smells/metrics.md` rather than a raw graph
59
+ 11. cross each co-change pair with the graph: an edge is weak evidence, while no edge across directories is an implicit-contract candidate
60
+ 12. read ESLint boundary rules from linter output without converting them, since the linter already executed those rules
61
+ 13. consolidate all violating edges for 1 declared rule into 1 candidate, after removing declared exceptions
62
+ 14. trace `min(declared entry points, 3)` scenarios, ranked by the number of modules each entry point reaches
63
+ 15. grep for feature-module imports inside the `shared`, `common`, `core`, and `utils` directories, which is the inverted direction
64
+ 16. apply the deletion test only to a module whose whole export set has 1 importer and that wraps or re-exports another module
65
+ 17. treat an interface or type re-exported unchanged through 3+ files as a pass-through chain
66
+ 18. inspect the top 20 modules and folders by fan-in for mixed concepts, and use folder instability only to rank layer checks
67
+ 19. name the module owning the rule and the module owning the state for each candidate, and mark the candidate speculative when neither is identifiable
68
+ 20. keep each collapsed project overview, then create 2..3 focused Mermaid diagrams tied to the top findings after triage
69
+ 21. generate SVG only after `dot -V` succeeds, and keep Mermaid when Graphviz is unavailable
70
+ 22. run these checks rather than reporting a cycle, hub, orphan, co-change pair, or Knip entry directly
71
+ ```bash
72
+ # SKILL is the directory this file was loaded from; the main workflow approved missing tools before this pass.
73
+ SKILL=<the directory this file was loaded from>
74
+ KNIP=$([ -x node_modules/.bin/knip ] && echo node_modules/.bin/knip || echo "npx -y knip@6.31.0")
75
+
76
+ mkdir -p code-smells
77
+
78
+ node "$SKILL/tools/discover-projects.mjs" > code-smells/projects.json
79
+ $KNIP --reporter json > code-smells/knip.json
80
+ node "$SKILL/tools/co-change.mjs" --projects code-smells/projects.json --out code-smells/co-change.md
81
+ node "$SKILL/tools/run-cruise.mjs" --projects code-smells/projects.json --out code-smells
82
+
83
+ # In a scoped mode, BASE is the comparison ref computed by scope_commands.
84
+ node "$SKILL/tools/run-cruise.mjs" --projects code-smells/projects.json --out code-smells --base "$BASE"
85
+
86
+ # After triage, create one diagram for a top finding.
87
+ node "$SKILL/tools/run-cruise.mjs" --projects code-smells/projects.json --out code-smells --focus '<module regex>' --name <finding>
88
+ ```
55
89
 
56
90
  checks:
57
91
  - shallow modules — understanding 1 concept requires bouncing across many tiny modules: Medium
@@ -76,6 +110,7 @@ checks:
76
110
  - under-engineering — event or string-keyed indirection between 2 modules that only ever talk to each other: Low, a direct typed call is checkable
77
111
  - dependency direction — a domain or computation module importing IO directly, `node:fs`, `node:http`, a DB client, or `fetch`, where the classification puts that IO behind a seam: Medium
78
112
  - dependency direction — a shared or leaf module, `utils/`, `types/`, or `core/`, importing from a feature module: High, the inverted direction is how import cycles start
113
+ - dependency direction — an import edge contradicting a layer rule the project declares in an ADR or ARCHITECTURE.md: High, quote the rule with its source line and list the violating edges
79
114
  - dependency direction — a wire or DTO type from an external API imported deep into a domain module instead of mapped at the seam that owns the external contract: Medium
80
115
  - state ownership — 2+ modules mutating 1 entity, cache, or record with no single owning module: High, the write paths cannot be tested or reasoned about apart
81
116
  - state ownership — fix: a shared mutable entity, by naming 1 owning module and turning each write into a named command on it
@@ -88,6 +123,7 @@ non_findings:
88
123
  - a module with exactly 1 importer that hides an ordering, an invariant, or a decision: 1 importer starts the deletion test and is not a finding by itself
89
124
  - a layer count: the direction of the dependencies is the finding, and a layer holding a real rule is not
90
125
  - a port with 1 production adapter whose test double checks the same contract: the double is the second adapter
126
+ - duplicated trees whose intent is documented, excluded by tool policy, or maintained in lockstep: report at most the synchronized-edit cost at Low, not the decision
91
127
  - formatting, import ordering, and file naming: `references/code-quality.md` owns them, and they are not architecture findings
92
128
 
93
129
  forbidden_behaviors:
@@ -102,7 +138,9 @@ forbidden_behaviors:
102
138
  - do not leave a new structure standing beside the old one: every proposal names the step deleting what it replaces
103
139
  - do not propose a change contradicting a decision under `docs/adr/` silently: name the ADR and the conflict, and leave the decision to the operator
104
140
  - do not report architecture candidates without naming 1 top recommendation and the reason it goes first
105
- - do not add an import-linting dependency for 1 rule: use `package.json#exports`, the linter the project already runs, or the workspace layout
141
+ - do not claim the repository has no cycles, orphans, or coupling issues below 100% Architecture coverage: name the successful projects
142
+ - do not add a dependency as the fix for 1 import rule: use `package.json#exports`, the project's linter, or the workspace layout
143
+ - do not treat an approved `npx` graph tool used during review as a project dependency: it changes neither `package.json` nor the lockfile
106
144
 
107
145
  dependency_classification:
108
146
 
@@ -115,6 +153,9 @@ dependency_classification:
115
153
 
116
154
  severity_mapping:
117
155
 
156
+ - map a project-declared severity to the finding: `error` to High, `warn` to Medium, and `info` to Low
157
+ - copy the declared rule comment into Evidence, since it states the project's rationale and accepted debt
158
+
118
159
  | Severity | Architecture criteria |
119
160
  |---|---|
120
161
  | Highest | an architecture issue directly causing a security vulnerability, data loss, or a production correctness bug |
@@ -124,6 +165,10 @@ severity_mapping:
124
165
 
125
166
  fixability:
126
167
 
168
+ - an `enforce` change is `report-only`, since scan does not write a project config
169
+ - a `delete` change is `auto` only when no module imports the target
170
+ - a deepening, merge, or ownership move is `needs-confirm`
171
+
127
172
  | Fixability | Meaning | Fix mode behaviour |
128
173
  |---|---|---|
129
174
  | `auto` | a local change: import path cleanup, a narrow circular-import break, a local module merge with co-located tests | applied in the normal fix loop |
@@ -134,17 +179,19 @@ report_format:
134
179
  ```md
135
180
  ### TITLE — Severity
136
181
 
137
- - **Confidence:** strong | worth-exploring | speculative
182
+ - **Confidence:** strong | worth-exploring
138
183
  - **Files:** relative/path/a.ts, relative/path/b.ts
139
184
  - **Problem:** why this causes friction now (not just a pattern name)
140
185
  - **Evidence:** the imports, callers, `git log` path, or test the finding rests on
141
- - **Proposed deepening:** plain-English description of what would change
142
- - **Interface shape:** rough sketch of the new interface (types, methods, key invariants)
143
- - **Dependency category:** in-process | local-substitutable | remote-owned | true-external
186
+ - **Change type:** deepening | ownership move | merge | delete | enforce
187
+ - **Proposed change:** plain-English description of what would change
188
+ - **Interface shape:** for deepening or merge only; rough sketch of the new interface and key invariants
189
+ - **Dependency category:** for deepening only; in-process | local-substitutable | remote-owned | true-external
190
+ - **Rule:** for enforce only; the exact rule text and the file where it would live
144
191
  - **Test strategy:** how tests would improve (what tests survive, what gets deleted, what's new)
145
- - **Migration:** prefactor | vertical slices | expand-contract, and the step deleting the replaced code
192
+ - **Migration:** for deepening, merge, ownership move, or delete; prefactor | vertical slices | expand-contract, and the deletion step
146
193
  - **Benefits:** locality gained, leverage gained, test impact
147
194
  - **Trade-offs:** what gets harder, what is genuinely uncertain
148
- - **Fixability:** auto | needs-confirm | report-only
195
+ - **Fixability:** auto | needs-confirm | report-only, and report-only for enforce, since a new rule fails the build before the edges it names are gone
149
196
  - **Top recommendation:** on exactly 1 entry in the report, the reason this candidate goes first
150
197
  ```