@lucasacoutinho/archer 0.2.1 → 0.3.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 (214) hide show
  1. package/README.md +144 -387
  2. package/dist/analyzer/Analyzer.d.ts +17 -0
  3. package/dist/analyzer/Analyzer.d.ts.map +1 -0
  4. package/dist/analyzer/Analyzer.js +15 -0
  5. package/dist/analyzer/Analyzer.js.map +1 -0
  6. package/dist/analyzer/bindings.d.ts +26 -0
  7. package/dist/analyzer/bindings.d.ts.map +1 -0
  8. package/dist/analyzer/bindings.js +61 -0
  9. package/dist/analyzer/bindings.js.map +1 -0
  10. package/dist/analyzer/errors.d.ts +34 -0
  11. package/dist/analyzer/errors.d.ts.map +1 -0
  12. package/dist/analyzer/errors.js +20 -0
  13. package/dist/analyzer/errors.js.map +1 -0
  14. package/dist/analyzer/graph-file.d.ts +13 -0
  15. package/dist/analyzer/graph-file.d.ts.map +1 -0
  16. package/dist/analyzer/graph-file.js +22 -0
  17. package/dist/analyzer/graph-file.js.map +1 -0
  18. package/dist/analyzer/paths.d.ts +12 -0
  19. package/dist/analyzer/paths.d.ts.map +1 -0
  20. package/dist/analyzer/paths.js +26 -0
  21. package/dist/analyzer/paths.js.map +1 -0
  22. package/dist/analyzer/server-exit.d.ts +19 -0
  23. package/dist/analyzer/server-exit.d.ts.map +1 -0
  24. package/dist/analyzer/server-exit.js +70 -0
  25. package/dist/analyzer/server-exit.js.map +1 -0
  26. package/dist/analyzer/source.d.ts +29 -0
  27. package/dist/analyzer/source.d.ts.map +1 -0
  28. package/dist/analyzer/source.js +58 -0
  29. package/dist/analyzer/source.js.map +1 -0
  30. package/dist/analyzer/syntax.d.ts +29 -0
  31. package/dist/analyzer/syntax.d.ts.map +1 -0
  32. package/dist/analyzer/syntax.js +226 -0
  33. package/dist/analyzer/syntax.js.map +1 -0
  34. package/dist/analyzer/targets.d.ts +26 -0
  35. package/dist/analyzer/targets.d.ts.map +1 -0
  36. package/dist/analyzer/targets.js +84 -0
  37. package/dist/analyzer/targets.js.map +1 -0
  38. package/dist/analyzer/typescript.d.ts +5 -0
  39. package/dist/analyzer/typescript.d.ts.map +1 -0
  40. package/dist/analyzer/typescript.js +221 -0
  41. package/dist/analyzer/typescript.js.map +1 -0
  42. package/dist/analyzer/workspace.d.ts +24 -0
  43. package/dist/analyzer/workspace.d.ts.map +1 -0
  44. package/dist/analyzer/workspace.js +9 -0
  45. package/dist/analyzer/workspace.js.map +1 -0
  46. package/dist/cli/explain.d.ts +3 -0
  47. package/dist/cli/explain.d.ts.map +1 -0
  48. package/dist/cli/explain.js +21 -0
  49. package/dist/cli/explain.js.map +1 -0
  50. package/dist/cli/main.d.ts +3 -0
  51. package/dist/cli/main.d.ts.map +1 -0
  52. package/dist/cli/main.js +159 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/config/input.d.ts +59 -0
  55. package/dist/config/input.d.ts.map +1 -0
  56. package/dist/config/input.js +115 -0
  57. package/dist/config/input.js.map +1 -0
  58. package/dist/config/rules.d.ts +94 -0
  59. package/dist/config/rules.d.ts.map +1 -0
  60. package/dist/config/rules.js +130 -0
  61. package/dist/config/rules.js.map +1 -0
  62. package/dist/core/baseline.d.ts +16 -0
  63. package/dist/core/baseline.d.ts.map +1 -0
  64. package/dist/core/baseline.js +25 -0
  65. package/dist/core/baseline.js.map +1 -0
  66. package/dist/core/check.d.ts +16 -0
  67. package/dist/core/check.d.ts.map +1 -0
  68. package/dist/core/check.js +271 -0
  69. package/dist/core/check.js.map +1 -0
  70. package/dist/core/config.d.ts +72 -0
  71. package/dist/core/config.d.ts.map +1 -0
  72. package/dist/core/config.js +78 -0
  73. package/dist/core/config.js.map +1 -0
  74. package/dist/core/fingerprint.d.ts +54 -0
  75. package/dist/core/fingerprint.d.ts.map +1 -0
  76. package/dist/core/fingerprint.js +182 -0
  77. package/dist/core/fingerprint.js.map +1 -0
  78. package/dist/core/keys.d.ts +10 -0
  79. package/dist/core/keys.d.ts.map +1 -0
  80. package/dist/core/keys.js +16 -0
  81. package/dist/core/keys.js.map +1 -0
  82. package/dist/core/lower.d.ts +13 -0
  83. package/dist/core/lower.d.ts.map +1 -0
  84. package/dist/core/lower.js +82 -0
  85. package/dist/core/lower.js.map +1 -0
  86. package/dist/core/model.d.ts +155 -0
  87. package/dist/core/model.d.ts.map +1 -0
  88. package/dist/core/model.js +116 -0
  89. package/dist/core/model.js.map +1 -0
  90. package/dist/core/pattern.d.ts +13 -0
  91. package/dist/core/pattern.d.ts.map +1 -0
  92. package/dist/core/pattern.js +29 -0
  93. package/dist/core/pattern.js.map +1 -0
  94. package/dist/core/reach.d.ts +10 -0
  95. package/dist/core/reach.d.ts.map +1 -0
  96. package/dist/core/reach.js +81 -0
  97. package/dist/core/reach.js.map +1 -0
  98. package/dist/core/rule.d.ts +200 -0
  99. package/dist/core/rule.d.ts.map +1 -0
  100. package/dist/core/rule.js +162 -0
  101. package/dist/core/rule.js.map +1 -0
  102. package/dist/core/selector.d.ts +7 -0
  103. package/dist/core/selector.d.ts.map +1 -0
  104. package/dist/core/selector.js +27 -0
  105. package/dist/core/selector.js.map +1 -0
  106. package/dist/core/structural.d.ts +10 -0
  107. package/dist/core/structural.d.ts.map +1 -0
  108. package/dist/core/structural.js +24 -0
  109. package/dist/core/structural.js.map +1 -0
  110. package/dist/core/text.d.ts +26 -0
  111. package/dist/core/text.d.ts.map +1 -0
  112. package/dist/core/text.js +144 -0
  113. package/dist/core/text.js.map +1 -0
  114. package/dist/effect.d.ts +19 -0
  115. package/dist/effect.d.ts.map +1 -0
  116. package/dist/effect.js +21 -0
  117. package/dist/effect.js.map +1 -0
  118. package/dist/index.d.ts +24 -44
  119. package/dist/index.d.ts.map +1 -0
  120. package/dist/index.js +59 -52
  121. package/dist/index.js.map +1 -0
  122. package/dist/report/agent.d.ts +3 -0
  123. package/dist/report/agent.d.ts.map +1 -0
  124. package/dist/report/agent.js +35 -0
  125. package/dist/report/agent.js.map +1 -0
  126. package/dist/report/dot.d.ts +4 -0
  127. package/dist/report/dot.d.ts.map +1 -0
  128. package/dist/report/dot.js +15 -0
  129. package/dist/report/dot.js.map +1 -0
  130. package/dist/report/github.d.ts +3 -0
  131. package/dist/report/github.d.ts.map +1 -0
  132. package/dist/report/github.js +18 -0
  133. package/dist/report/github.js.map +1 -0
  134. package/dist/report/json.d.ts +3 -0
  135. package/dist/report/json.d.ts.map +1 -0
  136. package/dist/report/json.js +4 -0
  137. package/dist/report/json.js.map +1 -0
  138. package/dist/report/message.d.ts +20 -0
  139. package/dist/report/message.d.ts.map +1 -0
  140. package/dist/report/message.js +73 -0
  141. package/dist/report/message.js.map +1 -0
  142. package/dist/report/pretty.d.ts +8 -0
  143. package/dist/report/pretty.d.ts.map +1 -0
  144. package/dist/report/pretty.js +89 -0
  145. package/dist/report/pretty.js.map +1 -0
  146. package/dist/report/render.d.ts +9 -0
  147. package/dist/report/render.d.ts.map +1 -0
  148. package/dist/report/render.js +10 -0
  149. package/dist/report/render.js.map +1 -0
  150. package/dist/report/report.d.ts +44 -0
  151. package/dist/report/report.d.ts.map +1 -0
  152. package/dist/report/report.js +49 -0
  153. package/dist/report/report.js.map +1 -0
  154. package/dist/report/sarif.d.ts +3 -0
  155. package/dist/report/sarif.d.ts.map +1 -0
  156. package/dist/report/sarif.js +68 -0
  157. package/dist/report/sarif.js.map +1 -0
  158. package/dist/run.d.ts +49 -0
  159. package/dist/run.d.ts.map +1 -0
  160. package/dist/run.js +87 -0
  161. package/dist/run.js.map +1 -0
  162. package/dist/shell/baseline-file.d.ts +20 -0
  163. package/dist/shell/baseline-file.d.ts.map +1 -0
  164. package/dist/shell/baseline-file.js +46 -0
  165. package/dist/shell/baseline-file.js.map +1 -0
  166. package/dist/shell/load-config.d.ts +19 -0
  167. package/dist/shell/load-config.d.ts.map +1 -0
  168. package/dist/shell/load-config.js +39 -0
  169. package/dist/shell/load-config.js.map +1 -0
  170. package/package.json +29 -177
  171. package/skills/archer/SKILL.md +127 -45
  172. package/skills/archer/agents/openai.yaml +2 -2
  173. package/skills/archer/references/rules.md +267 -54
  174. package/dist/bun/plugin.cjs +0 -85
  175. package/dist/bun/plugin.d.cts +0 -7
  176. package/dist/bun/plugin.d.ts +0 -7
  177. package/dist/bun/plugin.js +0 -11
  178. package/dist/bun.cjs +0 -2238
  179. package/dist/bun.d.cts +0 -13
  180. package/dist/bun.d.ts +0 -13
  181. package/dist/bun.js +0 -28
  182. package/dist/chunk-2AKMBLHG.js +0 -2165
  183. package/dist/chunk-67YEOTXP.js +0 -25
  184. package/dist/chunk-IDVLHNL2.js +0 -46
  185. package/dist/chunk-ZZ6TR4PO.js +0 -201
  186. package/dist/cli.js +0 -2643
  187. package/dist/index.cjs +0 -2484
  188. package/dist/index.d.cts +0 -45
  189. package/dist/jest/plugin.cjs +0 -85
  190. package/dist/jest/plugin.d.cts +0 -7
  191. package/dist/jest/plugin.d.ts +0 -7
  192. package/dist/jest/plugin.js +0 -11
  193. package/dist/jest.cjs +0 -2237
  194. package/dist/jest.d.cts +0 -13
  195. package/dist/jest.d.ts +0 -13
  196. package/dist/jest.js +0 -27
  197. package/dist/matchers-Cqnbsyxe.d.cts +0 -11
  198. package/dist/matchers-Cqnbsyxe.d.ts +0 -11
  199. package/dist/node.cjs +0 -2237
  200. package/dist/node.d.cts +0 -13
  201. package/dist/node.d.ts +0 -13
  202. package/dist/node.js +0 -27
  203. package/dist/reporters-BbAXMpuW.d.cts +0 -6
  204. package/dist/reporters-xh-HQneS.d.ts +0 -6
  205. package/dist/suite-BwnPDCvU.d.cts +0 -337
  206. package/dist/suite-BwnPDCvU.d.ts +0 -337
  207. package/dist/vitest/plugin.cjs +0 -85
  208. package/dist/vitest/plugin.d.cts +0 -7
  209. package/dist/vitest/plugin.d.ts +0 -7
  210. package/dist/vitest/plugin.js +0 -11
  211. package/dist/vitest.cjs +0 -2531
  212. package/dist/vitest.d.cts +0 -37
  213. package/dist/vitest.d.ts +0 -37
  214. package/dist/vitest.js +0 -139
package/package.json CHANGED
@@ -1,19 +1,13 @@
1
1
  {
2
2
  "name": "@lucasacoutinho/archer",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Architecture tests for TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "architecture",
7
7
  "architecture-testing",
8
- "archunit",
9
8
  "boundaries",
10
- "bun",
11
9
  "dependency-rules",
12
- "javascript",
13
- "jest",
14
- "monorepo",
15
- "typescript",
16
- "vitest"
10
+ "typescript"
17
11
  ],
18
12
  "homepage": "https://github.com/lucasacoutinho/archer#readme",
19
13
  "bugs": {
@@ -25,194 +19,52 @@
25
19
  "type": "git",
26
20
  "url": "git+https://github.com/lucasacoutinho/archer.git"
27
21
  },
28
- "bin": {
29
- "archer": "dist/cli.js"
30
- },
22
+ "type": "module",
31
23
  "files": [
32
24
  "dist",
33
25
  "skills",
34
- "README.md",
35
26
  "LICENSE",
36
27
  "NOTICE"
37
28
  ],
38
- "type": "module",
39
- "sideEffects": [
40
- "./dist/bun/plugin.*",
41
- "./dist/jest/plugin.*",
42
- "./dist/vitest/plugin.*"
43
- ],
44
- "main": "./dist/index.cjs",
45
- "module": "./dist/index.js",
46
- "types": "./dist/index.d.ts",
47
- "typesVersions": {
48
- "*": {
49
- "vitest": [
50
- "dist/vitest.d.ts"
51
- ],
52
- "vitest/plugin": [
53
- "dist/vitest/plugin.d.ts"
54
- ],
55
- "jest": [
56
- "dist/jest.d.ts"
57
- ],
58
- "jest/plugin": [
59
- "dist/jest/plugin.d.ts"
60
- ],
61
- "bun": [
62
- "dist/bun.d.ts"
63
- ],
64
- "bun/plugin": [
65
- "dist/bun/plugin.d.ts"
66
- ],
67
- "node": [
68
- "dist/node.d.ts"
69
- ],
70
- "*": [
71
- "dist/index.d.ts"
72
- ]
73
- }
74
- },
75
29
  "exports": {
76
30
  ".": {
77
- "import": {
78
- "types": "./dist/index.d.ts",
79
- "default": "./dist/index.js"
80
- },
81
- "require": {
82
- "types": "./dist/index.d.cts",
83
- "default": "./dist/index.cjs"
84
- }
85
- },
86
- "./vitest": {
87
- "import": {
88
- "types": "./dist/vitest.d.ts",
89
- "default": "./dist/vitest.js"
90
- },
91
- "require": {
92
- "types": "./dist/vitest.d.cts",
93
- "default": "./dist/vitest.cjs"
94
- }
95
- },
96
- "./vitest/plugin": {
97
- "import": {
98
- "types": "./dist/vitest/plugin.d.ts",
99
- "default": "./dist/vitest/plugin.js"
100
- },
101
- "require": {
102
- "types": "./dist/vitest/plugin.d.cts",
103
- "default": "./dist/vitest/plugin.cjs"
104
- }
105
- },
106
- "./jest": {
107
- "import": {
108
- "types": "./dist/jest.d.ts",
109
- "default": "./dist/jest.js"
110
- },
111
- "require": {
112
- "types": "./dist/jest.d.cts",
113
- "default": "./dist/jest.cjs"
114
- }
115
- },
116
- "./jest/plugin": {
117
- "import": {
118
- "types": "./dist/jest/plugin.d.ts",
119
- "default": "./dist/jest/plugin.js"
120
- },
121
- "require": {
122
- "types": "./dist/jest/plugin.d.cts",
123
- "default": "./dist/jest/plugin.cjs"
124
- }
125
- },
126
- "./bun": {
127
- "import": {
128
- "types": "./dist/bun.d.ts",
129
- "default": "./dist/bun.js"
130
- },
131
- "require": {
132
- "types": "./dist/bun.d.cts",
133
- "default": "./dist/bun.cjs"
134
- }
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
135
33
  },
136
- "./bun/plugin": {
137
- "import": {
138
- "types": "./dist/bun/plugin.d.ts",
139
- "default": "./dist/bun/plugin.js"
140
- },
141
- "require": {
142
- "types": "./dist/bun/plugin.d.cts",
143
- "default": "./dist/bun/plugin.cjs"
144
- }
145
- },
146
- "./node": {
147
- "import": {
148
- "types": "./dist/node.d.ts",
149
- "default": "./dist/node.js"
150
- },
151
- "require": {
152
- "types": "./dist/node.d.cts",
153
- "default": "./dist/node.cjs"
154
- }
34
+ "./effect": {
35
+ "types": "./dist/effect.d.ts",
36
+ "default": "./dist/effect.js"
155
37
  }
156
38
  },
39
+ "bin": {
40
+ "archer": "dist/cli/main.js"
41
+ },
42
+ "sideEffects": false,
43
+ "engines": {
44
+ "node": ">=22.18.0"
45
+ },
157
46
  "publishConfig": {
158
47
  "access": "public"
159
48
  },
160
49
  "scripts": {
161
- "build": "tsup",
162
- "check": "npm run fmt:check && npm run lint && npm run typecheck && npm test && npm run test:adapters && npm run build",
163
- "check:package": "npm run build && publint && attw --pack .",
164
- "fmt": "oxfmt --write .",
165
- "fmt:check": "oxfmt --check .",
166
- "lint": "oxlint",
167
- "lint:fix": "oxlint --fix",
168
- "prepublishOnly": "npm run check",
169
- "release:check": "npm run check && npm run check:package && npm audit --omit=dev && npm pack --dry-run",
50
+ "prepare": "effect-tsgo patch --typescript --no-oxlint",
51
+ "build": "tsc -p tsconfig.build.json",
52
+ "typecheck": "tsc -p tsconfig.json && sh scripts/check-declarations.sh",
170
53
  "test": "vitest run",
171
- "test:adapters": "npm run build && npm run test:jest && npm run test:node && npm run test:bun && npm run test:cli && npm run test:dogfood",
172
- "test:bun": "bun test test/runners/bun-adapter.test.mjs",
173
- "test:cli": "node --test test/runners/cli.test.mjs",
174
- "test:dogfood": "node dist/cli.js check --config archer.config.ts",
175
- "test:jest": "jest --runInBand --runTestsByPath test/runners/jest-adapter.test.cjs",
176
- "test:node": "node --test test/runners/node-adapter.test.mjs",
177
- "test:tia": "vitest run --config vitest.tia.config.ts",
178
- "test:tia:verify": "node config/run-tiacore.mjs verify --output .tia/verification.json -- npm run test:tia",
179
- "typecheck": "tsc --noEmit"
54
+ "smoke:bun": "bun scripts/bun-smoke.mjs",
55
+ "lean": "sh scripts/lean-audit.sh",
56
+ "check": "npm run lean && npm run typecheck && npm test && npm run build && npm run dogfood",
57
+ "dogfood": "node dist/cli/main.js check"
180
58
  },
181
59
  "dependencies": {
182
- "chokidar": "^5.0.0",
183
- "commander": "^14.0.3",
184
- "jiti": "^2.7.0",
185
- "picomatch": "^4.0.3",
186
- "tinyglobby": "^0.2.15",
187
- "typescript": "^5.9.0"
60
+ "@effect/platform-node": "4.0.6",
61
+ "effect": "4.0.6",
62
+ "typescript": "7.0.2"
188
63
  },
189
64
  "devDependencies": {
190
- "@arethetypeswrong/cli": "^0.18.2",
191
- "@jest/globals": "^30.4.1",
192
- "@types/bun": "^1.3.14",
193
- "@types/node": "^24.0.0",
194
- "@types/picomatch": "^4.0.2",
195
- "jest": "^30.4.2",
196
- "oxfmt": "^0.61.0",
197
- "oxlint": "^1.76.0",
198
- "publint": "^0.3.12",
199
- "tsup": "^8.5.0",
200
- "typescript": "^5.9.0",
201
- "vitest": "^4.0.0"
202
- },
203
- "peerDependencies": {
204
- "@jest/globals": ">=29.0.0 <31.0.0",
205
- "vitest": ">=3.0.0 <5.0.0"
206
- },
207
- "peerDependenciesMeta": {
208
- "@jest/globals": {
209
- "optional": true
210
- },
211
- "vitest": {
212
- "optional": true
213
- }
214
- },
215
- "engines": {
216
- "node": ">=20.0.0"
65
+ "@effect/tsgo": "0.56.1",
66
+ "@effect/vitest": "4.0.6",
67
+ "@types/node": "22.20.5",
68
+ "vitest": "5.0.5"
217
69
  }
218
70
  }
@@ -1,81 +1,163 @@
1
1
  ---
2
2
  name: archer
3
- description: Add, configure, run, and repair Archer architecture tests for JavaScript and TypeScript projects. Use when an agent needs to enforce module boundaries, named layers, dependency direction, public entrypoints, cycles, declaration conventions, or import rules; integrate Archer with its CLI, Vitest, Jest, Bun, or node:test; or interpret and fix Archer violations.
3
+ description: Set up, run, and fix architecture rules in TypeScript and JavaScript repositories with Archer (@lucasacoutinho/archer). Covers archer.config.ts (layers, stack, allow, publicApis, r.* rules), `archer check -r agent`, repairing violations in source without weakening the policy, baselines for adopting Archer on existing debt, and the one-line architecture test. Use when the user wants to enforce layers, module boundaries, dependency direction, import cycles, or public entrypoints, when the repo has an archer.config.* file, or when `archer check` or an architecture test reports findings. Do not use for ESLint import rules or other dependency linters.
4
4
  ---
5
5
 
6
6
  # Archer
7
7
 
8
- Use Archer to make architectural constraints executable. Prefer rules that describe an existing design decision and produce a specific source change when they fail.
8
+ Archer turns architecture decisions into rules that fail the build. A rule is plain data in `archer.config.ts`. `archer check` analyzes the project with TypeScript and reports every violation with its import site.
9
9
 
10
- ## Inspect the project
10
+ The rules are the user's policy. When a rule fails, change the source.
11
11
 
12
- 1. Read `package.json`, workspace configuration, and relevant `tsconfig` files.
13
- 2. Find existing `archer.config.*` and architecture tests before adding another entrypoint.
14
- 3. Identify real dependency directions from imports. Do not infer layers from folder names alone when the code says otherwise.
15
- 4. Read the installed Archer README or declarations when its version may differ from this skill.
12
+ ## Ground rules
16
13
 
17
- ## Set up Archer
14
+ - Fix findings in source. Do not delete or loosen a rule, lower a severity, widen `allow`, add a `{ not: ... }` exclusion, set `localOnly`, narrow `kinds`, set `allowComputed`, or record a baseline to get a green check.
15
+ - Make any of those changes only when the user asks for it. If a finding looks wrong or the rule looks outdated, say so and propose the change.
16
+ - Never claim a boundary holds when Archer could not prove it. Say what it could not see (see "Computed imports").
18
17
 
19
- Install `@lucasacoutinho/archer` as a development dependency with the repository's package manager. Prefer the CLI for a runner-independent check:
18
+ ## Inspect first
19
+
20
+ 1. Look for `archer.config.{ts,mts,js,mjs}`, `.archer-baseline`, and an existing architecture test. Update what exists instead of adding a second entrypoint.
21
+ 2. Read `package.json` and the tsconfig files. Archer analyzes only the files that the tsconfigs in `projects` list (default `tsconfig.json`). A file outside them is invisible to every rule, so list each package's tsconfig in a monorepo.
22
+ 3. Read real imports before choosing layers. Folder names suggest layers, but the imports show which directions the code already follows. Ask the user when the intended direction is not clear.
23
+ 4. If the installed version may differ from this skill, read `node_modules/@lucasacoutinho/archer/dist/index.d.ts`.
24
+
25
+ ## Set up
26
+
27
+ Install `@lucasacoutinho/archer` as a dev dependency with the repo's package manager. It needs Node 22.18 or later. Run `npx archer init` (or the package manager's local-exec equivalent) to write a starter `archer.config.ts`, then edit it to match the repo:
20
28
 
21
29
  ```ts
22
- import { defineConfig } from '@lucasacoutinho/archer'
30
+ import { defineConfig, presets, r } from "@lucasacoutinho/archer"
23
31
 
24
32
  export default defineConfig({
25
- rules(archer) {
26
- archer
27
- .expect('src/domain/**')
28
- .not.toDependOn('src/infrastructure/**')
29
- .as('boundaries/domain-isolation')
30
- .because('Domain code must remain independent of adapters.')
31
-
32
- archer.expect('src/**').toBeFreeOfCycles().as('dependencies/no-cycles')
33
+ layers: {
34
+ web: "src/web",
35
+ application: "src/application",
36
+ domain: "src/domain",
37
+ infrastructure: "src/infrastructure",
33
38
  },
39
+ stack: ["web", "application", "domain"],
40
+ allow: { infrastructure: ["application", "domain"] },
41
+ publicApis: { domain: "src/domain/index.ts" },
42
+ rules: [
43
+ ...presets.recommended,
44
+ r.forbid("domain/pure", "layer:domain", ["npm:*", "node:*"], {
45
+ reason: "The domain stays free of frameworks and I/O.",
46
+ }),
47
+ ],
34
48
  })
35
49
  ```
36
50
 
37
- Run the locally installed binary. With npm, use `npx --no-install archer check`; use the package manager's equivalent local-exec command elsewhere. Add that command to CI after it passes locally.
51
+ - `layers` names a selector per layer. Rules refer to it as `layer:name`.
52
+ - `stack` lists layers from top to bottom. Each layer may import the layers listed after it.
53
+ - `allow` adds layers a layer may import beyond its position in the stack.
54
+ - `publicApis` names, per layer, the entry files that other layers must import through.
55
+ - `rules` takes `r.*` constructors. Every rule needs an id that is unique and stays stable, because findings and baselines key on it.
56
+ - `projects`, `root`, `baseline`, and `failOnWarnings` are described in [references/rules.md](references/rules.md), together with every `r.*` constructor and the selector grammar. Open it when writing or reading a rule.
38
57
 
39
- Use `arch()` when the check should be registered as a test:
58
+ Layer rules constrain only direct imports of local files that belong to another declared layer. They ignore packages, built-ins, files outside every layer, and computed imports. Use `r.forbid` or `r.onlyDependOn` with `npm:*` or `node:*` for those. A layer named in neither `stack` nor `allow` has no limits of its own, but constrained layers may not import it unless `allow` says so, so put shared code at the bottom of `stack`.
40
59
 
41
- ```ts
42
- import { arch } from '@lucasacoutinho/archer/vitest'
60
+ The starter config names `src/app`, which may not exist. A rule whose subject matches no file passes, and so does the generated `layer/<name>` rule of an empty layer. Both are listed in `emptySubjects` in the summary line. Read that list after every run, and fix a non-empty one unless the code is meant to appear later.
43
61
 
44
- arch('domain boundaries').expect('src/domain/**').not.toDependOn('src/infrastructure/**')
62
+ A dependency rule whose target names files by a glob but selects none is listed in `emptyObjects`. That is almost always a mistyped path. A target that names only packages is never listed, so for package targets run the new rule against a minimal case (see "Test a rule without the filesystem" in the reference).
63
+
64
+ If the first run shows many violations, check the layer order and selectors before you blame the code.
65
+
66
+ ## Run
67
+
68
+ ```sh
69
+ npx archer check -r agent
45
70
  ```
46
71
 
47
- Replace `vitest` with `jest`, `bun`, or `node` for another adapter.
72
+ The command exits 0 when the verdict is `pass` and 1 when it is `fail`. It exits 2 when it cannot check, including on an unknown flag or value. The output has one `finding` line per violation and a `summary` line last:
48
73
 
49
- Use the matcher plugin when a project prefers `expect` assertions. Add `@lucasacoutinho/archer/vitest/plugin` to Vitest `setupFiles`, `@lucasacoutinho/archer/jest/plugin` to Jest `setupFilesAfterEnv`, or `@lucasacoutinho/archer/bun/plugin` to Bun's test `preload`. Then await `expect(archer).toRespectArchitecture()` in a dedicated architecture test.
74
+ ```text
75
+ finding id="layer/domain|/src/domain/order.ts|dependency|static|../infrastructure/db.ts|file|/src/infrastructure/db.ts" severity=error rule="layer/domain" subject="src/domain/order.ts" at="src/domain/order.ts:1:25" message="imports src/infrastructure/db.ts ('../infrastructure/db.ts')"
76
+ summary verdict=fail errors=1 warnings=0 infos=0 suppressed=0 stale=0 files=2 dependencies=1 emptySubjects=[] emptyObjects=[]
77
+ ```
50
78
 
51
- ## Write rules
79
+ - `id` is the fingerprint: rule, file, and what was found, with no line numbers or wording. It survives moving lines but changes when the specifier or target changes, so a finding is fixed when its `id` no longer appears. A file that repeats one import produces several lines with the same `id`.
80
+ - `severity`: `error` fails the check, `warning` fails it only with `failOnWarnings: true`, `info` never does.
81
+ - `rule` is a rule id from the config, or a generated `layer/<name>` or `public-api/<layer>`.
82
+ - `subject` is the file that breaks the rule.
83
+ - `at` is `file:line:column` of the import, call, or declaration. Findings about a whole file (missing dependency, export, file name, line count) have no `at`.
84
+ - `message` says what Archer found. `reason` is the rule's stated reason, when it has one. Read it before you choose a fix.
85
+ - In the summary, `suppressed` counts findings the baseline hides, `stale` counts baseline entries that no finding matches, `emptySubjects` lists rule ids that select no file, and `emptyObjects` lists dependency rules whose file-glob target selects nothing.
52
86
 
53
- - Start with high-confidence boundaries, cycles, and public entrypoints.
54
- - Give durable rules an ID with `.as()` and record the reason with `.because()`.
55
- - Use `.localDependencies()` for layer allow-lists so npm and platform modules do not need repetition.
56
- - Use dependency-kind modifiers when type-only, runtime, or dynamic coupling has different meaning.
57
- - Treat `dynamic:unknown` as unproven. Do not allow it just to make a check pass.
58
- - Avoid broad ignores. Narrow an exception to the smallest selector and explain it.
87
+ Other flags:
59
88
 
60
- Read [references/rules.md](references/rules.md) for selectors, dependency modifiers, named layers, semantic expectations, and presets.
89
+ - `-c <file>` picks a config.
90
+ - `-o <file>` writes the report to a file.
91
+ - `-w` re-runs on change.
92
+ - `--ignore-baseline` reports baselined findings too.
93
+ - `--graph <file>` checks a graph saved with `archer graph -o <file>`. The other reporters are `pretty` (the default), `json`, `sarif`, `github`, and `dot`.
61
94
 
62
- ## Diagnose and fix findings
95
+ Exit code 2 means Archer could not check at all, and the message says why. Typical causes:
63
96
 
64
- Run:
97
+ - **Invalid configuration, with a list of issues.** A field has the wrong shape. A selector does not parse (`[a-z]`, `a//b`, unbalanced braces, a bad `npm:` name). A layer is named like an integer such as `"0"`. A `layers` or `publicApis` selector uses `layer:`.
98
+ - **Invalid configuration, with one reason.** A rule, `stack`, `allow`, or `publicApis` refers to an unknown layer. Two rules share an id, and a generated `layer/<name>` counts. Or the config produces no rules at all.
99
+ - **No configuration file.** No `archer.config.*` is in the current directory. Pass `-c`.
100
+ - **No tsconfig, or one that does not parse.** A tsconfig in `projects` is missing or invalid. Check `projects` and `root`.
65
101
 
66
- ```sh
67
- npx --no-install archer check --reporter agent
102
+ ## Fix findings
103
+
104
+ Read the summary line first. Then take findings by rule, open the file at `at`, read `reason`, and change the code. After each batch, re-run `archer check -r agent`, then the repo's typecheck and tests.
105
+
106
+ | Finding | Usual fix |
107
+ | --- | --- |
108
+ | `layer/*`, `r.forbid`, `r.onlyDependOn`: "imports X" | Move the code into a layer that may own it. Or invert the dependency: put an interface in the lower layer, implement it in the higher one, and pass the implementation in. Deleting the import is a fix only when the code does not need it. |
109
+ | `public-api/*` | Import through the layer's entry file. If the symbol belongs to the public surface, export it from that entry file. |
110
+ | `archer/acyclic`: "is part of a dependency cycle: a.ts → b.ts → a.ts" | Move what both sides need into a third file that neither imports back. Every file in a cycle gets its own finding, so one fix clears several. |
111
+ | Transitive rule: "depends on x.ts through a.ts → b.ts → x.ts" | Cut the chain at the link that crosses the boundary. `at` is the first import in the chain, and the wrong link may be further down. |
112
+ | "does not satisfy the rule's requirement" | A `require`-style rule found nothing. Add the dependency, dependent, or call it asks for. |
113
+ | Declarations, exports, calls, file names, line counts | Change the declaration, narrow the exports, replace the call, rename the file, or split it. |
114
+ | Custom rule | The message is whatever the rule's check wrote. Read the check in the config. |
115
+
116
+ A fix that moves a file changes which layer it belongs to, so check that the new location is the one the layer selectors expect.
117
+
118
+ ### Computed imports
119
+
120
+ An `import(expr)` or `require(expr)` with a non-literal specifier has no static target, so Archer fails closed. `r.forbid` and `r.onlyDependOn` report it whatever the rule's object is, because it might load anything. `r.require` is never satisfied by one. `archer/resolvable`, which `presets.recommended` includes, reports it too. Layer rules skip it, because they follow local files only.
121
+
122
+ Replace the computed specifier with literals, so every target is analyzed and checked against the layers:
123
+
124
+ ```ts
125
+ const locales = {
126
+ en: () => import("./locales/en.ts"),
127
+ pt: () => import("./locales/pt.ts"),
128
+ }
68
129
  ```
69
130
 
70
- The first line contains the status and counts. Each remaining line is one finding with location, severity, rule ID, message, and any dependency, source, reason, or import chain.
131
+ If the dynamic load is deliberate, such as a plugin loader, tell the user. The usual policy for that is a narrow `r.resolvable(..., { allowComputed: true, reason })` on that one file, plus excluding the file from the subjects of the other rules that flag it. Apply that only after the user agrees, and name the file as unverified in your report.
132
+
133
+ ## Baselines
134
+
135
+ A baseline is for adopting Archer in a codebase that already has violations. Use it only when the user agrees to accept the current debt.
136
+
137
+ 1. Run `npx archer check -r agent`, fix what is cheap, and confirm that every remaining finding is debt the user accepts.
138
+ 2. Run `npx archer baseline record`. It replaces `.archer-baseline` (the `baseline` field sets the path) with the fingerprints of all current findings. Commit the file.
139
+ 3. From then on, the baselined findings are hidden and counted in `suppressed`. A new error still fails the check.
140
+ 4. As the debt gets fixed, `stale` grows. Run `npx archer baseline prune` to drop entries that no finding matches. It never adds an entry.
141
+
142
+ One entry hides every finding with that `id`, so a second copy of an already baselined import in the same file is hidden too. Do not run `record` to clear a failing check, and do not edit the baseline by hand. Renaming a rule id orphans that rule's entries, and its old findings come back.
143
+
144
+ ## Add the test
145
+
146
+ Add one test next to the repo's other tests, written for its runner:
147
+
148
+ ```ts
149
+ import { assertArchitecture, dispose } from "@lucasacoutinho/archer"
150
+ import { afterAll, test } from "vitest"
151
+
152
+ afterAll(dispose)
153
+
154
+ test("architecture", () => assertArchitecture(), 60_000)
155
+ ```
71
156
 
72
- 1. Open the reported file and follow the import chain when present.
73
- 2. Fix the source dependency or declaration that violates the stated rule.
74
- 3. Re-run the narrow Archer command, then the repository's normal checks.
75
- 4. Do not weaken a rule, add an ignore, or update the baseline unless the user explicitly approves that policy change.
157
+ `assertArchitecture` runs the whole check and rejects with an `ArchitectureError` when the verdict is `fail`. The error message is the pretty report, and `error.report` holds the data. It takes `{ config, cwd }`: `config` is a file path or an inline config, and `cwd` defaults to the process directory, so pass it when the runner starts elsewhere. Analysis can take a while on a large repo, so raise the runner's timeout. `dispose` stops the TypeScript server that the first run started, as Archer's own test does.
76
158
 
77
- Use `--update-baseline` only for deliberate staged adoption in an existing codebase. Commit the baseline when the project treats it as policy.
159
+ Add `archer check` to the repo's CI or a `package.json` script when the user wants the check outside the test run.
78
160
 
79
- ## Report the result
161
+ ## Report
80
162
 
81
- State which rules or files changed and include the final Archer summary. Separate source fixes from rule-policy changes. If computed imports, runtime injection, or generated code prevent static proof, name that limitation instead of claiming the boundary passed.
163
+ List the findings you fixed and the ones that remain, by `rule` and `id`. Report policy changes (rules, baseline, config) separately from source fixes, and say whether the user asked for them. Name anything Archer could not see: computed imports, files outside the tsconfigs, rules in `emptySubjects` or `emptyObjects`. End with the final `summary` line.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: 'Archer architecture tests'
3
- short_description: 'Add and enforce TypeScript architecture rules'
4
- default_prompt: 'Use $archer to add architecture tests to this TypeScript project and fix any reported violations.'
3
+ short_description: 'Add and enforce TypeScript and JavaScript architecture rules'
4
+ default_prompt: 'Use $archer to set up Archer in this repository, run the architecture check, and fix any findings in source without weakening the rules.'