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