@dommaker/harness 0.18.0 → 1.0.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 (366) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/bin/harness.js +136 -552
  3. package/dist/cli/commands/check.d.ts.map +1 -1
  4. package/dist/cli/commands/check.js +2 -13
  5. package/dist/cli/commands/check.js.map +1 -1
  6. package/dist/cli/commands/definitions.d.ts +90 -0
  7. package/dist/cli/commands/definitions.d.ts.map +1 -0
  8. package/dist/cli/commands/definitions.js +519 -0
  9. package/dist/cli/commands/definitions.js.map +1 -0
  10. package/dist/cli/commands/knowledge.js.map +1 -1
  11. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +3 -25
  12. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  13. package/dist/cli/commands/sync-docs/capabilities-syncer.js +3 -93
  14. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  15. package/dist/cli/commands/sync-docs/index.d.ts +1 -1
  16. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  17. package/dist/cli/commands/sync-docs/index.js +4 -4
  18. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  19. package/dist/cli/commands/update-user-model.d.ts +1 -0
  20. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  21. package/dist/cli/commands/update-user-model.js +12 -5
  22. package/dist/cli/commands/update-user-model.js.map +1 -1
  23. package/dist/context/index.d.ts +1 -5
  24. package/dist/context/index.d.ts.map +1 -1
  25. package/dist/context/index.js +1 -5
  26. package/dist/context/index.js.map +1 -1
  27. package/dist/context/knowledge-injector.d.ts +3 -3
  28. package/dist/context/knowledge-injector.d.ts.map +1 -1
  29. package/dist/context/knowledge-injector.js +1 -1
  30. package/dist/context/session-manager.d.ts +1 -12
  31. package/dist/context/session-manager.d.ts.map +1 -1
  32. package/dist/context/session-manager.js +0 -63
  33. package/dist/context/session-manager.js.map +1 -1
  34. package/dist/context/types.d.ts +1 -39
  35. package/dist/context/types.d.ts.map +1 -1
  36. package/dist/context/types.js +2 -15
  37. package/dist/context/types.js.map +1 -1
  38. package/dist/core/constraints/agent-prompt-renderer.d.ts +32 -0
  39. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -0
  40. package/dist/core/constraints/agent-prompt-renderer.js +58 -0
  41. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -0
  42. package/dist/core/constraints/capabilities-parser.d.ts +17 -2
  43. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  44. package/dist/core/constraints/capabilities-parser.js +84 -2
  45. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  46. package/dist/core/constraints/check-cache.d.ts +48 -3
  47. package/dist/core/constraints/check-cache.d.ts.map +1 -1
  48. package/dist/core/constraints/check-cache.js +53 -5
  49. package/dist/core/constraints/check-cache.js.map +1 -1
  50. package/dist/core/constraints/checker.d.ts +3 -17
  51. package/dist/core/constraints/checker.d.ts.map +1 -1
  52. package/dist/core/constraints/checker.js +7 -47
  53. package/dist/core/constraints/checker.js.map +1 -1
  54. package/dist/core/constraints/definitions.d.ts +0 -7
  55. package/dist/core/constraints/definitions.d.ts.map +1 -1
  56. package/dist/core/constraints/definitions.js +1 -8
  57. package/dist/core/constraints/definitions.js.map +1 -1
  58. package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
  59. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  60. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  61. package/dist/core/constraints/index.d.ts +5 -1
  62. package/dist/core/constraints/index.d.ts.map +1 -1
  63. package/dist/core/constraints/index.js +8 -3
  64. package/dist/core/constraints/index.js.map +1 -1
  65. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  66. package/dist/core/constraints/interceptor.js +1 -5
  67. package/dist/core/constraints/interceptor.js.map +1 -1
  68. package/dist/core/effective-constraints.d.ts.map +1 -1
  69. package/dist/core/effective-constraints.js +0 -1
  70. package/dist/core/effective-constraints.js.map +1 -1
  71. package/dist/core/project-config-loader.d.ts.map +1 -1
  72. package/dist/core/project-config-loader.js +1 -8
  73. package/dist/core/project-config-loader.js.map +1 -1
  74. package/dist/dashboard/stats.d.ts.map +1 -1
  75. package/dist/dashboard/stats.js.map +1 -1
  76. package/dist/dashboard/types.d.ts +2 -2
  77. package/dist/dashboard/types.d.ts.map +1 -1
  78. package/dist/failure/constraint-handler.d.ts.map +1 -1
  79. package/dist/failure/constraint-handler.js +0 -2
  80. package/dist/failure/constraint-handler.js.map +1 -1
  81. package/dist/gates/acceptance.d.ts +9 -1
  82. package/dist/gates/acceptance.d.ts.map +1 -1
  83. package/dist/gates/acceptance.js +21 -0
  84. package/dist/gates/acceptance.js.map +1 -1
  85. package/dist/gates/checker-gate.d.ts +23 -0
  86. package/dist/gates/checker-gate.d.ts.map +1 -0
  87. package/dist/gates/checker-gate.js +53 -0
  88. package/dist/gates/checker-gate.js.map +1 -0
  89. package/dist/gates/command.d.ts +9 -2
  90. package/dist/gates/command.d.ts.map +1 -1
  91. package/dist/gates/command.js +10 -0
  92. package/dist/gates/command.js.map +1 -1
  93. package/dist/gates/contract.d.ts +8 -2
  94. package/dist/gates/contract.d.ts.map +1 -1
  95. package/dist/gates/contract.js +9 -0
  96. package/dist/gates/contract.js.map +1 -1
  97. package/dist/gates/decision.d.ts +17 -0
  98. package/dist/gates/decision.d.ts.map +1 -0
  99. package/dist/gates/decision.js +25 -0
  100. package/dist/gates/decision.js.map +1 -0
  101. package/dist/gates/definitions.d.ts +63 -0
  102. package/dist/gates/definitions.d.ts.map +1 -0
  103. package/dist/gates/definitions.js +160 -0
  104. package/dist/gates/definitions.js.map +1 -0
  105. package/dist/gates/effective-gates.d.ts +30 -0
  106. package/dist/gates/effective-gates.d.ts.map +1 -0
  107. package/dist/gates/effective-gates.js +85 -0
  108. package/dist/gates/effective-gates.js.map +1 -0
  109. package/dist/gates/index.d.ts +25 -1
  110. package/dist/gates/index.d.ts.map +1 -1
  111. package/dist/gates/index.js +37 -1
  112. package/dist/gates/index.js.map +1 -1
  113. package/dist/gates/performance.d.ts +8 -2
  114. package/dist/gates/performance.d.ts.map +1 -1
  115. package/dist/gates/performance.js +9 -0
  116. package/dist/gates/performance.js.map +1 -1
  117. package/dist/gates/registry.d.ts +32 -0
  118. package/dist/gates/registry.d.ts.map +1 -0
  119. package/dist/gates/registry.js +90 -0
  120. package/dist/gates/registry.js.map +1 -0
  121. package/dist/gates/review.d.ts +8 -2
  122. package/dist/gates/review.d.ts.map +1 -1
  123. package/dist/gates/review.js +9 -0
  124. package/dist/gates/review.js.map +1 -1
  125. package/dist/gates/runner.d.ts +30 -0
  126. package/dist/gates/runner.d.ts.map +1 -0
  127. package/dist/gates/runner.js +38 -0
  128. package/dist/gates/runner.js.map +1 -0
  129. package/dist/gates/security.d.ts +8 -2
  130. package/dist/gates/security.d.ts.map +1 -1
  131. package/dist/gates/security.js +9 -0
  132. package/dist/gates/security.js.map +1 -1
  133. package/dist/gates/types.d.ts +41 -1
  134. package/dist/gates/types.d.ts.map +1 -1
  135. package/dist/hooks/bootstrap.d.ts.map +1 -1
  136. package/dist/hooks/bootstrap.js +0 -2
  137. package/dist/hooks/bootstrap.js.map +1 -1
  138. package/dist/hooks/config.d.ts +26 -0
  139. package/dist/hooks/config.d.ts.map +1 -0
  140. package/dist/hooks/config.js +30 -0
  141. package/dist/hooks/config.js.map +1 -0
  142. package/dist/hooks/index.d.ts +2 -1
  143. package/dist/hooks/index.d.ts.map +1 -1
  144. package/dist/hooks/index.js +4 -1
  145. package/dist/hooks/index.js.map +1 -1
  146. package/dist/hooks/registry.d.ts +18 -1
  147. package/dist/hooks/registry.d.ts.map +1 -1
  148. package/dist/hooks/registry.js +45 -0
  149. package/dist/hooks/registry.js.map +1 -1
  150. package/dist/hooks/types.d.ts +20 -0
  151. package/dist/hooks/types.d.ts.map +1 -1
  152. package/dist/index.d.ts +8 -8
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +13 -21
  155. package/dist/index.js.map +1 -1
  156. package/dist/knowledge/import.d.ts +2 -2
  157. package/dist/knowledge/import.d.ts.map +1 -1
  158. package/dist/knowledge/ingest.js.map +1 -1
  159. package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
  160. package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
  161. package/dist/knowledge/types.d.ts +5 -7
  162. package/dist/knowledge/types.d.ts.map +1 -1
  163. package/dist/knowledge/types.js +1 -1
  164. package/dist/knowledge/types.js.map +1 -1
  165. package/dist/monitoring/constraint-doctor.d.ts +4 -22
  166. package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
  167. package/dist/monitoring/constraint-doctor.js +7 -94
  168. package/dist/monitoring/constraint-doctor.js.map +1 -1
  169. package/dist/presets/standard.d.ts +1 -1
  170. package/dist/presets/standard.d.ts.map +1 -1
  171. package/dist/presets/standard.js +1 -2
  172. package/dist/presets/standard.js.map +1 -1
  173. package/dist/spec/annotation-checker.d.ts.map +1 -1
  174. package/dist/spec/annotation-checker.js +6 -26
  175. package/dist/spec/annotation-checker.js.map +1 -1
  176. package/dist/tools/index.d.ts +0 -4
  177. package/dist/tools/index.d.ts.map +1 -1
  178. package/dist/tools/index.js +0 -4
  179. package/dist/tools/index.js.map +1 -1
  180. package/dist/types/constraint.d.ts +1 -12
  181. package/dist/types/constraint.d.ts.map +1 -1
  182. package/dist/types/constraint.js +0 -3
  183. package/dist/types/constraint.js.map +1 -1
  184. package/dist/types/project-config.d.ts +0 -2
  185. package/dist/types/project-config.d.ts.map +1 -1
  186. package/dist/utils/file-walk.d.ts +2 -2
  187. package/dist/utils/file-walk.js +2 -2
  188. package/package.json +9 -4
  189. package/src/CONTEXT.md +14 -0
  190. package/src/__tests__/checker-extra.test.ts +2 -59
  191. package/src/__tests__/constraint-doctor.test.ts +1 -140
  192. package/src/__tests__/constraint-handler.test.ts +0 -4
  193. package/src/__tests__/project-config-loader-extra.test.ts +0 -20
  194. package/src/__tests__/project-config-loader.test.ts +0 -1
  195. package/src/cli/commands/CONTEXT.md +15 -7
  196. package/src/cli/commands/__tests__/analyze-sessions.test.ts +178 -0
  197. package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
  198. package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
  199. package/src/cli/commands/__tests__/check.test.ts +7 -75
  200. package/src/cli/commands/__tests__/registry.test.ts +201 -0
  201. package/src/cli/commands/__tests__/release.test.ts +162 -0
  202. package/src/cli/commands/__tests__/update-user-model.test.ts +205 -0
  203. package/src/cli/commands/check.ts +2 -14
  204. package/src/cli/commands/definitions.ts +593 -0
  205. package/src/cli/commands/knowledge.ts +3 -3
  206. package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
  207. package/src/cli/commands/sync-docs/index.ts +8 -5
  208. package/src/cli/commands/update-user-model.ts +15 -5
  209. package/src/context/CONTEXT.md +2 -7
  210. package/src/context/__tests__/session-manager.test.ts +0 -91
  211. package/src/context/index.ts +1 -5
  212. package/src/context/knowledge-injector.ts +3 -3
  213. package/src/context/session-manager.ts +0 -74
  214. package/src/context/types.ts +2 -63
  215. package/src/core/CONTEXT.md +1 -1
  216. package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +129 -0
  217. package/src/core/constraints/__tests__/check-cache.test.ts +227 -0
  218. package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
  219. package/src/core/constraints/agent-prompt-renderer.ts +71 -0
  220. package/src/core/constraints/capabilities-parser.ts +95 -2
  221. package/src/core/constraints/check-cache.ts +75 -3
  222. package/src/core/constraints/checker.ts +8 -53
  223. package/src/core/constraints/definitions.ts +0 -8
  224. package/src/core/constraints/doc-freshness/runner.ts +1 -2
  225. package/src/core/constraints/index.ts +11 -2
  226. package/src/core/constraints/interceptor.ts +1 -6
  227. package/src/core/effective-constraints.ts +0 -1
  228. package/src/core/project-config-loader.ts +1 -7
  229. package/src/dashboard/stats.ts +2 -2
  230. package/src/dashboard/types.ts +2 -2
  231. package/src/failure/constraint-handler.ts +0 -2
  232. package/src/gates/CONTEXT.md +16 -13
  233. package/src/gates/__tests__/checker-gate.test.ts +45 -0
  234. package/src/gates/__tests__/contract.test.ts +73 -0
  235. package/src/gates/__tests__/effective-gates.test.ts +82 -0
  236. package/src/gates/__tests__/gate-interface.test.ts +202 -0
  237. package/src/gates/__tests__/registry.test.ts +87 -0
  238. package/src/gates/__tests__/runner.test.ts +81 -0
  239. package/src/gates/acceptance.ts +24 -1
  240. package/src/gates/checker-gate.ts +54 -0
  241. package/src/gates/command.ts +13 -2
  242. package/src/gates/contract.ts +12 -2
  243. package/src/gates/decision.ts +27 -0
  244. package/src/gates/definitions.ts +207 -0
  245. package/src/gates/effective-gates.ts +106 -0
  246. package/src/gates/index.ts +38 -0
  247. package/src/gates/performance.ts +12 -2
  248. package/src/gates/registry.ts +107 -0
  249. package/src/gates/review.ts +12 -2
  250. package/src/gates/runner.ts +57 -0
  251. package/src/gates/security.ts +12 -2
  252. package/src/gates/types.ts +48 -1
  253. package/src/hooks/CONTEXT.md +12 -2
  254. package/src/hooks/__tests__/bootstrap.test.ts +0 -1
  255. package/src/hooks/__tests__/config.test.ts +19 -0
  256. package/src/hooks/__tests__/pipeline.test.ts +79 -0
  257. package/src/hooks/__tests__/registry.test.ts +67 -0
  258. package/src/hooks/bootstrap.ts +0 -2
  259. package/src/hooks/config.ts +29 -0
  260. package/src/hooks/index.ts +2 -1
  261. package/src/hooks/registry.ts +60 -1
  262. package/src/hooks/types.ts +21 -0
  263. package/src/index.ts +13 -22
  264. package/src/knowledge/import.ts +3 -3
  265. package/src/knowledge/ingest.ts +4 -4
  266. package/src/knowledge/lifecycle-hooks.ts +3 -3
  267. package/src/knowledge/types.ts +5 -8
  268. package/src/monitoring/constraint-doctor.ts +7 -106
  269. package/src/presets/__tests__/standard.test.ts +0 -2
  270. package/src/presets/standard.ts +1 -2
  271. package/src/spec/__tests__/annotation-checker.test.ts +9 -3
  272. package/src/spec/annotation-checker.ts +7 -29
  273. package/src/tools/CONTEXT.md +7 -14
  274. package/src/tools/index.ts +0 -4
  275. package/src/types/constraint.ts +1 -14
  276. package/src/types/project-config.ts +0 -3
  277. package/src/utils/file-walk.ts +2 -2
  278. package/dist/architecture/constraint-engine.d.ts +0 -67
  279. package/dist/architecture/constraint-engine.d.ts.map +0 -1
  280. package/dist/architecture/constraint-engine.js +0 -295
  281. package/dist/architecture/constraint-engine.js.map +0 -1
  282. package/dist/architecture/cross-project-checker.d.ts +0 -54
  283. package/dist/architecture/cross-project-checker.d.ts.map +0 -1
  284. package/dist/architecture/cross-project-checker.js +0 -467
  285. package/dist/architecture/cross-project-checker.js.map +0 -1
  286. package/dist/cli/commands/index.d.ts +0 -30
  287. package/dist/cli/commands/index.d.ts.map +0 -1
  288. package/dist/cli/commands/index.js +0 -91
  289. package/dist/cli/commands/index.js.map +0 -1
  290. package/dist/context/file-budget.d.ts +0 -40
  291. package/dist/context/file-budget.d.ts.map +0 -1
  292. package/dist/context/file-budget.js +0 -147
  293. package/dist/context/file-budget.js.map +0 -1
  294. package/dist/context/progressive-loader.d.ts +0 -109
  295. package/dist/context/progressive-loader.d.ts.map +0 -1
  296. package/dist/context/progressive-loader.js +0 -166
  297. package/dist/context/progressive-loader.js.map +0 -1
  298. package/dist/context/token-pipeline.d.ts +0 -55
  299. package/dist/context/token-pipeline.d.ts.map +0 -1
  300. package/dist/context/token-pipeline.js +0 -220
  301. package/dist/context/token-pipeline.js.map +0 -1
  302. package/dist/context/tool-output-budget.d.ts +0 -59
  303. package/dist/context/tool-output-budget.d.ts.map +0 -1
  304. package/dist/context/tool-output-budget.js +0 -177
  305. package/dist/context/tool-output-budget.js.map +0 -1
  306. package/dist/core/constraints/default-executors.d.ts +0 -10
  307. package/dist/core/constraints/default-executors.d.ts.map +0 -1
  308. package/dist/core/constraints/default-executors.js +0 -120
  309. package/dist/core/constraints/default-executors.js.map +0 -1
  310. package/dist/llm/adapter.d.ts +0 -36
  311. package/dist/llm/adapter.d.ts.map +0 -1
  312. package/dist/llm/adapter.js +0 -118
  313. package/dist/llm/adapter.js.map +0 -1
  314. package/dist/llm/index.d.ts +0 -3
  315. package/dist/llm/index.d.ts.map +0 -1
  316. package/dist/llm/index.js +0 -19
  317. package/dist/llm/index.js.map +0 -1
  318. package/dist/llm/types.d.ts +0 -62
  319. package/dist/llm/types.d.ts.map +0 -1
  320. package/dist/llm/types.js +0 -8
  321. package/dist/llm/types.js.map +0 -1
  322. package/dist/tools/core/index.d.ts +0 -13
  323. package/dist/tools/core/index.d.ts.map +0 -1
  324. package/dist/tools/core/index.js +0 -198
  325. package/dist/tools/core/index.js.map +0 -1
  326. package/dist/tools/loader.d.ts +0 -21
  327. package/dist/tools/loader.d.ts.map +0 -1
  328. package/dist/tools/loader.js +0 -199
  329. package/dist/tools/loader.js.map +0 -1
  330. package/dist/tools/registry.d.ts +0 -59
  331. package/dist/tools/registry.d.ts.map +0 -1
  332. package/dist/tools/registry.js +0 -115
  333. package/dist/tools/registry.js.map +0 -1
  334. package/dist/tools/types.d.ts +0 -38
  335. package/dist/tools/types.d.ts.map +0 -1
  336. package/dist/tools/types.js +0 -6
  337. package/dist/tools/types.js.map +0 -1
  338. package/src/__tests__/architecture-engine.test.ts +0 -619
  339. package/src/__tests__/context/progressive-loader.test.ts +0 -131
  340. package/src/__tests__/cross-project-checker.test.ts +0 -318
  341. package/src/architecture/CONTEXT.md +0 -22
  342. package/src/architecture/constraint-engine.ts +0 -351
  343. package/src/architecture/cross-project-checker.ts +0 -545
  344. package/src/cli/commands/index.ts +0 -30
  345. package/src/context/__tests__/file-budget.test.ts +0 -142
  346. package/src/context/__tests__/progressive-loader.test.ts +0 -264
  347. package/src/context/__tests__/token-pipeline.test.ts +0 -178
  348. package/src/context/__tests__/tool-output-budget.test.ts +0 -95
  349. package/src/context/file-budget.ts +0 -142
  350. package/src/context/progressive-loader.ts +0 -260
  351. package/src/context/token-pipeline.ts +0 -265
  352. package/src/context/tool-output-budget.ts +0 -178
  353. package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
  354. package/src/core/constraints/default-executors.ts +0 -92
  355. package/src/llm/CONTEXT.md +0 -21
  356. package/src/llm/__tests__/adapter.test.ts +0 -172
  357. package/src/llm/adapter.ts +0 -149
  358. package/src/llm/index.ts +0 -2
  359. package/src/llm/types.ts +0 -81
  360. package/src/tools/__tests__/loader.test.ts +0 -252
  361. package/src/tools/__tests__/registry.test.ts +0 -157
  362. package/src/tools/core/__tests__/core.test.ts +0 -122
  363. package/src/tools/core/index.ts +0 -201
  364. package/src/tools/loader.ts +0 -216
  365. package/src/tools/registry.ts +0 -128
  366. package/src/tools/types.ts +0 -46
@@ -1,351 +0,0 @@
1
- /**
2
- * 架构约束引擎
3
- *
4
- * 通用能力:加载规则、执行检查、生成报告
5
- * 规则定义:由项目提供(.architect/rules.yml)
6
- */
7
-
8
- // 架构约束相关类型(注意:与 constraint 模块的类型区分)
9
- export interface ArchitectureContext {
10
- files: string[];
11
- diff: string;
12
- pr?: {
13
- number: number;
14
- changedFiles: number;
15
- additions: number;
16
- deletions: number;
17
- };
18
- }
19
-
20
- export interface ArchitectureViolation {
21
- ruleId: string;
22
- message: string;
23
- files?: string[];
24
- severity: 'error' | 'warning';
25
- }
26
-
27
- export interface ArchitectureResult {
28
- passed: boolean;
29
- violations: ArchitectureViolation[];
30
- }
31
-
32
- export interface ModuleBoundary {
33
- /** 源文件 glob 模式 */
34
- from: string;
35
- /** 禁止导入的目标 glob 模式 */
36
- denyImport: string;
37
- }
38
-
39
- export interface ArchitectureRule {
40
- id: string;
41
- type: 'forbidden-pattern' | 'file-count' | 'module-boundary' | 'custom';
42
- severity?: 'error' | 'warning';
43
- // 通用配置
44
- scope?: string; // glob 模式
45
- message?: string; // 错误提示
46
- // forbidden-pattern
47
- patterns?: string[]; // 禁止的关键词
48
- // file-count
49
- threshold?: number; // 文件数阈值
50
- // module-boundary
51
- boundaries?: ModuleBoundary[]; // 模块边界定义
52
- // custom
53
- script?: string; // 自定义脚本路径
54
- }
55
-
56
- export class ArchitectureConstraintEngine {
57
- constructor(private rules: ArchitectureRule[]) {}
58
-
59
- async check(context: ArchitectureContext): Promise<ArchitectureResult> {
60
- const violations: ArchitectureViolation[] = [];
61
-
62
- for (const rule of this.rules) {
63
- const result = await this.checkRule(rule, context);
64
- violations.push(...result);
65
- }
66
-
67
- return {
68
- passed: violations.filter(v => v.severity === 'error').length === 0,
69
- violations,
70
- };
71
- }
72
-
73
- private async checkRule(
74
- rule: ArchitectureRule,
75
- context: ArchitectureContext
76
- ): Promise<ArchitectureViolation[]> {
77
- switch (rule.type) {
78
- case 'forbidden-pattern':
79
- return this.checkForbiddenPattern(rule, context);
80
- case 'file-count':
81
- return this.checkFileCount(rule, context);
82
- case 'module-boundary':
83
- return this.checkModuleBoundary(rule, context);
84
- default:
85
- return [];
86
- }
87
- }
88
-
89
- private checkForbiddenPattern(
90
- rule: ArchitectureRule,
91
- context: ArchitectureContext
92
- ): ArchitectureViolation[] {
93
- const violations: ArchitectureViolation[] = [];
94
- const patterns = rule.patterns || [];
95
-
96
- for (const file of context.files) {
97
- // 检查文件路径是否匹配 scope
98
- if (rule.scope && !this.matchGlob(file, rule.scope)) {
99
- continue;
100
- }
101
-
102
- // 检查文件名是否包含禁止模式
103
- for (const pattern of patterns) {
104
- if (file.toLowerCase().includes(pattern.toLowerCase())) {
105
- violations.push({
106
- ruleId: rule.id,
107
- message: rule.message || `File contains forbidden pattern: ${pattern}`,
108
- files: [file],
109
- severity: rule.severity || 'error',
110
- });
111
- }
112
- }
113
- }
114
-
115
- return violations;
116
- }
117
-
118
- private checkFileCount(
119
- rule: ArchitectureRule,
120
- context: ArchitectureContext
121
- ): ArchitectureViolation[] {
122
- const threshold = rule.threshold || 3;
123
- const count = context.files.length;
124
-
125
- if (count > threshold) {
126
- return [{
127
- ruleId: rule.id,
128
- message: rule.message || `Too many files changed (${count} > ${threshold}). Reuse check required.`,
129
- severity: rule.severity || 'warning',
130
- }];
131
- }
132
-
133
- return [];
134
- }
135
-
136
- private checkModuleBoundary(
137
- rule: ArchitectureRule,
138
- context: ArchitectureContext
139
- ): ArchitectureViolation[] {
140
- const violations: ArchitectureViolation[] = [];
141
- const boundaries = rule.boundaries || [];
142
-
143
- if (boundaries.length === 0) return [];
144
-
145
- // 从 diff 中提取每个文件新增的 import 语句
146
- const fileImports = this.extractImportsFromDiff(context.diff);
147
-
148
- for (const boundary of boundaries) {
149
- for (const [file, imports] of fileImports.entries()) {
150
- if (!this.matchGlob(file, boundary.from)) continue;
151
-
152
- for (const importPath of imports) {
153
- const resolved = this.resolveImport(file, importPath);
154
- if (resolved && this.matchGlob(resolved, boundary.denyImport)) {
155
- violations.push({
156
- ruleId: rule.id,
157
- message: rule.message || `${file} 不允许导入 ${importPath}(违反模块边界 ${boundary.from} → ${boundary.denyImport})`,
158
- files: [file],
159
- severity: rule.severity || 'error',
160
- });
161
- }
162
- }
163
- }
164
- }
165
-
166
- return violations;
167
- }
168
-
169
- /**
170
- * 从 diff 中提取每个文件新增的 import 路径
171
- * 只解析 + 开头的行(新增内容)
172
- */
173
- private extractImportsFromDiff(diff: string): Map<string, string[]> {
174
- const result = new Map<string, string[]>();
175
- let currentFile = '';
176
-
177
- for (const line of diff.split('\n')) {
178
- // 解析 diff 文件头: +++ b/src/foo.ts
179
- const fileMatch = line.match(/^\+\+\+ b\/(.+)$/);
180
- if (fileMatch) {
181
- currentFile = fileMatch[1];
182
- continue;
183
- }
184
-
185
- // 只处理新增行
186
- if (!line.startsWith('+') || line.startsWith('+++')) continue;
187
- if (!currentFile) continue;
188
-
189
- const content = line.slice(1);
190
-
191
- // 匹配各种 import 形式
192
- // import ... from 'path'
193
- // import 'path'
194
- // export ... from 'path'
195
- const importMatch = content.match(/(?:import|export)\s+.*?from\s+['"]([^'"]+)['"]/);
196
- const sideEffectMatch = content.match(/^import\s+['"]([^'"]+)['"]/);
197
-
198
- const importPath = importMatch?.[1] || sideEffectMatch?.[1];
199
- if (importPath) {
200
- const existing = result.get(currentFile) || [];
201
- existing.push(importPath);
202
- result.set(currentFile, existing);
203
- }
204
- }
205
-
206
- return result;
207
- }
208
-
209
- /**
210
- * 解析 import 路径为相对于项目根的路径
211
- * 相对路径: ./foo, ../bar → 基于导入文件解析
212
- * 非相对路径: @scope/foo → 返回 null(外部依赖)
213
- */
214
- private resolveImport(fromFile: string, importPath: string): string | null {
215
- // 外部依赖(非相对路径)跳过
216
- if (!importPath.startsWith('.')) return null;
217
-
218
- const fromDir = fromFile.includes('/') ? fromFile.slice(0, fromFile.lastIndexOf('/')) : '';
219
- const parts = (fromDir + '/' + importPath).split('/');
220
- const resolved: string[] = [];
221
-
222
- for (const part of parts) {
223
- if (part === '.' || part === '') continue;
224
- if (part === '..') {
225
- resolved.pop();
226
- } else {
227
- resolved.push(part);
228
- }
229
- }
230
-
231
- return resolved.join('/');
232
- }
233
-
234
- private matchGlob(file: string, pattern: string): boolean {
235
- // 简化的 glob 匹配
236
- let regexPattern = pattern;
237
-
238
- // 用占位符保护 ** 的不同位置,避免被 * 替换破坏
239
- const DS_MID = '\x00DSM\x00'; // /**/ 中间
240
- const DS_END = '\x00DSE\x00'; // ** 结尾
241
- const DS_SRT = '\x00DSS\x00'; // **/ 开头
242
-
243
- // 优先处理 /**/(中间的 **)
244
- regexPattern = regexPattern.replace(/\/\*\*\//g, `/${DS_MID}/`);
245
- // 处理 **/ 开头
246
- if (regexPattern.startsWith('**/')) {
247
- regexPattern = DS_SRT + regexPattern.slice(3);
248
- }
249
- // 处理剩余的 **(结尾)
250
- regexPattern = regexPattern.replace(/\*\*/g, DS_END);
251
-
252
- // 替换单 *(路径通配符)
253
- regexPattern = regexPattern.replace(/\*/g, '[^/]*');
254
-
255
- // 恢复 ** 占位符为正则
256
- const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
257
- regexPattern = regexPattern
258
- .replace(new RegExp(`/${esc(DS_MID)}/`, 'g'), '/(?:.*/)?')
259
- .replace(new RegExp(esc(DS_SRT), 'g'), '(?:.*/)?')
260
- .replace(new RegExp(esc(DS_END), 'g'), '.*');
261
-
262
- const regex = new RegExp(`^${regexPattern}$`);
263
- return regex.test(file);
264
- }
265
- }
266
-
267
- // 加载规则的工厂函数(支持 YAML 或 JSON)
268
- export async function loadArchitectureRules(configPath: string): Promise<ArchitectureRule[]> {
269
- const fs = await import('fs/promises');
270
-
271
- const content = await fs.readFile(configPath, 'utf-8');
272
-
273
- // 简单 YAML 解析(不依赖外部库)
274
- let config: { rules?: ArchitectureRule[] };
275
-
276
- if (configPath.endsWith('.json')) {
277
- config = JSON.parse(content);
278
- } else {
279
- // 简单 YAML 解析:提取 rules 数组
280
- config = parseSimpleYaml(content);
281
- }
282
-
283
- return config.rules || [];
284
- }
285
-
286
- // 简单的 YAML 子集解析器
287
- function parseSimpleYaml(content: string): { rules?: ArchitectureRule[] } {
288
- const lines = content.split('\n');
289
- const result: { rules?: ArchitectureRule[] } = { rules: [] };
290
- let currentRule: Partial<ArchitectureRule> | null = null;
291
- let currentArray: string[] | null = null;
292
-
293
- for (const line of lines) {
294
- const trimmed = line.trim();
295
-
296
- // 跳过注释和空行
297
- if (!trimmed || trimmed.startsWith('#')) continue;
298
-
299
- // 新规则开始
300
- if (trimmed.startsWith('- id:')) {
301
- if (currentRule && result.rules) {
302
- result.rules.push(currentRule as ArchitectureRule);
303
- }
304
- currentRule = { id: trimmed.replace('- id:', '').trim() };
305
- currentArray = null;
306
- }
307
- // 规则属性
308
- else if (currentRule && trimmed.includes(':')) {
309
- const colonIndex = trimmed.indexOf(':');
310
- const key = trimmed.slice(0, colonIndex).trim();
311
- const value = trimmed.slice(colonIndex + 1).trim();
312
-
313
- if (key === 'patterns') {
314
- currentArray = [];
315
- (currentRule as Record<string, string[]>)[key] = currentArray;
316
- } else if (value) {
317
- // 去除引号
318
- const cleanValue = value.replace(/^["']|["']$/g, '');
319
- if (key === 'threshold') {
320
- (currentRule as Record<string, number>)[key] = parseInt(cleanValue, 10);
321
- } else if (key === 'severity') {
322
- (currentRule as Record<string, 'error' | 'warning'>)[key] = cleanValue as 'error' | 'warning';
323
- } else {
324
- (currentRule as Record<string, string>)[key] = cleanValue;
325
- }
326
- }
327
- }
328
- // 数组元素
329
- else if (currentArray && trimmed.startsWith('- ')) {
330
- const value = trimmed.slice(2).trim().replace(/^["']|["']$/g, '');
331
- currentArray.push(value);
332
- }
333
- }
334
-
335
- // 添加最后一个规则
336
- if (currentRule && result.rules) {
337
- result.rules.push(currentRule as ArchitectureRule);
338
- }
339
-
340
- return result;
341
- }
342
-
343
- // CLI 入口
344
- export async function runArchitectureCheck(
345
- configPath: string,
346
- context: ArchitectureContext
347
- ): Promise<ArchitectureResult> {
348
- const rules = await loadArchitectureRules(configPath);
349
- const engine = new ArchitectureConstraintEngine(rules);
350
- return engine.check(context);
351
- }