@dommaker/harness 0.13.0 → 0.13.2

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 (420) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/bin/harness-knowledge-capture.js +99 -0
  3. package/bin/harness-knowledge-check.js +149 -0
  4. package/bin/harness-knowledge-track.js +92 -0
  5. package/bin/harness-knowledge-track.sh +47 -0
  6. package/bin/harness-sensitive-check.sh +67 -0
  7. package/dist/cli/commands/init.js +34 -2
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/package.json +6 -3
  10. package/src/CONTEXT.md +34 -0
  11. package/src/__tests__/acceptance-gate.test.ts +364 -0
  12. package/src/__tests__/annotation-checker.test.ts +183 -0
  13. package/src/__tests__/architecture-engine.test.ts +619 -0
  14. package/src/__tests__/checker-extra.test.ts +1627 -0
  15. package/src/__tests__/checker.test.ts +1268 -0
  16. package/src/__tests__/checkpoint.test.ts +697 -0
  17. package/src/__tests__/clean-state-extra.test.ts +301 -0
  18. package/src/__tests__/clean-state.test.ts +169 -0
  19. package/src/__tests__/constraint-doctor.test.ts +410 -0
  20. package/src/__tests__/constraint-evolver.test.ts +404 -0
  21. package/src/__tests__/constraint-handler.test.ts +136 -0
  22. package/src/__tests__/context/progressive-loader.test.ts +131 -0
  23. package/src/__tests__/contract-gate.test.ts +321 -0
  24. package/src/__tests__/cross-project-checker.test.ts +318 -0
  25. package/src/__tests__/cso-validator.test.ts +205 -0
  26. package/src/__tests__/detect-source-roots.test.ts +98 -0
  27. package/src/__tests__/doctor-evolver.test.ts +381 -0
  28. package/src/__tests__/error-classifier.test.ts +304 -0
  29. package/src/__tests__/exec.test.ts +83 -0
  30. package/src/__tests__/extend-exceptions.test.ts +149 -0
  31. package/src/__tests__/failure-recorder.test.ts +335 -0
  32. package/src/__tests__/hooks-pipeline.test.ts +214 -0
  33. package/src/__tests__/interceptor.test.ts +89 -0
  34. package/src/__tests__/iron-laws.test.ts +246 -0
  35. package/src/__tests__/long-running.test.ts +17 -0
  36. package/src/__tests__/passes-gate-check.test.ts +138 -0
  37. package/src/__tests__/passes-gate-extension.test.ts +151 -0
  38. package/src/__tests__/passes-gate-extra.test.ts +238 -0
  39. package/src/__tests__/passes-gate.test.ts +231 -0
  40. package/src/__tests__/performance-analyzer.test.ts +501 -0
  41. package/src/__tests__/performance-collector.test.ts +276 -0
  42. package/src/__tests__/performance-gate.test.ts +74 -0
  43. package/src/__tests__/presets.test.ts +104 -0
  44. package/src/__tests__/project-config-loader-extra.test.ts +269 -0
  45. package/src/__tests__/project-config-loader.test.ts +127 -0
  46. package/src/__tests__/review-gate.test.ts +92 -0
  47. package/src/__tests__/security-gate.test.ts +100 -0
  48. package/src/__tests__/session-startup.test.ts +334 -0
  49. package/src/__tests__/spec-validator.test.ts +57 -0
  50. package/src/__tests__/token-budget.test.ts +252 -0
  51. package/src/__tests__/trace-analyzer-extra.test.ts +307 -0
  52. package/src/__tests__/trace-analyzer.test.ts +131 -0
  53. package/src/__tests__/trace-collector.test.ts +258 -0
  54. package/src/__tests__/traces.test.ts +347 -0
  55. package/src/agents/CONTEXT.md +21 -0
  56. package/src/agents/__tests__/lifecycle.test.ts +248 -0
  57. package/src/agents/index.ts +2 -0
  58. package/src/agents/lifecycle.ts +201 -0
  59. package/src/agents/types.ts +46 -0
  60. package/src/architecture/CONTEXT.md +22 -0
  61. package/src/architecture/constraint-engine.ts +356 -0
  62. package/src/architecture/cross-project-checker.ts +563 -0
  63. package/src/cli/commands/CONTEXT.md +24 -0
  64. package/src/cli/commands/__tests__/acceptance.test.ts +180 -0
  65. package/src/cli/commands/__tests__/check.test.ts +598 -0
  66. package/src/cli/commands/__tests__/command.test.ts +149 -0
  67. package/src/cli/commands/__tests__/contract.test.ts +151 -0
  68. package/src/cli/commands/__tests__/failure.test.ts +199 -0
  69. package/src/cli/commands/__tests__/flow.test.ts +343 -0
  70. package/src/cli/commands/__tests__/init.test.ts +306 -0
  71. package/src/cli/commands/__tests__/passes-gate.test.ts +256 -0
  72. package/src/cli/commands/__tests__/performance.test.ts +116 -0
  73. package/src/cli/commands/__tests__/posteval-plan.test.ts +201 -0
  74. package/src/cli/commands/__tests__/report-extra.test.ts +98 -0
  75. package/src/cli/commands/__tests__/report.test.ts +109 -0
  76. package/src/cli/commands/__tests__/review.test.ts +153 -0
  77. package/src/cli/commands/__tests__/security.test.ts +147 -0
  78. package/src/cli/commands/__tests__/spec.test.ts +193 -0
  79. package/src/cli/commands/__tests__/status-extra.test.ts +223 -0
  80. package/src/cli/commands/__tests__/status.test.ts +306 -0
  81. package/src/cli/commands/__tests__/sync-docs.test.ts +335 -0
  82. package/src/cli/commands/__tests__/validate.test.ts +139 -0
  83. package/src/cli/commands/acceptance.ts +111 -0
  84. package/src/cli/commands/analyze-sessions.ts +488 -0
  85. package/src/cli/commands/check.ts +441 -0
  86. package/src/cli/commands/command.ts +90 -0
  87. package/src/cli/commands/constraints.ts +86 -0
  88. package/src/cli/commands/contract.ts +168 -0
  89. package/src/cli/commands/failure.ts +117 -0
  90. package/src/cli/commands/flow.ts +275 -0
  91. package/src/cli/commands/index.ts +26 -0
  92. package/src/cli/commands/init.ts +832 -0
  93. package/src/cli/commands/knowledge.ts +391 -0
  94. package/src/cli/commands/passes-gate.ts +158 -0
  95. package/src/cli/commands/performance.ts +108 -0
  96. package/src/cli/commands/posteval-plan.ts +85 -0
  97. package/src/cli/commands/release.ts +159 -0
  98. package/src/cli/commands/report.ts +198 -0
  99. package/src/cli/commands/review.ts +118 -0
  100. package/src/cli/commands/security.ts +122 -0
  101. package/src/cli/commands/spec.ts +158 -0
  102. package/src/cli/commands/status.ts +169 -0
  103. package/src/cli/commands/sync-docs.ts +743 -0
  104. package/src/cli/commands/update-user-model.ts +527 -0
  105. package/src/cli/commands/validate.ts +222 -0
  106. package/src/constraints/__tests__/lifecycle-runner.test.ts +246 -0
  107. package/src/constraints/__tests__/registry.test.ts +196 -0
  108. package/src/constraints/index.ts +3 -0
  109. package/src/constraints/lifecycle-runner.ts +184 -0
  110. package/src/constraints/registry.ts +134 -0
  111. package/src/constraints/types.ts +59 -0
  112. package/src/context/CONTEXT.md +28 -0
  113. package/src/context/__tests__/compaction.test.ts +169 -0
  114. package/src/context/__tests__/file-budget.test.ts +142 -0
  115. package/src/context/__tests__/knowledge-injector.test.ts +148 -0
  116. package/src/context/__tests__/progressive-loader.test.ts +264 -0
  117. package/src/context/__tests__/session-manager.test.ts +300 -0
  118. package/src/context/__tests__/token-budget.test.ts +338 -0
  119. package/src/context/__tests__/token-pipeline.test.ts +178 -0
  120. package/src/context/__tests__/tool-output-budget.test.ts +95 -0
  121. package/src/context/compaction.ts +202 -0
  122. package/src/context/file-budget.ts +142 -0
  123. package/src/context/index.ts +15 -0
  124. package/src/context/knowledge-injector.ts +168 -0
  125. package/src/context/progressive-loader.ts +260 -0
  126. package/src/context/session-manager.ts +343 -0
  127. package/src/context/token-budget.ts +295 -0
  128. package/src/context/token-pipeline.ts +265 -0
  129. package/src/context/tool-output-budget.ts +178 -0
  130. package/src/context/types.ts +160 -0
  131. package/src/core/CONTEXT.md +26 -0
  132. package/src/core/constraints/__tests__/default-executors.test.ts +57 -0
  133. package/src/core/constraints/__tests__/interceptor.test.ts +251 -0
  134. package/src/core/constraints/__tests__/prompt-injection.test.ts +89 -0
  135. package/src/core/constraints/check-cache.ts +96 -0
  136. package/src/core/constraints/checker.ts +1169 -0
  137. package/src/core/constraints/default-executors.ts +92 -0
  138. package/src/core/constraints/definitions.ts +1119 -0
  139. package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +551 -0
  140. package/src/core/constraints/doc-freshness/auto-fix.ts +69 -0
  141. package/src/core/constraints/doc-freshness/runner.ts +486 -0
  142. package/src/core/constraints/index.ts +78 -0
  143. package/src/core/constraints/interceptor.ts +220 -0
  144. package/src/core/constraints/prompt-injection.ts +73 -0
  145. package/src/core/index.ts +23 -0
  146. package/src/core/project-config-loader.ts +252 -0
  147. package/src/core/session/__tests__/startup.test.ts +281 -0
  148. package/src/core/session/clean-state.ts +178 -0
  149. package/src/core/session/index.ts +15 -0
  150. package/src/core/session/startup.ts +253 -0
  151. package/src/core/spec/__tests__/validator.test.ts +379 -0
  152. package/src/core/spec/index.ts +7 -0
  153. package/src/core/spec/validator.ts +383 -0
  154. package/src/core/validators/__tests__/passes-gate.test.ts +131 -0
  155. package/src/core/validators/checkpoint.ts +547 -0
  156. package/src/core/validators/cso.ts +124 -0
  157. package/src/core/validators/index.ts +25 -0
  158. package/src/core/validators/passes-gate.ts +613 -0
  159. package/src/dashboard/CONTEXT.md +21 -0
  160. package/src/dashboard/__tests__/data.test.ts +115 -0
  161. package/src/dashboard/__tests__/stats.test.ts +156 -0
  162. package/src/dashboard/data.ts +113 -0
  163. package/src/dashboard/index.ts +3 -0
  164. package/src/dashboard/stats.ts +127 -0
  165. package/src/dashboard/types.ts +113 -0
  166. package/src/evolution/CONTEXT.md +22 -0
  167. package/src/evolution/__tests__/auto-evolve.test.ts +320 -0
  168. package/src/evolution/auto-evolve.ts +98 -0
  169. package/src/evolution/index.ts +1 -0
  170. package/src/failure/CONTEXT.md +24 -0
  171. package/src/failure/classifier.ts +156 -0
  172. package/src/failure/constraint-handler.ts +173 -0
  173. package/src/failure/index.ts +35 -0
  174. package/src/failure/recorder.ts +201 -0
  175. package/src/failure/types.ts +165 -0
  176. package/src/gates/CONTEXT.md +27 -0
  177. package/src/gates/__tests__/acceptance.test.ts +608 -0
  178. package/src/gates/__tests__/command.test.ts +206 -0
  179. package/src/gates/__tests__/performance.test.ts +381 -0
  180. package/src/gates/__tests__/review.test.ts +302 -0
  181. package/src/gates/__tests__/security.test.ts +332 -0
  182. package/src/gates/acceptance.ts +493 -0
  183. package/src/gates/command.ts +444 -0
  184. package/src/gates/contract.ts +260 -0
  185. package/src/gates/index.ts +67 -0
  186. package/src/gates/performance.ts +359 -0
  187. package/src/gates/review.ts +169 -0
  188. package/src/gates/security.ts +244 -0
  189. package/src/gates/types.ts +159 -0
  190. package/src/governance/CONTEXT.md +22 -0
  191. package/src/governance/__tests__/executor.test.ts +249 -0
  192. package/src/governance/executor.ts +229 -0
  193. package/src/governance/index.ts +13 -0
  194. package/src/governance/types.ts +53 -0
  195. package/src/hooks/CONTEXT.md +23 -0
  196. package/src/hooks/__tests__/bootstrap.test.ts +184 -0
  197. package/src/hooks/bootstrap.ts +144 -0
  198. package/src/hooks/index.ts +15 -0
  199. package/src/hooks/pipeline.ts +157 -0
  200. package/src/hooks/registry.ts +88 -0
  201. package/src/hooks/types.ts +80 -0
  202. package/src/index.ts +198 -0
  203. package/src/knowledge/CONTEXT.md +29 -0
  204. package/src/knowledge/__tests__/doctor.test.ts +272 -0
  205. package/src/knowledge/__tests__/import.test.ts +376 -0
  206. package/src/knowledge/__tests__/ingest.test.ts +125 -0
  207. package/src/knowledge/__tests__/lifecycle-hooks.test.ts +137 -0
  208. package/src/knowledge/__tests__/lifecycle.test.ts +248 -0
  209. package/src/knowledge/__tests__/lint.test.ts +401 -0
  210. package/src/knowledge/__tests__/query.test.ts +185 -0
  211. package/src/knowledge/__tests__/reference-tracker.test.ts +129 -0
  212. package/src/knowledge/__tests__/store.test.ts +238 -0
  213. package/src/knowledge/doctor.ts +104 -0
  214. package/src/knowledge/import.ts +505 -0
  215. package/src/knowledge/index.ts +16 -0
  216. package/src/knowledge/ingest.ts +126 -0
  217. package/src/knowledge/lifecycle-hooks.ts +199 -0
  218. package/src/knowledge/lifecycle.ts +175 -0
  219. package/src/knowledge/lint.ts +360 -0
  220. package/src/knowledge/query.ts +150 -0
  221. package/src/knowledge/reference-tracker.ts +99 -0
  222. package/src/knowledge/store.ts +264 -0
  223. package/src/knowledge/types.ts +139 -0
  224. package/src/llm/CONTEXT.md +21 -0
  225. package/src/llm/__tests__/adapter.test.ts +172 -0
  226. package/src/llm/adapter.ts +149 -0
  227. package/src/llm/index.ts +2 -0
  228. package/src/llm/types.ts +81 -0
  229. package/src/monitoring/CONTEXT.md +29 -0
  230. package/src/monitoring/__tests__/context-tracker.test.ts +172 -0
  231. package/src/monitoring/__tests__/knowledge-doctor.test.ts +276 -0
  232. package/src/monitoring/__tests__/knowledge-evolver.test.ts +365 -0
  233. package/src/monitoring/__tests__/performance-analyzer.test.ts +137 -0
  234. package/src/monitoring/__tests__/performance-collector.test.ts +332 -0
  235. package/src/monitoring/__tests__/traces.test.ts +190 -0
  236. package/src/monitoring/constraint-doctor.ts +499 -0
  237. package/src/monitoring/constraint-evolver.ts +573 -0
  238. package/src/monitoring/context-tracker.ts +148 -0
  239. package/src/monitoring/index.ts +23 -0
  240. package/src/monitoring/knowledge-doctor.ts +323 -0
  241. package/src/monitoring/knowledge-evolver.ts +289 -0
  242. package/src/monitoring/performance-analyzer.ts +450 -0
  243. package/src/monitoring/performance-collector.ts +348 -0
  244. package/src/monitoring/trace-analyzer.ts +470 -0
  245. package/src/monitoring/traces.ts +339 -0
  246. package/src/presets/__tests__/standard.test.ts +104 -0
  247. package/src/presets/index.ts +16 -0
  248. package/src/presets/standard.ts +163 -0
  249. package/src/safety/CONTEXT.md +23 -0
  250. package/src/safety/__tests__/input-guardrail.test.ts +105 -0
  251. package/src/safety/__tests__/output-guardrail.test.ts +108 -0
  252. package/src/safety/__tests__/sandbox.test.ts +158 -0
  253. package/src/safety/__tests__/tool-guardrail.test.ts +127 -0
  254. package/src/safety/index.ts +5 -0
  255. package/src/safety/input-guardrail.ts +133 -0
  256. package/src/safety/output-guardrail.ts +174 -0
  257. package/src/safety/sandbox.ts +148 -0
  258. package/src/safety/tool-guardrail.ts +176 -0
  259. package/src/safety/types.ts +124 -0
  260. package/src/spec/CONTEXT.md +19 -0
  261. package/src/spec/__tests__/annotation-checker.test.ts +289 -0
  262. package/src/spec/annotation-checker.ts +342 -0
  263. package/src/tools/CONTEXT.md +25 -0
  264. package/src/tools/__tests__/loader.test.ts +252 -0
  265. package/src/tools/__tests__/registry.test.ts +157 -0
  266. package/src/tools/core/__tests__/core.test.ts +122 -0
  267. package/src/tools/core/index.ts +201 -0
  268. package/src/tools/core/package.json +7 -0
  269. package/src/tools/core/pnpm-lock.yaml +232 -0
  270. package/src/tools/definitions/core/code/fingerprint.sh +273 -0
  271. package/src/tools/definitions/core/code/fingerprint.yml +72 -0
  272. package/src/tools/definitions/core/code/parse.yml +68 -0
  273. package/src/tools/definitions/core/code/register-language.sh +475 -0
  274. package/src/tools/definitions/core/code/register-language.yml +74 -0
  275. package/src/tools/definitions/core/docker/build.yml +73 -0
  276. package/src/tools/definitions/core/docker/run.yml +81 -0
  277. package/src/tools/definitions/core/file/copy.yml +88 -0
  278. package/src/tools/definitions/core/file/read.yml +69 -0
  279. package/src/tools/definitions/core/file/write.yml +75 -0
  280. package/src/tools/definitions/core/git/branch.yml +61 -0
  281. package/src/tools/definitions/core/git/cleanup-worktree.yml +50 -0
  282. package/src/tools/definitions/core/git/clone.yml +69 -0
  283. package/src/tools/definitions/core/git/commit.yml +65 -0
  284. package/src/tools/definitions/core/git/push.yml +79 -0
  285. package/src/tools/definitions/core/git/worktree.yml +96 -0
  286. package/src/tools/definitions/core/notification/notify.sh +152 -0
  287. package/src/tools/definitions/core/notification/notify.yml +60 -0
  288. package/src/tools/definitions/core/npm/build.yml +75 -0
  289. package/src/tools/definitions/core/npm/install.yml +58 -0
  290. package/src/tools/definitions/core/npm/test.yml +75 -0
  291. package/src/tools/definitions/core/validation/check-reuse.yml +170 -0
  292. package/src/tools/definitions/core/validation/validate-handlers.yml +118 -0
  293. package/src/tools/definitions/core/validation/validate-readme-stats.yml +155 -0
  294. package/src/tools/definitions/core/validation/validate-yaml-syntax.yml +134 -0
  295. package/src/tools/definitions/ext/browser/browser-automate.sh +255 -0
  296. package/src/tools/definitions/ext/browser/browser-automate.yml +166 -0
  297. package/src/tools/definitions/ext/browser/browser-close.sh +104 -0
  298. package/src/tools/definitions/ext/browser/browser-close.yml +55 -0
  299. package/src/tools/definitions/ext/browser/browser-mcp-debug.sh +682 -0
  300. package/src/tools/definitions/ext/browser/browser-mcp-debug.yml +167 -0
  301. package/src/tools/definitions/ext/browser/browser-start.sh +101 -0
  302. package/src/tools/definitions/ext/browser/browser-start.yml +62 -0
  303. package/src/tools/definitions/registry.json +795 -0
  304. package/src/tools/definitions/std/README.md +350 -0
  305. package/src/tools/definitions/std/analysis/analyze-codebase.yml +72 -0
  306. package/src/tools/definitions/std/analysis/analyze-diff.yml +182 -0
  307. package/src/tools/definitions/std/analysis/analyze-git-diff.yml +70 -0
  308. package/src/tools/definitions/std/analysis/analyze-impact.yml +78 -0
  309. package/src/tools/definitions/std/analysis/analyze-requirements.yml +105 -0
  310. package/src/tools/definitions/std/analysis/brainstorm-options.yml +106 -0
  311. package/src/tools/definitions/std/analysis/build-graph.yml +154 -0
  312. package/src/tools/definitions/std/analysis/clarify-requirements.yml +90 -0
  313. package/src/tools/definitions/std/analysis/detect-context.yml +109 -0
  314. package/src/tools/definitions/std/analysis/detect-layers.yml +125 -0
  315. package/src/tools/definitions/std/analysis/generate-tour.yml +102 -0
  316. package/src/tools/definitions/std/analysis/search-enhance.yml +105 -0
  317. package/src/tools/definitions/std/backlog/add.yml +51 -0
  318. package/src/tools/definitions/std/backlog/decide.yml +39 -0
  319. package/src/tools/definitions/std/backlog/github-sync.yml +161 -0
  320. package/src/tools/definitions/std/backlog/list.yml +53 -0
  321. package/src/tools/definitions/std/backlog/load.yml +46 -0
  322. package/src/tools/definitions/std/backlog/resolve.yml +37 -0
  323. package/src/tools/definitions/std/backlog/update.yml +42 -0
  324. package/src/tools/definitions/std/bugfix/diagnose.yml +68 -0
  325. package/src/tools/definitions/std/bugfix/fix.yml +66 -0
  326. package/src/tools/definitions/std/bugfix/verify.yml +69 -0
  327. package/src/tools/definitions/std/constraint/check-decomposition.yml +103 -0
  328. package/src/tools/definitions/std/constraint/check-feature-list.yml +77 -0
  329. package/src/tools/definitions/std/constraint/check-progress.yml +97 -0
  330. package/src/tools/definitions/std/constraint/generate-report.yml +147 -0
  331. package/src/tools/definitions/std/deploy/commit-push.yml +33 -0
  332. package/src/tools/definitions/std/deploy/deploy-backend.yml +37 -0
  333. package/src/tools/definitions/std/deploy/deploy-frontend.yml +37 -0
  334. package/src/tools/definitions/std/deploy/rollback.yml +148 -0
  335. package/src/tools/definitions/std/design/analyze-architecture-impact.yml +113 -0
  336. package/src/tools/definitions/std/design/design-api.yml +55 -0
  337. package/src/tools/definitions/std/design/design-architecture.yml +97 -0
  338. package/src/tools/definitions/std/design/design-db.yml +81 -0
  339. package/src/tools/definitions/std/design/generate-spec.yml +182 -0
  340. package/src/tools/definitions/std/development/develop-api.yml +85 -0
  341. package/src/tools/definitions/std/development/develop-backend.yml +88 -0
  342. package/src/tools/definitions/std/development/develop-frontend.yml +87 -0
  343. package/src/tools/definitions/std/development/develop-shared-types.yml +74 -0
  344. package/src/tools/definitions/std/development/develop-task.yml +122 -0
  345. package/src/tools/definitions/std/development/develop-tests.yml +76 -0
  346. package/src/tools/definitions/std/development/execute-batch.yml +152 -0
  347. package/src/tools/definitions/std/development/fix-issues.yml +100 -0
  348. package/src/tools/definitions/std/development/implement-task.yml +124 -0
  349. package/src/tools/definitions/std/development/review-fix-cycle.yml +232 -0
  350. package/src/tools/definitions/std/evolution/implement.yml +125 -0
  351. package/src/tools/definitions/std/evolution/prioritize.yml +112 -0
  352. package/src/tools/definitions/std/evolution/report-gap.yml +166 -0
  353. package/src/tools/definitions/std/file/read.yml +21 -0
  354. package/src/tools/definitions/std/governance/aggregate-opinions.sh +201 -0
  355. package/src/tools/definitions/std/governance/aggregate-opinions.yml +79 -0
  356. package/src/tools/definitions/std/governance/audit-task.yml +178 -0
  357. package/src/tools/definitions/std/governance/cast-vote.yml +129 -0
  358. package/src/tools/definitions/std/governance/collect-issues.yml +16 -0
  359. package/src/tools/definitions/std/governance/create-signed-document.yml +23 -0
  360. package/src/tools/definitions/std/governance/create-voting-session.yml +101 -0
  361. package/src/tools/definitions/std/governance/define-stance.sh +243 -0
  362. package/src/tools/definitions/std/governance/define-stance.yml +82 -0
  363. package/src/tools/definitions/std/governance/generate-review-report.yml +23 -0
  364. package/src/tools/definitions/std/governance/impeach.yml +179 -0
  365. package/src/tools/definitions/std/governance/multi-stance-review.yml +154 -0
  366. package/src/tools/definitions/std/governance/prepare-artifact.yml +20 -0
  367. package/src/tools/definitions/std/governance/review-spec-approval.yml +262 -0
  368. package/src/tools/definitions/std/governance/rollback.yml +186 -0
  369. package/src/tools/definitions/std/governance/stance-review.yml +149 -0
  370. package/src/tools/definitions/std/governance/track-effect.yml +199 -0
  371. package/src/tools/definitions/std/governance/vote-tally.yml +144 -0
  372. package/src/tools/definitions/std/patch/execute.yml +51 -0
  373. package/src/tools/definitions/std/planning/generate-tasks.yml +36 -0
  374. package/src/tools/definitions/std/planning/load-tasks.yml +44 -0
  375. package/src/tools/definitions/std/planning/split-batch.yml +481 -0
  376. package/src/tools/definitions/std/planning/split-tasks.yml +359 -0
  377. package/src/tools/definitions/std/planning/update-tasks.yml +51 -0
  378. package/src/tools/definitions/std/planning/validate-tasks.yml +46 -0
  379. package/src/tools/definitions/std/project/decide-next-workflow.yml +38 -0
  380. package/src/tools/definitions/std/project/load-state.yml +31 -0
  381. package/src/tools/definitions/std/project/save-state.yml +32 -0
  382. package/src/tools/definitions/std/quality/check-quality.yml +241 -0
  383. package/src/tools/definitions/std/quality/debug-systematic.yml +163 -0
  384. package/src/tools/definitions/std/quality/handle-feedback.yml +108 -0
  385. package/src/tools/definitions/std/quality/review-code.yml +128 -0
  386. package/src/tools/definitions/std/quality/review-spec.yml +140 -0
  387. package/src/tools/definitions/std/quality/review-task.yml +190 -0
  388. package/src/tools/definitions/std/quality/run-tests.yml +80 -0
  389. package/src/tools/definitions/std/quick/analyze.yml +56 -0
  390. package/src/tools/definitions/std/quick/implement.yml +67 -0
  391. package/src/tools/definitions/std/quick/verify.yml +71 -0
  392. package/src/tools/definitions/std/verification/verify_completion.yml +113 -0
  393. package/src/tools/definitions/std/verification/wait-condition.yml +107 -0
  394. package/src/tools/index.ts +5 -0
  395. package/src/tools/loader.ts +216 -0
  396. package/src/tools/paths.ts +15 -0
  397. package/src/tools/registry.ts +128 -0
  398. package/src/tools/types.ts +46 -0
  399. package/src/types/__tests__/types.test.ts +86 -0
  400. package/src/types/checkpoint.ts +174 -0
  401. package/src/types/constraint.ts +335 -0
  402. package/src/types/cso.ts +37 -0
  403. package/src/types/enforcement.ts +48 -0
  404. package/src/types/index.ts +59 -0
  405. package/src/types/iron-law.ts +50 -0
  406. package/src/types/passes-gate.ts +185 -0
  407. package/src/types/performance.ts +295 -0
  408. package/src/types/project-config.ts +251 -0
  409. package/src/types/session.ts +132 -0
  410. package/src/types/spec.ts +91 -0
  411. package/src/types/trace.ts +228 -0
  412. package/src/utils/detect-source-roots.ts +114 -0
  413. package/src/utils/exec.ts +61 -0
  414. package/src/verification/CONTEXT.md +23 -0
  415. package/src/verification/__tests__/loop.test.ts +303 -0
  416. package/src/verification/__tests__/rules-based.test.ts +240 -0
  417. package/src/verification/index.ts +3 -0
  418. package/src/verification/loop.ts +137 -0
  419. package/src/verification/rules-based.ts +153 -0
  420. package/src/verification/types.ts +81 -0
@@ -0,0 +1,743 @@
1
+ /**
2
+ * harness sync-docs 命令
3
+ *
4
+ * 自动同步项目文档:CAPABILITIES.md、CONTEXT.md 检查、CHANGELOG 辅助
5
+ * --check 模式输出结构化信息,供 LLM 或 CI 消费
6
+ */
7
+
8
+ import chalk from 'chalk';
9
+ import * as fs from 'fs/promises';
10
+ import { existsSync, readFileSync, readdirSync } from 'fs';
11
+ import * as path from 'path';
12
+ import * as yaml from 'js-yaml';
13
+ import { IRON_LAWS, GUIDELINES, TIPS } from '../../core/constraints/definitions';
14
+ import { FreshnessRunner } from '../../core/constraints/doc-freshness/runner';
15
+ import { FreshnessAutoFix } from '../../core/constraints/doc-freshness/auto-fix';
16
+ import { detectSourceRoots } from '../../utils/detect-source-roots';
17
+ import type { DocFreshnessCheck } from '../../types/project-config';
18
+
19
+ export interface SyncDocsOptions {
20
+ /** 项目路径 */
21
+ projectPath?: string;
22
+ /** 只检查,不写入(CI 模式) */
23
+ check?: boolean;
24
+ /** 是否生成 CHANGELOG 条目 */
25
+ changelog?: boolean;
26
+ /** 输出 JSON 格式(供 LLM 消费) */
27
+ json?: boolean;
28
+ }
29
+
30
+ interface ModuleInfo {
31
+ name: string;
32
+ file: string;
33
+ description: string;
34
+ }
35
+
36
+ interface SyncResult {
37
+ added: string[];
38
+ removed: string[];
39
+ contextMissing: string[];
40
+ contextStale: string[];
41
+ }
42
+
43
+ /**
44
+ * 同步文档
45
+ */
46
+ export async function syncDocs(options: SyncDocsOptions): Promise<boolean> {
47
+ const projectPath = options.projectPath || process.cwd();
48
+ const isCheck = options.check === true;
49
+ const isJson = options.json === true;
50
+
51
+ if (!isJson) {
52
+ if (isCheck) {
53
+ console.log(chalk.blue('🔍 检查文档新鲜度...'));
54
+ } else {
55
+ console.log(chalk.blue('📝 同步文档...'));
56
+ }
57
+ }
58
+
59
+ const result: SyncResult = {
60
+ added: [],
61
+ removed: [],
62
+ contextMissing: [],
63
+ contextStale: [],
64
+ };
65
+
66
+ // 1. 扫描源码模块(从 governance config 读取目录列表,默认 src/)
67
+ const srcDirs = await getSourceDirs(projectPath);
68
+ let currentModules: ModuleInfo[] = [];
69
+ for (const srcDir of srcDirs) {
70
+ try {
71
+ const modules = await scanSourceModules(path.join(projectPath, srcDir), projectPath);
72
+ currentModules.push(...modules);
73
+ } catch {
74
+ if (!isJson) {
75
+ console.log(chalk.yellow(`⚠️ 未找到 ${srcDir} 目录,跳过`));
76
+ }
77
+ }
78
+ }
79
+
80
+ // 2. 解析现有 CAPABILITIES.md,检测格式
81
+ const capabilitiesPath = path.join(projectPath, 'CAPABILITIES.md');
82
+ let existingFiles: string[] = [];
83
+ let capsContent = '';
84
+ let capsIsCapabilityListing = false;
85
+ try {
86
+ capsContent = await fs.readFile(capabilitiesPath, 'utf-8');
87
+ if (isCapabilityListingFormat(capsContent)) {
88
+ capsIsCapabilityListing = true;
89
+ } else {
90
+ existingFiles = await parseCapabilitiesFiles(capabilitiesPath);
91
+ }
92
+ } catch {
93
+ // CAPABILITIES.md 不存在,将创建
94
+ }
95
+
96
+ // 3. 对比差异(分两种格式)
97
+ let capCountMismatches: string[] = [];
98
+
99
+ if (capsIsCapabilityListing) {
100
+ // 能力清单格式:委托 FreshnessRunner 对比计数
101
+ const countCheck = checkCapabilityCounts(projectPath);
102
+ capCountMismatches = countCheck.mismatches;
103
+ // 不填充 result.added/removed(文件级对比不适用于此格式)
104
+ } else {
105
+ // 传统的文件表格格式
106
+ const getBasename = (f: string) => {
107
+ const clean = f.endsWith('/') ? f.slice(0, -1) : f;
108
+ return clean.split('/').pop()!;
109
+ };
110
+ const currentBasenames = currentModules.map(m => getBasename(m.file));
111
+ result.added = currentBasenames.filter(f => !existingFiles.includes(f));
112
+ result.removed = existingFiles.filter(f => !currentBasenames.includes(f));
113
+ }
114
+
115
+ // 4. 检查 CONTEXT.md(缺失 + 过时)
116
+ // 4a. 配置中要求的目录:检查缺失
117
+ const contextDirs = await getRequiredContextDirs(projectPath);
118
+ for (const dir of contextDirs) {
119
+ const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
120
+ try {
121
+ await fs.access(contextPath);
122
+ } catch {
123
+ result.contextMissing.push(dir);
124
+ }
125
+ }
126
+
127
+ // 4b. 自动发现已有的 CONTEXT.md:检查过时
128
+ const existingContextFiles = await findExistingContextFiles(projectPath, srcDirs);
129
+ for (const dir of existingContextFiles) {
130
+ const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
131
+ try {
132
+ const contextStat = await fs.stat(contextPath);
133
+ const dirPath = path.join(projectPath, dir);
134
+ const latestTsMtime = await getLatestTsMtime(dirPath);
135
+ if (latestTsMtime && latestTsMtime > contextStat.mtimeMs) {
136
+ result.contextStale.push(dir);
137
+ }
138
+ } catch {
139
+ // 目录不存在或无法访问,跳过
140
+ }
141
+ }
142
+
143
+ const hasTableIssues = result.added.length > 0 || result.removed.length > 0;
144
+ const hasCapIssues = capCountMismatches.length > 0;
145
+ const hasContextIssues = result.contextMissing.length > 0 || result.contextStale.length > 0;
146
+ const hasIssues = hasTableIssues || hasCapIssues || hasContextIssues;
147
+
148
+ // 5. JSON 输出模式:结构化输出供 LLM 消费
149
+ if (isJson) {
150
+ const jsonOutput: Record<string, unknown> = {
151
+ stale: hasIssues,
152
+ format: capsIsCapabilityListing ? 'capability-listing' : 'file-table',
153
+ summary: {
154
+ added: result.added.length,
155
+ removed: result.removed.length,
156
+ capCountMismatches: capCountMismatches.length,
157
+ contextMissing: result.contextMissing.length,
158
+ contextStale: result.contextStale.length,
159
+ },
160
+ contextMissing: result.contextMissing.map(d => ({
161
+ dir: d,
162
+ file: `${d}/CONTEXT.md`,
163
+ })),
164
+ contextStale: result.contextStale.map(d => ({
165
+ dir: d,
166
+ file: `${d}/CONTEXT.md`,
167
+ })),
168
+ resolution: [] as Array<Record<string, unknown>>,
169
+ };
170
+
171
+ if (capsIsCapabilityListing && hasCapIssues) {
172
+ jsonOutput.capCountMismatches = capCountMismatches.map(m => ({ mismatch: m }));
173
+ (jsonOutput.resolution as Array<Record<string, unknown>>).push({
174
+ action: 'sync-capability-counts',
175
+ command: 'harness sync-docs',
176
+ details: 'CAPABILITIES.md 计数与代码不一致,运行 harness sync-docs 自动更新',
177
+ });
178
+ }
179
+
180
+ if (!capsIsCapabilityListing && hasTableIssues) {
181
+ jsonOutput.added = result.added.map(f => {
182
+ const getBasenameLocal = (s: string) => s.split('/').pop()!;
183
+ return {
184
+ file: f,
185
+ module: currentModules.find(m => getBasenameLocal(m.file) === f),
186
+ };
187
+ });
188
+ jsonOutput.removed = result.removed.map(f => ({ file: f }));
189
+ (jsonOutput.resolution as Array<Record<string, unknown>>).push({
190
+ action: 'sync-capabilities',
191
+ command: 'harness sync-docs',
192
+ });
193
+ }
194
+
195
+ if (hasContextIssues) {
196
+ (jsonOutput.resolution as Array<Record<string, unknown>>).push(
197
+ ...(result.contextMissing.length > 0
198
+ ? [{ action: 'create-context-md', command: 'harness sync-docs', dirs: result.contextMissing }]
199
+ : []),
200
+ ...(result.contextStale.length > 0
201
+ ? [{ action: 'update-context-md', command: 'harness sync-docs', dirs: result.contextStale }]
202
+ : []),
203
+ );
204
+ }
205
+
206
+ console.log(JSON.stringify(jsonOutput, null, 2));
207
+ return !hasIssues;
208
+ }
209
+
210
+ // 6. 人读输出模式
211
+ if (capsIsCapabilityListing && hasCapIssues) {
212
+ console.log(chalk.yellow(`\n📊 CAPABILITIES.md 计数不一致:`));
213
+ capCountMismatches.forEach(m => console.log(chalk.gray(` - ${m}`)));
214
+ }
215
+
216
+ if (result.added.length > 0) {
217
+ console.log(chalk.yellow(`\n📄 CAPABILITIES.md 缺少以下模块:`));
218
+ result.added.forEach(f => console.log(chalk.gray(` + ${f}`)));
219
+ }
220
+
221
+ if (result.removed.length > 0) {
222
+ console.log(chalk.yellow(`\n📄 CAPABILITIES.md 包含已删除的模块:`));
223
+ result.removed.forEach(f => console.log(chalk.gray(` - ${f}`)));
224
+ }
225
+
226
+ if (result.contextMissing.length > 0) {
227
+ console.log(chalk.yellow(`\n📋 缺少 CONTEXT.md:`));
228
+ result.contextMissing.forEach(d => console.log(chalk.gray(` - ${d}/CONTEXT.md`)));
229
+ }
230
+
231
+ if (result.contextStale.length > 0) {
232
+ console.log(chalk.yellow(`\n📋 CONTEXT.md 可能过时(源码比文档新):`));
233
+ result.contextStale.forEach(d => console.log(chalk.gray(` - ${d}/CONTEXT.md`)));
234
+ }
235
+
236
+ if (!hasIssues) {
237
+ console.log(chalk.green('✅ 所有文档都是最新的'));
238
+ return true;
239
+ }
240
+
241
+ // 7. 检查模式:只报告,不修改
242
+ if (isCheck) {
243
+ console.log(chalk.red('\n❌ 文档不是最新的,请运行 harness sync-docs 更新'));
244
+ return false;
245
+ }
246
+
247
+ // 8. 写入模式:更新文档
248
+ if (capsIsCapabilityListing && hasCapIssues) {
249
+ capsContent = updateCapabilityCounts(capsContent, projectPath);
250
+ await fs.writeFile(capabilitiesPath, capsContent, 'utf-8');
251
+ console.log(chalk.green(`\n✅ 已更新 CAPABILITIES.md 计数`));
252
+ }
253
+
254
+ if (!capsIsCapabilityListing && hasTableIssues) {
255
+ await updateCapabilitiesFile(capabilitiesPath, currentModules, existingFiles, result);
256
+ console.log(chalk.green(`\n✅ 已更新 CAPABILITIES.md`));
257
+ }
258
+
259
+ for (const dir of result.contextMissing) {
260
+ await createContextMd(projectPath, dir);
261
+ console.log(chalk.green(`✅ 已创建 ${dir}/CONTEXT.md`));
262
+ }
263
+
264
+ return !hasIssues;
265
+ }
266
+
267
+ /**
268
+ * 扫描源码目录,提取模块信息
269
+ */
270
+ async function scanSourceModules(srcDir: string, projectPath: string): Promise<ModuleInfo[]> {
271
+ const modules: ModuleInfo[] = [];
272
+
273
+ let entries: string[];
274
+ try {
275
+ entries = await fs.readdir(srcDir);
276
+ } catch {
277
+ return [];
278
+ }
279
+
280
+ for (const entry of entries) {
281
+ // 跳过测试目录和非源码目录
282
+ if (entry === '__tests__' || entry === 'node_modules' || entry === 'dist') continue;
283
+
284
+ const entryPath = path.join(srcDir, entry);
285
+ const stat = await fs.stat(entryPath);
286
+
287
+ if (stat.isDirectory()) {
288
+ // 子目录:递归扫描 .ts 文件(不报告目录条目本身)
289
+ const subFiles = await findTsFiles(entryPath);
290
+ for (const f of subFiles) {
291
+ modules.push({
292
+ name: path.basename(f, '.ts'),
293
+ file: path.relative(projectPath, f),
294
+ description: await extractFileDescription(f),
295
+ });
296
+ }
297
+ } else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts') && entry !== 'index.ts') {
298
+ modules.push({
299
+ name: path.basename(entry, '.ts'),
300
+ file: path.relative(projectPath, entryPath),
301
+ description: await extractFileDescription(entryPath),
302
+ });
303
+ }
304
+ }
305
+
306
+ return modules;
307
+ }
308
+
309
+ /**
310
+ * 递归查找 .ts 文件
311
+ */
312
+ async function findTsFiles(dir: string): Promise<string[]> {
313
+ const results: string[] = [];
314
+ let entries: string[];
315
+ try {
316
+ entries = await fs.readdir(dir);
317
+ } catch {
318
+ return [];
319
+ }
320
+
321
+ for (const entry of entries) {
322
+ if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist') continue;
323
+ if (entry === 'index.ts') continue; // barrel export
324
+ const entryPath = path.join(dir, entry);
325
+ const stat = await fs.stat(entryPath);
326
+ if (stat.isDirectory()) {
327
+ results.push(...await findTsFiles(entryPath));
328
+ } else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts')) {
329
+ results.push(entryPath);
330
+ }
331
+ }
332
+ return results;
333
+ }
334
+
335
+ /**
336
+ * 从文件提取描述
337
+ */
338
+ async function extractFileDescription(filePath: string): Promise<string> {
339
+ try {
340
+ const content = await fs.readFile(filePath, 'utf-8');
341
+ return extractFirstComment(content) || path.basename(filePath, '.ts');
342
+ } catch {
343
+ return path.basename(filePath, '.ts');
344
+ }
345
+ }
346
+
347
+ /**
348
+ * 提取文件第一行注释
349
+ */
350
+ function extractFirstComment(content: string): string | null {
351
+ // 匹配 /** ... */ 或 // ...
352
+ const jsdocMatch = content.match(/\/\*\*\s*\n\s*\*\s*(.+?)\s*\n/);
353
+ if (jsdocMatch) return jsdocMatch[1];
354
+
355
+ const lineMatch = content.match(/^\/\/\s*(.+)$/m);
356
+ if (lineMatch) return lineMatch[1];
357
+
358
+ return null;
359
+ }
360
+
361
+ /**
362
+ * 解析 CAPABILITIES.md 中的文件路径
363
+ *
364
+ * 提取所有表格行中提到的 .ts/.tsx/.js/.jsx 文件名(不含路径前缀)
365
+ * 用于模糊匹配:只要文档中提到了该文件名就算已记录
366
+ */
367
+ async function parseCapabilitiesFiles(capabilitiesPath: string): Promise<string[]> {
368
+ const content = await fs.readFile(capabilitiesPath, 'utf-8');
369
+ const files: string[] = [];
370
+ // 匹配表格行中的文件名(可能带路径前缀)
371
+ const tableRowRegex = /\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/g;
372
+ let match;
373
+ while ((match = tableRowRegex.exec(content)) !== null) {
374
+ const raw = match[1].trim();
375
+ // 提取基础文件名(如 core/constraints/definitions.ts → definitions.ts)
376
+ const basename = raw.split('/').pop()!;
377
+ if (!files.includes(basename)) {
378
+ files.push(basename);
379
+ }
380
+ }
381
+ // 也匹配目录条目(如 agents/、gates/)
382
+ const dirRegex = /\|\s*([^|]+?\/)\s*\|/g;
383
+ while ((match = dirRegex.exec(content)) !== null) {
384
+ const dir = match[1].trim();
385
+ if (!files.includes(dir)) {
386
+ files.push(dir);
387
+ }
388
+ }
389
+ return files;
390
+ }
391
+
392
+ /**
393
+ * 自动发现已有 CONTEXT.md 文件的目录(相对于 projectPath)
394
+ */
395
+ async function findExistingContextFiles(projectPath: string, srcDirs: string[]): Promise<string[]> {
396
+ const dirs: string[] = [];
397
+
398
+ async function scan(dir: string): Promise<void> {
399
+ let entries: string[];
400
+ try {
401
+ entries = await fs.readdir(dir);
402
+ } catch {
403
+ return;
404
+ }
405
+ for (const entry of entries) {
406
+ if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist') continue;
407
+ const entryPath = path.join(dir, entry);
408
+ const stat = await fs.stat(entryPath);
409
+ if (stat.isDirectory()) {
410
+ // 检查该目录是否有 CONTEXT.md
411
+ try {
412
+ await fs.access(path.join(entryPath, 'CONTEXT.md'));
413
+ dirs.push(path.relative(projectPath, entryPath));
414
+ } catch {
415
+ // 没有,继续递归
416
+ }
417
+ await scan(entryPath);
418
+ }
419
+ }
420
+ }
421
+
422
+ for (const srcDir of srcDirs) {
423
+ await scan(path.join(projectPath, srcDir));
424
+ }
425
+ return dirs;
426
+ }
427
+
428
+ /**
429
+ * 获取目录下最新 .ts 文件的修改时间
430
+ * 返回 null 如果目录不存在或没有 .ts 文件
431
+ */
432
+ async function getLatestTsMtime(dirPath: string): Promise<number | null> {
433
+ let latest: number | null = null;
434
+
435
+ async function scan(dir: string): Promise<void> {
436
+ let entries: string[];
437
+ try {
438
+ entries = await fs.readdir(dir);
439
+ } catch {
440
+ return;
441
+ }
442
+ for (const entry of entries) {
443
+ if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist') continue;
444
+ const entryPath = path.join(dir, entry);
445
+ const stat = await fs.stat(entryPath);
446
+ if (stat.isDirectory()) {
447
+ await scan(entryPath);
448
+ } else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts')) {
449
+ if (latest === null || stat.mtimeMs > latest) {
450
+ latest = stat.mtimeMs;
451
+ }
452
+ }
453
+ }
454
+ }
455
+
456
+ await scan(dirPath);
457
+ return latest;
458
+ }
459
+
460
+ /**
461
+ * 获取需要 CONTEXT.md 的目录列表
462
+ */
463
+ async function getRequiredContextDirs(projectPath: string): Promise<string[]> {
464
+ const configPath = path.join(projectPath, '.harness', 'config.yml');
465
+ try {
466
+ const content = await fs.readFile(configPath, 'utf-8');
467
+ const config = yaml.load(content) as Record<string, unknown>;
468
+ const governance = config.governance as Record<string, unknown> | undefined;
469
+ const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
470
+ if (contextFiles?.enabled) {
471
+ if (Array.isArray(contextFiles.required_dirs) && contextFiles.required_dirs.length > 0) {
472
+ return contextFiles.required_dirs as string[];
473
+ }
474
+ // Fallback: auto-discover from source roots
475
+ return detectSourceRoots(projectPath);
476
+ }
477
+ } catch {
478
+ // 配置不存在或无法解析
479
+ }
480
+ return [];
481
+ }
482
+
483
+ /**
484
+ * 获取源码扫描目录列表
485
+ * 优先从 governance.context_files.required_dirs 读取,默认 ['src']
486
+ */
487
+ async function getSourceDirs(projectPath: string): Promise<string[]> {
488
+ const requiredDirs = await getRequiredContextDirs(projectPath);
489
+ if (requiredDirs.length > 0) return requiredDirs;
490
+ return detectSourceRoots(projectPath);
491
+ }
492
+
493
+ /**
494
+ * 更新 CAPABILITIES.md 文件
495
+ */
496
+ async function updateCapabilitiesFile(
497
+ capabilitiesPath: string,
498
+ currentModules: ModuleInfo[],
499
+ existingFiles: string[],
500
+ result: SyncResult,
501
+ ): Promise<void> {
502
+ let content: string;
503
+ try {
504
+ content = await fs.readFile(capabilitiesPath, 'utf-8');
505
+ } catch {
506
+ // 文件不存在,创建新的
507
+ content = generateCapabilitiesContent(currentModules);
508
+ await fs.writeFile(capabilitiesPath, content, 'utf-8');
509
+ return;
510
+ }
511
+
512
+ // 如果有表格行,更新表格
513
+ if (existingFiles.length > 0) {
514
+ // 移除已删除文件的行
515
+ for (const removed of result.removed) {
516
+ const escapedFile = removed.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
517
+ const rowRegex = new RegExp(`^\\|[^|]*\\|\\s*${escapedFile}\\s*\\|.*$`, 'gm');
518
+ content = content.replace(rowRegex, '');
519
+ }
520
+
521
+ // 添加新文件的行(在最后一个表格行之后)
522
+ if (result.added.length > 0) {
523
+ const getBasenameLocal = (f: string) => f.split('/').pop()!;
524
+ const addedModules = currentModules.filter(m => result.added.includes(getBasenameLocal(m.file)));
525
+ const tableEndRegex = /(^\|[^|]+\|[^|]+\|[^|]+\|\s*$)/gm;
526
+ let lastTableRow = '';
527
+ let match;
528
+ while ((match = tableEndRegex.exec(content)) !== null) {
529
+ lastTableRow = match[0];
530
+ }
531
+
532
+ if (lastTableRow) {
533
+ const newRows = addedModules.map(m =>
534
+ `| ${m.name} | ${m.file} | ${m.description} |`
535
+ ).join('\n');
536
+ content = content.replace(lastTableRow, lastTableRow + '\n' + newRows);
537
+ }
538
+ }
539
+
540
+ // 清理多余空行
541
+ content = content.replace(/\n{3,}/g, '\n\n');
542
+ } else {
543
+ // 没有表格,追加模块表格
544
+ content += '\n\n' + generateModuleTable(currentModules);
545
+ }
546
+
547
+ // 更新最后更新时间
548
+ const now = new Date().toISOString().split('T')[0];
549
+ content = content.replace(
550
+ /最后更新[::].*/,
551
+ `最后更新: ${now}`
552
+ );
553
+
554
+ await fs.writeFile(capabilitiesPath, content, 'utf-8');
555
+ }
556
+
557
+ /**
558
+ * 生成 CAPABILITIES.md 内容
559
+ */
560
+ function generateCapabilitiesContent(modules: ModuleInfo[]): string {
561
+ const now = new Date().toISOString().split('T')[0];
562
+ return `# CAPABILITIES.md
563
+
564
+ > 最后更新: ${now}
565
+
566
+ ---
567
+
568
+ ${generateModuleTable(modules)}
569
+ `;
570
+ }
571
+
572
+ /**
573
+ * 生成模块表格
574
+ */
575
+ function generateModuleTable(modules: ModuleInfo[]): string {
576
+ if (modules.length === 0) return '';
577
+
578
+ const rows = modules.map(m =>
579
+ `| ${m.name} | ${m.file} | ${m.description} |`
580
+ ).join('\n');
581
+
582
+ return `| 模块 | 文件 | 说明 |\n|------|------|------|\n${rows}`;
583
+ }
584
+
585
+ /**
586
+ * 创建 CONTEXT.md 模板
587
+ */
588
+ async function createContextMd(projectPath: string, dir: string): Promise<void> {
589
+ const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
590
+ const dirName = path.basename(dir);
591
+
592
+ const content = `# ${dirName}
593
+
594
+ > 此文件描述 ${dir} 目录的职责和上下文。
595
+ > 请阅读本目录的源代码,然后填写以下各节。
596
+ > 如果使用 AI 编码助手,将本文件内容作为 prompt 请求它分析并填写。
597
+
598
+ ## 职责
599
+
600
+ 本目录的核心职责是?
601
+
602
+ ## 核心导出
603
+
604
+ 本目录对外暴露的主要模块/函数:
605
+
606
+ ## 依赖关系
607
+
608
+ 本目录依赖哪些其他模块,谁依赖本目录?
609
+
610
+ ## 注意事项
611
+
612
+ 开发时需要注意的约束或约定:
613
+ `;
614
+
615
+ await fs.mkdir(path.join(projectPath, dir), { recursive: true });
616
+ await fs.writeFile(contextPath, content, 'utf-8');
617
+ }
618
+
619
+ // ========================================
620
+ // 能力清单格式支持 (capability-listing)
621
+ // ========================================
622
+
623
+ /**
624
+ * 检测 CAPABILITIES.md 是否使用能力清单格式(计数行),而非文件表格格式
625
+ */
626
+ function isCapabilityListingFormat(content: string): boolean {
627
+ // 能力清单格式特征:包含 "CLI Commands (N)" / "Iron Laws (N)" 等计数行
628
+ return /CLI Commands\s*\(\d+\)/.test(content) ||
629
+ /Iron Laws?\s*\(\d+\)/.test(content) ||
630
+ /Guidelines?\s*\(\d+\)/.test(content);
631
+ }
632
+
633
+ /**
634
+ * 从文件表格格式中提取文件路径
635
+ */
636
+ function extractTableFiles(content: string): string[] {
637
+ const files: string[] = [];
638
+ const tableRowRegex = /\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/g;
639
+ let match;
640
+ while ((match = tableRowRegex.exec(content)) !== null) {
641
+ const raw = match[1].trim();
642
+ const basename = raw.split('/').pop()!;
643
+ if (!files.includes(basename)) {
644
+ files.push(basename);
645
+ }
646
+ }
647
+ return files;
648
+ }
649
+
650
+ /**
651
+ * 构建 CAPABILITIES.md 能力清单格式的检查配置
652
+ */
653
+ function buildCapabilityChecks(): DocFreshnessCheck[] {
654
+ return [
655
+ {
656
+ type: 'doc_regex_count',
657
+ doc: 'CAPABILITIES.md',
658
+ label: 'CLI Commands',
659
+ pattern: 'CLI Commands\\s*\\((\\d+)\\)',
660
+ actual: { kind: 'dir_count', path: 'src/cli/commands', extension: '.ts', exclude: ['index.ts'] },
661
+ },
662
+ {
663
+ type: 'doc_regex_count',
664
+ doc: 'CAPABILITIES.md',
665
+ label: 'Quality Gates',
666
+ pattern: 'Quality Gates?\\s*\\((\\d+)\\)',
667
+ actual: { kind: 'dir_count', path: 'src/gates', extension: '.ts', exclude: ['index.ts', 'types.ts'] },
668
+ },
669
+ {
670
+ type: 'doc_regex_count',
671
+ doc: 'CAPABILITIES.md',
672
+ label: 'Iron Laws',
673
+ pattern: 'Iron Laws?\\s*\\((\\d+)\\)',
674
+ actual: { kind: 'const_count', value: Object.keys(IRON_LAWS).length },
675
+ },
676
+ {
677
+ type: 'doc_regex_count',
678
+ doc: 'CAPABILITIES.md',
679
+ label: 'Guidelines',
680
+ pattern: 'Guidelines?\\s*\\((\\d+)\\)',
681
+ actual: { kind: 'const_count', value: Object.keys(GUIDELINES).length },
682
+ },
683
+ {
684
+ type: 'doc_regex_count',
685
+ doc: 'CAPABILITIES.md',
686
+ label: 'Tips',
687
+ pattern: 'Tips?\\s*\\((\\d+)\\)',
688
+ actual: { kind: 'const_count', value: Object.keys(TIPS).length },
689
+ },
690
+ ];
691
+ }
692
+
693
+ /**
694
+ * 使用 FreshnessRunner 检查能力清单计数是否与代码一致(--check 模式)
695
+ */
696
+ function checkCapabilityCounts(
697
+ projectPath: string
698
+ ): { match: boolean; mismatches: string[] } {
699
+ const runner = new FreshnessRunner();
700
+ const checks = buildCapabilityChecks();
701
+ const results = runner.runAll({ checks }, projectPath);
702
+
703
+ const mismatches: string[] = [];
704
+ for (const r of results) {
705
+ if (!r.pass && r.message) {
706
+ mismatches.push(r.message);
707
+ }
708
+ }
709
+
710
+ return { match: mismatches.length === 0, mismatches };
711
+ }
712
+
713
+ /**
714
+ * 更新 CAPABILITIES.md 中的能力清单计数(write 模式)
715
+ *
716
+ * 使用 FreshnessRunner 获取实际计数,然后 regex 替换文档计数行。
717
+ */
718
+ function updateCapabilityCounts(content: string, projectPath: string): string {
719
+ const runner = new FreshnessRunner();
720
+
721
+ const cliCount = runner.countFromDir('src/cli/commands', '.ts', ['index.ts'], projectPath);
722
+ const gateCount = runner.countFromDir('src/gates', '.ts', ['index.ts', 'types.ts'], projectPath);
723
+ const ironCount = Object.keys(IRON_LAWS).length;
724
+ const guideCount = Object.keys(GUIDELINES).length;
725
+ const tipsCount = Object.keys(TIPS).length;
726
+
727
+ const replacements: [RegExp, string][] = [
728
+ [/CLI Commands\s*\(\d+\)/, `CLI Commands (${cliCount})`],
729
+ [/Quality Gates?\s*\(\d+\)/, `Quality Gates (${gateCount})`],
730
+ [/Iron Laws?\s*\(\d+\)/, `Iron Laws (${ironCount})`],
731
+ [/Guidelines?\s*\(\d+\)/, `Guidelines (${guideCount})`],
732
+ [/Tips?\s*\(\d+\)/, `Tips (${tipsCount})`],
733
+ ];
734
+
735
+ for (const [regex, replacement] of replacements) {
736
+ if (regex.test(content)) {
737
+ content = content.replace(regex, replacement);
738
+ }
739
+ }
740
+
741
+ return content;
742
+ }
743
+