@dommaker/harness 1.3.0 → 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 (517) hide show
  1. package/CHANGELOG.md +8 -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 +29 -214
  43. package/dist/cli/commands/definitions.js.map +1 -1
  44. package/dist/cli/commands/failure.d.ts +5 -4
  45. package/dist/cli/commands/failure.d.ts.map +1 -1
  46. package/dist/cli/commands/failure.js +31 -25
  47. package/dist/cli/commands/failure.js.map +1 -1
  48. package/dist/cli/commands/init.d.ts +7 -5
  49. package/dist/cli/commands/init.d.ts.map +1 -1
  50. package/dist/cli/commands/init.js +147 -211
  51. package/dist/cli/commands/init.js.map +1 -1
  52. package/dist/cli/commands/knowledge.d.ts +21 -15
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +214 -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 +69 -59
  71. package/dist/cli/commands/release.js.map +1 -1
  72. package/dist/cli/commands/report.d.ts +2 -1
  73. package/dist/cli/commands/report.d.ts.map +1 -1
  74. package/dist/cli/commands/report.js +9 -5
  75. package/dist/cli/commands/report.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +3 -2
  77. package/dist/cli/commands/review.d.ts.map +1 -1
  78. package/dist/cli/commands/review.js +33 -29
  79. package/dist/cli/commands/review.js.map +1 -1
  80. package/dist/cli/commands/sdd.d.ts +2 -1
  81. package/dist/cli/commands/sdd.d.ts.map +1 -1
  82. package/dist/cli/commands/sdd.js +7 -5
  83. package/dist/cli/commands/sdd.js.map +1 -1
  84. package/dist/cli/commands/security.d.ts +3 -2
  85. package/dist/cli/commands/security.d.ts.map +1 -1
  86. package/dist/cli/commands/security.js +29 -26
  87. package/dist/cli/commands/security.js.map +1 -1
  88. package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
  89. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  90. package/dist/cli/commands/spec-baseline-check.js +15 -11
  91. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  92. package/dist/cli/commands/spec.d.ts +7 -2
  93. package/dist/cli/commands/spec.d.ts.map +1 -1
  94. package/dist/cli/commands/spec.js +61 -51
  95. package/dist/cli/commands/spec.js.map +1 -1
  96. package/dist/cli/commands/status.d.ts +2 -1
  97. package/dist/cli/commands/status.d.ts.map +1 -1
  98. package/dist/cli/commands/status.js +57 -53
  99. package/dist/cli/commands/status.js.map +1 -1
  100. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  101. package/dist/cli/commands/sync-docs/agents-syncer.js +5 -2
  102. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  103. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  104. package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
  105. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  106. package/dist/cli/commands/sync-docs/index.d.ts +2 -1
  107. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  108. package/dist/cli/commands/sync-docs/index.js +68 -88
  109. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  110. package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
  111. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  112. package/dist/cli/commands/sync-docs/project-reader.js +14 -16
  113. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  114. package/dist/cli/commands/update-user-model.d.ts +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/context/session-manager.d.ts.map +1 -1
  127. package/dist/context/session-manager.js +19 -32
  128. package/dist/context/session-manager.js.map +1 -1
  129. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  130. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  131. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  132. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  133. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  134. package/dist/core/constraints/capabilities-parser.js +32 -28
  135. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  136. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  137. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  138. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  139. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  140. package/dist/core/constraints/checker.d.ts +21 -43
  141. package/dist/core/constraints/checker.d.ts.map +1 -1
  142. package/dist/core/constraints/checker.js +64 -115
  143. package/dist/core/constraints/checker.js.map +1 -1
  144. package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
  145. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  146. package/dist/core/constraints/checkers/capability-sync.js +29 -69
  147. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  148. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  149. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  150. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  151. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  152. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  153. package/dist/core/constraints/checkers/docs-freshness.js +37 -63
  154. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  155. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  156. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  158. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  159. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  160. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  161. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  162. package/dist/core/constraints/checkers/types.d.ts +2 -2
  163. package/dist/core/constraints/context-builder.d.ts +15 -14
  164. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  165. package/dist/core/constraints/context-builder.js +32 -96
  166. package/dist/core/constraints/context-builder.js.map +1 -1
  167. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  168. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  169. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  170. package/dist/core/constraints/git-evidence.d.ts +60 -0
  171. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  172. package/dist/core/constraints/git-evidence.js +88 -0
  173. package/dist/core/constraints/git-evidence.js.map +1 -0
  174. package/dist/core/constraints/injection-drift.d.ts +3 -20
  175. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  176. package/dist/core/constraints/injection-drift.js +10 -57
  177. package/dist/core/constraints/injection-drift.js.map +1 -1
  178. package/dist/core/constraints/injection-writer.d.ts +113 -0
  179. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  180. package/dist/core/constraints/injection-writer.js +228 -0
  181. package/dist/core/constraints/injection-writer.js.map +1 -0
  182. package/dist/core/constraints/usage-report.d.ts +22 -1
  183. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  184. package/dist/core/constraints/usage-report.js +27 -20
  185. package/dist/core/constraints/usage-report.js.map +1 -1
  186. package/dist/core/index.d.ts +2 -2
  187. package/dist/core/index.d.ts.map +1 -1
  188. package/dist/core/index.js +4 -2
  189. package/dist/core/index.js.map +1 -1
  190. package/dist/core/project-config-loader.d.ts +32 -1
  191. package/dist/core/project-config-loader.d.ts.map +1 -1
  192. package/dist/core/project-config-loader.js +39 -9
  193. package/dist/core/project-config-loader.js.map +1 -1
  194. package/dist/core/validators/passes-gate.d.ts +15 -64
  195. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  196. package/dist/core/validators/passes-gate.js +84 -222
  197. package/dist/core/validators/passes-gate.js.map +1 -1
  198. package/dist/core/validators/test-output.d.ts +58 -0
  199. package/dist/core/validators/test-output.d.ts.map +1 -0
  200. package/dist/core/validators/test-output.js +109 -0
  201. package/dist/core/validators/test-output.js.map +1 -0
  202. package/dist/failure/recorder.d.ts +6 -0
  203. package/dist/failure/recorder.d.ts.map +1 -1
  204. package/dist/failure/recorder.js +12 -8
  205. package/dist/failure/recorder.js.map +1 -1
  206. package/dist/gates/acceptance.d.ts +1 -48
  207. package/dist/gates/acceptance.d.ts.map +1 -1
  208. package/dist/gates/acceptance.js +6 -38
  209. package/dist/gates/acceptance.js.map +1 -1
  210. package/dist/gates/checker-gate.d.ts.map +1 -1
  211. package/dist/gates/checker-gate.js +6 -11
  212. package/dist/gates/checker-gate.js.map +1 -1
  213. package/dist/gates/command.d.ts.map +1 -1
  214. package/dist/gates/command.js +10 -22
  215. package/dist/gates/command.js.map +1 -1
  216. package/dist/gates/contract.d.ts.map +1 -1
  217. package/dist/gates/contract.js +22 -61
  218. package/dist/gates/contract.js.map +1 -1
  219. package/dist/gates/performance.d.ts.map +1 -1
  220. package/dist/gates/performance.js +13 -31
  221. package/dist/gates/performance.js.map +1 -1
  222. package/dist/gates/review.d.ts.map +1 -1
  223. package/dist/gates/review.js +21 -53
  224. package/dist/gates/review.js.map +1 -1
  225. package/dist/gates/security.d.ts.map +1 -1
  226. package/dist/gates/security.js +14 -32
  227. package/dist/gates/security.js.map +1 -1
  228. package/dist/gates/types.d.ts +28 -5
  229. package/dist/gates/types.d.ts.map +1 -1
  230. package/dist/gates/types.js +38 -1
  231. package/dist/gates/types.js.map +1 -1
  232. package/dist/hooks/bootstrap.d.ts +2 -0
  233. package/dist/hooks/bootstrap.d.ts.map +1 -1
  234. package/dist/hooks/bootstrap.js +5 -2
  235. package/dist/hooks/bootstrap.js.map +1 -1
  236. package/dist/index.d.ts +1 -2
  237. package/dist/index.d.ts.map +1 -1
  238. package/dist/index.js.map +1 -1
  239. package/dist/knowledge/audit.d.ts +7 -2
  240. package/dist/knowledge/audit.d.ts.map +1 -1
  241. package/dist/knowledge/audit.js +59 -79
  242. package/dist/knowledge/audit.js.map +1 -1
  243. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  244. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  245. package/dist/knowledge/flywheel-metrics.js +47 -0
  246. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  247. package/dist/knowledge/import.d.ts.map +1 -1
  248. package/dist/knowledge/import.js +4 -14
  249. package/dist/knowledge/import.js.map +1 -1
  250. package/dist/knowledge/index-generator.d.ts.map +1 -1
  251. package/dist/knowledge/index-generator.js +17 -14
  252. package/dist/knowledge/index-generator.js.map +1 -1
  253. package/dist/knowledge/ingest.d.ts.map +1 -1
  254. package/dist/knowledge/ingest.js +9 -4
  255. package/dist/knowledge/ingest.js.map +1 -1
  256. package/dist/knowledge/migration.d.ts.map +1 -1
  257. package/dist/knowledge/migration.js +11 -8
  258. package/dist/knowledge/migration.js.map +1 -1
  259. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  260. package/dist/knowledge/reference-tracker.js +12 -9
  261. package/dist/knowledge/reference-tracker.js.map +1 -1
  262. package/dist/knowledge/store.d.ts +15 -0
  263. package/dist/knowledge/store.d.ts.map +1 -1
  264. package/dist/knowledge/store.js +59 -10
  265. package/dist/knowledge/store.js.map +1 -1
  266. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  267. package/dist/monitoring/context-tracker.js +11 -20
  268. package/dist/monitoring/context-tracker.js.map +1 -1
  269. package/dist/monitoring/index.d.ts +1 -1
  270. package/dist/monitoring/index.d.ts.map +1 -1
  271. package/dist/monitoring/index.js +1 -2
  272. package/dist/monitoring/index.js.map +1 -1
  273. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  274. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  275. package/dist/monitoring/trace-analyzer.js +19 -2
  276. package/dist/monitoring/trace-analyzer.js.map +1 -1
  277. package/dist/monitoring/traces.d.ts +21 -2
  278. package/dist/monitoring/traces.d.ts.map +1 -1
  279. package/dist/monitoring/traces.js +38 -29
  280. package/dist/monitoring/traces.js.map +1 -1
  281. package/dist/release/index.d.ts +2 -2
  282. package/dist/release/index.d.ts.map +1 -1
  283. package/dist/release/index.js +1 -5
  284. package/dist/release/index.js.map +1 -1
  285. package/dist/sdd/index-generator.js +10 -11
  286. package/dist/sdd/index-generator.js.map +1 -1
  287. package/dist/test-setup/project-fixture.d.ts +48 -0
  288. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  289. package/dist/test-setup/project-fixture.js +106 -0
  290. package/dist/test-setup/project-fixture.js.map +1 -0
  291. package/dist/types/constraint.d.ts +0 -18
  292. package/dist/types/constraint.d.ts.map +1 -1
  293. package/dist/types/constraint.js.map +1 -1
  294. package/dist/types/passes-gate.d.ts +10 -25
  295. package/dist/types/passes-gate.d.ts.map +1 -1
  296. package/dist/types/project-config.d.ts +1 -1
  297. package/dist/types/session.d.ts +6 -5
  298. package/dist/types/session.d.ts.map +1 -1
  299. package/dist/utils/exec.d.ts +10 -0
  300. package/dist/utils/exec.d.ts.map +1 -1
  301. package/dist/utils/exec.js +12 -0
  302. package/dist/utils/exec.js.map +1 -1
  303. package/dist/utils/file-walk.d.ts +9 -1
  304. package/dist/utils/file-walk.d.ts.map +1 -1
  305. package/dist/utils/file-walk.js +10 -1
  306. package/dist/utils/file-walk.js.map +1 -1
  307. package/dist/utils/frontmatter.d.ts +73 -0
  308. package/dist/utils/frontmatter.d.ts.map +1 -0
  309. package/dist/utils/frontmatter.js +141 -0
  310. package/dist/utils/frontmatter.js.map +1 -0
  311. package/dist/utils/jsonl.d.ts +92 -0
  312. package/dist/utils/jsonl.d.ts.map +1 -0
  313. package/dist/utils/jsonl.js +166 -0
  314. package/dist/utils/jsonl.js.map +1 -0
  315. package/dist/utils/package-version.d.ts +16 -0
  316. package/dist/utils/package-version.d.ts.map +1 -0
  317. package/dist/utils/package-version.js +63 -0
  318. package/dist/utils/package-version.js.map +1 -0
  319. package/package.json +1 -1
  320. package/src/CONTEXT.md +7 -2
  321. package/src/__tests__/acceptance-gate.test.ts +3 -1
  322. package/src/__tests__/checker-extra.test.ts +7 -1218
  323. package/src/__tests__/context-files-resolution.test.ts +116 -0
  324. package/src/__tests__/effective-constraints.test.ts +10 -13
  325. package/src/__tests__/exec.test.ts +35 -1
  326. package/src/__tests__/layering.test.ts +152 -0
  327. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  328. package/src/__tests__/passes-gate.test.ts +142 -106
  329. package/src/__tests__/skip-semantics.test.ts +5 -9
  330. package/src/cli/__tests__/command-contract.test.ts +170 -0
  331. package/src/cli/command-contract.ts +108 -0
  332. package/src/cli/commands/CONTEXT.md +22 -7
  333. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  334. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  335. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
  336. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  337. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  338. package/src/cli/commands/__tests__/check.test.ts +378 -382
  339. package/src/cli/commands/__tests__/command.test.ts +85 -70
  340. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  341. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  342. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  343. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  344. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  345. package/src/cli/commands/__tests__/init.test.ts +77 -48
  346. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  347. package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
  348. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  349. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  350. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  351. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  352. package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
  353. package/src/cli/commands/__tests__/registry.test.ts +69 -35
  354. package/src/cli/commands/__tests__/release.test.ts +209 -80
  355. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  356. package/src/cli/commands/__tests__/report.test.ts +25 -25
  357. package/src/cli/commands/__tests__/review.test.ts +33 -31
  358. package/src/cli/commands/__tests__/security.test.ts +23 -27
  359. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  360. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  361. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  362. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  363. package/src/cli/commands/__tests__/status.test.ts +34 -34
  364. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
  365. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  366. package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
  367. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  368. package/src/cli/commands/acceptance.ts +35 -24
  369. package/src/cli/commands/analyze-sessions.ts +25 -24
  370. package/src/cli/commands/check.ts +88 -65
  371. package/src/cli/commands/command.ts +30 -17
  372. package/src/cli/commands/constraints-report.ts +37 -34
  373. package/src/cli/commands/constraints-retire.ts +58 -55
  374. package/src/cli/commands/constraints.ts +10 -27
  375. package/src/cli/commands/contract.ts +51 -42
  376. package/src/cli/commands/definitions.ts +36 -222
  377. package/src/cli/commands/failure.ts +33 -26
  378. package/src/cli/commands/init.ts +166 -220
  379. package/src/cli/commands/knowledge.ts +225 -203
  380. package/src/cli/commands/passes-gate.ts +58 -68
  381. package/src/cli/commands/performance.ts +23 -18
  382. package/src/cli/commands/posteval-plan.ts +29 -15
  383. package/src/cli/commands/release.ts +70 -59
  384. package/src/cli/commands/report.ts +9 -5
  385. package/src/cli/commands/review.ts +39 -29
  386. package/src/cli/commands/sdd.ts +7 -5
  387. package/src/cli/commands/security.ts +35 -26
  388. package/src/cli/commands/spec-baseline-check.ts +16 -11
  389. package/src/cli/commands/spec.ts +66 -53
  390. package/src/cli/commands/status.ts +59 -53
  391. package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
  392. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  393. package/src/cli/commands/sync-docs/index.ts +73 -100
  394. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  395. package/src/cli/commands/update-user-model.ts +33 -45
  396. package/src/cli/commands/validate.ts +31 -25
  397. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  398. package/src/cli/session-mining/transcript.ts +26 -8
  399. package/src/context/session-manager.ts +19 -33
  400. package/src/core/CONTEXT.md +9 -6
  401. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  402. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  403. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  404. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  405. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  406. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  407. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  408. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  409. package/src/core/constraints/capabilities-parser.ts +53 -35
  410. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  411. package/src/core/constraints/checker.ts +75 -130
  412. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
  413. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  414. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
  415. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  416. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  417. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  418. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  419. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  420. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
  421. package/src/core/constraints/checkers/capability-sync.ts +29 -88
  422. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  423. package/src/core/constraints/checkers/docs-freshness.ts +40 -69
  424. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  425. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  426. package/src/core/constraints/checkers/types.ts +2 -2
  427. package/src/core/constraints/context-builder.ts +39 -91
  428. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  429. package/src/core/constraints/git-evidence.ts +120 -0
  430. package/src/core/constraints/injection-drift.ts +13 -60
  431. package/src/core/constraints/injection-writer.ts +212 -0
  432. package/src/core/constraints/usage-report.ts +36 -17
  433. package/src/core/index.ts +4 -2
  434. package/src/core/project-config-loader.ts +49 -9
  435. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  436. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  437. package/src/core/validators/passes-gate.ts +86 -248
  438. package/src/core/validators/test-output.ts +128 -0
  439. package/src/failure/__tests__/recorder.test.ts +168 -0
  440. package/src/failure/recorder.ts +12 -10
  441. package/src/gates/CONTEXT.md +6 -3
  442. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  443. package/src/gates/__tests__/acceptance.test.ts +4 -86
  444. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  445. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  446. package/src/gates/acceptance.ts +19 -84
  447. package/src/gates/checker-gate.ts +8 -7
  448. package/src/gates/command.ts +10 -16
  449. package/src/gates/contract.ts +22 -61
  450. package/src/gates/performance.ts +10 -22
  451. package/src/gates/review.ts +21 -41
  452. package/src/gates/security.ts +10 -22
  453. package/src/gates/types.ts +70 -5
  454. package/src/hooks/CONTEXT.md +1 -1
  455. package/src/hooks/bootstrap.ts +5 -2
  456. package/src/index.ts +0 -4
  457. package/src/knowledge/CONTEXT.md +8 -3
  458. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  459. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  460. package/src/knowledge/__tests__/import.test.ts +44 -0
  461. package/src/knowledge/__tests__/index-generator.test.ts +53 -0
  462. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  463. package/src/knowledge/__tests__/migration.test.ts +47 -0
  464. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  465. package/src/knowledge/__tests__/store.test.ts +194 -0
  466. package/src/knowledge/audit.ts +67 -71
  467. package/src/knowledge/flywheel-metrics.ts +71 -0
  468. package/src/knowledge/import.ts +4 -18
  469. package/src/knowledge/index-generator.ts +19 -14
  470. package/src/knowledge/ingest.ts +9 -4
  471. package/src/knowledge/migration.ts +11 -8
  472. package/src/knowledge/reference-tracker.ts +12 -8
  473. package/src/knowledge/store.ts +64 -11
  474. package/src/monitoring/CONTEXT.md +8 -3
  475. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  476. package/src/monitoring/__tests__/traces.test.ts +57 -0
  477. package/src/monitoring/context-tracker.ts +11 -23
  478. package/src/monitoring/index.ts +1 -1
  479. package/src/monitoring/trace-analyzer.ts +21 -2
  480. package/src/monitoring/traces.ts +39 -36
  481. package/src/release/CONTEXT.md +1 -0
  482. package/src/release/__tests__/barrel.test.ts +60 -0
  483. package/src/release/index.ts +2 -13
  484. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  485. package/src/sdd/index-generator.ts +9 -9
  486. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  487. package/src/test-setup/project-fixture.ts +87 -0
  488. package/src/tools/CONTEXT.md +6 -0
  489. package/src/tools/__tests__/registry.test.ts +221 -0
  490. package/src/types/__tests__/types.test.ts +4 -5
  491. package/src/types/constraint.ts +0 -25
  492. package/src/types/passes-gate.ts +10 -30
  493. package/src/types/project-config.ts +1 -1
  494. package/src/types/session.ts +5 -5
  495. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  496. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  497. package/src/utils/__tests__/jsonl.test.ts +187 -0
  498. package/src/utils/__tests__/package-version.test.ts +97 -0
  499. package/src/utils/exec.ts +15 -0
  500. package/src/utils/file-walk.ts +11 -2
  501. package/src/utils/frontmatter.ts +121 -0
  502. package/src/utils/jsonl.ts +167 -0
  503. package/src/utils/package-version.ts +27 -0
  504. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  505. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  506. package/dist/cli/commands/doc-freshness-check.js +0 -352
  507. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  508. package/dist/types/index.d.ts +0 -14
  509. package/dist/types/index.d.ts.map +0 -1
  510. package/dist/types/index.js +0 -39
  511. package/dist/types/index.js.map +0 -1
  512. package/src/__tests__/long-running.test.ts +0 -17
  513. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  514. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  515. package/src/cli/commands/doc-freshness-check.ts +0 -386
  516. package/src/spec/CONTEXT.md +0 -11
  517. package/src/types/index.ts +0 -47
@@ -43,6 +43,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
44
  exports.knowledgeList = knowledgeList;
45
45
  exports.knowledgeSearch = knowledgeSearch;
46
+ exports.knowledgeSearchCommand = knowledgeSearchCommand;
46
47
  exports.knowledgeImport = knowledgeImport;
47
48
  exports.knowledgeDecay = knowledgeDecay;
48
49
  exports.knowledgeStats = knowledgeStats;
@@ -63,13 +64,15 @@ const query_1 = require("../../knowledge/query");
63
64
  const lifecycle_1 = require("../../knowledge/lifecycle");
64
65
  const import_1 = require("../../knowledge/import");
65
66
  const audit_1 = require("../../knowledge/audit");
67
+ const flywheel_metrics_1 = require("../../knowledge/flywheel-metrics");
66
68
  const migration_1 = require("../../knowledge/migration");
67
69
  const index_generator_1 = require("../../knowledge/index-generator");
70
+ const command_contract_1 = require("../command-contract");
68
71
  /**
69
72
  * 知识库列表
70
73
  */
71
- async function knowledgeList(options) {
72
- const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
74
+ async function knowledgeList(options, io = command_contract_1.processIO) {
75
+ const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
73
76
  const filter = { excludeArchived: false };
74
77
  if (options.type) {
75
78
  filter.types = options.type.split(',');
@@ -82,54 +85,72 @@ async function knowledgeList(options) {
82
85
  }
83
86
  const entries = store.list(filter);
84
87
  if (options.json) {
85
- console.log(JSON.stringify({ total: entries.length, entries }, null, 2));
86
- return;
88
+ (0, command_contract_1.log)(io, JSON.stringify({ total: entries.length, entries }, null, 2));
89
+ return { kind: 'ok' };
87
90
  }
88
91
  if (entries.length === 0) {
89
- console.log(chalk_1.default.yellow('知识库为空'));
90
- return;
92
+ (0, command_contract_1.log)(io, chalk_1.default.yellow('知识库为空'));
93
+ return { kind: 'ok' };
91
94
  }
92
- console.log(chalk_1.default.blue(`📚 知识库 (${entries.length} 条)\n`));
95
+ (0, command_contract_1.log)(io, chalk_1.default.blue(`📚 知识库 (${entries.length} 条)\n`));
93
96
  for (const entry of entries) {
94
97
  const maturityColor = entry.maturity === 'proven' ? chalk_1.default.green
95
98
  : entry.maturity === 'verified' ? chalk_1.default.cyan
96
99
  : entry.maturity === 'archived' ? chalk_1.default.gray
97
100
  : chalk_1.default.yellow;
98
- console.log(` ${maturityColor(`[${entry.maturity}]`)} ${chalk_1.default.bold(entry.title)}`);
99
- console.log(` ${chalk_1.default.gray(`id: ${entry.id} | type: ${entry.type} | layer: ${entry.layer}`)}`);
101
+ (0, command_contract_1.log)(io, ` ${maturityColor(`[${entry.maturity}]`)} ${chalk_1.default.bold(entry.title)}`);
102
+ (0, command_contract_1.log)(io, ` ${chalk_1.default.gray(`id: ${entry.id} | type: ${entry.type} | layer: ${entry.layer}`)}`);
100
103
  if (entry.tags.length > 0) {
101
- console.log(` ${chalk_1.default.gray(`tags: ${entry.tags.join(', ')}`)}`);
104
+ (0, command_contract_1.log)(io, ` ${chalk_1.default.gray(`tags: ${entry.tags.join(', ')}`)}`);
102
105
  }
103
106
  }
107
+ return { kind: 'ok' };
104
108
  }
105
109
  /**
106
110
  * 知识库搜索
107
111
  */
108
- async function knowledgeSearch(query, options) {
109
- const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
112
+ async function knowledgeSearch(query, options, io = command_contract_1.processIO) {
113
+ const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
110
114
  const queryEngine = new query_1.KnowledgeQuery(store);
111
115
  const matched = queryEngine.search(query, { limit: options.limit || 20 });
112
116
  if (options.json) {
113
- console.log(JSON.stringify({ query, total: matched.length, entries: matched }, null, 2));
114
- return;
117
+ (0, command_contract_1.log)(io, JSON.stringify({ query, total: matched.length, entries: matched }, null, 2));
118
+ return { kind: 'ok' };
115
119
  }
116
120
  if (matched.length === 0) {
117
- console.log(chalk_1.default.yellow(`未找到匹配 "${query}" 的知识条目`));
118
- return;
121
+ (0, command_contract_1.log)(io, chalk_1.default.yellow(`未找到匹配 "${query}" 的知识条目`));
122
+ return { kind: 'ok' };
119
123
  }
120
- console.log(chalk_1.default.blue(`🔍 搜索 "${query}" (${matched.length} 条结果)\n`));
124
+ (0, command_contract_1.log)(io, chalk_1.default.blue(`🔍 搜索 "${query}" (${matched.length} 条结果)\n`));
121
125
  for (const entry of matched) {
122
- console.log(` ${chalk_1.default.bold(entry.title)} ${chalk_1.default.gray(`[${entry.maturity}]`)}`);
126
+ (0, command_contract_1.log)(io, ` ${chalk_1.default.bold(entry.title)} ${chalk_1.default.gray(`[${entry.maturity}]`)}`);
123
127
  const preview = entry.content.slice(0, 100).replace(/\n/g, ' ');
124
- console.log(` ${chalk_1.default.gray(preview)}${entry.content.length > 100 ? '...' : ''}`);
128
+ (0, command_contract_1.log)(io, ` ${chalk_1.default.gray(preview)}${entry.content.length > 100 ? '...' : ''}`);
125
129
  }
130
+ return { kind: 'ok' };
131
+ }
132
+ /**
133
+ * knowledge search 子命令入口(候选7):缺参闸门 + limit 强转编组,
134
+ * 自 definitions.ts 的 args 复印块移回命令模块(interface/测试面所在)。
135
+ */
136
+ async function knowledgeSearchCommand(positionals, options, io = command_contract_1.processIO) {
137
+ const query = positionals[0];
138
+ if (!query) {
139
+ (0, command_contract_1.logError)(io, '请提供搜索关键词');
140
+ return { kind: 'usage-error', reason: 'knowledge search 缺少关键词位置参数' };
141
+ }
142
+ return knowledgeSearch(String(query), {
143
+ projectPath: options.projectPath,
144
+ json: options.json,
145
+ limit: parseInt(String(options.limit), 10),
146
+ });
126
147
  }
127
148
  /**
128
149
  * 知识库导入(冷启动)
129
150
  */
130
- async function knowledgeImport(options) {
151
+ async function knowledgeImport(options, io = command_contract_1.processIO) {
131
152
  const projectPath = options.projectPath || process.cwd();
132
- const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
153
+ const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
133
154
  const sources = options.sources
134
155
  ? options.sources.split(',')
135
156
  : ['code', 'git', 'docs'];
@@ -141,60 +162,62 @@ async function knowledgeImport(options) {
141
162
  if (options.reset) {
142
163
  importer.resetState();
143
164
  if (!options.json) {
144
- console.log(chalk_1.default.yellow('🔄 已重置导入状态'));
165
+ (0, command_contract_1.log)(io, chalk_1.default.yellow('🔄 已重置导入状态'));
145
166
  }
146
167
  }
147
168
  if (!options.json) {
148
- console.log(chalk_1.default.blue(`📥 开始导入知识 (源: ${sources.join(', ')})...`));
169
+ (0, command_contract_1.log)(io, chalk_1.default.blue(`📥 开始导入知识 (源: ${sources.join(', ')})...`));
149
170
  }
150
171
  const results = await importer.importAll();
151
172
  const totalImported = results.reduce((sum, r) => sum + r.entries.length, 0);
152
173
  const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
153
174
  if (options.json) {
154
- console.log(JSON.stringify({ totalImported, totalErrors, results }, null, 2));
155
- return;
175
+ (0, command_contract_1.log)(io, JSON.stringify({ totalImported, totalErrors, results }, null, 2));
176
+ return { kind: 'ok' };
156
177
  }
157
178
  for (const result of results) {
158
179
  if (result.entries.length > 0) {
159
- console.log(chalk_1.default.green(` ✅ ${result.source.type}: ${result.entries.length} 条`));
180
+ (0, command_contract_1.log)(io, chalk_1.default.green(` ✅ ${result.source.type}: ${result.entries.length} 条`));
160
181
  }
161
182
  if (result.errors.length > 0) {
162
183
  for (const err of result.errors) {
163
- console.log(chalk_1.default.red(` ❌ ${result.source.type}: ${err.message}`));
184
+ (0, command_contract_1.log)(io, chalk_1.default.red(` ❌ ${result.source.type}: ${err.message}`));
164
185
  }
165
186
  }
166
187
  }
167
- console.log(chalk_1.default.green(`\n✅ 导入完成: ${totalImported} 条,${totalErrors} 个错误`));
188
+ (0, command_contract_1.log)(io, chalk_1.default.green(`\n✅ 导入完成: ${totalImported} 条,${totalErrors} 个错误`));
189
+ return { kind: 'ok' };
168
190
  }
169
191
  /**
170
192
  * 知识库衰减周期
171
193
  */
172
- async function knowledgeDecay(options) {
173
- const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
194
+ async function knowledgeDecay(options, io = command_contract_1.processIO) {
195
+ const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
174
196
  const lifecycle = new lifecycle_1.KnowledgeLifecycle(store);
175
197
  if (!options.json) {
176
- console.log(chalk_1.default.blue('🔄 运行衰减周期...'));
198
+ (0, command_contract_1.log)(io, chalk_1.default.blue('🔄 运行衰减周期...'));
177
199
  }
178
200
  const changes = lifecycle.runDecayCycle();
179
201
  if (options.json) {
180
- console.log(JSON.stringify({ changes }, null, 2));
181
- return;
202
+ (0, command_contract_1.log)(io, JSON.stringify({ changes }, null, 2));
203
+ return { kind: 'ok' };
182
204
  }
183
205
  if (changes.length === 0) {
184
- console.log(chalk_1.default.green('✅ 没有需要衰减的知识条目'));
185
- return;
206
+ (0, command_contract_1.log)(io, chalk_1.default.green('✅ 没有需要衰减的知识条目'));
207
+ return { kind: 'ok' };
186
208
  }
187
- console.log(chalk_1.default.yellow(`📉 ${changes.length} 条知识发生衰减:\n`));
209
+ (0, command_contract_1.log)(io, chalk_1.default.yellow(`📉 ${changes.length} 条知识发生衰减:\n`));
188
210
  for (const change of changes) {
189
- console.log(` ${change.entryId}: ${chalk_1.default.red(change.from)} → ${chalk_1.default.green(change.to)}`);
190
- console.log(` ${chalk_1.default.gray(change.reason)}`);
211
+ (0, command_contract_1.log)(io, ` ${change.entryId}: ${chalk_1.default.red(change.from)} → ${chalk_1.default.green(change.to)}`);
212
+ (0, command_contract_1.log)(io, ` ${chalk_1.default.gray(change.reason)}`);
191
213
  }
214
+ return { kind: 'ok' };
192
215
  }
193
216
  /**
194
217
  * 知识库统计
195
218
  */
196
- async function knowledgeStats(options) {
197
- const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
219
+ async function knowledgeStats(options, io = command_contract_1.processIO) {
220
+ const store = new store_1.FileKnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
198
221
  const entries = store.list({ excludeArchived: false });
199
222
  const active = entries.filter(e => e.maturity !== 'archived');
200
223
  const byType = {};
@@ -205,53 +228,52 @@ async function knowledgeStats(options) {
205
228
  byMaturity[entry.maturity] = (byMaturity[entry.maturity] || 0) + 1;
206
229
  byLayer[entry.layer] = (byLayer[entry.layer] || 0) + 1;
207
230
  }
208
- // D6 flywheel metrics
209
- const withRefs = active.filter(e => e.referencedBy.length > 0).length;
210
- const refCoverage = active.length > 0 ? Math.round(withRefs / active.length * 100) : 0;
211
- const avgRefs = active.length > 0
212
- ? Math.round(active.reduce((sum, e) => sum + e.referencedBy.length, 0) / active.length * 10) / 10
213
- : 0;
214
- let consumptionHitRate = 0;
231
+ // 飞轮指标:计算唯一实现在 knowledge/flywheel-metrics,此处只做 fs 读取 + 展示层单位映射
232
+ let dailyConsumptionEvents = 0;
215
233
  try {
216
234
  const statsPath = path.join(store.getBaseDir(), '.consumption-stats.json');
217
235
  if (fs.existsSync(statsPath)) {
218
236
  const stats = JSON.parse(fs.readFileSync(statsPath, 'utf-8'));
219
- const dailyEvents = stats.dailyEvents || 0;
220
- consumptionHitRate = active.length > 0 ? Math.min(Math.round(dailyEvents / active.length * 100), 100) : 0;
237
+ dailyConsumptionEvents = stats.dailyEvents || 0;
221
238
  }
222
239
  }
223
240
  catch { /* best-effort */ }
241
+ const metrics = (0, flywheel_metrics_1.evaluateFlywheel)({ entries: active, dailyConsumptionEvents });
242
+ const refCoverage = Math.round(metrics.refCoverage * 100);
243
+ const avgRefs = Math.round(metrics.avgRefs * 10) / 10;
244
+ const consumptionHitRate = Math.round(metrics.consumptionHitRate * 100);
224
245
  const flywheel = { refCoverage, avgRefs, consumptionHitRate };
225
246
  if (options.json) {
226
- console.log(JSON.stringify({ total: entries.length, byType, byMaturity, byLayer, flywheel }, null, 2));
227
- return;
247
+ (0, command_contract_1.log)(io, JSON.stringify({ total: entries.length, byType, byMaturity, byLayer, flywheel }, null, 2));
248
+ return { kind: 'ok' };
228
249
  }
229
- console.log(chalk_1.default.blue(`📊 知识库统计\n`));
230
- console.log(chalk_1.default.bold(` 总计: ${entries.length} 条 (活跃: ${active.length})\n`));
231
- console.log(chalk_1.default.bold(' 按类型:'));
250
+ (0, command_contract_1.log)(io, chalk_1.default.blue(`📊 知识库统计\n`));
251
+ (0, command_contract_1.log)(io, chalk_1.default.bold(` 总计: ${entries.length} 条 (活跃: ${active.length})\n`));
252
+ (0, command_contract_1.log)(io, chalk_1.default.bold(' 按类型:'));
232
253
  for (const [type, count] of Object.entries(byType)) {
233
- console.log(` ${type}: ${count}`);
254
+ (0, command_contract_1.log)(io, ` ${type}: ${count}`);
234
255
  }
235
- console.log(chalk_1.default.bold('\n 按成熟度:'));
256
+ (0, command_contract_1.log)(io, chalk_1.default.bold('\n 按成熟度:'));
236
257
  for (const [maturity, count] of Object.entries(byMaturity)) {
237
258
  const color = maturity === 'proven' ? chalk_1.default.green : maturity === 'archived' ? chalk_1.default.gray : chalk_1.default.yellow;
238
- console.log(` ${color(maturity)}: ${count}`);
259
+ (0, command_contract_1.log)(io, ` ${color(maturity)}: ${count}`);
239
260
  }
240
- console.log(chalk_1.default.bold('\n 按层级:'));
261
+ (0, command_contract_1.log)(io, chalk_1.default.bold('\n 按层级:'));
241
262
  for (const [layer, count] of Object.entries(byLayer)) {
242
- console.log(` ${layer}: ${count}`);
263
+ (0, command_contract_1.log)(io, ` ${layer}: ${count}`);
243
264
  }
244
- console.log(chalk_1.default.bold('\n 飞轮指标:'));
245
- console.log(` 引用覆盖: ${refCoverage}%`);
246
- console.log(` 平均引用: ${avgRefs}`);
247
- console.log(` 消费命中率: ${consumptionHitRate}%`);
265
+ (0, command_contract_1.log)(io, chalk_1.default.bold('\n 飞轮指标:'));
266
+ (0, command_contract_1.log)(io, ` 引用覆盖: ${refCoverage}%`);
267
+ (0, command_contract_1.log)(io, ` 平均引用: ${avgRefs}`);
268
+ (0, command_contract_1.log)(io, ` 消费命中率: ${consumptionHitRate}%`);
269
+ return { kind: 'ok' };
248
270
  }
249
271
  /**
250
272
  * 设计时知识沉淀:写入 KnowledgeStore + 同步 Prisma Document(Studio UI 可见)
251
273
  *
252
274
  * 调用 Studio API POST /api/knowledge/upsert(内部端点,无 auth)
253
275
  */
254
- async function knowledgeUpsert(options) {
276
+ async function knowledgeUpsert(options, io = command_contract_1.processIO) {
255
277
  const apiPort = process.env.API_PORT || '3001';
256
278
  const url = `http://localhost:${apiPort}/api/knowledge/upsert`;
257
279
  // Read content from file if --file specified
@@ -261,14 +283,14 @@ async function knowledgeUpsert(options) {
261
283
  content = fs.readFileSync(options.file, 'utf-8');
262
284
  }
263
285
  catch (e) {
264
- console.error(chalk_1.default.red(`Failed to read file: ${options.file}`));
265
- console.error(chalk_1.default.red(String(e)));
266
- process.exit(1);
286
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`Failed to read file: ${options.file}`));
287
+ (0, command_contract_1.logError)(io, chalk_1.default.red(String(e)));
288
+ return { kind: 'fail', reason: `无法读取 --file ${options.file}: ${String(e)}` };
267
289
  }
268
290
  }
269
291
  if (!options.scope || !options.title || !content) {
270
- console.error(chalk_1.default.red('--scope, --title, and --content (or --file) are required'));
271
- process.exit(1);
292
+ (0, command_contract_1.logError)(io, chalk_1.default.red('--scope, --title, and --content (or --file) are required'));
293
+ return { kind: 'usage-error', reason: '--scope/--title/--content(--file) 缺一不可' };
272
294
  }
273
295
  try {
274
296
  const res = await fetch(url, {
@@ -284,100 +306,103 @@ async function knowledgeUpsert(options) {
284
306
  });
285
307
  if (!res.ok) {
286
308
  const err = await res.json().catch(() => ({ error: res.statusText }));
287
- console.error(chalk_1.default.red(`API error ${res.status}: ${err.error || res.statusText}`));
288
- process.exit(1);
309
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`API error ${res.status}: ${err.error || res.statusText}`));
310
+ return { kind: 'fail', reason: `upsert API error ${res.status}: ${err.error || res.statusText}` };
289
311
  }
290
312
  const result = await res.json();
291
- console.log(chalk_1.default.green(`✅ Knowledge upserted`));
292
- console.log(` KnowledgeStore: ${result.knowledgeStore?.action} → ${result.knowledgeStore?.entryId}`);
293
- console.log(` Studio UI: ${result.prismaDocument?.action} → ${result.prismaDocument?.docId || 'skipped'}`);
313
+ (0, command_contract_1.log)(io, chalk_1.default.green(`✅ Knowledge upserted`));
314
+ (0, command_contract_1.log)(io, ` KnowledgeStore: ${result.knowledgeStore?.action} → ${result.knowledgeStore?.entryId}`);
315
+ (0, command_contract_1.log)(io, ` Studio UI: ${result.prismaDocument?.action} → ${result.prismaDocument?.docId || 'skipped'}`);
316
+ return { kind: 'ok' };
294
317
  }
295
318
  catch (e) {
296
319
  if (e?.code === 'ECONNREFUSED') {
297
- console.error(chalk_1.default.red(`Cannot reach Studio API at ${url}. Is the API running?`));
320
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`Cannot reach Studio API at ${url}. Is the API running?`));
298
321
  }
299
322
  else {
300
- console.error(chalk_1.default.red(`Upsert failed: ${e.message}`));
323
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`Upsert failed: ${e.message}`));
301
324
  }
302
- process.exit(1);
325
+ return { kind: 'fail', reason: `knowledge upsert 失败: ${e?.code === 'ECONNREFUSED' ? `Studio API 不可达 ${url}` : e.message}` };
303
326
  }
304
327
  }
305
328
  /**
306
329
  /**
307
330
  * RAG 同步:扫描 .harness/knowledge-docs/ 输出需要 ingest 的文件列表
308
331
  */
309
- async function knowledgeSyncRag(options) {
332
+ async function knowledgeSyncRag(options, io = command_contract_1.processIO) {
310
333
  const projectPath = options.projectPath || process.cwd();
311
334
  const docsDir = `${projectPath}/.harness/knowledge-docs`;
312
335
  if (!fs.existsSync(docsDir)) {
313
- console.log(chalk_1.default.yellow('No .harness/knowledge-docs/ directory'));
314
- return;
336
+ (0, command_contract_1.log)(io, chalk_1.default.yellow('No .harness/knowledge-docs/ directory'));
337
+ return { kind: 'ok' };
315
338
  }
316
339
  const files = fs.readdirSync(docsDir).filter(f => f.endsWith('.md'));
317
340
  if (files.length === 0) {
318
- console.log(chalk_1.default.gray('No knowledge docs found'));
319
- return;
341
+ (0, command_contract_1.log)(io, chalk_1.default.gray('No knowledge docs found'));
342
+ return { kind: 'ok' };
320
343
  }
321
344
  if (options.json) {
322
- console.log(JSON.stringify({ directory: docsDir, files }, null, 2));
323
- return;
345
+ (0, command_contract_1.log)(io, JSON.stringify({ directory: docsDir, files }, null, 2));
346
+ return { kind: 'ok' };
324
347
  }
325
- console.log(chalk_1.default.blue(`📄 RAG sync candidates: ${files.length} files in ${docsDir}\n`));
348
+ (0, command_contract_1.log)(io, chalk_1.default.blue(`📄 RAG sync candidates: ${files.length} files in ${docsDir}\n`));
326
349
  for (const file of files) {
327
- console.log(chalk_1.default.cyan(` ${file}`));
350
+ (0, command_contract_1.log)(io, chalk_1.default.cyan(` ${file}`));
328
351
  }
329
- console.log();
330
- console.log(chalk_1.default.gray('Run `mcp__local-rag__ingest_file` for each to sync to RAG'));
352
+ (0, command_contract_1.log)(io);
353
+ (0, command_contract_1.log)(io, chalk_1.default.gray('Run `mcp__local-rag__ingest_file` for each to sync to RAG'));
354
+ return { kind: 'ok' };
331
355
  }
332
356
  /**
333
357
  * 知识同步状态:检测所有 tracked scope 的新鲜度
334
358
  */
335
- async function knowledgeSyncStatus(options) {
359
+ async function knowledgeSyncStatus(options, io = command_contract_1.processIO) {
336
360
  const apiPort = process.env.API_PORT || '3001';
337
361
  const url = `http://localhost:${apiPort}/api/knowledge/sync-status`;
338
362
  try {
339
363
  const res = await fetch(url);
340
364
  if (!res.ok) {
341
365
  const err = await res.json().catch(() => ({ error: res.statusText }));
342
- console.error(chalk_1.default.red(`API error ${res.status}: ${err.error || res.statusText}`));
343
- process.exit(1);
366
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`API error ${res.status}: ${err.error || res.statusText}`));
367
+ return { kind: 'fail', reason: `sync-status API error ${res.status}: ${err.error || res.statusText}` };
344
368
  }
345
369
  const data = await res.json();
346
370
  if (options.json) {
347
- console.log(JSON.stringify(data, null, 2));
348
- return;
371
+ (0, command_contract_1.log)(io, JSON.stringify(data, null, 2));
372
+ return { kind: 'ok' };
349
373
  }
350
- console.log(chalk_1.default.blue(`🔄 KnowledgeSync Status\n`));
351
- console.log(chalk_1.default.bold(`Tracked scopes: ${data.trackedScopes?.join(', ') || 'none'}`));
352
- console.log(chalk_1.default.bold(`Stale entries: ${data.stale?.length || 0}`));
374
+ (0, command_contract_1.log)(io, chalk_1.default.blue(`🔄 KnowledgeSync Status\n`));
375
+ (0, command_contract_1.log)(io, chalk_1.default.bold(`Tracked scopes: ${data.trackedScopes?.join(', ') || 'none'}`));
376
+ (0, command_contract_1.log)(io, chalk_1.default.bold(`Stale entries: ${data.stale?.length || 0}`));
353
377
  if (data.stale?.length > 0) {
354
- console.log(chalk_1.default.yellow(`\n⚠️ Stale knowledge:\n`));
378
+ (0, command_contract_1.log)(io, chalk_1.default.yellow(`\n⚠️ Stale knowledge:\n`));
355
379
  for (const s of data.stale) {
356
- console.log(chalk_1.default.yellow(` ${s.scope} (${s.title}): ${s.stalenessHours}h old, files changed: ${s.changedFiles.join(', ')}`));
380
+ (0, command_contract_1.log)(io, chalk_1.default.yellow(` ${s.scope} (${s.title}): ${s.stalenessHours}h old, files changed: ${s.changedFiles.join(', ')}`));
357
381
  }
358
382
  }
359
383
  else {
360
- console.log(chalk_1.default.green('\n✅ All knowledge fresh'));
384
+ (0, command_contract_1.log)(io, chalk_1.default.green('\n✅ All knowledge fresh'));
361
385
  }
362
386
  if (data.healed?.length > 0) {
363
- console.log(chalk_1.default.cyan(`\n🩹 Auto-healed: ${data.healed.join(', ')}`));
387
+ (0, command_contract_1.log)(io, chalk_1.default.cyan(`\n🩹 Auto-healed: ${data.healed.join(', ')}`));
364
388
  }
365
389
  }
366
390
  catch (e) {
367
391
  if (e?.code === 'ECONNREFUSED') {
368
- console.error(chalk_1.default.red(`Cannot reach Studio API at ${url}`));
392
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`Cannot reach Studio API at ${url}`));
369
393
  }
370
394
  else {
371
- console.error(chalk_1.default.red(`Sync check failed: ${e.message}`));
395
+ (0, command_contract_1.logError)(io, chalk_1.default.red(`Sync check failed: ${e.message}`));
372
396
  }
373
- process.exit(1);
397
+ return { kind: 'fail', reason: `knowledge sync status 检查失败: ${e?.code === 'ECONNREFUSED' ? `Studio API 不可达 ${url}` : e.message}` };
374
398
  }
399
+ return { kind: 'ok' };
375
400
  }
376
401
  /**
377
402
  * 飞轮健康检查 — 零 token 检测知识飞轮数据流状态
378
403
  */
379
- async function knowledgeHealth(options) {
380
- const baseDir = options.dir || getKnowledgeDir(options.projectPath);
404
+ async function knowledgeHealth(options, io = command_contract_1.processIO) {
405
+ const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
381
406
  const store = new store_1.FileKnowledgeStore({ baseDir });
382
407
  const entries = store.list({ excludeArchived: true });
383
408
  const issues = [];
@@ -409,18 +434,16 @@ async function knowledgeHealth(options) {
409
434
  else {
410
435
  issues.push({ severity: 'info', entry: '-', detail: `消费追踪数据不存在(${statsPath})` });
411
436
  }
412
- // D4: 飞轮指标
413
- const withRefs = entries.filter(e => e.referencedBy.length > 0).length;
414
- const refCoverage = entries.length > 0 ? Math.round(withRefs / entries.length * 100) : 0;
415
- const avgRefs = entries.length > 0
416
- ? Math.round(entries.reduce((sum, e) => sum + e.referencedBy.length, 0) / entries.length * 10) / 10
417
- : 0;
437
+ // D4: 飞轮指标(计算唯一实现在 knowledge/flywheel-metrics;人口为本命令的 excludeArchived 切片)
438
+ const metrics = (0, flywheel_metrics_1.evaluateFlywheel)({ entries });
439
+ const refCoverage = Math.round(metrics.refCoverage * 100);
440
+ const avgRefs = Math.round(metrics.avgRefs * 10) / 10;
418
441
  // 健康分计算
419
442
  const totalIssues = issues.filter(i => i.severity === 'error').length * 3
420
443
  + issues.filter(i => i.severity === 'warn').length * 1;
421
444
  const healthScore = Math.max(0, 100 - totalIssues);
422
445
  if (options.json) {
423
- console.log(JSON.stringify({
446
+ (0, command_contract_1.log)(io, JSON.stringify({
424
447
  healthScore,
425
448
  summary: {
426
449
  total: entries.length,
@@ -432,36 +455,37 @@ async function knowledgeHealth(options) {
432
455
  },
433
456
  issues: issues.slice(0, 50),
434
457
  }, null, 2));
435
- return;
436
- }
437
- console.log(chalk_1.default.blue('🏥 飞轮健康检查\n'));
438
- console.log(chalk_1.default.bold(` 健康分: ${healthScore >= 80 ? chalk_1.default.green(healthScore) : healthScore >= 60 ? chalk_1.default.yellow(healthScore) : chalk_1.default.red(healthScore)}/100`));
439
- console.log(chalk_1.default.bold(` 活跃条目: ${entries.length}`));
440
- console.log();
441
- console.log(chalk_1.default.bold(' 数据流状态:'));
442
- console.log(` 引用密度: ${lowRefEntries === 0 ? chalk_1.default.green('✓') : chalk_1.default.yellow(`⚠ ${lowRefEntries} 个零引用 verified`)}`);
443
- console.log(` 新鲜度: ${staleEntries === 0 ? chalk_1.default.green('✓') : chalk_1.default.yellow(`⚠ ${staleEntries} 个过期 draft`)}`);
444
- console.log(` 消费追踪: ${consumptionData ? chalk_1.default.green('✓') : chalk_1.default.gray('○ 未启用')}`);
445
- console.log();
446
- console.log(chalk_1.default.bold(' 飞轮指标:'));
447
- console.log(` 引用覆盖: ${refCoverage}%`);
448
- console.log(` 平均引用: ${avgRefs}`);
449
- console.log();
458
+ return { kind: 'ok' };
459
+ }
460
+ (0, command_contract_1.log)(io, chalk_1.default.blue('🏥 飞轮健康检查\n'));
461
+ (0, command_contract_1.log)(io, chalk_1.default.bold(` 健康分: ${healthScore >= 80 ? chalk_1.default.green(healthScore) : healthScore >= 60 ? chalk_1.default.yellow(healthScore) : chalk_1.default.red(healthScore)}/100`));
462
+ (0, command_contract_1.log)(io, chalk_1.default.bold(` 活跃条目: ${entries.length}`));
463
+ (0, command_contract_1.log)(io);
464
+ (0, command_contract_1.log)(io, chalk_1.default.bold(' 数据流状态:'));
465
+ (0, command_contract_1.log)(io, ` 引用密度: ${lowRefEntries === 0 ? chalk_1.default.green('✓') : chalk_1.default.yellow(`⚠ ${lowRefEntries} 个零引用 verified`)}`);
466
+ (0, command_contract_1.log)(io, ` 新鲜度: ${staleEntries === 0 ? chalk_1.default.green('✓') : chalk_1.default.yellow(`⚠ ${staleEntries} 个过期 draft`)}`);
467
+ (0, command_contract_1.log)(io, ` 消费追踪: ${consumptionData ? chalk_1.default.green('✓') : chalk_1.default.gray('○ 未启用')}`);
468
+ (0, command_contract_1.log)(io);
469
+ (0, command_contract_1.log)(io, chalk_1.default.bold(' 飞轮指标:'));
470
+ (0, command_contract_1.log)(io, ` 引用覆盖: ${refCoverage}%`);
471
+ (0, command_contract_1.log)(io, ` 平均引用: ${avgRefs}`);
472
+ (0, command_contract_1.log)(io);
450
473
  if (issues.length > 0) {
451
- console.log(chalk_1.default.bold(` 问题 (前 20):`));
474
+ (0, command_contract_1.log)(io, chalk_1.default.bold(` 问题 (前 20):`));
452
475
  for (const issue of issues.slice(0, 20)) {
453
476
  const sevColor = issue.severity === 'error' ? chalk_1.default.red
454
477
  : issue.severity === 'warn' ? chalk_1.default.yellow
455
478
  : chalk_1.default.gray;
456
- console.log(` ${sevColor(`[${issue.severity}]`)} ${issue.entry}: ${issue.detail}`);
479
+ (0, command_contract_1.log)(io, ` ${sevColor(`[${issue.severity}]`)} ${issue.entry}: ${issue.detail}`);
457
480
  }
458
481
  if (issues.length > 20) {
459
- console.log(chalk_1.default.gray(` ... 还有 ${issues.length - 20} 条`));
482
+ (0, command_contract_1.log)(io, chalk_1.default.gray(` ... 还有 ${issues.length - 20} 条`));
460
483
  }
461
484
  }
462
485
  else {
463
- console.log(chalk_1.default.green(' ✓ 无问题'));
486
+ (0, command_contract_1.log)(io, chalk_1.default.green(' ✓ 无问题'));
464
487
  }
488
+ return { kind: 'ok' };
465
489
  }
466
490
  /** 缺省知识库数据根(相对用户 home) */
467
491
  const KNOWLEDGE_DATA_DIR = path.join('.harness', 'knowledge');
@@ -476,7 +500,7 @@ function hasKnowledgeData(dir) {
476
500
  return false;
477
501
  }
478
502
  }
479
- function getKnowledgeDir(projectPath) {
503
+ function getKnowledgeDir(projectPath, io) {
480
504
  if (projectPath)
481
505
  return `${projectPath}/.harness/knowledge`;
482
506
  if (process.env.KNOWLEDGE_BASE_DIR)
@@ -487,7 +511,7 @@ function getKnowledgeDir(projectPath) {
487
511
  if (!legacyKnowledgeDirNotified) {
488
512
  legacyKnowledgeDirNotified = true;
489
513
  // stderr:不污染 --json 的 stdout 输出
490
- console.error(chalk_1.default.yellow(`⚠ 缺省知识库根已改为 ${defaultDir};旧目录 ${legacyDir} 仍有数据,本次沿用(免迁移,可用 KNOWLEDGE_BASE_DIR 覆盖)`));
514
+ (0, command_contract_1.logError)(io, chalk_1.default.yellow(`⚠ 缺省知识库根已改为 ${defaultDir};旧目录 ${legacyDir} 仍有数据,本次沿用(免迁移,可用 KNOWLEDGE_BASE_DIR 覆盖)`));
491
515
  }
492
516
  return legacyDir;
493
517
  }
@@ -496,29 +520,29 @@ function getKnowledgeDir(projectPath) {
496
520
  /**
497
521
  * 知识库质量审计
498
522
  */
499
- async function knowledgeAudit(options) {
523
+ async function knowledgeAudit(options, io = command_contract_1.processIO) {
500
524
  const audit = new audit_1.KnowledgeAudit({
501
- baseDir: options.dir || getKnowledgeDir(options.projectPath),
525
+ baseDir: options.dir || getKnowledgeDir(options.projectPath, io),
502
526
  shortContentThreshold: options.threshold ? parseInt(options.threshold, 10) : undefined,
503
527
  });
504
528
  const isDryRun = options.dryRun && !options.fix;
505
529
  if (!options.json && !isDryRun) {
506
- console.log(chalk_1.default.blue('🔍 知识库质量审计...\n'));
530
+ (0, command_contract_1.log)(io, chalk_1.default.blue('🔍 知识库质量审计...\n'));
507
531
  }
508
532
  const report = audit.run({ autoFix: options.fix && !isDryRun });
509
533
  if (options.json) {
510
- console.log(JSON.stringify(report, null, 2));
511
- return;
534
+ (0, command_contract_1.log)(io, JSON.stringify(report, null, 2));
535
+ return { kind: 'ok' };
512
536
  }
513
537
  // Summary
514
- console.log(chalk_1.default.bold(` 总条目: ${report.totalEntries}`));
515
- console.log(chalk_1.default.bold(` 健康分: ${report.healthScore.before}/100`));
538
+ (0, command_contract_1.log)(io, chalk_1.default.bold(` 总条目: ${report.totalEntries}`));
539
+ (0, command_contract_1.log)(io, chalk_1.default.bold(` 健康分: ${report.healthScore.before}/100`));
516
540
  if (report.autoFixed > 0) {
517
- console.log(chalk_1.default.green(` 自动修复: ${report.autoFixed} 条`));
518
- console.log(chalk_1.default.bold(` 修复后: ${report.healthScore.after}/100`));
541
+ (0, command_contract_1.log)(io, chalk_1.default.green(` 自动修复: ${report.autoFixed} 条`));
542
+ (0, command_contract_1.log)(io, chalk_1.default.bold(` 修复后: ${report.healthScore.after}/100`));
519
543
  }
520
- console.log();
521
- // Dimension scores
544
+ (0, command_contract_1.log)(io);
545
+ // Dimension scores(Record 全键标注 → 编译期闭环,#109)
522
546
  const dimLabels = {
523
547
  structure: 'D1 结构完整性',
524
548
  content: 'D2 内容质量',
@@ -526,105 +550,95 @@ async function knowledgeAudit(options) {
526
550
  maturity: 'D4 成熟度健康',
527
551
  freshness: 'D5 新鲜度',
528
552
  flywheel: 'D6 飞轮验证',
553
+ incremental: 'D7 增量存活',
529
554
  };
530
- console.log(chalk_1.default.bold(' 维度评分:'));
555
+ (0, command_contract_1.log)(io, chalk_1.default.bold(' 维度评分:'));
531
556
  for (const [key, dim] of Object.entries(report.dimensions)) {
532
- const label = dimLabels[key] || key;
557
+ const label = dimLabels[key];
533
558
  const scoreColor = dim.score >= 80 ? chalk_1.default.green : dim.score >= 60 ? chalk_1.default.yellow : chalk_1.default.red;
534
- console.log(` ${label}: ${scoreColor(`${dim.score}/100`)} (${dim.issues} 问题)`);
535
- }
536
- console.log();
537
- // Issues by rule
538
- const ruleLabels = {
539
- 'frontmatter-missing': 'frontmatter 缺失',
540
- 'test-data-pollution': '测试数据污染',
541
- 'daily-audit-noise': '每日审计噪音',
542
- 'zero-content-proven': '零内容 proven',
543
- 'short-content': '短内容',
544
- 'maturity-inflation': '成熟度虚高',
545
- 'title-duplicate': '标题重复',
546
- 'source-refs-bloat': 'sourceReferences 膨胀',
547
- 'fragment-cluster': '碎片集群',
548
- 'promotion-blocked': 'promotion 受阻',
549
- 'orphan-draft': '孤儿 draft',
550
- 'stale-entry': '过期条目',
551
- };
559
+ (0, command_contract_1.log)(io, ` ${label}: ${scoreColor(`${dim.score}/100`)} (${dim.issues} 问题)`);
560
+ }
561
+ (0, command_contract_1.log)(io);
562
+ // Issues by rule(label 正本在 audit.ts 规则定义,AUDIT_RULE_LABELS 编译期闭环,#109)
552
563
  for (const [rule, count] of Object.entries(report.summary)) {
553
564
  if (count === 0)
554
565
  continue;
555
- const label = ruleLabels[rule] || rule;
556
- console.log(` ${chalk_1.default.red(`${label}: ${count}`)}`);
566
+ const label = audit_1.AUDIT_RULE_LABELS[rule];
567
+ (0, command_contract_1.log)(io, ` ${chalk_1.default.red(`${label}: ${count}`)}`);
557
568
  }
558
569
  // Detail (first 20)
559
570
  if (report.issues.length > 0) {
560
- console.log(chalk_1.default.bold(`\n 问题详情 (前 20 条):\n`));
571
+ (0, command_contract_1.log)(io, chalk_1.default.bold(`\n 问题详情 (前 20 条):\n`));
561
572
  for (const issue of report.issues.slice(0, 20)) {
562
573
  const severityColor = issue.severity === 'critical' ? chalk_1.default.red
563
574
  : issue.severity === 'high' ? chalk_1.default.yellow
564
575
  : chalk_1.default.gray;
565
- console.log(` ${severityColor(`[${issue.severity}]`)} ${issue.entryId}: ${issue.title}`);
566
- console.log(` ${chalk_1.default.gray(issue.detail)} → ${issue.action}`);
576
+ (0, command_contract_1.log)(io, ` ${severityColor(`[${issue.severity}]`)} ${issue.entryId}: ${issue.title}`);
577
+ (0, command_contract_1.log)(io, ` ${chalk_1.default.gray(issue.detail)} → ${issue.action}`);
567
578
  }
568
579
  if (report.issues.length > 20) {
569
- console.log(chalk_1.default.gray(`\n ... 还有 ${report.issues.length - 20} 条`));
580
+ (0, command_contract_1.log)(io, chalk_1.default.gray(`\n ... 还有 ${report.issues.length - 20} 条`));
570
581
  }
571
582
  }
572
583
  if (!options.fix && report.issues.length > 0) {
573
- console.log(chalk_1.default.yellow(`\n 使用 --fix 自动修复`));
584
+ (0, command_contract_1.log)(io, chalk_1.default.yellow(`\n 使用 --fix 自动修复`));
574
585
  }
575
586
  // Auto-rebuild index after audit (files may have changed)
576
- const baseDir = options.dir || getKnowledgeDir(options.projectPath);
587
+ const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
577
588
  const idxGen = new index_generator_1.KnowledgeIndexGenerator(baseDir);
578
589
  idxGen.regenerate();
579
590
  if (!options.json) {
580
- console.log(chalk_1.default.gray(` 📇 索引已重建`));
591
+ (0, command_contract_1.log)(io, chalk_1.default.gray(` 📇 索引已重建`));
581
592
  }
593
+ return { kind: 'ok' };
582
594
  }
583
595
  /**
584
596
  * 知识库快照(KR4 存活率追踪)
585
597
  */
586
- function knowledgeSnapshot(options) {
587
- const store = new store_1.FileKnowledgeStore({ baseDir: options.dir || getKnowledgeDir(options.projectPath) });
598
+ function knowledgeSnapshot(options, io = command_contract_1.processIO) {
599
+ const store = new store_1.FileKnowledgeStore({ baseDir: options.dir || getKnowledgeDir(options.projectPath, io) });
588
600
  const snapPath = store.snapshot();
589
601
  if (options.json) {
590
- console.log(JSON.stringify({ snapshotPath: snapPath }));
591
- return;
602
+ (0, command_contract_1.log)(io, JSON.stringify({ snapshotPath: snapPath }));
603
+ return { kind: 'ok' };
592
604
  }
593
- console.log(chalk_1.default.green(`✅ 快照已保存: ${snapPath}`));
605
+ (0, command_contract_1.log)(io, chalk_1.default.green(`✅ 快照已保存: ${snapPath}`));
606
+ return { kind: 'ok' };
594
607
  }
595
608
  /**
596
609
  * 知识库迁移 — 为现有条目添加 AS-021 新字段
597
610
  */
598
- function knowledgeMigrate(options) {
599
- const baseDir = options.dir || getKnowledgeDir(options.projectPath);
611
+ function knowledgeMigrate(options, io = command_contract_1.processIO) {
612
+ const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
600
613
  if (options.json) {
601
614
  const result = (0, migration_1.migrateKnowledgeEntries)(baseDir);
602
- console.log(JSON.stringify(result, null, 2));
603
- return;
615
+ (0, command_contract_1.log)(io, JSON.stringify(result, null, 2));
616
+ return { kind: 'ok' };
604
617
  }
605
- console.log(chalk_1.default.blue('🔄 迁移知识条目(添加 consumptionMode/origin 字段)\n'));
618
+ (0, command_contract_1.log)(io, chalk_1.default.blue('🔄 迁移知识条目(添加 consumptionMode/origin 字段)\n'));
606
619
  const result = (0, migration_1.migrateKnowledgeEntries)(baseDir);
607
- console.log(chalk_1.default.green(` 总计: ${result.total} 条`));
608
- console.log(chalk_1.default.green(` 已迁移: ${result.migrated} 条`));
609
- console.log(chalk_1.default.gray(` 已跳过: ${result.skipped} 条`));
620
+ (0, command_contract_1.log)(io, chalk_1.default.green(` 总计: ${result.total} 条`));
621
+ (0, command_contract_1.log)(io, chalk_1.default.green(` 已迁移: ${result.migrated} 条`));
622
+ (0, command_contract_1.log)(io, chalk_1.default.gray(` 已跳过: ${result.skipped} 条`));
610
623
  if (result.errors.length > 0) {
611
- console.log(chalk_1.default.red(`\n 错误: ${result.errors.length} 条`));
624
+ (0, command_contract_1.log)(io, chalk_1.default.red(`\n 错误: ${result.errors.length} 条`));
612
625
  for (const err of result.errors.slice(0, 5)) {
613
- console.log(chalk_1.default.red(` ${err}`));
626
+ (0, command_contract_1.log)(io, chalk_1.default.red(` ${err}`));
614
627
  }
615
628
  }
616
629
  if (result.migrated === 0 && result.errors.length === 0) {
617
- console.log(chalk_1.default.green('\n✅ 所有条目已是最新,无需迁移'));
630
+ (0, command_contract_1.log)(io, chalk_1.default.green('\n✅ 所有条目已是最新,无需迁移'));
618
631
  }
619
632
  else if (result.migrated > 0) {
620
- console.log(chalk_1.default.green(`\n✅ 迁移完成`));
633
+ (0, command_contract_1.log)(io, chalk_1.default.green(`\n✅ 迁移完成`));
621
634
  }
635
+ return { kind: 'ok' };
622
636
  }
623
637
  /**
624
638
  * 知识库索引重建 — 生成 _index.md 供 Agent grep 使用,同时同步 index.json
625
639
  */
626
- function knowledgeIndex(options) {
627
- const baseDir = options.dir || getKnowledgeDir(options.projectPath);
640
+ function knowledgeIndex(options, io = command_contract_1.processIO) {
641
+ const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
628
642
  const gen = new index_generator_1.KnowledgeIndexGenerator(baseDir);
629
643
  const beforeSize = (() => {
630
644
  const indexPath = path.join(baseDir, '_index.md');
@@ -637,20 +651,21 @@ function knowledgeIndex(options) {
637
651
  const afterSize = Buffer.byteLength(output, 'utf-8');
638
652
  const lineCount = output.split('\n').filter(l => !l.startsWith('#')).length;
639
653
  if (options.json) {
640
- console.log(JSON.stringify({
654
+ (0, command_contract_1.log)(io, JSON.stringify({
641
655
  path: path.join(baseDir, '_index.md'),
642
656
  entries: lineCount,
643
657
  size: afterSize,
644
658
  previousSize: beforeSize,
645
659
  }));
646
- return;
660
+ return { kind: 'ok' };
647
661
  }
648
- console.log(chalk_1.default.blue(`📇 索引已重建`));
649
- console.log(chalk_1.default.gray(` 路径: ${path.join(baseDir, '_index.md')}`));
650
- console.log(chalk_1.default.green(` 条目: ${lineCount}`));
651
- console.log(chalk_1.default.green(` 大小: ${(afterSize / 1024).toFixed(1)} KB`));
662
+ (0, command_contract_1.log)(io, chalk_1.default.blue(`📇 索引已重建`));
663
+ (0, command_contract_1.log)(io, chalk_1.default.gray(` 路径: ${path.join(baseDir, '_index.md')}`));
664
+ (0, command_contract_1.log)(io, chalk_1.default.green(` 条目: ${lineCount}`));
665
+ (0, command_contract_1.log)(io, chalk_1.default.green(` 大小: ${(afterSize / 1024).toFixed(1)} KB`));
652
666
  if (beforeSize > 0) {
653
- console.log(chalk_1.default.gray(` 旧大小: ${(beforeSize / 1024).toFixed(1)} KB`));
667
+ (0, command_contract_1.log)(io, chalk_1.default.gray(` 旧大小: ${(beforeSize / 1024).toFixed(1)} KB`));
654
668
  }
669
+ return { kind: 'ok' };
655
670
  }
656
671
  //# sourceMappingURL=knowledge.js.map