@dommaker/harness 1.3.0 → 1.5.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 (532) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +0 -2
  3. package/bin/harness.js +86 -32
  4. package/dist/cli/command-contract.d.ts +83 -0
  5. package/dist/cli/command-contract.d.ts.map +1 -0
  6. package/dist/cli/command-contract.js +66 -0
  7. package/dist/cli/command-contract.js.map +1 -0
  8. package/dist/cli/commands/acceptance.d.ts +3 -2
  9. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  10. package/dist/cli/commands/acceptance.js +28 -24
  11. package/dist/cli/commands/acceptance.js.map +1 -1
  12. package/dist/cli/commands/analyze-sessions.d.ts +2 -1
  13. package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
  14. package/dist/cli/commands/analyze-sessions.js +25 -24
  15. package/dist/cli/commands/analyze-sessions.js.map +1 -1
  16. package/dist/cli/commands/check.d.ts +14 -2
  17. package/dist/cli/commands/check.d.ts.map +1 -1
  18. package/dist/cli/commands/check.js +104 -61
  19. package/dist/cli/commands/check.js.map +1 -1
  20. package/dist/cli/commands/command.d.ts +5 -1
  21. package/dist/cli/commands/command.d.ts.map +1 -1
  22. package/dist/cli/commands/command.js +26 -17
  23. package/dist/cli/commands/command.js.map +1 -1
  24. package/dist/cli/commands/constraints-report.d.ts +2 -1
  25. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  26. package/dist/cli/commands/constraints-report.js +37 -33
  27. package/dist/cli/commands/constraints-report.js.map +1 -1
  28. package/dist/cli/commands/constraints-retire.d.ts +6 -5
  29. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  30. package/dist/cli/commands/constraints-retire.js +50 -52
  31. package/dist/cli/commands/constraints-retire.js.map +1 -1
  32. package/dist/cli/commands/constraints.d.ts +2 -1
  33. package/dist/cli/commands/constraints.d.ts.map +1 -1
  34. package/dist/cli/commands/constraints.js +10 -27
  35. package/dist/cli/commands/constraints.js.map +1 -1
  36. package/dist/cli/commands/contract.d.ts +3 -2
  37. package/dist/cli/commands/contract.d.ts.map +1 -1
  38. package/dist/cli/commands/contract.js +45 -42
  39. package/dist/cli/commands/contract.js.map +1 -1
  40. package/dist/cli/commands/definitions.d.ts +11 -11
  41. package/dist/cli/commands/definitions.d.ts.map +1 -1
  42. package/dist/cli/commands/definitions.js +27 -219
  43. package/dist/cli/commands/definitions.js.map +1 -1
  44. package/dist/cli/commands/failure.d.ts +5 -4
  45. package/dist/cli/commands/failure.d.ts.map +1 -1
  46. package/dist/cli/commands/failure.js +31 -25
  47. package/dist/cli/commands/failure.js.map +1 -1
  48. package/dist/cli/commands/init.d.ts +7 -5
  49. package/dist/cli/commands/init.d.ts.map +1 -1
  50. package/dist/cli/commands/init.js +147 -211
  51. package/dist/cli/commands/init.js.map +1 -1
  52. package/dist/cli/commands/knowledge.d.ts +17 -30
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +183 -273
  55. package/dist/cli/commands/knowledge.js.map +1 -1
  56. package/dist/cli/commands/passes-gate.d.ts +8 -2
  57. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  58. package/dist/cli/commands/passes-gate.js +44 -66
  59. package/dist/cli/commands/passes-gate.js.map +1 -1
  60. package/dist/cli/commands/performance.d.ts +2 -1
  61. package/dist/cli/commands/performance.d.ts.map +1 -1
  62. package/dist/cli/commands/performance.js +20 -18
  63. package/dist/cli/commands/performance.js.map +1 -1
  64. package/dist/cli/commands/posteval-plan.d.ts +5 -1
  65. package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
  66. package/dist/cli/commands/posteval-plan.js +26 -15
  67. package/dist/cli/commands/posteval-plan.js.map +1 -1
  68. package/dist/cli/commands/release.d.ts +6 -1
  69. package/dist/cli/commands/release.d.ts.map +1 -1
  70. package/dist/cli/commands/release.js +69 -59
  71. package/dist/cli/commands/release.js.map +1 -1
  72. package/dist/cli/commands/report.d.ts +2 -1
  73. package/dist/cli/commands/report.d.ts.map +1 -1
  74. package/dist/cli/commands/report.js +9 -5
  75. package/dist/cli/commands/report.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +3 -2
  77. package/dist/cli/commands/review.d.ts.map +1 -1
  78. package/dist/cli/commands/review.js +33 -29
  79. package/dist/cli/commands/review.js.map +1 -1
  80. package/dist/cli/commands/sdd.d.ts +2 -1
  81. package/dist/cli/commands/sdd.d.ts.map +1 -1
  82. package/dist/cli/commands/sdd.js +7 -5
  83. package/dist/cli/commands/sdd.js.map +1 -1
  84. package/dist/cli/commands/security.d.ts +3 -2
  85. package/dist/cli/commands/security.d.ts.map +1 -1
  86. package/dist/cli/commands/security.js +29 -26
  87. package/dist/cli/commands/security.js.map +1 -1
  88. package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
  89. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  90. package/dist/cli/commands/spec-baseline-check.js +15 -11
  91. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  92. package/dist/cli/commands/spec.d.ts +7 -2
  93. package/dist/cli/commands/spec.d.ts.map +1 -1
  94. package/dist/cli/commands/spec.js +61 -51
  95. package/dist/cli/commands/spec.js.map +1 -1
  96. package/dist/cli/commands/status.d.ts +2 -1
  97. package/dist/cli/commands/status.d.ts.map +1 -1
  98. package/dist/cli/commands/status.js +57 -53
  99. package/dist/cli/commands/status.js.map +1 -1
  100. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  101. package/dist/cli/commands/sync-docs/agents-syncer.js +5 -2
  102. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  103. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  104. package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
  105. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  106. package/dist/cli/commands/sync-docs/index.d.ts +2 -1
  107. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  108. package/dist/cli/commands/sync-docs/index.js +68 -88
  109. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  110. package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
  111. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  112. package/dist/cli/commands/sync-docs/project-reader.js +14 -16
  113. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  114. package/dist/cli/commands/update-user-model.d.ts +9 -3
  115. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  116. package/dist/cli/commands/update-user-model.js +56 -61
  117. package/dist/cli/commands/update-user-model.js.map +1 -1
  118. package/dist/cli/commands/validate.d.ts +4 -3
  119. package/dist/cli/commands/validate.d.ts.map +1 -1
  120. package/dist/cli/commands/validate.js +28 -26
  121. package/dist/cli/commands/validate.js.map +1 -1
  122. package/dist/cli/session-mining/transcript.d.ts +7 -1
  123. package/dist/cli/session-mining/transcript.d.ts.map +1 -1
  124. package/dist/cli/session-mining/transcript.js +21 -14
  125. package/dist/cli/session-mining/transcript.js.map +1 -1
  126. package/dist/context/session-manager.d.ts.map +1 -1
  127. package/dist/context/session-manager.js +19 -32
  128. package/dist/context/session-manager.js.map +1 -1
  129. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  130. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  131. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  132. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  133. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  134. package/dist/core/constraints/capabilities-parser.js +32 -28
  135. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  136. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  137. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  138. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  139. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  140. package/dist/core/constraints/checker.d.ts +21 -43
  141. package/dist/core/constraints/checker.d.ts.map +1 -1
  142. package/dist/core/constraints/checker.js +68 -117
  143. package/dist/core/constraints/checker.js.map +1 -1
  144. package/dist/core/constraints/checkers/capability-sync.d.ts +20 -8
  145. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  146. package/dist/core/constraints/checkers/capability-sync.js +66 -77
  147. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  148. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  149. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  150. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  151. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  152. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  153. package/dist/core/constraints/checkers/docs-freshness.js +49 -66
  154. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  155. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  156. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  158. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  159. package/dist/core/constraints/checkers/index.d.ts +2 -2
  160. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  161. package/dist/core/constraints/checkers/index.js +2 -1
  162. package/dist/core/constraints/checkers/index.js.map +1 -1
  163. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  164. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  165. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  166. package/dist/core/constraints/checkers/types.d.ts +43 -4
  167. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  168. package/dist/core/constraints/checkers/types.js +33 -0
  169. package/dist/core/constraints/checkers/types.js.map +1 -1
  170. package/dist/core/constraints/context-builder.d.ts +15 -14
  171. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  172. package/dist/core/constraints/context-builder.js +32 -96
  173. package/dist/core/constraints/context-builder.js.map +1 -1
  174. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  175. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  176. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  177. package/dist/core/constraints/git-evidence.d.ts +60 -0
  178. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  179. package/dist/core/constraints/git-evidence.js +88 -0
  180. package/dist/core/constraints/git-evidence.js.map +1 -0
  181. package/dist/core/constraints/injection-drift.d.ts +3 -20
  182. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  183. package/dist/core/constraints/injection-drift.js +10 -57
  184. package/dist/core/constraints/injection-drift.js.map +1 -1
  185. package/dist/core/constraints/injection-writer.d.ts +113 -0
  186. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  187. package/dist/core/constraints/injection-writer.js +228 -0
  188. package/dist/core/constraints/injection-writer.js.map +1 -0
  189. package/dist/core/constraints/usage-report.d.ts +22 -1
  190. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  191. package/dist/core/constraints/usage-report.js +27 -20
  192. package/dist/core/constraints/usage-report.js.map +1 -1
  193. package/dist/core/index.d.ts +2 -2
  194. package/dist/core/index.d.ts.map +1 -1
  195. package/dist/core/index.js +4 -2
  196. package/dist/core/index.js.map +1 -1
  197. package/dist/core/project-config-loader.d.ts +32 -1
  198. package/dist/core/project-config-loader.d.ts.map +1 -1
  199. package/dist/core/project-config-loader.js +39 -9
  200. package/dist/core/project-config-loader.js.map +1 -1
  201. package/dist/core/validators/passes-gate.d.ts +15 -64
  202. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  203. package/dist/core/validators/passes-gate.js +84 -222
  204. package/dist/core/validators/passes-gate.js.map +1 -1
  205. package/dist/core/validators/test-output.d.ts +58 -0
  206. package/dist/core/validators/test-output.d.ts.map +1 -0
  207. package/dist/core/validators/test-output.js +109 -0
  208. package/dist/core/validators/test-output.js.map +1 -0
  209. package/dist/failure/recorder.d.ts +6 -0
  210. package/dist/failure/recorder.d.ts.map +1 -1
  211. package/dist/failure/recorder.js +12 -8
  212. package/dist/failure/recorder.js.map +1 -1
  213. package/dist/gates/acceptance.d.ts +1 -48
  214. package/dist/gates/acceptance.d.ts.map +1 -1
  215. package/dist/gates/acceptance.js +6 -38
  216. package/dist/gates/acceptance.js.map +1 -1
  217. package/dist/gates/checker-gate.d.ts +2 -1
  218. package/dist/gates/checker-gate.d.ts.map +1 -1
  219. package/dist/gates/checker-gate.js +11 -13
  220. package/dist/gates/checker-gate.js.map +1 -1
  221. package/dist/gates/command.d.ts.map +1 -1
  222. package/dist/gates/command.js +10 -22
  223. package/dist/gates/command.js.map +1 -1
  224. package/dist/gates/contract.d.ts.map +1 -1
  225. package/dist/gates/contract.js +22 -61
  226. package/dist/gates/contract.js.map +1 -1
  227. package/dist/gates/performance.d.ts.map +1 -1
  228. package/dist/gates/performance.js +13 -31
  229. package/dist/gates/performance.js.map +1 -1
  230. package/dist/gates/review.d.ts.map +1 -1
  231. package/dist/gates/review.js +21 -53
  232. package/dist/gates/review.js.map +1 -1
  233. package/dist/gates/security.d.ts.map +1 -1
  234. package/dist/gates/security.js +14 -32
  235. package/dist/gates/security.js.map +1 -1
  236. package/dist/gates/types.d.ts +28 -5
  237. package/dist/gates/types.d.ts.map +1 -1
  238. package/dist/gates/types.js +38 -1
  239. package/dist/gates/types.js.map +1 -1
  240. package/dist/hooks/bootstrap.d.ts +2 -0
  241. package/dist/hooks/bootstrap.d.ts.map +1 -1
  242. package/dist/hooks/bootstrap.js +5 -2
  243. package/dist/hooks/bootstrap.js.map +1 -1
  244. package/dist/index.d.ts +1 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js.map +1 -1
  247. package/dist/knowledge/audit.d.ts +7 -2
  248. package/dist/knowledge/audit.d.ts.map +1 -1
  249. package/dist/knowledge/audit.js +59 -79
  250. package/dist/knowledge/audit.js.map +1 -1
  251. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  252. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  253. package/dist/knowledge/flywheel-metrics.js +47 -0
  254. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  255. package/dist/knowledge/import.d.ts.map +1 -1
  256. package/dist/knowledge/import.js +4 -14
  257. package/dist/knowledge/import.js.map +1 -1
  258. package/dist/knowledge/index-generator.d.ts.map +1 -1
  259. package/dist/knowledge/index-generator.js +17 -14
  260. package/dist/knowledge/index-generator.js.map +1 -1
  261. package/dist/knowledge/ingest.d.ts.map +1 -1
  262. package/dist/knowledge/ingest.js +9 -4
  263. package/dist/knowledge/ingest.js.map +1 -1
  264. package/dist/knowledge/migration.d.ts.map +1 -1
  265. package/dist/knowledge/migration.js +11 -8
  266. package/dist/knowledge/migration.js.map +1 -1
  267. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  268. package/dist/knowledge/reference-tracker.js +12 -9
  269. package/dist/knowledge/reference-tracker.js.map +1 -1
  270. package/dist/knowledge/store.d.ts +15 -0
  271. package/dist/knowledge/store.d.ts.map +1 -1
  272. package/dist/knowledge/store.js +59 -10
  273. package/dist/knowledge/store.js.map +1 -1
  274. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  275. package/dist/monitoring/context-tracker.js +11 -20
  276. package/dist/monitoring/context-tracker.js.map +1 -1
  277. package/dist/monitoring/index.d.ts +1 -1
  278. package/dist/monitoring/index.d.ts.map +1 -1
  279. package/dist/monitoring/index.js +1 -2
  280. package/dist/monitoring/index.js.map +1 -1
  281. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  282. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  283. package/dist/monitoring/trace-analyzer.js +19 -2
  284. package/dist/monitoring/trace-analyzer.js.map +1 -1
  285. package/dist/monitoring/traces.d.ts +21 -2
  286. package/dist/monitoring/traces.d.ts.map +1 -1
  287. package/dist/monitoring/traces.js +38 -29
  288. package/dist/monitoring/traces.js.map +1 -1
  289. package/dist/release/index.d.ts +2 -2
  290. package/dist/release/index.d.ts.map +1 -1
  291. package/dist/release/index.js +1 -5
  292. package/dist/release/index.js.map +1 -1
  293. package/dist/sdd/index-generator.js +10 -11
  294. package/dist/sdd/index-generator.js.map +1 -1
  295. package/dist/test-setup/project-fixture.d.ts +48 -0
  296. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  297. package/dist/test-setup/project-fixture.js +106 -0
  298. package/dist/test-setup/project-fixture.js.map +1 -0
  299. package/dist/types/constraint.d.ts +8 -18
  300. package/dist/types/constraint.d.ts.map +1 -1
  301. package/dist/types/constraint.js +6 -1
  302. package/dist/types/constraint.js.map +1 -1
  303. package/dist/types/passes-gate.d.ts +10 -25
  304. package/dist/types/passes-gate.d.ts.map +1 -1
  305. package/dist/types/project-config.d.ts +1 -1
  306. package/dist/types/session.d.ts +6 -5
  307. package/dist/types/session.d.ts.map +1 -1
  308. package/dist/types/trace.d.ts +9 -0
  309. package/dist/types/trace.d.ts.map +1 -1
  310. package/dist/utils/exec.d.ts +10 -0
  311. package/dist/utils/exec.d.ts.map +1 -1
  312. package/dist/utils/exec.js +12 -0
  313. package/dist/utils/exec.js.map +1 -1
  314. package/dist/utils/file-walk.d.ts +9 -1
  315. package/dist/utils/file-walk.d.ts.map +1 -1
  316. package/dist/utils/file-walk.js +10 -1
  317. package/dist/utils/file-walk.js.map +1 -1
  318. package/dist/utils/frontmatter.d.ts +73 -0
  319. package/dist/utils/frontmatter.d.ts.map +1 -0
  320. package/dist/utils/frontmatter.js +141 -0
  321. package/dist/utils/frontmatter.js.map +1 -0
  322. package/dist/utils/jsonl.d.ts +92 -0
  323. package/dist/utils/jsonl.d.ts.map +1 -0
  324. package/dist/utils/jsonl.js +166 -0
  325. package/dist/utils/jsonl.js.map +1 -0
  326. package/dist/utils/package-version.d.ts +16 -0
  327. package/dist/utils/package-version.d.ts.map +1 -0
  328. package/dist/utils/package-version.js +63 -0
  329. package/dist/utils/package-version.js.map +1 -0
  330. package/package.json +1 -1
  331. package/src/CONTEXT.md +8 -2
  332. package/src/__tests__/acceptance-gate.test.ts +3 -1
  333. package/src/__tests__/checker-extra.test.ts +7 -1218
  334. package/src/__tests__/context-files-resolution.test.ts +116 -0
  335. package/src/__tests__/effective-constraints.test.ts +10 -13
  336. package/src/__tests__/exec.test.ts +35 -1
  337. package/src/__tests__/layering.test.ts +152 -0
  338. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  339. package/src/__tests__/passes-gate.test.ts +130 -101
  340. package/src/__tests__/skip-semantics.test.ts +5 -9
  341. package/src/cli/__tests__/command-contract.test.ts +170 -0
  342. package/src/cli/command-contract.ts +108 -0
  343. package/src/cli/commands/CONTEXT.md +23 -8
  344. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  345. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  346. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +107 -0
  347. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  348. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  349. package/src/cli/commands/__tests__/check.test.ts +448 -378
  350. package/src/cli/commands/__tests__/command.test.ts +85 -70
  351. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  352. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  353. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  354. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  355. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  356. package/src/cli/commands/__tests__/init.test.ts +77 -48
  357. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  358. package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
  359. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  360. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  361. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  362. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  363. package/src/cli/commands/__tests__/project-path-convention.test.ts +231 -0
  364. package/src/cli/commands/__tests__/registry.test.ts +75 -35
  365. package/src/cli/commands/__tests__/release.test.ts +209 -80
  366. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  367. package/src/cli/commands/__tests__/report.test.ts +25 -25
  368. package/src/cli/commands/__tests__/review.test.ts +33 -31
  369. package/src/cli/commands/__tests__/security.test.ts +23 -27
  370. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  371. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  372. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  373. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  374. package/src/cli/commands/__tests__/status.test.ts +34 -34
  375. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
  376. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  377. package/src/cli/commands/__tests__/update-user-model.test.ts +97 -34
  378. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  379. package/src/cli/commands/acceptance.ts +35 -24
  380. package/src/cli/commands/analyze-sessions.ts +25 -24
  381. package/src/cli/commands/check.ts +127 -66
  382. package/src/cli/commands/command.ts +30 -17
  383. package/src/cli/commands/constraints-report.ts +37 -34
  384. package/src/cli/commands/constraints-retire.ts +58 -55
  385. package/src/cli/commands/constraints.ts +10 -27
  386. package/src/cli/commands/contract.ts +51 -42
  387. package/src/cli/commands/definitions.ts +34 -227
  388. package/src/cli/commands/failure.ts +33 -26
  389. package/src/cli/commands/init.ts +166 -220
  390. package/src/cli/commands/knowledge.ts +192 -289
  391. package/src/cli/commands/passes-gate.ts +58 -68
  392. package/src/cli/commands/performance.ts +23 -18
  393. package/src/cli/commands/posteval-plan.ts +29 -15
  394. package/src/cli/commands/release.ts +70 -59
  395. package/src/cli/commands/report.ts +9 -5
  396. package/src/cli/commands/review.ts +39 -29
  397. package/src/cli/commands/sdd.ts +7 -5
  398. package/src/cli/commands/security.ts +35 -26
  399. package/src/cli/commands/spec-baseline-check.ts +16 -11
  400. package/src/cli/commands/spec.ts +66 -53
  401. package/src/cli/commands/status.ts +59 -53
  402. package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
  403. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  404. package/src/cli/commands/sync-docs/index.ts +73 -100
  405. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  406. package/src/cli/commands/update-user-model.ts +64 -61
  407. package/src/cli/commands/validate.ts +31 -25
  408. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  409. package/src/cli/session-mining/transcript.ts +26 -8
  410. package/src/context/session-manager.ts +19 -33
  411. package/src/core/CONTEXT.md +10 -6
  412. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  413. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  414. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  415. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  416. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  417. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  418. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  419. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  420. package/src/core/constraints/capabilities-parser.ts +53 -35
  421. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  422. package/src/core/constraints/checker.ts +82 -133
  423. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +273 -0
  424. package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
  425. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  426. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +126 -0
  427. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  428. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  429. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  430. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  431. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  432. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +153 -0
  433. package/src/core/constraints/checkers/capability-sync.ts +75 -96
  434. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  435. package/src/core/constraints/checkers/docs-freshness.ts +54 -70
  436. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  437. package/src/core/constraints/checkers/index.ts +9 -2
  438. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  439. package/src/core/constraints/checkers/types.ts +60 -4
  440. package/src/core/constraints/context-builder.ts +39 -91
  441. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  442. package/src/core/constraints/git-evidence.ts +120 -0
  443. package/src/core/constraints/injection-drift.ts +13 -60
  444. package/src/core/constraints/injection-writer.ts +212 -0
  445. package/src/core/constraints/usage-report.ts +36 -17
  446. package/src/core/index.ts +4 -2
  447. package/src/core/project-config-loader.ts +49 -9
  448. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  449. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  450. package/src/core/validators/passes-gate.ts +86 -248
  451. package/src/core/validators/test-output.ts +128 -0
  452. package/src/failure/__tests__/recorder.test.ts +168 -0
  453. package/src/failure/recorder.ts +12 -10
  454. package/src/gates/CONTEXT.md +7 -4
  455. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  456. package/src/gates/__tests__/acceptance.test.ts +4 -86
  457. package/src/gates/__tests__/checker-gate.test.ts +24 -0
  458. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  459. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  460. package/src/gates/acceptance.ts +19 -84
  461. package/src/gates/checker-gate.ts +12 -15
  462. package/src/gates/command.ts +10 -16
  463. package/src/gates/contract.ts +22 -61
  464. package/src/gates/performance.ts +10 -22
  465. package/src/gates/review.ts +21 -41
  466. package/src/gates/security.ts +10 -22
  467. package/src/gates/types.ts +70 -5
  468. package/src/hooks/CONTEXT.md +1 -1
  469. package/src/hooks/bootstrap.ts +5 -2
  470. package/src/index.ts +0 -4
  471. package/src/knowledge/CONTEXT.md +8 -3
  472. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  473. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  474. package/src/knowledge/__tests__/import.test.ts +44 -0
  475. package/src/knowledge/__tests__/index-generator.test.ts +53 -0
  476. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  477. package/src/knowledge/__tests__/migration.test.ts +47 -0
  478. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  479. package/src/knowledge/__tests__/store.test.ts +194 -0
  480. package/src/knowledge/audit.ts +67 -71
  481. package/src/knowledge/flywheel-metrics.ts +71 -0
  482. package/src/knowledge/import.ts +4 -18
  483. package/src/knowledge/index-generator.ts +19 -14
  484. package/src/knowledge/ingest.ts +9 -4
  485. package/src/knowledge/migration.ts +11 -8
  486. package/src/knowledge/reference-tracker.ts +12 -8
  487. package/src/knowledge/store.ts +64 -11
  488. package/src/monitoring/CONTEXT.md +8 -3
  489. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  490. package/src/monitoring/__tests__/traces.test.ts +57 -0
  491. package/src/monitoring/context-tracker.ts +11 -23
  492. package/src/monitoring/index.ts +1 -1
  493. package/src/monitoring/trace-analyzer.ts +21 -2
  494. package/src/monitoring/traces.ts +39 -36
  495. package/src/release/CONTEXT.md +1 -0
  496. package/src/release/__tests__/barrel.test.ts +60 -0
  497. package/src/release/index.ts +2 -13
  498. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  499. package/src/sdd/index-generator.ts +9 -9
  500. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  501. package/src/test-setup/project-fixture.ts +87 -0
  502. package/src/tools/CONTEXT.md +6 -0
  503. package/src/tools/__tests__/registry.test.ts +221 -0
  504. package/src/types/__tests__/types.test.ts +4 -5
  505. package/src/types/constraint.ts +16 -27
  506. package/src/types/passes-gate.ts +10 -30
  507. package/src/types/project-config.ts +1 -1
  508. package/src/types/session.ts +5 -5
  509. package/src/types/trace.ts +10 -0
  510. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  511. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  512. package/src/utils/__tests__/jsonl.test.ts +187 -0
  513. package/src/utils/__tests__/package-version.test.ts +97 -0
  514. package/src/utils/exec.ts +15 -0
  515. package/src/utils/file-walk.ts +11 -2
  516. package/src/utils/frontmatter.ts +121 -0
  517. package/src/utils/jsonl.ts +167 -0
  518. package/src/utils/package-version.ts +27 -0
  519. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  520. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  521. package/dist/cli/commands/doc-freshness-check.js +0 -352
  522. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  523. package/dist/types/index.d.ts +0 -14
  524. package/dist/types/index.d.ts.map +0 -1
  525. package/dist/types/index.js +0 -39
  526. package/dist/types/index.js.map +0 -1
  527. package/src/__tests__/long-running.test.ts +0 -17
  528. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  529. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  530. package/src/cli/commands/doc-freshness-check.ts +0 -386
  531. package/src/spec/CONTEXT.md +0 -11
  532. package/src/types/index.ts +0 -47
@@ -8,8 +8,9 @@ import chalk from 'chalk';
8
8
  import * as fs from 'fs/promises';
9
9
  import * as path from 'path';
10
10
  import { execAsync } from '../../utils/exec';
11
- import { PassesGate } from '../../core/validators/passes-gate';
11
+ import { PassesGate, detectTestCommand } from '../../core/validators/passes-gate';
12
12
  import type { PassesGateConfig } from '../../types/passes-gate';
13
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
13
14
 
14
15
  export interface PassesGateOptions {
15
16
  /** 测试命令 */
@@ -22,45 +23,14 @@ export interface PassesGateOptions {
22
23
  maxRetries?: number;
23
24
  }
24
25
 
25
- /**
26
- * ę£€ęµ‹é”¹ē›®ēš„ęµ‹čÆ•å‘½ä»¤
27
- */
28
- async function detectTestCommand(projectPath: string): Promise<string | undefined> {
29
- const packageJsonPath = path.join(projectPath, 'package.json');
30
-
31
- try {
32
- const content = await fs.readFile(packageJsonPath, 'utf-8');
33
- const pkg = JSON.parse(content);
34
-
35
- if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified"') {
36
- return 'npm test';
37
- }
38
- if (pkg.scripts?.['test:ci']) {
39
- return 'npm run test:ci';
40
- }
41
- } catch {
42
- // ę²”ęœ‰ package.json
43
- }
44
-
45
- // ę£€ęŸ„å…¶ä»–é”¹ē›®ē±»åž‹
46
- try {
47
- await fs.access(path.join(projectPath, 'pytest.ini'));
48
- return 'pytest';
49
- } catch {}
50
-
51
- try {
52
- await fs.access(path.join(projectPath, 'go.mod'));
53
- return 'go test ./...';
54
- } catch {}
55
-
56
- return undefined;
57
- }
58
-
59
26
  /**
60
27
  * ę‰§č”Œęµ‹čÆ•é—ØęŽ§
61
28
  */
62
- export async function runPassesGate(options: PassesGateOptions): Promise<void> {
63
- console.log(chalk.blue('🚦 čæč”Œęµ‹čÆ•é—ØęŽ§...'));
29
+ export async function runPassesGate(
30
+ options: PassesGateOptions,
31
+ io: CommandIO = processIO,
32
+ ): Promise<CommandResult> {
33
+ log(io, chalk.blue('🚦 čæč”Œęµ‹čÆ•é—ØęŽ§...'));
64
34
 
65
35
  const projectPath = options.projectPath || process.cwd();
66
36
 
@@ -71,12 +41,12 @@ export async function runPassesGate(options: PassesGateOptions): Promise<void> {
71
41
  }
72
42
 
73
43
  if (!testCommand) {
74
- console.log(chalk.yellow('āš ļø ęœŖę£€ęµ‹åˆ°ęµ‹čÆ•å‘½ä»¤ļ¼Œč·³čæ‡ęµ‹čÆ•é—ØęŽ§'));
75
- console.log(chalk.gray('ęē¤ŗ: 使用 --test-command ęŒ‡å®šęµ‹čÆ•å‘½ä»¤'));
76
- return;
44
+ log(io, chalk.yellow('āš ļø ęœŖę£€ęµ‹åˆ°ęµ‹čÆ•å‘½ä»¤ļ¼Œč·³čæ‡ęµ‹čÆ•é—ØęŽ§'));
45
+ log(io, chalk.gray('ęē¤ŗ: 使用 --test-command ęŒ‡å®šęµ‹čÆ•å‘½ä»¤'));
46
+ return { kind: 'skip', reason: 'ęœŖę£€ęµ‹åˆ°ęµ‹čÆ•å‘½ä»¤' };
77
47
  }
78
48
 
79
- console.log(chalk.gray(`测试命令: ${testCommand}`));
49
+ log(io, chalk.gray(`测试命令: ${testCommand}`));
80
50
 
81
51
  // é…ē½®
82
52
  const config: PassesGateConfig = {
@@ -91,43 +61,63 @@ export async function runPassesGate(options: PassesGateOptions): Promise<void> {
91
61
  const passesGate = new PassesGate(config);
92
62
 
93
63
  try {
94
- const result = await passesGate.runTests();
64
+ const result = await passesGate.runTests(projectPath);
95
65
 
96
- console.log();
97
- console.log(chalk.gray('ęµ‹čÆ•ē»“ęžœ:'));
98
- console.log(chalk.gray(` é€ščæ‡: ${result.passedTests}/${result.totalTests}`));
99
- console.log(chalk.gray(` 失蓄: ${result.failedTests}/${result.totalTests}`));
100
- console.log(chalk.gray(` 耗ꗶ: ${result.duration}ms`));
66
+ log(io);
67
+ log(io, chalk.gray('ęµ‹čÆ•ē»“ęžœ:'));
68
+ log(io, chalk.gray(` é€ščæ‡: ${result.passedTests}/${result.totalTests}`));
69
+ log(io, chalk.gray(` 失蓄: ${result.failedTests}/${result.totalTests}`));
70
+ log(io, chalk.gray(` 耗ꗶ: ${result.duration}ms`));
101
71
 
102
72
  if (result.passed) {
103
- console.log();
104
- console.log(chalk.green('āœ… ęµ‹čÆ•é—ØęŽ§é€ščæ‡'));
105
- console.log(chalk.green(' task.passes = true (ē”±ęµ‹čÆ•ē»“ęžœč®¾ē½®)'));
73
+ log(io);
74
+ log(io, chalk.green('āœ… ęµ‹čÆ•é—ØęŽ§é€ščæ‡'));
75
+ log(io, chalk.green(' task.passes = true (ē”±ęµ‹čÆ•ē»“ęžœč®¾ē½®)'));
106
76
  } else {
107
- console.log();
108
- console.log(chalk.red('āŒ ęµ‹čÆ•é—ØęŽ§ęœŖé€ščæ‡'));
109
- console.log(chalk.red(' task.passes = false'));
77
+ log(io);
78
+ log(io, chalk.red('āŒ ęµ‹čÆ•é—ØęŽ§ęœŖé€ščæ‡'));
79
+ log(io, chalk.red(' task.passes = false'));
110
80
 
111
81
  if (result.failures && result.failures.length > 0) {
112
- console.log(chalk.red('\nå¤±č“„ēš„ęµ‹čÆ•:'));
82
+ log(io, chalk.red('\nå¤±č“„ēš„ęµ‹čÆ•:'));
113
83
  result.failures.forEach(f => {
114
- console.log(chalk.red(` - ${f.name}: ${f.message}`));
84
+ log(io, chalk.red(` - ${f.name}: ${f.message}`));
115
85
  });
116
86
  }
117
87
 
118
- process.exit(1);
88
+ return { kind: 'fail', reason: `passes-gate denied: ${result.failedTests}/${result.totalTests} 个测试失蓄` };
119
89
  }
90
+ return { kind: 'ok' };
120
91
  } catch (error) {
121
- console.log(chalk.red(`\nāŒ ęµ‹čÆ•ę‰§č”Œå¤±č“„: ${(error as Error).message}`));
122
- process.exit(1);
92
+ log(io, chalk.red(`\nāŒ ęµ‹čÆ•ę‰§č”Œå¤±č“„: ${(error as Error).message}`));
93
+ return { kind: 'fail', reason: `passes-gate ęµ‹čÆ•ę‰§č”Œå¼‚åøø: ${(error as Error).message}` };
123
94
  }
124
95
  }
125
96
 
97
+ /**
98
+ * --coverage č·Æē”±å…„å£ļ¼ˆå€™é€‰7ļ¼‰ļ¼šprojectPath å…œåŗ• + é˜ˆå€¼å¼ŗč½¬ē¼–ē»„ļ¼Œ
99
+ * 自 definitions.ts ēš„ optionRoutes args é—­åŒ…ē§»å›žå‘½ä»¤ęØ”å—ć€‚
100
+ */
101
+ export async function coverageCheck(
102
+ options: Record<string, unknown>,
103
+ io: CommandIO = processIO,
104
+ ): Promise<CommandResult> {
105
+ return checkCoverage(
106
+ (options.projectPath as string) || process.cwd(),
107
+ parseInt(String(options.coverageThreshold), 10),
108
+ io,
109
+ );
110
+ }
111
+
126
112
  /**
127
113
  * ę£€ęŸ„ęµ‹čÆ•č¦†ē›–ēŽ‡
128
114
  */
129
- export async function checkCoverage(projectPath: string, threshold: number = 80): Promise<boolean> {
130
- console.log(chalk.blue(`šŸ“Š ę£€ęŸ„ęµ‹čÆ•č¦†ē›–ēŽ‡ (阈值: ${threshold}%)...`));
115
+ export async function checkCoverage(
116
+ projectPath: string,
117
+ threshold: number = 80,
118
+ io: CommandIO = processIO,
119
+ ): Promise<CommandResult> {
120
+ log(io, chalk.blue(`šŸ“Š ę£€ęŸ„ęµ‹čÆ•č¦†ē›–ēŽ‡ (阈值: ${threshold}%)...`));
131
121
 
132
122
  try {
133
123
  // čæč”Œč¦†ē›–ēŽ‡ę£€ęŸ„
@@ -142,17 +132,17 @@ export async function checkCoverage(projectPath: string, threshold: number = 80)
142
132
 
143
133
  const totalCoverage = coverage.total?.lines?.pct || 0;
144
134
 
145
- console.log(chalk.gray(`å½“å‰č¦†ē›–ēŽ‡: ${totalCoverage}%`));
135
+ log(io, chalk.gray(`å½“å‰č¦†ē›–ēŽ‡: ${totalCoverage}%`));
146
136
 
147
137
  if (totalCoverage >= threshold) {
148
- console.log(chalk.green(`āœ… č¦†ē›–ēŽ‡č¾¾ę ‡ (${totalCoverage}% >= ${threshold}%)`));
149
- return true;
150
- } else {
151
- console.log(chalk.red(`āŒ č¦†ē›–ēŽ‡äøč¶³ (${totalCoverage}% < ${threshold}%)`));
152
- return false;
138
+ log(io, chalk.green(`āœ… č¦†ē›–ēŽ‡č¾¾ę ‡ (${totalCoverage}% >= ${threshold}%)`));
139
+ return { kind: 'ok' };
153
140
  }
141
+ log(io, chalk.red(`āŒ č¦†ē›–ēŽ‡äøč¶³ (${totalCoverage}% < ${threshold}%)`));
142
+ // åŽ†å²č”Œäøŗļ¼š--coverage č·Æē”±ēš„ęœŖč¾¾ę ‡äøę”¹é€€å‡ŗē ļ¼ˆä»Šę—„čæ”å›žå€¼č¢« bin äø¢å¼ƒļ¼‰ā†’ skip äæē•™ 0 面
143
+ return { kind: 'skip', reason: `č¦†ē›–ēŽ‡äøč¶³: ${totalCoverage}% < ${threshold}%` };
154
144
  } catch (error) {
155
- console.log(chalk.yellow(`āš ļø ę— ę³•čŽ·å–č¦†ē›–ēŽ‡äæ”ęÆ: ${(error as Error).message}`));
156
- return true; // ę— ę³•čŽ·å–ę—¶č·³čæ‡ę£€ęŸ„
145
+ log(io, chalk.yellow(`āš ļø ę— ę³•čŽ·å–č¦†ē›–ēŽ‡äæ”ęÆ: ${(error as Error).message}`));
146
+ return { kind: 'skip', reason: `ę— ę³•čŽ·å–č¦†ē›–ēŽ‡äæ”ęÆ: ${(error as Error).message}` }; // ę— ę³•čŽ·å–ę—¶č·³čæ‡ę£€ęŸ„
157
147
  }
158
148
  }
@@ -6,6 +6,7 @@
6
6
 
7
7
  import chalk from 'chalk';
8
8
  import { PerformanceGate } from '../../gates/performance';
9
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
9
10
 
10
11
  export interface PerformanceOptions {
11
12
  /** 锹目路径 */
@@ -27,8 +28,11 @@ export interface PerformanceOptions {
27
28
  /**
28
29
  * ę‰§č”Œę€§čƒ½é—ØęŽ§
29
30
  */
30
- export async function performance(options: PerformanceOptions): Promise<void> {
31
- console.log(chalk.blue('⚔ ę€§čƒ½é—ØęŽ§ę£€ęŸ„...'));
31
+ export async function performance(
32
+ options: PerformanceOptions,
33
+ io: CommandIO = processIO,
34
+ ): Promise<CommandResult> {
35
+ log(io, chalk.blue('⚔ ę€§čƒ½é—ØęŽ§ę£€ęŸ„...'));
32
36
 
33
37
  const projectPath = options.projectPath || process.cwd();
34
38
 
@@ -62,47 +66,48 @@ export async function performance(options: PerformanceOptions): Promise<void> {
62
66
  } as any);
63
67
 
64
68
  if (result.passed) {
65
- console.log();
66
- console.log(chalk.green('āœ… ę€§čƒ½é—ØęŽ§ę£€ęŸ„é€ščæ‡'));
69
+ log(io);
70
+ log(io, chalk.green('āœ… ę€§čƒ½é—ØęŽ§ę£€ęŸ„é€ščæ‡'));
67
71
 
68
72
  // ę˜¾ē¤ŗčÆ¦ē»†ęŒ‡ę ‡
69
73
  if (result.details?.metrics) {
70
- console.log();
71
- console.log(chalk.gray('ę€§čƒ½ęŒ‡ę ‡:'));
74
+ log(io);
75
+ log(io, chalk.gray('ę€§čƒ½ęŒ‡ę ‡:'));
72
76
 
73
77
  const metrics = result.details.metrics as any;
74
78
  if (metrics.coverage !== undefined) {
75
79
  const threshold = options.coverageThreshold || 80;
76
80
  const status = metrics.coverage >= threshold ? 'āœ…' : 'āŒ';
77
- console.log(chalk.gray(` č¦†ē›–ēŽ‡: ${metrics.coverage.toFixed(2)}% ${status}`));
81
+ log(io, chalk.gray(` č¦†ē›–ēŽ‡: ${metrics.coverage.toFixed(2)}% ${status}`));
78
82
  }
79
83
 
80
84
  if (metrics.bundleSize !== undefined) {
81
85
  const thresholdKB = options.bundleThreshold || 500;
82
86
  const actualKB = metrics.bundleSize / 1024;
83
87
  const status = actualKB <= thresholdKB ? 'āœ…' : 'āŒ';
84
- console.log(chalk.gray(` ę‰“åŒ…å¤§å°: ${actualKB.toFixed(2)} KB ${status}`));
88
+ log(io, chalk.gray(` ę‰“åŒ…å¤§å°: ${actualKB.toFixed(2)} KB ${status}`));
85
89
  }
86
90
 
87
91
  if (metrics.benchmarkTime !== undefined) {
88
- console.log(chalk.gray(` åŸŗå‡†ęµ‹čÆ•: ${metrics.benchmarkTime}ms`));
92
+ log(io, chalk.gray(` åŸŗå‡†ęµ‹čÆ•: ${metrics.benchmarkTime}ms`));
89
93
  }
90
94
  }
91
95
  } else {
92
- console.log();
93
- console.log(chalk.red('āŒ ę€§čƒ½é—ØęŽ§ę£€ęŸ„å¤±č“„'));
94
- console.log(chalk.red(` ${result.message}`));
96
+ log(io);
97
+ log(io, chalk.red('āŒ ę€§čƒ½é—ØęŽ§ę£€ęŸ„å¤±č“„'));
98
+ log(io, chalk.red(` ${result.message}`));
95
99
  if (result.details?.failures) {
96
100
  (result.details.failures as string[]).forEach((failure: string) => {
97
- console.log(chalk.red(` - ${failure}`));
101
+ log(io, chalk.red(` - ${failure}`));
98
102
  });
99
103
  }
100
- process.exit(1);
104
+ return { kind: 'fail', reason: `performance gate denied: ${result.message}` };
101
105
  }
106
+ return { kind: 'ok' };
102
107
  } catch (error: any) {
103
- console.log();
104
- console.log(chalk.red('āŒ ę€§čƒ½é—ØęŽ§ę£€ęŸ„å‡ŗé”™'));
105
- console.log(chalk.red(` ${error.message}`));
106
- process.exit(1);
108
+ log(io);
109
+ log(io, chalk.red('āŒ ę€§čƒ½é—ØęŽ§ę£€ęŸ„å‡ŗé”™'));
110
+ log(io, chalk.red(` ${error.message}`));
111
+ return { kind: 'fail', reason: `performance gate error: ${error.message}` };
107
112
  }
108
113
  }
@@ -3,9 +3,13 @@
3
3
  *
4
4
  * č°ƒē”Ø Studio PostEval API,验证 plan ę–‡ä»¶ēš„ checklist items ę˜Æå¦éƒ½ęœ‰åÆ¹åŗ”ēš„ staged diff怂
5
5
  * pre-commit hook ä½æē”Øę­¤å‘½ä»¤é˜²ę­¢"å‡č£…å®Œęˆ"怂
6
+ *
7
+ * åˆ¤å®šē»čæ”å›žå€¼å¤–ęŗ¢ļ¼ˆęž¶ęž„čÆ„å®”å€™é€‰7ļ¼‰ļ¼šAPI äøåÆē”Ø/5xx 属"未判定"→ skipļ¼ˆé€€å‡ŗē é¢ 0 äøå˜ļ¼‰ļ¼›
8
+ * č¦†ē›–ēŽ‡äøč¶³äøŽ 4xx → fail,reason ęŒ‡ę˜Žę˜Æå“Ŗäø€é”¹å¦å†³ć€‚
6
9
  */
7
10
 
8
11
  import chalk from 'chalk';
12
+ import { log, logError, processIO, type CommandIO, type CommandResult } from '../command-contract';
9
13
 
10
14
  export interface PostEvalPlanOptions {
11
15
  planPath: string;
@@ -38,7 +42,10 @@ async function fetchWithRetry(url: string, body: string, retries: number): Promi
38
42
  throw lastError;
39
43
  }
40
44
 
41
- export async function postevalPlan(options: PostEvalPlanOptions): Promise<void> {
45
+ export async function postevalPlan(
46
+ options: PostEvalPlanOptions,
47
+ io: CommandIO = processIO,
48
+ ): Promise<CommandResult> {
42
49
  const apiPort = process.env.API_PORT || '3001';
43
50
  const url = `http://localhost:${apiPort}/api/v1/agents/post-eval/plan-coverage`;
44
51
 
@@ -48,12 +55,12 @@ export async function postevalPlan(options: PostEvalPlanOptions): Promise<void>
48
55
  if (!res.ok) {
49
56
  if (res.status >= 500) {
50
57
  // Server error — allow commit with warning (don't block on infrastructure failure)
51
- console.error(chalk.yellow(`āš ļø PostEval API unavailable (${res.status}), allowing commit with warning`));
52
- console.error(chalk.yellow(` Plan: ${options.planPath}`));
53
- process.exit(0);
58
+ logError(io, chalk.yellow(`āš ļø PostEval API unavailable (${res.status}), allowing commit with warning`));
59
+ logError(io, chalk.yellow(` Plan: ${options.planPath}`));
60
+ return { kind: 'skip', reason: `PostEval API ęœåŠ”ē«Æé”™čÆÆ ${res.status},未判定` };
54
61
  }
55
- console.error(chalk.red(`āŒ PostEval API error: ${res.status} ${res.statusText}`));
56
- process.exit(1);
62
+ logError(io, chalk.red(`āŒ PostEval API error: ${res.status} ${res.statusText}`));
63
+ return { kind: 'fail', reason: `PostEval API error: ${res.status} ${res.statusText}` };
57
64
  }
58
65
 
59
66
  const report = await res.json() as {
@@ -65,20 +72,27 @@ export async function postevalPlan(options: PostEvalPlanOptions): Promise<void>
65
72
  const pct = Math.round(report.completeness * 100);
66
73
 
67
74
  if (report.completeness < 1) {
68
- console.error(chalk.red(`āŒ Plan coverage: ${pct}% (${report.matchedAcs.length}/${report.matchedAcs.length + report.missedAcs.length})`));
75
+ logError(io, chalk.red(`āŒ Plan coverage: ${pct}% (${report.matchedAcs.length}/${report.matchedAcs.length + report.missedAcs.length})`));
69
76
  if (report.missedAcs.length > 0) {
70
- console.error(chalk.red('Missed items:'));
71
- report.missedAcs.forEach((item: string) => console.error(chalk.red(` - ${item}`)));
77
+ logError(io, chalk.red('Missed items:'));
78
+ report.missedAcs.forEach((item: string) => logError(io, chalk.red(` - ${item}`)));
72
79
  }
73
- process.exit(1);
80
+ return {
81
+ kind: 'fail',
82
+ reason: `plan coverage ${pct}% < 100%,缺 ${report.missedAcs.length} 锹: ${report.missedAcs.join('; ')}`,
83
+ };
74
84
  }
75
85
 
76
- console.log(chalk.green(`āœ… Plan coverage: ${pct}%`));
86
+ log(io, chalk.green(`āœ… Plan coverage: ${pct}%`));
87
+ return { kind: 'ok' };
77
88
  } catch (error: any) {
78
89
  // API unreachable after all retries — allow with warning (don't block commits on infra)
79
- console.error(chalk.yellow(`āš ļø Studio API unreachable after ${MAX_RETRIES} retries, allowing commit with warning`));
80
- console.error(chalk.yellow(` Plan: ${options.planPath}`));
81
- console.error(chalk.yellow(` Error: ${error?.code || error?.message || String(error)}`));
82
- process.exit(0);
90
+ logError(io, chalk.yellow(`āš ļø Studio API unreachable after ${MAX_RETRIES} retries, allowing commit with warning`));
91
+ logError(io, chalk.yellow(` Plan: ${options.planPath}`));
92
+ logError(io, chalk.yellow(` Error: ${error?.code || error?.message || String(error)}`));
93
+ return {
94
+ kind: 'skip',
95
+ reason: `Studio API äøåÆč¾¾ļ¼ˆ${error?.code || error?.message || String(error)}ļ¼‰ļ¼ŒęœŖåˆ¤å®š`,
96
+ };
83
97
  }
84
98
  }
@@ -15,6 +15,10 @@
15
15
  * 7. Check private flag
16
16
  * 8. npm publish (or skip if PR flow)
17
17
  * 9. GitHub Release
18
+ *
19
+ * åˆ¤å®šē»čæ”å›žå€¼å¤–ęŗ¢ļ¼ˆęž¶ęž„čÆ„å®”å€™é€‰7ļ¼‰ļ¼š12 äøŖå‰ē½®é—øé—Øē»“ęŒ**é¦–č“„å³åœ**ļ¼ŒęÆäøŖé—øé—Øå¤±č“„
20
+ * čæ”å›ž `gate <id>: <原因>`ļ¼›é€€å‡ŗē ę˜ å°„åœØ bin/harness.js å”Æäø€äø€å¤„ć€‚č¾“å‡ŗē»ę³Øå…„ io
21
+ * ęµå¼ę‰“å°ļ¼ˆåˆ†é’Ÿēŗ§å­čæ›ēØ‹é˜¶ę®µäøčƒ½é™é»˜ļ¼‰ć€‚
18
22
  */
19
23
 
20
24
  import { execSync } from 'child_process';
@@ -22,6 +26,7 @@ import * as fs from 'fs';
22
26
  import * as path from 'path';
23
27
  import chalk from 'chalk';
24
28
  import { verifyReleaseArtifacts } from '../../release';
29
+ import { log, logError, processIO, type CommandIO, type CommandResult } from '../command-contract';
25
30
 
26
31
  export interface ReleaseOptions {
27
32
  bumpType?: 'patch' | 'minor' | 'major';
@@ -39,7 +44,12 @@ async function run(cmd: string, cwd: string, timeout = 60_000): Promise<{ stdout
39
44
  }
40
45
  }
41
46
 
42
- export async function release(options: ReleaseOptions): Promise<void> {
47
+ /** é—øé—Øå¤±č“„ē»“ęžœļ¼šreason å«é—øé—Øę ‡čÆ†ļ¼ŒåÆč¢«č°ƒē”Øę–¹é€é—øé—Øę–­čØ€ */
48
+ function gateFail(gate: string, detail: string): CommandResult {
49
+ return { kind: 'fail', reason: `gate ${gate}: ${detail}` };
50
+ }
51
+
52
+ export async function release(options: ReleaseOptions, io: CommandIO = processIO): Promise<CommandResult> {
43
53
  const pkgPath = process.cwd();
44
54
  const bumpType = options.bumpType || 'patch';
45
55
  const dryRun = options.dryRun === 'true';
@@ -47,59 +57,59 @@ export async function release(options: ReleaseOptions): Promise<void> {
47
57
  // ── 1. Verify package ──
48
58
  const pkgJsonPath = path.join(pkgPath, 'package.json');
49
59
  if (!fs.existsSync(pkgJsonPath)) {
50
- console.error(chalk.red(`āŒ Not a package: ${pkgPath}`));
51
- process.exit(1);
60
+ logError(io, chalk.red(`āŒ Not a package: ${pkgPath}`));
61
+ return gateFail('package', `not a package: ${pkgPath}`);
52
62
  }
53
63
  const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
54
64
  const pkgName = pkgJson.name;
55
65
  const oldVersion = pkgJson.version;
56
- console.log(chalk.cyan(`šŸ“¦ ${pkgName}@${oldVersion}`));
57
- console.log(chalk.cyan(` bump: ${bumpType}${dryRun ? ' (dry-run)' : ''}`));
66
+ log(io, chalk.cyan(`šŸ“¦ ${pkgName}@${oldVersion}`));
67
+ log(io, chalk.cyan(` bump: ${bumpType}${dryRun ? ' (dry-run)' : ''}`));
58
68
 
59
69
  // ── 2. Check branch (must be on master/main) ──
60
70
  const branchResult = await run('git rev-parse --abbrev-ref HEAD', pkgPath);
61
71
  const currentBranch = branchResult.stdout.trim();
62
72
  if (currentBranch !== 'master' && currentBranch !== 'main') {
63
- console.error(chalk.red(`āŒ Must be on master or main branch to release. Currently on: ${currentBranch}`));
64
- console.error(chalk.gray(' Run: git checkout master && git merge ' + currentBranch));
65
- process.exit(1);
73
+ logError(io, chalk.red(`āŒ Must be on master or main branch to release. Currently on: ${currentBranch}`));
74
+ logError(io, chalk.gray(' Run: git checkout master && git merge ' + currentBranch));
75
+ return gateFail('branch', `must be on master or main branch, currently on ${currentBranch}`);
66
76
  }
67
- console.log(chalk.green(`āœ… git: on ${currentBranch}`));
77
+ log(io, chalk.green(`āœ… git: on ${currentBranch}`));
68
78
 
69
79
  // ── 2b. Check remote sync ──
70
80
  await run('git fetch origin', pkgPath, 15_000);
71
81
  const behind = await run(`git rev-list --count HEAD..origin/${currentBranch}`, pkgPath);
72
82
  if (behind.stdout.trim() !== '0') {
73
- console.error(chalk.red(`āŒ Local ${currentBranch} is ${behind.stdout.trim()} commits behind origin/${currentBranch}. Pull first.`));
74
- process.exit(1);
83
+ logError(io, chalk.red(`āŒ Local ${currentBranch} is ${behind.stdout.trim()} commits behind origin/${currentBranch}. Pull first.`));
84
+ return gateFail('remote-sync', `local ${currentBranch} is ${behind.stdout.trim()} commits behind origin/${currentBranch}`);
75
85
  }
76
- console.log(chalk.green('āœ… git: synced with remote'));
86
+ log(io, chalk.green('āœ… git: synced with remote'));
77
87
 
78
88
  // ── 2c. Check clean working tree ──
79
89
  const stat = await run('git status --porcelain -uno', pkgPath);
80
90
  if (stat.stdout.length > 0) {
81
- console.error(chalk.red('āŒ Uncommitted changes. Commit or stash before releasing.'));
82
- console.error(chalk.gray(stat.stdout.slice(0, 500)));
83
- process.exit(1);
91
+ logError(io, chalk.red('āŒ Uncommitted changes. Commit or stash before releasing.'));
92
+ logError(io, chalk.gray(stat.stdout.slice(0, 500)));
93
+ return gateFail('clean-tree', 'working tree has uncommitted changes');
84
94
  }
85
- console.log(chalk.green('āœ… git: clean'));
95
+ log(io, chalk.green('āœ… git: clean'));
86
96
 
87
97
  // ── 3. tsc build ──
88
- console.log(chalk.cyan('šŸ”Ø Building...'));
98
+ log(io, chalk.cyan('šŸ”Ø Building...'));
89
99
  const build = await run('npx tsc', pkgPath, 60_000);
90
100
  if (build.stderr && !build.stdout) {
91
- console.error(chalk.red('āŒ tsc failed:'), build.stderr.slice(0, 800));
92
- process.exit(1);
101
+ logError(io, chalk.red('āŒ tsc failed:'), build.stderr.slice(0, 800));
102
+ return gateFail('build', `tsc failed: ${build.stderr.slice(0, 200)}`);
93
103
  }
94
- console.log(chalk.green('āœ… tsc: built'));
104
+ log(io, chalk.green('āœ… tsc: built'));
95
105
 
96
106
  // ── 4. Verify distļ¼ˆå‘åøƒē‰©å®Œę•“ę€§č‡Ŗę£€ļ¼Œharness#77ļ¼šęø…å•éšęŗē ē»“ęŠ¤ļ¼Œäøå†ęœ¬ę–‡ä»¶ē”¬ē¼–ē ļ¼‰ā”€ā”€
97
107
  const integrity = verifyReleaseArtifacts(pkgPath);
98
108
  if (!integrity.ok) {
99
- console.error(chalk.red(`āŒ dist missing critical artifacts (${integrity.missing.length}): ${integrity.missing.join(', ')}`));
100
- process.exit(1);
109
+ logError(io, chalk.red(`āŒ dist missing critical artifacts (${integrity.missing.length}): ${integrity.missing.join(', ')}`));
110
+ return gateFail('dist-artifacts', `missing critical artifacts: ${integrity.missing.join(', ')}`);
101
111
  }
102
- console.log(chalk.green(`āœ… dist: verified (${integrity.checked.length} critical artifacts)`));
112
+ log(io, chalk.green(`āœ… dist: verified (${integrity.checked.length} critical artifacts)`));
103
113
 
104
114
  if (dryRun) {
105
115
  const [major, minor, patch] = oldVersion.split('.').map(Number);
@@ -107,12 +117,12 @@ export async function release(options: ReleaseOptions): Promise<void> {
107
117
  if (bumpType === 'major') newVer = `${major + 1}.0.0`;
108
118
  else if (bumpType === 'minor') newVer = `${major}.${minor + 1}.0`;
109
119
  else newVer = `${major}.${minor}.${patch + 1}`;
110
- console.log(chalk.yellow(`šŸ Dry-run complete. Would publish: ${pkgName}@${newVer}`));
111
- process.exit(0);
120
+ log(io, chalk.yellow(`šŸ Dry-run complete. Would publish: ${pkgName}@${newVer}`));
121
+ return { kind: 'ok' };
112
122
  }
113
123
 
114
124
  // ── 5. Bump version (npm version creates git commit + tag atomically) ──
115
- console.log(chalk.cyan('šŸ”¢ Bumping version...'));
125
+ log(io, chalk.cyan('šŸ”¢ Bumping version...'));
116
126
  const [major, minor, patch] = oldVersion.split('.').map(Number);
117
127
  let expectedNew: string;
118
128
  if (bumpType === 'major') expectedNew = `${major + 1}.0.0`;
@@ -121,28 +131,28 @@ export async function release(options: ReleaseOptions): Promise<void> {
121
131
  const expectedTag = `v${expectedNew}`;
122
132
  const tagExists = await run(`git tag -l "${expectedTag}"`, pkgPath);
123
133
  if (tagExists.stdout.trim() === expectedTag) {
124
- console.error(chalk.red(`āŒ Tag ${expectedTag} already exists. Version may have been released already.`));
125
- process.exit(1);
134
+ logError(io, chalk.red(`āŒ Tag ${expectedTag} already exists. Version may have been released already.`));
135
+ return gateFail('tag-conflict', `tag ${expectedTag} already exists`);
126
136
  }
127
137
 
128
138
  const bump = await run(`npm version ${bumpType} -m "release: %s"`, pkgPath);
129
139
  if (bump.stderr && !bump.stdout) {
130
- console.error(chalk.red('āŒ npm version failed:'), bump.stderr);
131
- process.exit(1);
140
+ logError(io, chalk.red('āŒ npm version failed:'), bump.stderr);
141
+ return gateFail('version-bump', `npm version ${bumpType} failed: ${bump.stderr.slice(0, 200)}`);
132
142
  }
133
143
  const newVersion = bump.stdout.trim().replace(/^v/, '');
134
144
  const tag = `v${newVersion}`;
135
- console.log(chalk.green(`āœ… version: ${oldVersion} → ${newVersion} (committed + tagged)`));
145
+ log(io, chalk.green(`āœ… version: ${oldVersion} → ${newVersion} (committed + tagged)`));
136
146
 
137
147
  // ── 6. Push (handle protected branch via PR) ──
138
- console.log(chalk.cyan('ā¬†ļø Pushing...'));
148
+ log(io, chalk.cyan('ā¬†ļø Pushing...'));
139
149
  const push = await run(`git push origin ${currentBranch}`, pkgPath, 30_000);
140
150
  const isProtected = push.stderr.includes('GH006') || push.stderr.includes('protected branch');
141
151
 
142
152
  if (isProtected) {
143
153
  // Protected branch: create release branch + PR
144
154
  const releaseBranch = `release/v${newVersion}`;
145
- console.log(chalk.yellow(`āš ļø ${currentBranch} is protected. Creating PR via ${releaseBranch}...`));
155
+ log(io, chalk.yellow(`āš ļø ${currentBranch} is protected. Creating PR via ${releaseBranch}...`));
146
156
 
147
157
  await run(`git checkout -b ${releaseBranch}`, pkgPath);
148
158
  await run(`git push origin ${releaseBranch}`, pkgPath, 30_000);
@@ -151,59 +161,59 @@ export async function release(options: ReleaseOptions): Promise<void> {
151
161
  const prBody = `## Release ${pkgName}@${newVersion}\n\n- Bump version: ${oldVersion} → ${newVersion}\n- Changelog updated\n\nAfter merge, push tag to trigger npm publish:\n\`\`\`\ngit push origin ${tag}\n\`\`\``;
152
162
  const pr = await run(`gh pr create --title "release: ${pkgName}@${newVersion}" --body "${prBody}" --base ${currentBranch}`, pkgPath, 30_000);
153
163
  if (pr.stdout) {
154
- console.log(chalk.green(`āœ… PR created: ${pr.stdout.trim()}`));
164
+ log(io, chalk.green(`āœ… PR created: ${pr.stdout.trim()}`));
155
165
  } else {
156
- console.error(chalk.red('āŒ PR creation failed:'), pr.stderr.slice(0, 500));
157
- process.exit(1);
166
+ logError(io, chalk.red('āŒ PR creation failed:'), pr.stderr.slice(0, 500));
167
+ return gateFail('pr-create', `gh pr create failed: ${pr.stderr.slice(0, 200)}`);
158
168
  }
159
169
 
160
170
  // Switch back to original branch
161
171
  await run(`git checkout ${currentBranch}`, pkgPath);
162
172
 
163
- console.log(chalk.cyan('\nšŸ“‹ Next steps:'));
164
- console.log(chalk.gray(` 1. Merge the PR: ${pr.stdout.trim()}`));
165
- console.log(chalk.gray(` 2. After merge, push tag to trigger npm publish:`));
166
- console.log(chalk.gray(` git push origin ${tag}`));
167
- console.log(chalk.gray(` 3. Or run: gh release create ${tag} --generate-notes`));
168
- return;
173
+ log(io, chalk.cyan('\nšŸ“‹ Next steps:'));
174
+ log(io, chalk.gray(` 1. Merge the PR: ${pr.stdout.trim()}`));
175
+ log(io, chalk.gray(` 2. After merge, push tag to trigger npm publish:`));
176
+ log(io, chalk.gray(` git push origin ${tag}`));
177
+ log(io, chalk.gray(` 3. Or run: gh release create ${tag} --generate-notes`));
178
+ return { kind: 'ok' };
169
179
  }
170
180
 
171
181
  if (push.stderr && push.stderr.includes('error')) {
172
- console.error(chalk.red('āŒ git push failed:'), push.stderr.slice(0, 500));
173
- process.exit(1);
182
+ logError(io, chalk.red('āŒ git push failed:'), push.stderr.slice(0, 500));
183
+ return gateFail('git-push', `git push origin ${currentBranch} failed: ${push.stderr.slice(0, 200)}`);
174
184
  }
175
185
  await run(`git push origin ${tag}`, pkgPath, 30_000);
176
- console.log(chalk.green('āœ… git: pushed'));
186
+ log(io, chalk.green('āœ… git: pushed'));
177
187
 
178
188
  // ── 7. Check private flag ──
179
189
  if (pkgJson.private) {
180
- console.error(chalk.red('āŒ package.json has "private": true. Remove it before publishing.'));
181
- console.error(chalk.gray(' The release tool will NOT auto-remove private flag.'));
182
- process.exit(1);
190
+ logError(io, chalk.red('āŒ package.json has "private": true. Remove it before publishing.'));
191
+ logError(io, chalk.gray(' The release tool will NOT auto-remove private flag.'));
192
+ return gateFail('private-flag', 'package.json has "private": true');
183
193
  }
184
- console.log(chalk.green('āœ… package: not private'));
194
+ log(io, chalk.green('āœ… package: not private'));
185
195
 
186
196
  // ── 8. npm publish ──
187
197
  const origRegistry = await run('npm config get registry', pkgPath);
188
198
  await run('npm config set registry https://registry.npmjs.org/', pkgPath);
189
- console.log(chalk.cyan('šŸ“¤ Publishing to npm...'));
199
+ log(io, chalk.cyan('šŸ“¤ Publishing to npm...'));
190
200
  await run('npm publish --registry https://registry.npmjs.org/', pkgPath, 180_000);
191
201
  await run(`npm config set registry ${origRegistry.stdout}`, pkgPath);
192
202
  const verify = await run(`npm view ${pkgName} version --registry https://registry.npmjs.org/`, pkgPath);
193
203
  if (verify.stdout.trim() === newVersion) {
194
- console.log(chalk.green(`āœ… npm: published ${pkgName}@${newVersion}`));
204
+ log(io, chalk.green(`āœ… npm: published ${pkgName}@${newVersion}`));
195
205
  } else {
196
- console.error(chalk.red(`āŒ npm publish verification failed. Expected ${newVersion}, registry has ${verify.stdout.trim() || '???'}`));
197
- process.exit(1);
206
+ logError(io, chalk.red(`āŒ npm publish verification failed. Expected ${newVersion}, registry has ${verify.stdout.trim() || '???'}`));
207
+ return gateFail('npm-publish-verify', `expected ${newVersion}, registry has ${verify.stdout.trim() || '???'}`);
198
208
  }
199
209
 
200
210
  // ── 9. GitHub Release ──
201
- console.log(chalk.cyan('šŸ™ Creating GitHub Release...'));
211
+ log(io, chalk.cyan('šŸ™ Creating GitHub Release...'));
202
212
  const gh = await run(`gh release create ${tag} --generate-notes`, pkgPath, 30_000);
203
213
  if (gh.stderr && gh.stderr.includes('already exists')) {
204
- console.log(chalk.yellow(`āš ļø gh: release ${tag} already exists`));
214
+ log(io, chalk.yellow(`āš ļø gh: release ${tag} already exists`));
205
215
  } else if (gh.stderr) {
206
- console.log(chalk.yellow(`āš ļø gh: release may have failed (non-fatal): ${gh.stderr.slice(0, 200)}`));
216
+ log(io, chalk.yellow(`āš ļø gh: release may have failed (non-fatal): ${gh.stderr.slice(0, 200)}`));
207
217
  } else {
208
218
  let repoUrl = '';
209
219
  try {
@@ -211,9 +221,10 @@ export async function release(options: ReleaseOptions): Promise<void> {
211
221
  const m = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/\s.]+?)(?:\.git)?$/);
212
222
  if (m) repoUrl = `https://github.com/${m[1]}/${m[2]}/releases/tag/${tag}`;
213
223
  } catch { /* no remote */ }
214
- console.log(chalk.green(`āœ… GitHub Release: ${repoUrl || tag}`));
224
+ log(io, chalk.green(`āœ… GitHub Release: ${repoUrl || tag}`));
215
225
  }
216
226
 
217
- console.log(chalk.green(`\nšŸŽ‰ Released ${pkgName}@${newVersion}`));
218
- console.log(chalk.gray(` npm: https://www.npmjs.com/package/${pkgName}/v/${newVersion}`));
227
+ log(io, chalk.green(`\nšŸŽ‰ Released ${pkgName}@${newVersion}`));
228
+ log(io, chalk.gray(` npm: https://www.npmjs.com/package/${pkgName}/v/${newVersion}`));
229
+ return { kind: 'ok' };
219
230
  }