@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
@@ -5,18 +5,27 @@
5
5
  */
6
6
 
7
7
  import chalk from 'chalk';
8
- import { readFileSync } from 'fs';
9
8
  import * as fs from 'fs/promises';
10
9
  import * as path from 'path';
11
10
  import * as yaml from 'js-yaml';
12
11
  import { createExampleCheckpoint, createExampleResolutions } from './validate';
13
12
  import { detectSourceRoots } from '../../utils/detect-source-roots';
13
+ import { getHarnessPackageVersion } from '../../utils/package-version';
14
14
  import { getEffectiveConstraints } from '../../core/effective-constraints';
15
+ import type { GovernanceConfig } from '../../types/project-config';
15
16
  import {
16
17
  CONSTRAINTS_START_MARKER,
17
18
  CONSTRAINTS_END_MARKER,
18
19
  renderConstraintsSection,
19
20
  } from '../../core/constraints/injection-renderer';
21
+ import {
22
+ replaceStandaloneRange,
23
+ replaceEnclosedRange,
24
+ cutMarkerBlock,
25
+ resolveGovernanceLanding,
26
+ GOVERNANCE_HEADING,
27
+ } from '../../core/constraints/injection-writer';
28
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
20
29
 
21
30
  export interface InitOptions {
22
31
  /** 项目路径 */
@@ -67,9 +76,9 @@ const PRESETS = {
67
76
  };
68
77
 
69
78
  /**
70
- * 治理预设
79
+ * 治理预设(形状 = 写入 config.yml 的 governance 段,直接用类型正本)
71
80
  */
72
- const GOVERNANCE_PRESETS: Record<string, Record<string, unknown>> = {
81
+ const GOVERNANCE_PRESETS: Record<string, GovernanceConfig> = {
73
82
  minimal: {
74
83
  level: 'minimal',
75
84
  docs: {
@@ -135,117 +144,111 @@ const GOVERNANCE_PRESETS: Record<string, Record<string, unknown>> = {
135
144
  /**
136
145
  * 初始化项目
137
146
  */
138
- export async function init(options: InitOptions): Promise<void> {
147
+ export async function init(options: InitOptions, io: CommandIO = processIO): Promise<CommandResult> {
139
148
  // 只输出代码片段
140
149
  if (options.printSnippets) {
141
- printSnippets();
142
- return;
150
+ printSnippets(io);
151
+ return { kind: 'ok' };
143
152
  }
144
153
 
145
- console.log(chalk.blue('🚀 初始化 harness 配置...'));
154
+ log(io, chalk.blue('🚀 初始化 harness 配置...'));
146
155
 
147
156
  const projectPath = options.projectPath || process.cwd();
148
157
  const configDir = path.join(projectPath, '.harness');
149
158
 
150
159
  // 创建配置目录
151
160
  await fs.mkdir(configDir, { recursive: true });
152
- console.log(chalk.gray(`配置目录: ${configDir}`));
161
+ log(io, chalk.gray(`配置目录: ${configDir}`));
153
162
 
154
163
  // 选择预设
155
164
  const preset = PRESETS[options.preset];
156
- console.log(chalk.gray(`预设: ${options.preset}`));
165
+ log(io, chalk.gray(`预设: ${options.preset}`));
157
166
 
158
167
  // 合并治理配置
159
168
  const configData: Record<string, unknown> = { ...preset };
160
169
  if (options.governance) {
161
170
  configData.governance = GOVERNANCE_PRESETS[options.governance];
162
- console.log(chalk.gray(`治理级别: ${options.governance}`));
171
+ log(io, chalk.gray(`治理级别: ${options.governance}`));
163
172
  }
164
173
 
165
174
  // 写入 harness 版本
166
- const pkgVersion = getPackageVersion();
175
+ const pkgVersion = getHarnessPackageVersion();
167
176
  configData.harness = { version: pkgVersion };
168
177
 
169
178
  // 写入配置文件(harness 管理的配置,始终重新生成)
170
179
  const configPath = path.join(configDir, 'config.yml');
171
180
  const configContent = yaml.dump(configData, { indent: 2 });
172
181
  await fs.writeFile(configPath, configContent, 'utf-8');
173
- console.log(chalk.green(`✅ 已创建配置文件: ${configPath} (v${pkgVersion})`));
182
+ log(io, chalk.green(`✅ 已创建配置文件: ${configPath} (v${pkgVersion})`));
174
183
 
175
184
  // 创建检查点示例
176
- await createExampleCheckpoint(projectPath);
185
+ await createExampleCheckpoint(projectPath, io);
177
186
 
178
187
  // 创建 Resolutions(RKB 狗粮 — 约束 → 已知解法映射)
179
- await createExampleResolutions(projectPath);
188
+ await createExampleResolutions(projectPath, io);
180
189
 
181
190
  // 创建自定义约束示例
182
- await createCustomConstraintsExample(projectPath);
191
+ await createCustomConstraintsExample(projectPath, io);
183
192
 
184
193
  // CAPABILITIES.md / CHANGELOG.md 由 sync-docs(AI 治理)管理,init 不创建
185
194
 
186
195
  // 创建 Git hooks
187
196
  if (options.gitHooks !== false) {
188
- await setupGitHooks(projectPath);
197
+ await setupGitHooks(projectPath, io);
189
198
  }
190
199
 
191
200
  // 创建 GitHub Actions
192
201
  if (options.githubActions !== false) {
193
- await setupGitHubActions(projectPath);
202
+ await setupGitHubActions(projectPath, io);
194
203
  }
195
204
 
196
205
  // 治理相关文件生成
197
206
  if (options.governance) {
198
- await setupGovernance(projectPath, options.governance);
199
- }
200
-
201
- console.log();
202
- console.log(chalk.green('✅ harness 初始化完成!'));
203
- console.log();
204
- console.log(chalk.gray('下一步:'));
205
- console.log(chalk.gray(' 1. 编辑 .harness/config.yml 自定义配置'));
206
- console.log(chalk.gray(' 2. 编辑 .harness/custom-constraints.yml 添加项目约束'));
207
- console.log(chalk.gray(' 3. 正常开发,每次 git commit 会自动检查约束'));
208
- console.log(chalk.gray(' 4. 运行 harness status 查看状态'));
209
- console.log();
210
- console.log(chalk.blue('💡 提示: 使用 harness init --print-snippets 查看配置代码片段'));
211
- }
212
-
213
- /**
214
- * 获取 harness 包版本
215
- */
216
- function getPackageVersion(): string {
217
- try {
218
- // eslint-disable-next-line @typescript-eslint/no-var-requires
219
- return require('../../../package.json').version;
220
- } catch {
221
- return 'unknown';
222
- }
207
+ await setupGovernance(projectPath, options.governance, io);
208
+ }
209
+
210
+ log(io);
211
+ log(io, chalk.green('✅ harness 初始化完成!'));
212
+ log(io);
213
+ log(io, chalk.gray('下一步:'));
214
+ log(io, chalk.gray(' 1. 编辑 .harness/config.yml 自定义配置'));
215
+ log(io, chalk.gray(' 2. 编辑 .harness/custom-constraints.yml 添加项目约束'));
216
+ log(io, chalk.gray(' 3. 正常开发,每次 git commit 会自动检查约束'));
217
+ log(io, chalk.gray(' 4. 运行 harness status 查看状态'));
218
+ log(io);
219
+ log(io, chalk.blue('💡 提示: 使用 harness init --print-snippets 查看配置代码片段'));
220
+ return { kind: 'ok' };
223
221
  }
224
222
 
225
223
  /**
226
224
  * 输出代码片段
227
225
  */
228
- function printSnippets(): void {
229
- console.log(chalk.blue('📄 Harness 配置代码片段'));
230
- console.log();
226
+ function printSnippets(io: CommandIO): void {
227
+ log(io, chalk.blue('📄 Harness 配置代码片段'));
228
+ log(io);
231
229
 
232
- console.log(chalk.yellow('Git pre-commit hook:'));
233
- console.log(chalk.gray('添加到 .git/hooks/pre-commit'));
234
- console.log();
235
- console.log(chalk.cyan(PRE_COMMIT_SNIPPET));
230
+ log(io, chalk.yellow('Git pre-commit hook:'));
231
+ log(io, chalk.gray('添加到 .git/hooks/pre-commit'));
232
+ log(io);
233
+ log(io, chalk.cyan(PRE_COMMIT_SNIPPET));
236
234
 
237
- console.log(chalk.yellow('GitHub Actions:'));
238
- console.log(chalk.gray('添加到 .github/workflows/*.yml 的 jobs 中'));
239
- console.log();
240
- console.log(chalk.cyan(GITHUB_ACTIONS_SNIPPET));
235
+ log(io, chalk.yellow('GitHub Actions:'));
236
+ log(io, chalk.gray('添加到 .github/workflows/*.yml 的 jobs 中'));
237
+ log(io);
238
+ log(io, chalk.cyan(GITHUB_ACTIONS_SNIPPET));
241
239
 
242
- console.log(chalk.blue('💡 提示: 运行 npx @dommaker/harness init 自动创建配置文件'));
240
+ log(io, chalk.blue('💡 提示: 运行 npx @dommaker/harness init 自动创建配置文件'));
243
241
  }
244
242
 
245
243
  /**
246
- * Git pre-commit 代码片段
244
+ * Git pre-commit 代码片段(#103:打印片段与落盘 hook 的唯一正本;
245
+ * 落盘 hook = `#!/bin/sh` 头 + 本常量,改钩子只改这里)
247
246
  */
248
247
  const PRE_COMMIT_SNIPPET = `
248
+ echo "🔍 Running harness checks..."
249
+
250
+ STAGED=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)
251
+
249
252
  # Harness 约束检查
250
253
  npx @dommaker/harness check --staged
251
254
  if [ $? -ne 0 ]; then
@@ -254,7 +257,6 @@ if [ $? -ne 0 ]; then
254
257
  fi
255
258
 
256
259
  # Plan coverage check (via PostEval)
257
- STAGED=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)
258
260
  if command -v npx > /dev/null 2>&1; then
259
261
  PLAN_FILES=$(echo "$STAGED" | grep -E 'plans/.*\\.md$|\\.plan\\.md$' || true)
260
262
  if [ -n "$PLAN_FILES" ]; then
@@ -267,6 +269,8 @@ if command -v npx > /dev/null 2>&1; then
267
269
  done
268
270
  fi
269
271
  fi
272
+
273
+ echo "✅ All checks passed"
270
274
  `;
271
275
 
272
276
  /**
@@ -287,7 +291,7 @@ const GITHUB_ACTIONS_SNIPPET = `
287
291
  /**
288
292
  * 设置 Git hooks
289
293
  */
290
- async function setupGitHooks(projectPath: string): Promise<void> {
294
+ async function setupGitHooks(projectPath: string, io: CommandIO): Promise<void> {
291
295
  const gitDir = path.join(projectPath, '.git');
292
296
  const hooksDir = path.join(gitDir, 'hooks');
293
297
  const preCommitPath = path.join(hooksDir, 'pre-commit');
@@ -295,8 +299,8 @@ async function setupGitHooks(projectPath: string): Promise<void> {
295
299
  try {
296
300
  await fs.access(gitDir);
297
301
  } catch {
298
- console.log(chalk.yellow('⚠️ 未检测到 Git 仓库,跳过 Git hooks'));
299
- console.log(chalk.gray('💡 初始化 Git 后可运行 npx @dommaker/harness init --print-snippets 查看配置'));
302
+ log(io, chalk.yellow('⚠️ 未检测到 Git 仓库,跳过 Git hooks'));
303
+ log(io, chalk.gray('💡 初始化 Git 后可运行 npx @dommaker/harness init --print-snippets 查看配置'));
300
304
  return;
301
305
  }
302
306
 
@@ -306,52 +310,25 @@ async function setupGitHooks(projectPath: string): Promise<void> {
306
310
  try {
307
311
  await fs.access(preCommitPath);
308
312
  // 已存在,输出代码片段
309
- console.log(chalk.yellow('⚠️ .git/hooks/pre-commit 已存在'));
310
- console.log(chalk.gray('💡 请手动添加以下内容到文件末尾:'));
311
- console.log();
312
- console.log(chalk.cyan(PRE_COMMIT_SNIPPET));
313
+ log(io, chalk.yellow('⚠️ .git/hooks/pre-commit 已存在'));
314
+ log(io, chalk.gray('💡 请手动添加以下内容到文件末尾:'));
315
+ log(io);
316
+ log(io, chalk.cyan(PRE_COMMIT_SNIPPET));
313
317
  } catch {
314
- // 不存在,创建文件
318
+ // 不存在,创建文件:shebang 头 + 共享片段(#103 同源,无第二份文本)
315
319
  const preCommitContent = `#!/bin/sh
316
320
  # Harness pre-commit hook
317
-
318
- echo "🔍 Running harness checks..."
319
-
320
- STAGED=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)
321
-
322
- # 铁律检查
323
- npx @dommaker/harness check --staged
324
- if [ $? -ne 0 ]; then
325
- echo "❌ Iron law check failed"
326
- exit 1
327
- fi
328
-
329
- # Plan coverage check (via PostEval)
330
- if command -v npx > /dev/null 2>&1; then
331
- PLAN_FILES=$(echo "$STAGED" | grep -E 'plans/.*\\.md$|\\.plan\\.md$' || true)
332
- if [ -n "$PLAN_FILES" ]; then
333
- echo "📋 Checking plan coverage..."
334
- for plan in $PLAN_FILES; do
335
- npx @dommaker/harness posteval-plan "$plan" || {
336
- echo "🛑 Plan coverage incomplete. See above for missed items."
337
- exit 1
338
- }
339
- done
340
- fi
341
- fi
342
-
343
- echo "✅ All checks passed"
344
- `;
321
+ ${PRE_COMMIT_SNIPPET}`;
345
322
  await fs.writeFile(preCommitPath, preCommitContent, 'utf-8');
346
323
  await fs.chmod(preCommitPath, 0o755);
347
- console.log(chalk.green(`✅ 已创建 .git/hooks/pre-commit`));
324
+ log(io, chalk.green(`✅ 已创建 .git/hooks/pre-commit`));
348
325
  }
349
326
  }
350
327
 
351
328
  /**
352
329
  * 设置 GitHub Actions
353
330
  */
354
- async function setupGitHubActions(projectPath: string): Promise<void> {
331
+ async function setupGitHubActions(projectPath: string, io: CommandIO): Promise<void> {
355
332
  const workflowsDir = path.join(projectPath, '.github', 'workflows');
356
333
  const workflowPath = path.join(workflowsDir, 'harness-check.yml');
357
334
 
@@ -360,13 +337,13 @@ async function setupGitHubActions(projectPath: string): Promise<void> {
360
337
 
361
338
  if (existingFiles.length > 0) {
362
339
  // 已有 CI 配置,输出代码片段
363
- console.log(chalk.yellow('⚠️ 检测到已存在的 CI 配置:'));
340
+ log(io, chalk.yellow('⚠️ 检测到已存在的 CI 配置:'));
364
341
  existingFiles.forEach(f => {
365
- console.log(chalk.gray(` - .github/workflows/${f}`));
342
+ log(io, chalk.gray(` - .github/workflows/${f}`));
366
343
  });
367
- console.log(chalk.gray('💡 请手动添加以下内容到 jobs 中:'));
368
- console.log();
369
- console.log(chalk.cyan(GITHUB_ACTIONS_SNIPPET));
344
+ log(io, chalk.gray('💡 请手动添加以下内容到 jobs 中:'));
345
+ log(io);
346
+ log(io, chalk.cyan(GITHUB_ACTIONS_SNIPPET));
370
347
  return;
371
348
  }
372
349
 
@@ -406,7 +383,7 @@ jobs:
406
383
  `;
407
384
 
408
385
  await fs.writeFile(workflowPath, workflowContent, 'utf-8');
409
- console.log(chalk.green(`✅ 已创建 .github/workflows/harness-check.yml`));
386
+ log(io, chalk.green(`✅ 已创建 .github/workflows/harness-check.yml`));
410
387
  }
411
388
 
412
389
  /**
@@ -428,14 +405,14 @@ async function findCiWorkflows(workflowsDir: string): Promise<string[]> {
428
405
  /**
429
406
  * 创建自定义约束示例
430
407
  */
431
- async function createCustomConstraintsExample(projectPath: string): Promise<void> {
408
+ async function createCustomConstraintsExample(projectPath: string, io: CommandIO): Promise<void> {
432
409
  const configDir = path.join(projectPath, '.harness');
433
410
  const customConstraintsPath = path.join(configDir, 'custom-constraints.yml');
434
411
 
435
412
  // 如果已存在,不覆盖
436
413
  try {
437
414
  await fs.access(customConstraintsPath);
438
- console.log(chalk.gray(`custom-constraints.yml 已存在`));
415
+ log(io, chalk.gray(`custom-constraints.yml 已存在`));
439
416
  return;
440
417
  } catch {
441
418
  // 文件不存在,创建
@@ -477,7 +454,7 @@ custom_constraints:
477
454
  `;
478
455
 
479
456
  await fs.writeFile(customConstraintsPath, content, 'utf-8');
480
- console.log(chalk.green(`✅ 已创建自定义约束示例: custom-constraints.yml`));
457
+ log(io, chalk.green(`✅ 已创建自定义约束示例: custom-constraints.yml`));
481
458
  }
482
459
 
483
460
  /**
@@ -513,7 +490,7 @@ function renderOutputStyleSection(): string {
513
490
  * - 无标记且 `## Output Style` 段为用户自写(特征串不匹配):跳过并提示,不重复追加
514
491
  * - 完全没有该段:在文件顶部插入标记版
515
492
  */
516
- export async function setupClaudeMdOutputStyle(projectPath: string): Promise<void> {
493
+ export async function setupClaudeMdOutputStyle(projectPath: string, io: CommandIO = processIO): Promise<void> {
517
494
  const claudeMdPath = path.join(projectPath, 'CLAUDE.md');
518
495
  let content: string;
519
496
  try {
@@ -524,20 +501,20 @@ export async function setupClaudeMdOutputStyle(projectPath: string): Promise<voi
524
501
  }
525
502
 
526
503
  const section = renderOutputStyleSection();
527
- const startIdx = content.indexOf(OUTPUT_STYLE_START);
528
- const endIdx = content.indexOf(OUTPUT_STYLE_END);
529
-
530
- if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
531
- // 替换标记间内容(标记本身也替换,保持最新文本)。
532
- // 尾部换行规范化:标记后恰好保留一个换行分隔,保证重复运行幂等。
533
- const after = content.slice(endIdx + OUTPUT_STYLE_END.length).replace(/^\n+/, '');
534
- const newContent = content.slice(0, startIdx) + section + (after ? '\n' + after : '');
535
- if (newContent !== content) {
536
- await fs.writeFile(claudeMdPath, newContent, 'utf-8');
537
- console.log(chalk.green('✅ 已更新 CLAUDE.md Output Style 段'));
504
+ const write = replaceStandaloneRange(content, OUTPUT_STYLE_START, OUTPUT_STYLE_END, section);
505
+
506
+ if (write.kind === 'updated') {
507
+ // 尾部换行规范化在 writer 内(幂等)。
508
+ if (write.content !== content) {
509
+ await fs.writeFile(claudeMdPath, write.content, 'utf-8');
510
+ log(io, chalk.green('✅ 已更新 CLAUDE.md Output Style 段'));
538
511
  }
539
512
  return;
540
513
  }
514
+ if (write.kind === 'half') {
515
+ log(io, chalk.yellow('⚠️ CLAUDE.md 中 HARNESS_OUTPUT_STYLE 标记残缺(单边或乱序),跳过 Output Style 注入,请人工修复'));
516
+ return;
517
+ }
541
518
 
542
519
  // 检测旧版无标记 `## Output Style` 段
543
520
  const legacyMatch = /^## Output Style[ \t]*$/m.exec(content);
@@ -550,7 +527,7 @@ export async function setupClaudeMdOutputStyle(projectPath: string): Promise<voi
550
527
 
551
528
  if (!legacySection.includes(LEGACY_OUTPUT_STYLE_FINGERPRINT)) {
552
529
  // 用户自写的同名 section:不动,也不追加(避免重复)
553
- console.log(chalk.yellow('⚠️ CLAUDE.md 已存在自定义 "## Output Style" 段,跳过 harness Output Style 注入'));
530
+ log(io, chalk.yellow('⚠️ CLAUDE.md 已存在自定义 "## Output Style" 段,跳过 harness Output Style 注入'));
554
531
  return;
555
532
  }
556
533
 
@@ -559,13 +536,13 @@ export async function setupClaudeMdOutputStyle(projectPath: string): Promise<voi
559
536
  const after = content.slice(sectionEnd);
560
537
  const newContent = before + section + (after.length > 0 ? '\n' + after : '');
561
538
  await fs.writeFile(claudeMdPath, newContent, 'utf-8');
562
- console.log(chalk.green('✅ 已将 CLAUDE.md Output Style 段迁移为标记化管理'));
539
+ log(io, chalk.green('✅ 已将 CLAUDE.md Output Style 段迁移为标记化管理'));
563
540
  return;
564
541
  }
565
542
 
566
543
  // 完全没有该段:插入文件顶部
567
544
  await fs.writeFile(claudeMdPath, section + '\n' + content, 'utf-8');
568
- console.log(chalk.green('✅ 已在 CLAUDE.md 顶部写入 Output Style 段'));
545
+ log(io, chalk.green('✅ 已在 CLAUDE.md 顶部写入 Output Style 段'));
569
546
  }
570
547
 
571
548
  /**
@@ -584,15 +561,15 @@ const GOVERNANCE_PRESERVE_END = '<!-- /PRESERVE:governance -->';
584
561
  * 有标记则只替换标记区间,段内其余手写内容(治理契约引言/流程/纪律等)原样保留;
585
562
  * 无标记(纯手写段)则在段尾追加注入段,不动手写内容
586
563
  * - 无该段:在文件末尾追加
587
- * - 段标记残缺(只有单边标记):不写入,告警交由人工修复(防二次损坏)
564
+ * - 段标记残缺(外层或段内 HARNESS_CONSTRAINTS 单边/乱序):不写入,告警交由人工修复(防二次损坏)
588
565
  */
589
- export async function setupAgentsMdConstraints(projectPath: string): Promise<void> {
566
+ export async function setupAgentsMdConstraints(projectPath: string, io: CommandIO = processIO): Promise<void> {
590
567
  const agentsMdPath = path.join(projectPath, 'AGENTS.md');
591
- const version = getPackageVersion();
568
+ const version = getHarnessPackageVersion();
592
569
 
593
570
  const constraints = getEffectiveConstraints(projectPath);
594
571
  const bodyOnly = renderConstraintsSection(constraints, version);
595
- const block = `${GOVERNANCE_PRESERVE_BEGIN}\n## Governance Rules\n${bodyOnly}${GOVERNANCE_PRESERVE_END}\n`;
572
+ const block = `${GOVERNANCE_PRESERVE_BEGIN}\n${GOVERNANCE_HEADING}\n${bodyOnly}${GOVERNANCE_PRESERVE_END}\n`;
596
573
 
597
574
  let existingContent: string | null = null;
598
575
  try {
@@ -610,57 +587,47 @@ export async function setupAgentsMdConstraints(projectPath: string): Promise<voi
610
587
  block,
611
588
  ].join('\n');
612
589
  await fs.writeFile(agentsMdPath, skeleton, 'utf-8');
613
- console.log(chalk.green(`✅ 已创建 AGENTS.md 并写入治理契约 PRESERVE:governance 段 (v${version})`));
590
+ log(io, chalk.green(`✅ 已创建 AGENTS.md 并写入治理契约 PRESERVE:governance 段 (v${version})`));
614
591
  return;
615
592
  }
616
593
 
617
- const startIdx = existingContent.indexOf(GOVERNANCE_PRESERVE_BEGIN);
618
- const endIdx = existingContent.indexOf(GOVERNANCE_PRESERVE_END);
594
+ const preserve = cutMarkerBlock(existingContent, GOVERNANCE_PRESERVE_BEGIN, GOVERNANCE_PRESERVE_END);
619
595
 
620
- if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
621
- // 段内机器管理的只有 HARNESS_CONSTRAINTS 标记区间;其余手写内容原样保留
622
- const before = existingContent.slice(0, startIdx);
623
- const blockContent = existingContent.slice(startIdx + GOVERNANCE_PRESERVE_BEGIN.length, endIdx);
624
- const after = existingContent.slice(endIdx + GOVERNANCE_PRESERVE_END.length).replace(/^\n+/, '');
625
-
626
- const csIdx = blockContent.indexOf(CONSTRAINTS_START_MARKER);
627
- const ceIdx = blockContent.indexOf(CONSTRAINTS_END_MARKER);
628
- let newBlockContent: string;
629
- if (csIdx !== -1 && ceIdx !== -1 && ceIdx > csIdx) {
630
- // 只替换标记区间。bodyOnly 自带结尾换行,故剥掉尾部恰好一个前导换行
631
- // (END 标记行的行尾换行),其余手写内容逐字保留,保证幂等。
632
- newBlockContent =
633
- blockContent.slice(0, csIdx) +
634
- bodyOnly +
635
- blockContent.slice(ceIdx + CONSTRAINTS_END_MARKER.length).replace(/^\n/, '');
636
- } else {
637
- // 纯手写段(无约束标记):段尾追加注入段,手写内容不动
638
- newBlockContent = blockContent.trimEnd() + '\n\n## Governance Rules\n' + bodyOnly;
639
- }
640
-
641
- const newContent =
642
- before +
643
- GOVERNANCE_PRESERVE_BEGIN +
644
- newBlockContent +
645
- GOVERNANCE_PRESERVE_END +
646
- '\n' +
647
- (after ? '\n' + after : '');
648
- if (newContent !== existingContent) {
649
- await fs.writeFile(agentsMdPath, newContent, 'utf-8');
650
- console.log(chalk.green(`✅ 已更新 AGENTS.md 治理契约 PRESERVE:governance 段 (v${version})`));
651
- }
596
+ if (preserve === 'half') {
597
+ log(io, chalk.yellow('⚠️ AGENTS.md PRESERVE:governance 标记残缺(只有单边),跳过治理契约写入,请人工修复'));
652
598
  return;
653
599
  }
654
600
 
655
- if (startIdx !== -1 || endIdx !== -1) {
656
- console.log(chalk.yellow('⚠️ AGENTS.md PRESERVE:governance 标记残缺(只有单边),跳过治理契约写入,请人工修复'));
601
+ if (preserve !== 'absent') {
602
+ // 段内机器管理的只有 HARNESS_CONSTRAINTS 标记区间;其余手写内容原样保留
603
+ const inner = replaceEnclosedRange(preserve.inner, CONSTRAINTS_START_MARKER, CONSTRAINTS_END_MARKER, bodyOnly);
604
+ if (inner.kind === 'half') {
605
+ log(io, chalk.yellow('⚠️ AGENTS.md PRESERVE:governance 段内 HARNESS_CONSTRAINTS 标记残缺(单边或乱序),跳过治理契约写入,请人工修复'));
606
+ return;
607
+ }
608
+ const newInner =
609
+ inner.kind === 'updated'
610
+ ? inner.content
611
+ : // 纯手写段(无约束标记):段尾追加注入段,手写内容不动
612
+ preserve.inner.trimEnd() + '\n\n' + GOVERNANCE_HEADING + '\n' + bodyOnly;
613
+
614
+ const write = replaceStandaloneRange(
615
+ existingContent,
616
+ GOVERNANCE_PRESERVE_BEGIN,
617
+ GOVERNANCE_PRESERVE_END,
618
+ GOVERNANCE_PRESERVE_BEGIN + newInner + GOVERNANCE_PRESERVE_END + '\n'
619
+ );
620
+ if (write.kind === 'updated' && write.content !== existingContent) {
621
+ await fs.writeFile(agentsMdPath, write.content, 'utf-8');
622
+ log(io, chalk.green(`✅ 已更新 AGENTS.md 治理契约 PRESERVE:governance 段 (v${version})`));
623
+ }
657
624
  return;
658
625
  }
659
626
 
660
627
  // 无该段:文件末尾追加
661
628
  const newContent = existingContent.trimEnd() + '\n\n' + block;
662
629
  await fs.writeFile(agentsMdPath, newContent, 'utf-8');
663
- console.log(chalk.green(`✅ 已追加治理契约 PRESERVE:governance 段到 AGENTS.md (v${version})`));
630
+ log(io, chalk.green(`✅ 已追加治理契约 PRESERVE:governance 段到 AGENTS.md (v${version})`));
664
631
  }
665
632
 
666
633
  /**
@@ -673,23 +640,15 @@ export async function setupAgentsMdConstraints(projectPath: string): Promise<voi
673
640
  * - 如果存在 HARNESS_CONSTRAINTS_START/END 标记,替换标记间内容
674
641
  * - 如果不存在标记,在文件末尾追加约束段
675
642
  */
676
- export async function setupClaudeMdConstraints(projectPath: string): Promise<void> {
643
+ export async function setupClaudeMdConstraints(projectPath: string, io: CommandIO = processIO): Promise<void> {
677
644
  const claudeMdPath = path.join(projectPath, 'CLAUDE.md');
678
645
 
679
- // 读取 harness 版本
680
- let version = 'unknown';
681
- try {
682
- const pkgPath = path.join(__dirname, '../../../package.json');
683
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
684
- version = pkg.version;
685
- } catch {
686
- // fallback to 'unknown'
687
- }
646
+ const version = getHarnessPackageVersion();
688
647
 
689
648
  // 生效约束集 → 渲染期望段(纯函数,与写文件分离)
690
649
  const constraints = getEffectiveConstraints(projectPath);
691
650
  const bodyOnly = renderConstraintsSection(constraints, version);
692
- const fullSection = '## Governance Rules\n' + bodyOnly;
651
+ const fullSection = GOVERNANCE_HEADING + '\n' + bodyOnly;
693
652
 
694
653
  // 检查 CLAUDE.md 是否存在
695
654
  let existingContent: string;
@@ -704,65 +663,53 @@ export async function setupClaudeMdConstraints(projectPath: string): Promise<voi
704
663
  if (!fileExists) {
705
664
  // 创建新文件
706
665
  await fs.writeFile(claudeMdPath, fullSection, 'utf-8');
707
- console.log(chalk.green(`✅ 已创建 CLAUDE.md 并写入治理约束 (v${version})`));
666
+ log(io, chalk.green(`✅ 已创建 CLAUDE.md 并写入治理约束 (v${version})`));
708
667
  return;
709
668
  }
710
669
 
711
- const startIdx = existingContent.indexOf(CONSTRAINTS_START_MARKER);
712
- const endIdx = existingContent.indexOf(CONSTRAINTS_END_MARKER);
670
+ const write = replaceStandaloneRange(existingContent, CONSTRAINTS_START_MARKER, CONSTRAINTS_END_MARKER, bodyOnly);
713
671
 
714
- if (startIdx !== -1 && endIdx !== -1) {
715
- // 替换标记间内容(标记本身也替换,保持包括最新版本号)。
716
- // 尾部换行规范化:标记后恰好保留一个换行分隔,保证重复运行幂等。
717
- const before = existingContent.slice(0, startIdx);
718
- const after = existingContent.slice(endIdx + CONSTRAINTS_END_MARKER.length).replace(/^\n+/, '');
719
- const newContent = before + bodyOnly + (after ? '\n' + after : '');
720
- await fs.writeFile(claudeMdPath, newContent, 'utf-8');
721
- console.log(chalk.green(`✅ 已更新 CLAUDE.md 治理约束 (v${version})`));
672
+ if (write.kind === 'updated') {
673
+ // 替换标记区间含标记本身(保持包括最新版本号),尾部换行规范化在 writer 内
674
+ await fs.writeFile(claudeMdPath, write.content, 'utf-8');
675
+ log(io, chalk.green(`✅ 已更新 CLAUDE.md 治理约束 (v${version})`));
676
+ } else if (write.kind === 'half') {
677
+ log(io, chalk.yellow('⚠️ CLAUDE.md HARNESS_CONSTRAINTS 标记残缺(单边或乱序),跳过治理约束注入,请人工修复'));
722
678
  } else {
723
679
  // 在文件末尾追加完整段
724
680
  const newContent = existingContent.trimEnd() + '\n\n' + fullSection;
725
681
  await fs.writeFile(claudeMdPath, newContent, 'utf-8');
726
- console.log(chalk.green(`✅ 已追加治理约束到 CLAUDE.md (v${version})`));
682
+ log(io, chalk.green(`✅ 已追加治理约束到 CLAUDE.md (v${version})`));
727
683
  }
728
684
  }
729
685
 
730
686
  /**
731
687
  * 治理约束段写入落点路由(studio #302,ADR 2026-08-21 落点模型)
732
688
  *
689
+ * 判定收口在 core/constraints/injection-writer resolveGovernanceLanding:
733
690
  * - 旧模型仓(CLAUDE.md 已有 HARNESS_CONSTRAINTS 标记或 `## Governance Rules` 块):
734
691
  * 继续写 CLAUDE.md——init 幂等重跑不破坏既有仓,不制造双份约束正本
735
692
  * - 其余(新仓初始化):写 AGENTS.md PRESERVE:governance 段(入库公共面正本)
736
693
  */
737
694
  export async function setupGovernanceConstraints(projectPath: string): Promise<void> {
738
- let claudeContent: string | null = null;
739
- try {
740
- claudeContent = await fs.readFile(path.join(projectPath, 'CLAUDE.md'), 'utf-8');
741
- } catch {
742
- // CLAUDE.md 不存在 → 新仓
743
- }
744
-
745
- if (
746
- claudeContent !== null &&
747
- (claudeContent.includes(CONSTRAINTS_START_MARKER) || /^##\s+Governance Rules/m.test(claudeContent))
748
- ) {
749
- return setupClaudeMdConstraints(projectPath);
750
- }
751
- return setupAgentsMdConstraints(projectPath);
695
+ const { target } = resolveGovernanceLanding(projectPath);
696
+ return target === 'claude-md'
697
+ ? setupClaudeMdConstraints(projectPath)
698
+ : setupAgentsMdConstraints(projectPath);
752
699
  }
753
700
 
754
701
  /**
755
702
  * 设置治理相关文件
756
703
  */
757
- async function setupGovernance(projectPath: string, level: string): Promise<void> {
758
- const governance = GOVERNANCE_PRESETS[level] as Record<string, unknown>;
704
+ async function setupGovernance(projectPath: string, level: string, io: CommandIO): Promise<void> {
705
+ const governance = GOVERNANCE_PRESETS[level];
759
706
  if (!governance) return;
760
707
 
761
- console.log();
762
- console.log(chalk.blue('📋 设置治理文件...'));
708
+ log(io);
709
+ log(io, chalk.blue('📋 设置治理文件...'));
763
710
 
764
711
  // 1. 生成 CHANGELOG.md
765
- await createChangelog(projectPath, governance);
712
+ await createChangelog(projectPath, governance, io);
766
713
 
767
714
  // 2. 在 CLAUDE.md 中写入 Output Style 段(仅在不存在时创建)
768
715
  await setupClaudeMdOutputStyle(projectPath);
@@ -771,38 +718,37 @@ async function setupGovernance(projectPath: string, level: string): Promise<void
771
718
  // 旧模型仓 → CLAUDE.md,落点路由见 setupGovernanceConstraints)
772
719
  await setupGovernanceConstraints(projectPath);
773
720
 
774
- // 4. 生成 CONTEXT.md 文件
775
- const contextConfig = governance.context_files as Record<string, unknown> | undefined;
721
+ // 4. 生成 CONTEXT.md 文件(预设形状即 GovernanceConfig,无需再 cast)
722
+ const contextConfig = governance.context_files;
776
723
  if (contextConfig?.enabled) {
777
- let requiredDirs = (contextConfig.required_dirs as string[]) || [];
724
+ let requiredDirs = contextConfig.required_dirs ?? [];
778
725
  if (requiredDirs.length === 0) {
779
726
  requiredDirs = detectSourceRoots(projectPath);
780
727
  }
781
728
  for (const dir of requiredDirs) {
782
- await createContextMd(projectPath, dir);
729
+ await createContextMd(projectPath, dir, io);
783
730
  }
784
731
  }
785
732
 
786
733
  // 4. 生成治理 CI workflow
787
- await setupGovernanceWorkflow(projectPath, level);
734
+ await setupGovernanceWorkflow(projectPath, level, io);
788
735
  }
789
736
 
790
737
  /**
791
738
  * 创建 CHANGELOG.md
792
739
  */
793
- async function createChangelog(projectPath: string, governance: Record<string, unknown>): Promise<void> {
740
+ async function createChangelog(projectPath: string, governance: GovernanceConfig, io: CommandIO): Promise<void> {
794
741
  const changelogPath = path.join(projectPath, 'CHANGELOG.md');
795
742
 
796
743
  try {
797
744
  await fs.access(changelogPath);
798
- console.log(chalk.gray(`CHANGELOG.md 已存在`));
745
+ log(io, chalk.gray(`CHANGELOG.md 已存在`));
799
746
  return;
800
747
  } catch {
801
748
  // 文件不存在,创建
802
749
  }
803
750
 
804
- const changelogConfig = governance.changelog as Record<string, unknown> | undefined;
805
- const format = (changelogConfig?.format as string) || 'keep-a-changelog';
751
+ const format = governance.changelog?.format || 'keep-a-changelog';
806
752
 
807
753
  let content: string;
808
754
  if (format === 'keep-a-changelog') {
@@ -836,18 +782,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
836
782
  }
837
783
 
838
784
  await fs.writeFile(changelogPath, content, 'utf-8');
839
- console.log(chalk.green(`✅ 已创建 CHANGELOG.md`));
785
+ log(io, chalk.green(`✅ 已创建 CHANGELOG.md`));
840
786
  }
841
787
 
842
788
  /**
843
789
  * 在指定目录创建 CONTEXT.md
844
790
  */
845
- async function createContextMd(projectPath: string, dir: string): Promise<void> {
791
+ async function createContextMd(projectPath: string, dir: string, io: CommandIO): Promise<void> {
846
792
  const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
847
793
 
848
794
  try {
849
795
  await fs.access(contextPath);
850
- console.log(chalk.gray(`${dir}/CONTEXT.md 已存在`));
796
+ log(io, chalk.gray(`${dir}/CONTEXT.md 已存在`));
851
797
  return;
852
798
  } catch {
853
799
  // 文件不存在,创建
@@ -858,7 +804,7 @@ async function createContextMd(projectPath: string, dir: string): Promise<void>
858
804
  try {
859
805
  await fs.access(dirPath);
860
806
  } catch {
861
- console.log(chalk.yellow(`⚠️ 目录 ${dir} 不存在,跳过 CONTEXT.md`));
807
+ log(io, chalk.yellow(`⚠️ 目录 ${dir} 不存在,跳过 CONTEXT.md`));
862
808
  return;
863
809
  }
864
810
 
@@ -885,7 +831,7 @@ async function createContextMd(projectPath: string, dir: string): Promise<void>
885
831
  `;
886
832
 
887
833
  await fs.writeFile(contextPath, content, 'utf-8');
888
- console.log(chalk.green(`✅ 已创建 ${dir}/CONTEXT.md`));
834
+ log(io, chalk.green(`✅ 已创建 ${dir}/CONTEXT.md`));
889
835
  }
890
836
 
891
837
  /**
@@ -911,14 +857,14 @@ async function findGovernanceCoverage(workflowsDir: string): Promise<string | un
911
857
  /**
912
858
  * 设置治理 CI workflow
913
859
  */
914
- async function setupGovernanceWorkflow(projectPath: string, level: string): Promise<void> {
860
+ async function setupGovernanceWorkflow(projectPath: string, level: string, io: CommandIO): Promise<void> {
915
861
  const workflowsDir = path.join(projectPath, '.github', 'workflows');
916
862
  const workflowPath = path.join(workflowsDir, 'harness-governance.yml');
917
863
 
918
864
  // 检查是否已存在
919
865
  try {
920
866
  await fs.access(workflowPath);
921
- console.log(chalk.gray(`harness-governance.yml 已存在`));
867
+ log(io, chalk.gray(`harness-governance.yml 已存在`));
922
868
  return;
923
869
  } catch {
924
870
  // 不存在,继续创建
@@ -927,7 +873,7 @@ async function setupGovernanceWorkflow(projectPath: string, level: string): Prom
927
873
  // 能力检测:已有 workflow 已跑 harness 治理命令时跳过,避免重复 CI 面
928
874
  const coveredBy = await findGovernanceCoverage(workflowsDir);
929
875
  if (coveredBy) {
930
- console.log(chalk.gray(`治理检查已由 ${coveredBy} 覆盖,跳过创建 harness-governance.yml`));
876
+ log(io, chalk.gray(`治理检查已由 ${coveredBy} 覆盖,跳过创建 harness-governance.yml`));
931
877
  return;
932
878
  }
933
879
 
@@ -972,5 +918,5 @@ ${docsCheckStep}
972
918
  `;
973
919
 
974
920
  await fs.writeFile(workflowPath, workflowContent, 'utf-8');
975
- console.log(chalk.green(`✅ 已创建 .github/workflows/harness-governance.yml`));
921
+ log(io, chalk.green(`✅ 已创建 .github/workflows/harness-governance.yml`));
976
922
  }