@dommaker/harness 0.19.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 (348) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/harness.js +136 -557
  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/context/index.d.ts +1 -5
  20. package/dist/context/index.d.ts.map +1 -1
  21. package/dist/context/index.js +1 -5
  22. package/dist/context/index.js.map +1 -1
  23. package/dist/context/knowledge-injector.d.ts +3 -3
  24. package/dist/context/knowledge-injector.d.ts.map +1 -1
  25. package/dist/context/knowledge-injector.js +1 -1
  26. package/dist/context/session-manager.d.ts +1 -12
  27. package/dist/context/session-manager.d.ts.map +1 -1
  28. package/dist/context/session-manager.js +0 -63
  29. package/dist/context/session-manager.js.map +1 -1
  30. package/dist/context/types.d.ts +1 -39
  31. package/dist/context/types.d.ts.map +1 -1
  32. package/dist/context/types.js +2 -15
  33. package/dist/context/types.js.map +1 -1
  34. package/dist/core/constraints/agent-prompt-renderer.d.ts +1 -1
  35. package/dist/core/constraints/agent-prompt-renderer.js +1 -1
  36. package/dist/core/constraints/capabilities-parser.d.ts +17 -2
  37. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  38. package/dist/core/constraints/capabilities-parser.js +84 -2
  39. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  40. package/dist/core/constraints/checker.d.ts +3 -17
  41. package/dist/core/constraints/checker.d.ts.map +1 -1
  42. package/dist/core/constraints/checker.js +7 -47
  43. package/dist/core/constraints/checker.js.map +1 -1
  44. package/dist/core/constraints/definitions.d.ts +0 -7
  45. package/dist/core/constraints/definitions.d.ts.map +1 -1
  46. package/dist/core/constraints/definitions.js +1 -8
  47. package/dist/core/constraints/definitions.js.map +1 -1
  48. package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
  49. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  50. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  51. package/dist/core/constraints/index.d.ts +1 -1
  52. package/dist/core/constraints/index.d.ts.map +1 -1
  53. package/dist/core/constraints/index.js +2 -3
  54. package/dist/core/constraints/index.js.map +1 -1
  55. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  56. package/dist/core/constraints/interceptor.js +1 -5
  57. package/dist/core/constraints/interceptor.js.map +1 -1
  58. package/dist/core/effective-constraints.d.ts.map +1 -1
  59. package/dist/core/effective-constraints.js +0 -1
  60. package/dist/core/effective-constraints.js.map +1 -1
  61. package/dist/core/project-config-loader.d.ts.map +1 -1
  62. package/dist/core/project-config-loader.js +1 -8
  63. package/dist/core/project-config-loader.js.map +1 -1
  64. package/dist/dashboard/stats.d.ts.map +1 -1
  65. package/dist/dashboard/stats.js.map +1 -1
  66. package/dist/dashboard/types.d.ts +2 -2
  67. package/dist/dashboard/types.d.ts.map +1 -1
  68. package/dist/failure/constraint-handler.d.ts.map +1 -1
  69. package/dist/failure/constraint-handler.js +0 -2
  70. package/dist/failure/constraint-handler.js.map +1 -1
  71. package/dist/gates/acceptance.d.ts +9 -1
  72. package/dist/gates/acceptance.d.ts.map +1 -1
  73. package/dist/gates/acceptance.js +21 -0
  74. package/dist/gates/acceptance.js.map +1 -1
  75. package/dist/gates/checker-gate.d.ts +23 -0
  76. package/dist/gates/checker-gate.d.ts.map +1 -0
  77. package/dist/gates/checker-gate.js +53 -0
  78. package/dist/gates/checker-gate.js.map +1 -0
  79. package/dist/gates/command.d.ts +9 -2
  80. package/dist/gates/command.d.ts.map +1 -1
  81. package/dist/gates/command.js +10 -0
  82. package/dist/gates/command.js.map +1 -1
  83. package/dist/gates/contract.d.ts +8 -2
  84. package/dist/gates/contract.d.ts.map +1 -1
  85. package/dist/gates/contract.js +9 -0
  86. package/dist/gates/contract.js.map +1 -1
  87. package/dist/gates/decision.d.ts +17 -0
  88. package/dist/gates/decision.d.ts.map +1 -0
  89. package/dist/gates/decision.js +25 -0
  90. package/dist/gates/decision.js.map +1 -0
  91. package/dist/gates/definitions.d.ts +63 -0
  92. package/dist/gates/definitions.d.ts.map +1 -0
  93. package/dist/gates/definitions.js +160 -0
  94. package/dist/gates/definitions.js.map +1 -0
  95. package/dist/gates/effective-gates.d.ts +30 -0
  96. package/dist/gates/effective-gates.d.ts.map +1 -0
  97. package/dist/gates/effective-gates.js +85 -0
  98. package/dist/gates/effective-gates.js.map +1 -0
  99. package/dist/gates/index.d.ts +25 -1
  100. package/dist/gates/index.d.ts.map +1 -1
  101. package/dist/gates/index.js +37 -1
  102. package/dist/gates/index.js.map +1 -1
  103. package/dist/gates/performance.d.ts +8 -2
  104. package/dist/gates/performance.d.ts.map +1 -1
  105. package/dist/gates/performance.js +9 -0
  106. package/dist/gates/performance.js.map +1 -1
  107. package/dist/gates/registry.d.ts +32 -0
  108. package/dist/gates/registry.d.ts.map +1 -0
  109. package/dist/gates/registry.js +90 -0
  110. package/dist/gates/registry.js.map +1 -0
  111. package/dist/gates/review.d.ts +8 -2
  112. package/dist/gates/review.d.ts.map +1 -1
  113. package/dist/gates/review.js +9 -0
  114. package/dist/gates/review.js.map +1 -1
  115. package/dist/gates/runner.d.ts +30 -0
  116. package/dist/gates/runner.d.ts.map +1 -0
  117. package/dist/gates/runner.js +38 -0
  118. package/dist/gates/runner.js.map +1 -0
  119. package/dist/gates/security.d.ts +8 -2
  120. package/dist/gates/security.d.ts.map +1 -1
  121. package/dist/gates/security.js +9 -0
  122. package/dist/gates/security.js.map +1 -1
  123. package/dist/gates/types.d.ts +41 -1
  124. package/dist/gates/types.d.ts.map +1 -1
  125. package/dist/hooks/bootstrap.d.ts.map +1 -1
  126. package/dist/hooks/bootstrap.js +0 -2
  127. package/dist/hooks/bootstrap.js.map +1 -1
  128. package/dist/hooks/config.d.ts +26 -0
  129. package/dist/hooks/config.d.ts.map +1 -0
  130. package/dist/hooks/config.js +30 -0
  131. package/dist/hooks/config.js.map +1 -0
  132. package/dist/hooks/index.d.ts +2 -1
  133. package/dist/hooks/index.d.ts.map +1 -1
  134. package/dist/hooks/index.js +4 -1
  135. package/dist/hooks/index.js.map +1 -1
  136. package/dist/hooks/registry.d.ts +18 -1
  137. package/dist/hooks/registry.d.ts.map +1 -1
  138. package/dist/hooks/registry.js +45 -0
  139. package/dist/hooks/registry.js.map +1 -1
  140. package/dist/hooks/types.d.ts +20 -0
  141. package/dist/hooks/types.d.ts.map +1 -1
  142. package/dist/index.d.ts +4 -8
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +5 -20
  145. package/dist/index.js.map +1 -1
  146. package/dist/knowledge/import.d.ts +2 -2
  147. package/dist/knowledge/import.d.ts.map +1 -1
  148. package/dist/knowledge/ingest.js.map +1 -1
  149. package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
  150. package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
  151. package/dist/knowledge/types.d.ts +5 -7
  152. package/dist/knowledge/types.d.ts.map +1 -1
  153. package/dist/knowledge/types.js +1 -1
  154. package/dist/knowledge/types.js.map +1 -1
  155. package/dist/monitoring/constraint-doctor.d.ts +4 -22
  156. package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
  157. package/dist/monitoring/constraint-doctor.js +7 -94
  158. package/dist/monitoring/constraint-doctor.js.map +1 -1
  159. package/dist/presets/standard.d.ts +1 -1
  160. package/dist/presets/standard.d.ts.map +1 -1
  161. package/dist/presets/standard.js +1 -2
  162. package/dist/presets/standard.js.map +1 -1
  163. package/dist/spec/annotation-checker.d.ts.map +1 -1
  164. package/dist/spec/annotation-checker.js +6 -26
  165. package/dist/spec/annotation-checker.js.map +1 -1
  166. package/dist/tools/index.d.ts +0 -4
  167. package/dist/tools/index.d.ts.map +1 -1
  168. package/dist/tools/index.js +0 -4
  169. package/dist/tools/index.js.map +1 -1
  170. package/dist/types/constraint.d.ts +1 -12
  171. package/dist/types/constraint.d.ts.map +1 -1
  172. package/dist/types/constraint.js +0 -3
  173. package/dist/types/constraint.js.map +1 -1
  174. package/dist/types/project-config.d.ts +0 -2
  175. package/dist/types/project-config.d.ts.map +1 -1
  176. package/dist/utils/file-walk.d.ts +2 -2
  177. package/dist/utils/file-walk.js +2 -2
  178. package/package.json +6 -1
  179. package/src/CONTEXT.md +14 -0
  180. package/src/__tests__/checker-extra.test.ts +2 -59
  181. package/src/__tests__/constraint-doctor.test.ts +1 -140
  182. package/src/__tests__/constraint-handler.test.ts +0 -4
  183. package/src/__tests__/project-config-loader-extra.test.ts +0 -20
  184. package/src/__tests__/project-config-loader.test.ts +0 -1
  185. package/src/cli/commands/CONTEXT.md +15 -7
  186. package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
  187. package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
  188. package/src/cli/commands/__tests__/check.test.ts +7 -75
  189. package/src/cli/commands/__tests__/registry.test.ts +201 -0
  190. package/src/cli/commands/check.ts +2 -14
  191. package/src/cli/commands/definitions.ts +593 -0
  192. package/src/cli/commands/knowledge.ts +3 -3
  193. package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
  194. package/src/cli/commands/sync-docs/index.ts +8 -5
  195. package/src/context/CONTEXT.md +2 -7
  196. package/src/context/__tests__/session-manager.test.ts +0 -91
  197. package/src/context/index.ts +1 -5
  198. package/src/context/knowledge-injector.ts +3 -3
  199. package/src/context/session-manager.ts +0 -74
  200. package/src/context/types.ts +2 -63
  201. package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
  202. package/src/core/constraints/agent-prompt-renderer.ts +1 -1
  203. package/src/core/constraints/capabilities-parser.ts +95 -2
  204. package/src/core/constraints/checker.ts +8 -53
  205. package/src/core/constraints/definitions.ts +0 -8
  206. package/src/core/constraints/doc-freshness/runner.ts +1 -2
  207. package/src/core/constraints/index.ts +1 -2
  208. package/src/core/constraints/interceptor.ts +1 -6
  209. package/src/core/effective-constraints.ts +0 -1
  210. package/src/core/project-config-loader.ts +1 -7
  211. package/src/dashboard/stats.ts +2 -2
  212. package/src/dashboard/types.ts +2 -2
  213. package/src/failure/constraint-handler.ts +0 -2
  214. package/src/gates/CONTEXT.md +16 -13
  215. package/src/gates/__tests__/checker-gate.test.ts +45 -0
  216. package/src/gates/__tests__/contract.test.ts +73 -0
  217. package/src/gates/__tests__/effective-gates.test.ts +82 -0
  218. package/src/gates/__tests__/gate-interface.test.ts +202 -0
  219. package/src/gates/__tests__/registry.test.ts +87 -0
  220. package/src/gates/__tests__/runner.test.ts +81 -0
  221. package/src/gates/acceptance.ts +24 -1
  222. package/src/gates/checker-gate.ts +54 -0
  223. package/src/gates/command.ts +13 -2
  224. package/src/gates/contract.ts +12 -2
  225. package/src/gates/decision.ts +27 -0
  226. package/src/gates/definitions.ts +207 -0
  227. package/src/gates/effective-gates.ts +106 -0
  228. package/src/gates/index.ts +38 -0
  229. package/src/gates/performance.ts +12 -2
  230. package/src/gates/registry.ts +107 -0
  231. package/src/gates/review.ts +12 -2
  232. package/src/gates/runner.ts +57 -0
  233. package/src/gates/security.ts +12 -2
  234. package/src/gates/types.ts +48 -1
  235. package/src/hooks/CONTEXT.md +12 -2
  236. package/src/hooks/__tests__/bootstrap.test.ts +0 -1
  237. package/src/hooks/__tests__/config.test.ts +19 -0
  238. package/src/hooks/__tests__/pipeline.test.ts +79 -0
  239. package/src/hooks/__tests__/registry.test.ts +67 -0
  240. package/src/hooks/bootstrap.ts +0 -2
  241. package/src/hooks/config.ts +29 -0
  242. package/src/hooks/index.ts +2 -1
  243. package/src/hooks/registry.ts +60 -1
  244. package/src/hooks/types.ts +21 -0
  245. package/src/index.ts +5 -22
  246. package/src/knowledge/import.ts +3 -3
  247. package/src/knowledge/ingest.ts +4 -4
  248. package/src/knowledge/lifecycle-hooks.ts +3 -3
  249. package/src/knowledge/types.ts +5 -8
  250. package/src/monitoring/constraint-doctor.ts +7 -106
  251. package/src/presets/__tests__/standard.test.ts +0 -2
  252. package/src/presets/standard.ts +1 -2
  253. package/src/spec/__tests__/annotation-checker.test.ts +9 -3
  254. package/src/spec/annotation-checker.ts +7 -29
  255. package/src/tools/CONTEXT.md +7 -14
  256. package/src/tools/index.ts +0 -4
  257. package/src/types/constraint.ts +1 -14
  258. package/src/types/project-config.ts +0 -3
  259. package/src/utils/file-walk.ts +2 -2
  260. package/dist/architecture/constraint-engine.d.ts +0 -67
  261. package/dist/architecture/constraint-engine.d.ts.map +0 -1
  262. package/dist/architecture/constraint-engine.js +0 -295
  263. package/dist/architecture/constraint-engine.js.map +0 -1
  264. package/dist/architecture/cross-project-checker.d.ts +0 -54
  265. package/dist/architecture/cross-project-checker.d.ts.map +0 -1
  266. package/dist/architecture/cross-project-checker.js +0 -467
  267. package/dist/architecture/cross-project-checker.js.map +0 -1
  268. package/dist/cli/commands/index.d.ts +0 -30
  269. package/dist/cli/commands/index.d.ts.map +0 -1
  270. package/dist/cli/commands/index.js +0 -91
  271. package/dist/cli/commands/index.js.map +0 -1
  272. package/dist/context/file-budget.d.ts +0 -40
  273. package/dist/context/file-budget.d.ts.map +0 -1
  274. package/dist/context/file-budget.js +0 -147
  275. package/dist/context/file-budget.js.map +0 -1
  276. package/dist/context/progressive-loader.d.ts +0 -109
  277. package/dist/context/progressive-loader.d.ts.map +0 -1
  278. package/dist/context/progressive-loader.js +0 -166
  279. package/dist/context/progressive-loader.js.map +0 -1
  280. package/dist/context/token-pipeline.d.ts +0 -55
  281. package/dist/context/token-pipeline.d.ts.map +0 -1
  282. package/dist/context/token-pipeline.js +0 -220
  283. package/dist/context/token-pipeline.js.map +0 -1
  284. package/dist/context/tool-output-budget.d.ts +0 -59
  285. package/dist/context/tool-output-budget.d.ts.map +0 -1
  286. package/dist/context/tool-output-budget.js +0 -177
  287. package/dist/context/tool-output-budget.js.map +0 -1
  288. package/dist/core/constraints/default-executors.d.ts +0 -10
  289. package/dist/core/constraints/default-executors.d.ts.map +0 -1
  290. package/dist/core/constraints/default-executors.js +0 -120
  291. package/dist/core/constraints/default-executors.js.map +0 -1
  292. package/dist/llm/adapter.d.ts +0 -36
  293. package/dist/llm/adapter.d.ts.map +0 -1
  294. package/dist/llm/adapter.js +0 -118
  295. package/dist/llm/adapter.js.map +0 -1
  296. package/dist/llm/index.d.ts +0 -3
  297. package/dist/llm/index.d.ts.map +0 -1
  298. package/dist/llm/index.js +0 -19
  299. package/dist/llm/index.js.map +0 -1
  300. package/dist/llm/types.d.ts +0 -62
  301. package/dist/llm/types.d.ts.map +0 -1
  302. package/dist/llm/types.js +0 -8
  303. package/dist/llm/types.js.map +0 -1
  304. package/dist/tools/core/index.d.ts +0 -13
  305. package/dist/tools/core/index.d.ts.map +0 -1
  306. package/dist/tools/core/index.js +0 -198
  307. package/dist/tools/core/index.js.map +0 -1
  308. package/dist/tools/loader.d.ts +0 -21
  309. package/dist/tools/loader.d.ts.map +0 -1
  310. package/dist/tools/loader.js +0 -199
  311. package/dist/tools/loader.js.map +0 -1
  312. package/dist/tools/registry.d.ts +0 -59
  313. package/dist/tools/registry.d.ts.map +0 -1
  314. package/dist/tools/registry.js +0 -115
  315. package/dist/tools/registry.js.map +0 -1
  316. package/dist/tools/types.d.ts +0 -38
  317. package/dist/tools/types.d.ts.map +0 -1
  318. package/dist/tools/types.js +0 -6
  319. package/dist/tools/types.js.map +0 -1
  320. package/src/__tests__/architecture-engine.test.ts +0 -619
  321. package/src/__tests__/context/progressive-loader.test.ts +0 -131
  322. package/src/__tests__/cross-project-checker.test.ts +0 -318
  323. package/src/architecture/CONTEXT.md +0 -22
  324. package/src/architecture/constraint-engine.ts +0 -351
  325. package/src/architecture/cross-project-checker.ts +0 -545
  326. package/src/cli/commands/index.ts +0 -30
  327. package/src/context/__tests__/file-budget.test.ts +0 -142
  328. package/src/context/__tests__/progressive-loader.test.ts +0 -264
  329. package/src/context/__tests__/token-pipeline.test.ts +0 -178
  330. package/src/context/__tests__/tool-output-budget.test.ts +0 -95
  331. package/src/context/file-budget.ts +0 -142
  332. package/src/context/progressive-loader.ts +0 -260
  333. package/src/context/token-pipeline.ts +0 -265
  334. package/src/context/tool-output-budget.ts +0 -178
  335. package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
  336. package/src/core/constraints/default-executors.ts +0 -92
  337. package/src/llm/CONTEXT.md +0 -21
  338. package/src/llm/__tests__/adapter.test.ts +0 -172
  339. package/src/llm/adapter.ts +0 -149
  340. package/src/llm/index.ts +0 -2
  341. package/src/llm/types.ts +0 -81
  342. package/src/tools/__tests__/loader.test.ts +0 -252
  343. package/src/tools/__tests__/registry.test.ts +0 -157
  344. package/src/tools/core/__tests__/core.test.ts +0 -122
  345. package/src/tools/core/index.ts +0 -201
  346. package/src/tools/loader.ts +0 -216
  347. package/src/tools/registry.ts +0 -128
  348. 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
- }