@jokerized/getresearchdone 0.4.1

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 (711) hide show
  1. package/.claude-plugin/plugin.json +103 -0
  2. package/README.md +211 -0
  3. package/agents/grd-baseline-assessor.md +684 -0
  4. package/agents/grd-code-reviewer.md +300 -0
  5. package/agents/grd-codebase-mapper.md +355 -0
  6. package/agents/grd-critique-agent.md +119 -0
  7. package/agents/grd-debugger.md +519 -0
  8. package/agents/grd-deep-diver.md +737 -0
  9. package/agents/grd-eval-planner.md +913 -0
  10. package/agents/grd-eval-reporter.md +717 -0
  11. package/agents/grd-executor.md +683 -0
  12. package/agents/grd-feasibility-analyst.md +624 -0
  13. package/agents/grd-integration-checker.md +367 -0
  14. package/agents/grd-knowledge-miner.md +81 -0
  15. package/agents/grd-migrator.md +88 -0
  16. package/agents/grd-phase-researcher.md +697 -0
  17. package/agents/grd-plan-checker.md +443 -0
  18. package/agents/grd-planner.md +1532 -0
  19. package/agents/grd-product-owner.md +562 -0
  20. package/agents/grd-project-researcher.md +513 -0
  21. package/agents/grd-research-synthesizer.md +273 -0
  22. package/agents/grd-roadmapper.md +798 -0
  23. package/agents/grd-surveyor.md +566 -0
  24. package/agents/grd-verifier.md +893 -0
  25. package/bin/gd.js +4 -0
  26. package/bin/gd.ts +227 -0
  27. package/bin/grd-manifest.js +4 -0
  28. package/bin/grd-manifest.ts +286 -0
  29. package/bin/grd-mcp-server.js +4 -0
  30. package/bin/grd-mcp-server.ts +124 -0
  31. package/bin/grd-tools.js +4 -0
  32. package/bin/grd-tools.ts +2471 -0
  33. package/bin/postinstall.js +4 -0
  34. package/bin/postinstall.ts +80 -0
  35. package/commands/add-phase.md +123 -0
  36. package/commands/add-todo.md +87 -0
  37. package/commands/assess-baseline.md +289 -0
  38. package/commands/autopilot.md +100 -0
  39. package/commands/autoplan.md +55 -0
  40. package/commands/check-todos.md +87 -0
  41. package/commands/compare-methods.md +262 -0
  42. package/commands/complete-milestone.md +225 -0
  43. package/commands/debug.md +372 -0
  44. package/commands/deep-dive.md +288 -0
  45. package/commands/discover.md +281 -0
  46. package/commands/discuss-phase.md +188 -0
  47. package/commands/discuss.md +55 -0
  48. package/commands/eval-report.md +310 -0
  49. package/commands/evolve.md +79 -0
  50. package/commands/execute-phase.md +1017 -0
  51. package/commands/feasibility.md +292 -0
  52. package/commands/help.md +407 -0
  53. package/commands/init.md +1508 -0
  54. package/commands/insert-phase.md +113 -0
  55. package/commands/iterate.md +327 -0
  56. package/commands/list-phase-assumptions.md +217 -0
  57. package/commands/long-term-roadmap.md +202 -0
  58. package/commands/map-codebase.md +111 -0
  59. package/commands/migrate.md +159 -0
  60. package/commands/new-milestone.md +169 -0
  61. package/commands/pause-work.md +83 -0
  62. package/commands/plan-milestone-gaps.md +373 -0
  63. package/commands/plan-phase.md +655 -0
  64. package/commands/principles.md +328 -0
  65. package/commands/product-plan.md +319 -0
  66. package/commands/progress.md +481 -0
  67. package/commands/quick.md +167 -0
  68. package/commands/reapply-patches.md +154 -0
  69. package/commands/remove-phase.md +97 -0
  70. package/commands/requirement.md +96 -0
  71. package/commands/resume-project.md +113 -0
  72. package/commands/settings.md +1144 -0
  73. package/commands/survey.md +242 -0
  74. package/commands/sync.md +246 -0
  75. package/commands/tracker-setup.md +322 -0
  76. package/commands/update.md +202 -0
  77. package/commands/verify-phase.md +335 -0
  78. package/commands/verify-work.md +701 -0
  79. package/commands/wireup.md +29 -0
  80. package/dist/bin/gd.d.ts +3 -0
  81. package/dist/bin/gd.d.ts.map +1 -0
  82. package/dist/bin/gd.js +178 -0
  83. package/dist/bin/gd.js.map +1 -0
  84. package/dist/bin/grd-manifest.d.ts +3 -0
  85. package/dist/bin/grd-manifest.d.ts.map +1 -0
  86. package/dist/bin/grd-manifest.js +202 -0
  87. package/dist/bin/grd-manifest.js.map +1 -0
  88. package/dist/bin/grd-mcp-server.d.ts +3 -0
  89. package/dist/bin/grd-mcp-server.d.ts.map +1 -0
  90. package/dist/bin/grd-mcp-server.js +71 -0
  91. package/dist/bin/grd-mcp-server.js.map +1 -0
  92. package/dist/bin/grd-tools.d.ts +3 -0
  93. package/dist/bin/grd-tools.d.ts.map +1 -0
  94. package/dist/bin/grd-tools.js +1680 -0
  95. package/dist/bin/grd-tools.js.map +1 -0
  96. package/dist/bin/postinstall.d.ts +3 -0
  97. package/dist/bin/postinstall.d.ts.map +1 -0
  98. package/dist/bin/postinstall.js +61 -0
  99. package/dist/bin/postinstall.js.map +1 -0
  100. package/dist/lib/autopilot-milestone.d.ts +2 -0
  101. package/dist/lib/autopilot-milestone.d.ts.map +1 -0
  102. package/dist/lib/autopilot-milestone.js +94 -0
  103. package/dist/lib/autopilot-milestone.js.map +1 -0
  104. package/dist/lib/autopilot-pipeline.d.ts +2 -0
  105. package/dist/lib/autopilot-pipeline.d.ts.map +1 -0
  106. package/dist/lib/autopilot-pipeline.js +830 -0
  107. package/dist/lib/autopilot-pipeline.js.map +1 -0
  108. package/dist/lib/autopilot-waves.d.ts +2 -0
  109. package/dist/lib/autopilot-waves.d.ts.map +1 -0
  110. package/dist/lib/autopilot-waves.js +266 -0
  111. package/dist/lib/autopilot-waves.js.map +1 -0
  112. package/dist/lib/autopilot.d.ts +2 -0
  113. package/dist/lib/autopilot.d.ts.map +1 -0
  114. package/dist/lib/autopilot.js +1314 -0
  115. package/dist/lib/autopilot.js.map +1 -0
  116. package/dist/lib/autoplan.d.ts +2 -0
  117. package/dist/lib/autoplan.d.ts.map +1 -0
  118. package/dist/lib/autoplan.js +198 -0
  119. package/dist/lib/autoplan.js.map +1 -0
  120. package/dist/lib/autoresearch.d.ts +2 -0
  121. package/dist/lib/autoresearch.d.ts.map +1 -0
  122. package/dist/lib/autoresearch.js +626 -0
  123. package/dist/lib/autoresearch.js.map +1 -0
  124. package/dist/lib/backend.d.ts +2 -0
  125. package/dist/lib/backend.d.ts.map +1 -0
  126. package/dist/lib/backend.js +1036 -0
  127. package/dist/lib/backend.js.map +1 -0
  128. package/dist/lib/benchmark.d.ts +99 -0
  129. package/dist/lib/benchmark.d.ts.map +1 -0
  130. package/dist/lib/benchmark.js +278 -0
  131. package/dist/lib/benchmark.js.map +1 -0
  132. package/dist/lib/citations.d.ts +2 -0
  133. package/dist/lib/citations.d.ts.map +1 -0
  134. package/dist/lib/citations.js +642 -0
  135. package/dist/lib/citations.js.map +1 -0
  136. package/dist/lib/cleanup.d.ts +2 -0
  137. package/dist/lib/cleanup.d.ts.map +1 -0
  138. package/dist/lib/cleanup.js +1222 -0
  139. package/dist/lib/cleanup.js.map +1 -0
  140. package/dist/lib/cli/adapters.d.ts +10 -0
  141. package/dist/lib/cli/adapters.d.ts.map +1 -0
  142. package/dist/lib/cli/adapters.js +27 -0
  143. package/dist/lib/cli/adapters.js.map +1 -0
  144. package/dist/lib/cli/agent.d.ts +17 -0
  145. package/dist/lib/cli/agent.d.ts.map +1 -0
  146. package/dist/lib/cli/agent.js +53 -0
  147. package/dist/lib/cli/agent.js.map +1 -0
  148. package/dist/lib/cli/index.d.ts +21 -0
  149. package/dist/lib/cli/index.d.ts.map +1 -0
  150. package/dist/lib/cli/index.js +264 -0
  151. package/dist/lib/cli/index.js.map +1 -0
  152. package/dist/lib/cli/output.d.ts +20 -0
  153. package/dist/lib/cli/output.d.ts.map +1 -0
  154. package/dist/lib/cli/output.js +22 -0
  155. package/dist/lib/cli/output.js.map +1 -0
  156. package/dist/lib/cli/scan-dispatch.d.ts +9 -0
  157. package/dist/lib/cli/scan-dispatch.d.ts.map +1 -0
  158. package/dist/lib/cli/scan-dispatch.js +107 -0
  159. package/dist/lib/cli/scan-dispatch.js.map +1 -0
  160. package/dist/lib/cli/tools.d.ts +16 -0
  161. package/dist/lib/cli/tools.d.ts.map +1 -0
  162. package/dist/lib/cli/tools.js +168 -0
  163. package/dist/lib/cli/tools.js.map +1 -0
  164. package/dist/lib/commands/_dashboard-parsers.d.ts +2 -0
  165. package/dist/lib/commands/_dashboard-parsers.d.ts.map +1 -0
  166. package/dist/lib/commands/_dashboard-parsers.js +192 -0
  167. package/dist/lib/commands/_dashboard-parsers.js.map +1 -0
  168. package/dist/lib/commands/analysis.d.ts +2 -0
  169. package/dist/lib/commands/analysis.d.ts.map +1 -0
  170. package/dist/lib/commands/analysis.js +1418 -0
  171. package/dist/lib/commands/analysis.js.map +1 -0
  172. package/dist/lib/commands/assumptions.d.ts +2 -0
  173. package/dist/lib/commands/assumptions.d.ts.map +1 -0
  174. package/dist/lib/commands/assumptions.js +166 -0
  175. package/dist/lib/commands/assumptions.js.map +1 -0
  176. package/dist/lib/commands/blame.d.ts +2 -0
  177. package/dist/lib/commands/blame.d.ts.map +1 -0
  178. package/dist/lib/commands/blame.js +133 -0
  179. package/dist/lib/commands/blame.js.map +1 -0
  180. package/dist/lib/commands/budget.d.ts +2 -0
  181. package/dist/lib/commands/budget.d.ts.map +1 -0
  182. package/dist/lib/commands/budget.js +100 -0
  183. package/dist/lib/commands/budget.js.map +1 -0
  184. package/dist/lib/commands/check-plans.d.ts +2 -0
  185. package/dist/lib/commands/check-plans.d.ts.map +1 -0
  186. package/dist/lib/commands/check-plans.js +190 -0
  187. package/dist/lib/commands/check-plans.js.map +1 -0
  188. package/dist/lib/commands/config.d.ts +2 -0
  189. package/dist/lib/commands/config.d.ts.map +1 -0
  190. package/dist/lib/commands/config.js +188 -0
  191. package/dist/lib/commands/config.js.map +1 -0
  192. package/dist/lib/commands/dashboard.d.ts +2 -0
  193. package/dist/lib/commands/dashboard.d.ts.map +1 -0
  194. package/dist/lib/commands/dashboard.js +466 -0
  195. package/dist/lib/commands/dashboard.js.map +1 -0
  196. package/dist/lib/commands/estimate.d.ts +2 -0
  197. package/dist/lib/commands/estimate.d.ts.map +1 -0
  198. package/dist/lib/commands/estimate.js +148 -0
  199. package/dist/lib/commands/estimate.js.map +1 -0
  200. package/dist/lib/commands/eval-diff.d.ts +2 -0
  201. package/dist/lib/commands/eval-diff.d.ts.map +1 -0
  202. package/dist/lib/commands/eval-diff.js +213 -0
  203. package/dist/lib/commands/eval-diff.js.map +1 -0
  204. package/dist/lib/commands/freshness.d.ts +2 -0
  205. package/dist/lib/commands/freshness.d.ts.map +1 -0
  206. package/dist/lib/commands/freshness.js +163 -0
  207. package/dist/lib/commands/freshness.js.map +1 -0
  208. package/dist/lib/commands/health.d.ts +2 -0
  209. package/dist/lib/commands/health.d.ts.map +1 -0
  210. package/dist/lib/commands/health.js +435 -0
  211. package/dist/lib/commands/health.js.map +1 -0
  212. package/dist/lib/commands/index.d.ts +2 -0
  213. package/dist/lib/commands/index.d.ts.map +1 -0
  214. package/dist/lib/commands/index.js +128 -0
  215. package/dist/lib/commands/index.js.map +1 -0
  216. package/dist/lib/commands/install.d.ts +56 -0
  217. package/dist/lib/commands/install.d.ts.map +1 -0
  218. package/dist/lib/commands/install.js +214 -0
  219. package/dist/lib/commands/install.js.map +1 -0
  220. package/dist/lib/commands/knowhow-aggregator.d.ts +2 -0
  221. package/dist/lib/commands/knowhow-aggregator.d.ts.map +1 -0
  222. package/dist/lib/commands/knowhow-aggregator.js +279 -0
  223. package/dist/lib/commands/knowhow-aggregator.js.map +1 -0
  224. package/dist/lib/commands/knowledge-search.d.ts +2 -0
  225. package/dist/lib/commands/knowledge-search.d.ts.map +1 -0
  226. package/dist/lib/commands/knowledge-search.js +113 -0
  227. package/dist/lib/commands/knowledge-search.js.map +1 -0
  228. package/dist/lib/commands/long-term-roadmap.d.ts +2 -0
  229. package/dist/lib/commands/long-term-roadmap.d.ts.map +1 -0
  230. package/dist/lib/commands/long-term-roadmap.js +272 -0
  231. package/dist/lib/commands/long-term-roadmap.js.map +1 -0
  232. package/dist/lib/commands/patterns.d.ts +91 -0
  233. package/dist/lib/commands/patterns.d.ts.map +1 -0
  234. package/dist/lib/commands/patterns.js +391 -0
  235. package/dist/lib/commands/patterns.js.map +1 -0
  236. package/dist/lib/commands/phase-info.d.ts +2 -0
  237. package/dist/lib/commands/phase-info.d.ts.map +1 -0
  238. package/dist/lib/commands/phase-info.js +509 -0
  239. package/dist/lib/commands/phase-info.js.map +1 -0
  240. package/dist/lib/commands/plan-lint.d.ts +56 -0
  241. package/dist/lib/commands/plan-lint.d.ts.map +1 -0
  242. package/dist/lib/commands/plan-lint.js +481 -0
  243. package/dist/lib/commands/plan-lint.js.map +1 -0
  244. package/dist/lib/commands/plan-phase.d.ts +53 -0
  245. package/dist/lib/commands/plan-phase.d.ts.map +1 -0
  246. package/dist/lib/commands/plan-phase.js +288 -0
  247. package/dist/lib/commands/plan-phase.js.map +1 -0
  248. package/dist/lib/commands/progress.d.ts +2 -0
  249. package/dist/lib/commands/progress.d.ts.map +1 -0
  250. package/dist/lib/commands/progress.js +266 -0
  251. package/dist/lib/commands/progress.js.map +1 -0
  252. package/dist/lib/commands/quality.d.ts +2 -0
  253. package/dist/lib/commands/quality.d.ts.map +1 -0
  254. package/dist/lib/commands/quality.js +80 -0
  255. package/dist/lib/commands/quality.js.map +1 -0
  256. package/dist/lib/commands/rollback.d.ts +2 -0
  257. package/dist/lib/commands/rollback.d.ts.map +1 -0
  258. package/dist/lib/commands/rollback.js +145 -0
  259. package/dist/lib/commands/rollback.js.map +1 -0
  260. package/dist/lib/commands/scan.d.ts +25 -0
  261. package/dist/lib/commands/scan.d.ts.map +1 -0
  262. package/dist/lib/commands/scan.js +28 -0
  263. package/dist/lib/commands/scan.js.map +1 -0
  264. package/dist/lib/commands/search.d.ts +2 -0
  265. package/dist/lib/commands/search.d.ts.map +1 -0
  266. package/dist/lib/commands/search.js +212 -0
  267. package/dist/lib/commands/search.js.map +1 -0
  268. package/dist/lib/commands/select-candidate.d.ts +128 -0
  269. package/dist/lib/commands/select-candidate.d.ts.map +1 -0
  270. package/dist/lib/commands/select-candidate.js +518 -0
  271. package/dist/lib/commands/select-candidate.js.map +1 -0
  272. package/dist/lib/commands/singularity.d.ts +2 -0
  273. package/dist/lib/commands/singularity.d.ts.map +1 -0
  274. package/dist/lib/commands/singularity.js +185 -0
  275. package/dist/lib/commands/singularity.js.map +1 -0
  276. package/dist/lib/commands/slug-timestamp.d.ts +2 -0
  277. package/dist/lib/commands/slug-timestamp.d.ts.map +1 -0
  278. package/dist/lib/commands/slug-timestamp.js +54 -0
  279. package/dist/lib/commands/slug-timestamp.js.map +1 -0
  280. package/dist/lib/commands/tail.d.ts +2 -0
  281. package/dist/lib/commands/tail.d.ts.map +1 -0
  282. package/dist/lib/commands/tail.js +100 -0
  283. package/dist/lib/commands/tail.js.map +1 -0
  284. package/dist/lib/commands/todo.d.ts +2 -0
  285. package/dist/lib/commands/todo.d.ts.map +1 -0
  286. package/dist/lib/commands/todo.js +200 -0
  287. package/dist/lib/commands/todo.js.map +1 -0
  288. package/dist/lib/commands/watch.d.ts +2 -0
  289. package/dist/lib/commands/watch.d.ts.map +1 -0
  290. package/dist/lib/commands/watch.js +72 -0
  291. package/dist/lib/commands/watch.js.map +1 -0
  292. package/dist/lib/complexity.d.ts +55 -0
  293. package/dist/lib/complexity.d.ts.map +1 -0
  294. package/dist/lib/complexity.js +80 -0
  295. package/dist/lib/complexity.js.map +1 -0
  296. package/dist/lib/context/agents.d.ts +2 -0
  297. package/dist/lib/context/agents.d.ts.map +1 -0
  298. package/dist/lib/context/agents.js +344 -0
  299. package/dist/lib/context/agents.js.map +1 -0
  300. package/dist/lib/context/base.d.ts +2 -0
  301. package/dist/lib/context/base.d.ts.map +1 -0
  302. package/dist/lib/context/base.js +81 -0
  303. package/dist/lib/context/base.js.map +1 -0
  304. package/dist/lib/context/execute.d.ts +2 -0
  305. package/dist/lib/context/execute.d.ts.map +1 -0
  306. package/dist/lib/context/execute.js +753 -0
  307. package/dist/lib/context/execute.js.map +1 -0
  308. package/dist/lib/context/index.d.ts +2 -0
  309. package/dist/lib/context/index.d.ts.map +1 -0
  310. package/dist/lib/context/index.js +88 -0
  311. package/dist/lib/context/index.js.map +1 -0
  312. package/dist/lib/context/progress.d.ts +2 -0
  313. package/dist/lib/context/progress.d.ts.map +1 -0
  314. package/dist/lib/context/progress.js +178 -0
  315. package/dist/lib/context/progress.js.map +1 -0
  316. package/dist/lib/context/project.d.ts +2 -0
  317. package/dist/lib/context/project.d.ts.map +1 -0
  318. package/dist/lib/context/project.js +413 -0
  319. package/dist/lib/context/project.js.map +1 -0
  320. package/dist/lib/context/research.d.ts +2 -0
  321. package/dist/lib/context/research.d.ts.map +1 -0
  322. package/dist/lib/context/research.js +466 -0
  323. package/dist/lib/context/research.js.map +1 -0
  324. package/dist/lib/dead-ends.d.ts +28 -0
  325. package/dist/lib/dead-ends.d.ts.map +1 -0
  326. package/dist/lib/dead-ends.js +451 -0
  327. package/dist/lib/dead-ends.js.map +1 -0
  328. package/dist/lib/deps.d.ts +2 -0
  329. package/dist/lib/deps.d.ts.map +1 -0
  330. package/dist/lib/deps.js +630 -0
  331. package/dist/lib/deps.js.map +1 -0
  332. package/dist/lib/discussion.d.ts +2 -0
  333. package/dist/lib/discussion.d.ts.map +1 -0
  334. package/dist/lib/discussion.js +1041 -0
  335. package/dist/lib/discussion.js.map +1 -0
  336. package/dist/lib/drift.d.ts +36 -0
  337. package/dist/lib/drift.d.ts.map +1 -0
  338. package/dist/lib/drift.js +481 -0
  339. package/dist/lib/drift.js.map +1 -0
  340. package/dist/lib/evolve/_dimensions-features.d.ts +2 -0
  341. package/dist/lib/evolve/_dimensions-features.d.ts.map +1 -0
  342. package/dist/lib/evolve/_dimensions-features.js +369 -0
  343. package/dist/lib/evolve/_dimensions-features.js.map +1 -0
  344. package/dist/lib/evolve/_dimensions.d.ts +2 -0
  345. package/dist/lib/evolve/_dimensions.d.ts.map +1 -0
  346. package/dist/lib/evolve/_dimensions.js +358 -0
  347. package/dist/lib/evolve/_dimensions.js.map +1 -0
  348. package/dist/lib/evolve/_product-ideation.d.ts +2 -0
  349. package/dist/lib/evolve/_product-ideation.d.ts.map +1 -0
  350. package/dist/lib/evolve/_product-ideation.js +281 -0
  351. package/dist/lib/evolve/_product-ideation.js.map +1 -0
  352. package/dist/lib/evolve/_prompts.d.ts +2 -0
  353. package/dist/lib/evolve/_prompts.d.ts.map +1 -0
  354. package/dist/lib/evolve/_prompts.js +153 -0
  355. package/dist/lib/evolve/_prompts.js.map +1 -0
  356. package/dist/lib/evolve/cli.d.ts +2 -0
  357. package/dist/lib/evolve/cli.d.ts.map +1 -0
  358. package/dist/lib/evolve/cli.js +224 -0
  359. package/dist/lib/evolve/cli.js.map +1 -0
  360. package/dist/lib/evolve/discovery.d.ts +2 -0
  361. package/dist/lib/evolve/discovery.d.ts.map +1 -0
  362. package/dist/lib/evolve/discovery.js +391 -0
  363. package/dist/lib/evolve/discovery.js.map +1 -0
  364. package/dist/lib/evolve/index.d.ts +2 -0
  365. package/dist/lib/evolve/index.d.ts.map +1 -0
  366. package/dist/lib/evolve/index.js +88 -0
  367. package/dist/lib/evolve/index.js.map +1 -0
  368. package/dist/lib/evolve/orchestrator.d.ts +2 -0
  369. package/dist/lib/evolve/orchestrator.d.ts.map +1 -0
  370. package/dist/lib/evolve/orchestrator.js +851 -0
  371. package/dist/lib/evolve/orchestrator.js.map +1 -0
  372. package/dist/lib/evolve/scoring.d.ts +2 -0
  373. package/dist/lib/evolve/scoring.d.ts.map +1 -0
  374. package/dist/lib/evolve/scoring.js +118 -0
  375. package/dist/lib/evolve/scoring.js.map +1 -0
  376. package/dist/lib/evolve/state.d.ts +2 -0
  377. package/dist/lib/evolve/state.d.ts.map +1 -0
  378. package/dist/lib/evolve/state.js +264 -0
  379. package/dist/lib/evolve/state.js.map +1 -0
  380. package/dist/lib/evolve/types.d.ts +249 -0
  381. package/dist/lib/evolve/types.d.ts.map +1 -0
  382. package/dist/lib/evolve/types.js +3 -0
  383. package/dist/lib/evolve/types.js.map +1 -0
  384. package/dist/lib/frontmatter.d.ts +2 -0
  385. package/dist/lib/frontmatter.d.ts.map +1 -0
  386. package/dist/lib/frontmatter.js +513 -0
  387. package/dist/lib/frontmatter.js.map +1 -0
  388. package/dist/lib/gates.d.ts +2 -0
  389. package/dist/lib/gates.d.ts.map +1 -0
  390. package/dist/lib/gates.js +578 -0
  391. package/dist/lib/gates.js.map +1 -0
  392. package/dist/lib/genome.d.ts +10 -0
  393. package/dist/lib/genome.d.ts.map +1 -0
  394. package/dist/lib/genome.js +368 -0
  395. package/dist/lib/genome.js.map +1 -0
  396. package/dist/lib/got.d.ts +2 -0
  397. package/dist/lib/got.d.ts.map +1 -0
  398. package/dist/lib/got.js +280 -0
  399. package/dist/lib/got.js.map +1 -0
  400. package/dist/lib/invariants.d.ts +2 -0
  401. package/dist/lib/invariants.d.ts.map +1 -0
  402. package/dist/lib/invariants.js +298 -0
  403. package/dist/lib/invariants.js.map +1 -0
  404. package/dist/lib/knowledge.d.ts +2 -0
  405. package/dist/lib/knowledge.d.ts.map +1 -0
  406. package/dist/lib/knowledge.js +658 -0
  407. package/dist/lib/knowledge.js.map +1 -0
  408. package/dist/lib/long-term-roadmap.d.ts +2 -0
  409. package/dist/lib/long-term-roadmap.d.ts.map +1 -0
  410. package/dist/lib/long-term-roadmap.js +602 -0
  411. package/dist/lib/long-term-roadmap.js.map +1 -0
  412. package/dist/lib/markdown-split.d.ts +2 -0
  413. package/dist/lib/markdown-split.d.ts.map +1 -0
  414. package/dist/lib/markdown-split.js +199 -0
  415. package/dist/lib/markdown-split.js.map +1 -0
  416. package/dist/lib/mcp-server.d.ts +2 -0
  417. package/dist/lib/mcp-server.d.ts.map +1 -0
  418. package/dist/lib/mcp-server.js +2424 -0
  419. package/dist/lib/mcp-server.js.map +1 -0
  420. package/dist/lib/metrics.d.ts +16 -0
  421. package/dist/lib/metrics.d.ts.map +1 -0
  422. package/dist/lib/metrics.js +48 -0
  423. package/dist/lib/metrics.js.map +1 -0
  424. package/dist/lib/overstory.d.ts +2 -0
  425. package/dist/lib/overstory.d.ts.map +1 -0
  426. package/dist/lib/overstory.js +211 -0
  427. package/dist/lib/overstory.js.map +1 -0
  428. package/dist/lib/parallel.d.ts +2 -0
  429. package/dist/lib/parallel.d.ts.map +1 -0
  430. package/dist/lib/parallel.js +349 -0
  431. package/dist/lib/parallel.js.map +1 -0
  432. package/dist/lib/paths.d.ts +2 -0
  433. package/dist/lib/paths.d.ts.map +1 -0
  434. package/dist/lib/paths.js +254 -0
  435. package/dist/lib/paths.js.map +1 -0
  436. package/dist/lib/phase-complete-llm.d.ts +22 -0
  437. package/dist/lib/phase-complete-llm.d.ts.map +1 -0
  438. package/dist/lib/phase-complete-llm.js +331 -0
  439. package/dist/lib/phase-complete-llm.js.map +1 -0
  440. package/dist/lib/phase-complete.d.ts +46 -0
  441. package/dist/lib/phase-complete.d.ts.map +1 -0
  442. package/dist/lib/phase-complete.js +278 -0
  443. package/dist/lib/phase-complete.js.map +1 -0
  444. package/dist/lib/phase-io.d.ts +2 -0
  445. package/dist/lib/phase-io.d.ts.map +1 -0
  446. package/dist/lib/phase-io.js +126 -0
  447. package/dist/lib/phase-io.js.map +1 -0
  448. package/dist/lib/phase.d.ts +2 -0
  449. package/dist/lib/phase.d.ts.map +1 -0
  450. package/dist/lib/phase.js +1344 -0
  451. package/dist/lib/phase.js.map +1 -0
  452. package/dist/lib/plan-tournament.d.ts +63 -0
  453. package/dist/lib/plan-tournament.d.ts.map +1 -0
  454. package/dist/lib/plan-tournament.js +353 -0
  455. package/dist/lib/plan-tournament.js.map +1 -0
  456. package/dist/lib/refinement.d.ts +74 -0
  457. package/dist/lib/refinement.d.ts.map +1 -0
  458. package/dist/lib/refinement.js +283 -0
  459. package/dist/lib/refinement.js.map +1 -0
  460. package/dist/lib/requirements.d.ts +2 -0
  461. package/dist/lib/requirements.d.ts.map +1 -0
  462. package/dist/lib/requirements.js +355 -0
  463. package/dist/lib/requirements.js.map +1 -0
  464. package/dist/lib/research-bundle.d.ts +2 -0
  465. package/dist/lib/research-bundle.d.ts.map +1 -0
  466. package/dist/lib/research-bundle.js +246 -0
  467. package/dist/lib/research-bundle.js.map +1 -0
  468. package/dist/lib/roadmap.d.ts +2 -0
  469. package/dist/lib/roadmap.d.ts.map +1 -0
  470. package/dist/lib/roadmap.js +541 -0
  471. package/dist/lib/roadmap.js.map +1 -0
  472. package/dist/lib/sample.d.ts +16 -0
  473. package/dist/lib/sample.d.ts.map +1 -0
  474. package/dist/lib/sample.js +20 -0
  475. package/dist/lib/sample.js.map +1 -0
  476. package/dist/lib/scaffold.d.ts +2 -0
  477. package/dist/lib/scaffold.d.ts.map +1 -0
  478. package/dist/lib/scaffold.js +355 -0
  479. package/dist/lib/scaffold.js.map +1 -0
  480. package/dist/lib/scan/_utils.d.ts +11 -0
  481. package/dist/lib/scan/_utils.d.ts.map +1 -0
  482. package/dist/lib/scan/_utils.js +36 -0
  483. package/dist/lib/scan/_utils.js.map +1 -0
  484. package/dist/lib/scan/base64.d.ts +15 -0
  485. package/dist/lib/scan/base64.d.ts.map +1 -0
  486. package/dist/lib/scan/base64.js +66 -0
  487. package/dist/lib/scan/base64.js.map +1 -0
  488. package/dist/lib/scan/ignorefile.d.ts +30 -0
  489. package/dist/lib/scan/ignorefile.d.ts.map +1 -0
  490. package/dist/lib/scan/ignorefile.js +101 -0
  491. package/dist/lib/scan/ignorefile.js.map +1 -0
  492. package/dist/lib/scan/injection.d.ts +14 -0
  493. package/dist/lib/scan/injection.d.ts.map +1 -0
  494. package/dist/lib/scan/injection.js +39 -0
  495. package/dist/lib/scan/injection.js.map +1 -0
  496. package/dist/lib/scan/patterns.d.ts +17 -0
  497. package/dist/lib/scan/patterns.d.ts.map +1 -0
  498. package/dist/lib/scan/patterns.js +123 -0
  499. package/dist/lib/scan/patterns.js.map +1 -0
  500. package/dist/lib/scan/strip-markdown.d.ts +7 -0
  501. package/dist/lib/scan/strip-markdown.d.ts.map +1 -0
  502. package/dist/lib/scan/strip-markdown.js +38 -0
  503. package/dist/lib/scan/strip-markdown.js.map +1 -0
  504. package/dist/lib/scan/types.d.ts +23 -0
  505. package/dist/lib/scan/types.d.ts.map +1 -0
  506. package/dist/lib/scan/types.js +3 -0
  507. package/dist/lib/scan/types.js.map +1 -0
  508. package/dist/lib/scheduler-wait.d.ts +2 -0
  509. package/dist/lib/scheduler-wait.d.ts.map +1 -0
  510. package/dist/lib/scheduler-wait.js +59 -0
  511. package/dist/lib/scheduler-wait.js.map +1 -0
  512. package/dist/lib/scheduler.d.ts +254 -0
  513. package/dist/lib/scheduler.d.ts.map +1 -0
  514. package/dist/lib/scheduler.js +1147 -0
  515. package/dist/lib/scheduler.js.map +1 -0
  516. package/dist/lib/state.d.ts +2 -0
  517. package/dist/lib/state.d.ts.map +1 -0
  518. package/dist/lib/state.js +744 -0
  519. package/dist/lib/state.js.map +1 -0
  520. package/dist/lib/think.d.ts +18 -0
  521. package/dist/lib/think.d.ts.map +1 -0
  522. package/dist/lib/think.js +317 -0
  523. package/dist/lib/think.js.map +1 -0
  524. package/dist/lib/tracker.d.ts +2 -0
  525. package/dist/lib/tracker.d.ts.map +1 -0
  526. package/dist/lib/tracker.js +1121 -0
  527. package/dist/lib/tracker.js.map +1 -0
  528. package/dist/lib/types.d.ts +1514 -0
  529. package/dist/lib/types.d.ts.map +1 -0
  530. package/dist/lib/types.js +4 -0
  531. package/dist/lib/types.js.map +1 -0
  532. package/dist/lib/utils.d.ts +2 -0
  533. package/dist/lib/utils.d.ts.map +1 -0
  534. package/dist/lib/utils.js +1363 -0
  535. package/dist/lib/utils.js.map +1 -0
  536. package/dist/lib/verify.d.ts +2 -0
  537. package/dist/lib/verify.d.ts.map +1 -0
  538. package/dist/lib/verify.js +1153 -0
  539. package/dist/lib/verify.js.map +1 -0
  540. package/dist/lib/wireup/autofix.d.ts +2 -0
  541. package/dist/lib/wireup/autofix.d.ts.map +1 -0
  542. package/dist/lib/wireup/autofix.js +188 -0
  543. package/dist/lib/wireup/autofix.js.map +1 -0
  544. package/dist/lib/wireup/cli.d.ts +2 -0
  545. package/dist/lib/wireup/cli.d.ts.map +1 -0
  546. package/dist/lib/wireup/cli.js +194 -0
  547. package/dist/lib/wireup/cli.js.map +1 -0
  548. package/dist/lib/wireup/detection.d.ts +47 -0
  549. package/dist/lib/wireup/detection.d.ts.map +1 -0
  550. package/dist/lib/wireup/detection.js +410 -0
  551. package/dist/lib/wireup/detection.js.map +1 -0
  552. package/dist/lib/wireup/discovery.d.ts +2 -0
  553. package/dist/lib/wireup/discovery.d.ts.map +1 -0
  554. package/dist/lib/wireup/discovery.js +934 -0
  555. package/dist/lib/wireup/discovery.js.map +1 -0
  556. package/dist/lib/wireup/execution.d.ts +2 -0
  557. package/dist/lib/wireup/execution.d.ts.map +1 -0
  558. package/dist/lib/wireup/execution.js +573 -0
  559. package/dist/lib/wireup/execution.js.map +1 -0
  560. package/dist/lib/wireup/index.d.ts +2 -0
  561. package/dist/lib/wireup/index.d.ts.map +1 -0
  562. package/dist/lib/wireup/index.js +85 -0
  563. package/dist/lib/wireup/index.js.map +1 -0
  564. package/dist/lib/wireup/orchestrator.d.ts +2 -0
  565. package/dist/lib/wireup/orchestrator.d.ts.map +1 -0
  566. package/dist/lib/wireup/orchestrator.js +366 -0
  567. package/dist/lib/wireup/orchestrator.js.map +1 -0
  568. package/dist/lib/wireup/report.d.ts +47 -0
  569. package/dist/lib/wireup/report.d.ts.map +1 -0
  570. package/dist/lib/wireup/report.js +201 -0
  571. package/dist/lib/wireup/report.js.map +1 -0
  572. package/dist/lib/wireup/scenarios.d.ts +2 -0
  573. package/dist/lib/wireup/scenarios.d.ts.map +1 -0
  574. package/dist/lib/wireup/scenarios.js +516 -0
  575. package/dist/lib/wireup/scenarios.js.map +1 -0
  576. package/dist/lib/wireup/state.d.ts +2 -0
  577. package/dist/lib/wireup/state.d.ts.map +1 -0
  578. package/dist/lib/wireup/state.js +102 -0
  579. package/dist/lib/wireup/state.js.map +1 -0
  580. package/dist/lib/wireup/types.d.ts +376 -0
  581. package/dist/lib/wireup/types.d.ts.map +1 -0
  582. package/dist/lib/wireup/types.js +3 -0
  583. package/dist/lib/wireup/types.js.map +1 -0
  584. package/dist/lib/worktree.d.ts +2 -0
  585. package/dist/lib/worktree.d.ts.map +1 -0
  586. package/dist/lib/worktree.js +999 -0
  587. package/dist/lib/worktree.js.map +1 -0
  588. package/lib/autopilot-milestone.ts +136 -0
  589. package/lib/autopilot-pipeline.ts +1179 -0
  590. package/lib/autopilot-waves.ts +361 -0
  591. package/lib/autopilot.ts +1874 -0
  592. package/lib/autoplan.ts +280 -0
  593. package/lib/autoresearch.js +4 -0
  594. package/lib/autoresearch.ts +886 -0
  595. package/lib/backend.ts +1252 -0
  596. package/lib/benchmark.ts +341 -0
  597. package/lib/citations.ts +760 -0
  598. package/lib/cleanup.ts +1588 -0
  599. package/lib/cli/adapters.ts +41 -0
  600. package/lib/cli/agent.ts +83 -0
  601. package/lib/cli/index.ts +273 -0
  602. package/lib/cli/output.ts +33 -0
  603. package/lib/cli/scan-dispatch.ts +130 -0
  604. package/lib/cli/tools.ts +198 -0
  605. package/lib/commands/_dashboard-parsers.ts +275 -0
  606. package/lib/commands/analysis.ts +1851 -0
  607. package/lib/commands/assumptions.ts +232 -0
  608. package/lib/commands/blame.ts +174 -0
  609. package/lib/commands/budget.ts +148 -0
  610. package/lib/commands/check-plans.ts +233 -0
  611. package/lib/commands/config.ts +287 -0
  612. package/lib/commands/dashboard.ts +680 -0
  613. package/lib/commands/estimate.ts +204 -0
  614. package/lib/commands/eval-diff.ts +252 -0
  615. package/lib/commands/freshness.ts +213 -0
  616. package/lib/commands/health.ts +607 -0
  617. package/lib/commands/index.ts +266 -0
  618. package/lib/commands/install.ts +307 -0
  619. package/lib/commands/knowhow-aggregator.ts +345 -0
  620. package/lib/commands/knowledge-search.ts +153 -0
  621. package/lib/commands/long-term-roadmap.ts +390 -0
  622. package/lib/commands/patterns.ts +465 -0
  623. package/lib/commands/phase-info.ts +698 -0
  624. package/lib/commands/plan-lint.ts +546 -0
  625. package/lib/commands/plan-phase.ts +375 -0
  626. package/lib/commands/progress.ts +319 -0
  627. package/lib/commands/quality.ts +138 -0
  628. package/lib/commands/rollback.ts +195 -0
  629. package/lib/commands/scan.ts +72 -0
  630. package/lib/commands/search.ts +300 -0
  631. package/lib/commands/select-candidate.ts +687 -0
  632. package/lib/commands/singularity.ts +222 -0
  633. package/lib/commands/slug-timestamp.ts +74 -0
  634. package/lib/commands/tail.ts +129 -0
  635. package/lib/commands/todo.ts +273 -0
  636. package/lib/commands/watch.ts +80 -0
  637. package/lib/complexity.ts +117 -0
  638. package/lib/context/agents.ts +505 -0
  639. package/lib/context/base.ts +123 -0
  640. package/lib/context/execute.ts +977 -0
  641. package/lib/context/index.ts +110 -0
  642. package/lib/context/progress.ts +278 -0
  643. package/lib/context/project.ts +531 -0
  644. package/lib/context/research.ts +646 -0
  645. package/lib/dead-ends.ts +506 -0
  646. package/lib/deps.ts +773 -0
  647. package/lib/discussion.ts +1275 -0
  648. package/lib/drift.ts +519 -0
  649. package/lib/evolve/_dimensions-features.ts +525 -0
  650. package/lib/evolve/_dimensions.ts +511 -0
  651. package/lib/evolve/_product-ideation.ts +405 -0
  652. package/lib/evolve/_prompts.ts +178 -0
  653. package/lib/evolve/cli.ts +330 -0
  654. package/lib/evolve/discovery.ts +571 -0
  655. package/lib/evolve/index.ts +105 -0
  656. package/lib/evolve/orchestrator.ts +1139 -0
  657. package/lib/evolve/scoring.ts +167 -0
  658. package/lib/evolve/state.ts +330 -0
  659. package/lib/evolve/types.ts +290 -0
  660. package/lib/frontmatter.ts +615 -0
  661. package/lib/gates.ts +695 -0
  662. package/lib/genome.ts +402 -0
  663. package/lib/got.js +4 -0
  664. package/lib/got.ts +361 -0
  665. package/lib/invariants.ts +378 -0
  666. package/lib/knowledge.ts +768 -0
  667. package/lib/long-term-roadmap.ts +806 -0
  668. package/lib/markdown-split.ts +273 -0
  669. package/lib/mcp-server.ts +3292 -0
  670. package/lib/metrics.ts +49 -0
  671. package/lib/overstory.ts +270 -0
  672. package/lib/parallel.ts +570 -0
  673. package/lib/paths.ts +293 -0
  674. package/lib/phase-complete-llm.ts +376 -0
  675. package/lib/phase-complete.ts +366 -0
  676. package/lib/phase-io.ts +101 -0
  677. package/lib/phase.ts +1981 -0
  678. package/lib/plan-tournament.ts +426 -0
  679. package/lib/refinement.ts +349 -0
  680. package/lib/requirements.ts +469 -0
  681. package/lib/research-bundle.ts +300 -0
  682. package/lib/roadmap.ts +775 -0
  683. package/lib/scaffold.ts +480 -0
  684. package/lib/scan/_utils.ts +37 -0
  685. package/lib/scan/base64.ts +90 -0
  686. package/lib/scan/ignorefile.ts +109 -0
  687. package/lib/scan/injection.ts +67 -0
  688. package/lib/scan/patterns.ts +139 -0
  689. package/lib/scan/strip-markdown.ts +39 -0
  690. package/lib/scan/types.ts +28 -0
  691. package/lib/scheduler-wait.ts +58 -0
  692. package/lib/scheduler.ts +1370 -0
  693. package/lib/state.ts +1000 -0
  694. package/lib/think.ts +365 -0
  695. package/lib/tracker.ts +1591 -0
  696. package/lib/types.ts +1663 -0
  697. package/lib/utils.ts +1479 -0
  698. package/lib/verify.ts +1434 -0
  699. package/lib/wireup/autofix.ts +241 -0
  700. package/lib/wireup/cli.ts +278 -0
  701. package/lib/wireup/detection.ts +542 -0
  702. package/lib/wireup/discovery.ts +1063 -0
  703. package/lib/wireup/execution.ts +686 -0
  704. package/lib/wireup/index.ts +117 -0
  705. package/lib/wireup/orchestrator.ts +519 -0
  706. package/lib/wireup/report.ts +286 -0
  707. package/lib/wireup/scenarios.ts +616 -0
  708. package/lib/wireup/state.ts +139 -0
  709. package/lib/wireup/types.ts +436 -0
  710. package/lib/worktree.ts +1309 -0
  711. package/package.json +67 -0
@@ -0,0 +1,1041 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const { execFileSync } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { detectAvailableBackends, buildBackendEnv } = require('./backend');
7
+ const { discussionsDir } = require('./paths');
8
+ const { safeReadFile } = require('./utils');
9
+ // --- Constants ---------------------------------------------------------------
10
+ /**
11
+ * Sonnet-tier model ceiling for primary-backend discussion subagent spawns.
12
+ * Mirrors the pattern from lib/wireup/state.ts and lib/evolve/state.ts.
13
+ * REQ-149: discussion subagents must not exceed sonnet tier.
14
+ */
15
+ const DISCUSSION_SONNET_MODEL = 'sonnet';
16
+ /**
17
+ * Default dispatch timeout: 5 minutes.
18
+ */
19
+ const DEFAULT_DISPATCH_TIMEOUT_MS = 5 * 60 * 1000;
20
+ /**
21
+ * Valid severity levels for review findings. Shared across all review functions.
22
+ */
23
+ const SEVERITY_VALUES = ['blocker', 'warning', 'suggestion'];
24
+ function coerceSeverity(raw) {
25
+ const normalized = typeof raw === 'string' ? raw.toLowerCase() : '';
26
+ return SEVERITY_VALUES.includes(normalized) ? normalized : 'warning';
27
+ }
28
+ function reviewFallbackMessage(response) {
29
+ return response.stderr
30
+ ? `Reviewer dispatch failed: ${response.stderr.slice(0, 200)}`
31
+ : `Reviewer returned unparseable response: ${response.response_text.slice(0, 200)}`;
32
+ }
33
+ /**
34
+ * Resolve the reviewer backend from config. Returns null if not configured,
35
+ * not available, or same as primary backend (when requireDifferentFromPrimary is true).
36
+ */
37
+ function resolveReviewer(config, cwd, opts) {
38
+ if (!config.backend_roles?.reviewer)
39
+ return null;
40
+ const reviewerBackend = config.backend_roles.reviewer;
41
+ if ((opts?.requireDifferentFromPrimary ?? true) && config.backend && reviewerBackend === config.backend) {
42
+ return null;
43
+ }
44
+ const availability = detectAvailableBackends(cwd);
45
+ if (!availability[reviewerBackend]?.available)
46
+ return null;
47
+ return { backend: reviewerBackend, availability };
48
+ }
49
+ /**
50
+ * CLI binary name and argument builder for each dispatchable backend.
51
+ * Only the four backends with known CLI invocation conventions are included.
52
+ */
53
+ const BACKEND_CLI_MAP = {
54
+ claude: {
55
+ bin: 'claude',
56
+ buildArgs: (prompt, model) => [
57
+ '--print',
58
+ '-p',
59
+ prompt,
60
+ ...(model ? ['--model', model] : []),
61
+ ],
62
+ },
63
+ codex: {
64
+ bin: 'codex',
65
+ buildArgs: (prompt, _model) => ['exec', prompt],
66
+ },
67
+ gemini: {
68
+ bin: 'gemini',
69
+ buildArgs: (prompt, model) => [
70
+ '-p',
71
+ prompt,
72
+ '--approval-mode',
73
+ 'yolo',
74
+ ...(model ? ['-m', model] : []),
75
+ ],
76
+ },
77
+ opencode: {
78
+ bin: 'opencode',
79
+ buildArgs: (prompt, model) => [
80
+ 'run',
81
+ ...(model ? ['-m', model] : []),
82
+ prompt,
83
+ ],
84
+ },
85
+ };
86
+ // --- Functions ---------------------------------------------------------------
87
+ /**
88
+ * Dispatch a prompt to a backend CLI subprocess and return a typed response.
89
+ *
90
+ * Validates the backend is dispatchable and currently available on PATH before
91
+ * spawning. Times out after `options.timeout_ms` (default 5 minutes) and
92
+ * returns a structured error response instead of throwing.
93
+ *
94
+ * @param backendId - Which backend CLI to invoke (must be in BACKEND_CLI_MAP)
95
+ * @param prompt - The full prompt string to send to the backend
96
+ * @param options - Optional dispatch configuration (timeout, cwd, model override)
97
+ * @returns A BackendResponse with response_text populated on success, or
98
+ * stderr populated with an error description on failure
99
+ */
100
+ function dispatchToBackend(backendId, prompt, options) {
101
+ const cliEntry = BACKEND_CLI_MAP[backendId];
102
+ if (!cliEntry) {
103
+ return {
104
+ backend: backendId,
105
+ response_text: '',
106
+ duration_ms: 0,
107
+ stderr: `Backend "${backendId}" is not dispatchable`,
108
+ };
109
+ }
110
+ const cwd = options?.cwd ?? process.cwd();
111
+ const availability = options?._availability ?? detectAvailableBackends(cwd);
112
+ if (!availability[backendId]?.available) {
113
+ return {
114
+ backend: backendId,
115
+ response_text: '',
116
+ duration_ms: 0,
117
+ stderr: `Backend "${backendId}" is not available on PATH`,
118
+ };
119
+ }
120
+ const timeout = options?.timeout_ms ?? DEFAULT_DISPATCH_TIMEOUT_MS;
121
+ const args = cliEntry.buildArgs(prompt, options?.model);
122
+ const start = Date.now();
123
+ try {
124
+ const stdout = execFileSync(cliEntry.bin, args, {
125
+ timeout,
126
+ encoding: 'utf-8',
127
+ cwd,
128
+ stdio: ['pipe', 'pipe', 'pipe'],
129
+ maxBuffer: 10 * 1024 * 1024,
130
+ env: buildBackendEnv(backendId),
131
+ });
132
+ return {
133
+ backend: backendId,
134
+ response_text: stdout.trim(),
135
+ duration_ms: Date.now() - start,
136
+ stderr: '',
137
+ };
138
+ }
139
+ catch (err) {
140
+ const duration_ms = Date.now() - start;
141
+ const error = err;
142
+ if (error.killed || error.signal === 'SIGTERM') {
143
+ // Distinguish maxBuffer exceeded (fast kill) from timeout (near-timeout kill)
144
+ const isTimeout = duration_ms >= timeout * 0.9;
145
+ return {
146
+ backend: backendId,
147
+ response_text: '',
148
+ duration_ms,
149
+ stderr: isTimeout
150
+ ? `Dispatch timed out after ${timeout}ms`
151
+ : `Dispatch killed (maxBuffer exceeded or signal) after ${duration_ms}ms`,
152
+ };
153
+ }
154
+ return {
155
+ backend: backendId,
156
+ response_text: '',
157
+ duration_ms,
158
+ stderr: error.stderr || error.message || 'Unknown dispatch error',
159
+ };
160
+ }
161
+ }
162
+ /**
163
+ * Build the synthesis prompt from the original topic and round 1 entries.
164
+ *
165
+ * Internal helper — not exported.
166
+ *
167
+ * @param topic - The original discussion topic/question
168
+ * @param roundEntries - Entries from round 1 (mix of BackendResponse and skipped)
169
+ * @returns A formatted prompt string for the synthesizer backend
170
+ */
171
+ function buildSynthesisPrompt(topic, roundEntries) {
172
+ const responseSections = roundEntries
173
+ .map((entry) => {
174
+ if ('skipped' in entry) {
175
+ return `### ${entry.backend} Response\n[SKIPPED]`;
176
+ }
177
+ return `### ${entry.backend} Response\n${entry.response_text}`;
178
+ })
179
+ .join('\n\n');
180
+ return [
181
+ 'You are synthesizing responses from multiple AI backends on the following topic:',
182
+ '',
183
+ '## Topic',
184
+ topic,
185
+ '',
186
+ '## Responses',
187
+ '',
188
+ responseSections,
189
+ '',
190
+ '## Instructions',
191
+ 'Synthesize the above responses. Identify areas of consensus, disagreement, and unique insights. Provide a unified recommendation.',
192
+ ].join('\n');
193
+ }
194
+ /**
195
+ * Build the markdown content for a discussion history file.
196
+ *
197
+ * Internal helper — not exported.
198
+ *
199
+ * @param result - The completed DiscussionResult
200
+ * @param phase - Phase identifier used in the header
201
+ * @param type - Discussion type label used in the header
202
+ * @returns Formatted markdown string for writing to disk
203
+ */
204
+ function buildDiscussionMarkdown(result, phase, type) {
205
+ const lines = [];
206
+ lines.push(`# Discussion: ${result.topic}`, '');
207
+ lines.push(`**Phase:** ${phase} **Type:** ${type} **Participants:** ${result.participants.join(', ')}`);
208
+ lines.push(`**Synthesizer:** ${result.synthesis.backend} **Rounds:** ${result.rounds.length} **Duration:** ${result.duration_ms}ms`);
209
+ lines.push(`**Timestamp:** ${new Date().toISOString()}`, '');
210
+ for (let i = 0; i < result.rounds.length; i++) {
211
+ lines.push(`## Round ${i + 1}`, '');
212
+ for (const entry of result.rounds[i]) {
213
+ if ('skipped' in entry) {
214
+ lines.push(`### ${entry.backend} Response`, `[SKIPPED: ${entry.reason}]`, '---', '');
215
+ }
216
+ else {
217
+ lines.push(`### ${entry.backend} Response`, entry.response_text, '---', '');
218
+ }
219
+ }
220
+ if (i === 0) {
221
+ lines.push(`## Synthesis (${result.synthesis.backend})`, '', result.synthesis.response_text, '');
222
+ }
223
+ }
224
+ lines.push('## Outcome', '', result.synthesis.response_text, '');
225
+ return lines.join('\n');
226
+ }
227
+ /**
228
+ * Dispatch a prompt to all participants and collect results.
229
+ * Skips unavailable participants with a `{ skipped: true }` entry.
230
+ */
231
+ function dispatchRound(participants, prompt, availability, dispatchOpts) {
232
+ return participants.map((participant) => {
233
+ if (!availability[participant]?.available) {
234
+ return { backend: participant, skipped: true, reason: `Backend "${participant}" is not available` };
235
+ }
236
+ return dispatchToBackend(participant, prompt, dispatchOpts);
237
+ });
238
+ }
239
+ /**
240
+ * Run a multi-backend discussion and return a structured result.
241
+ *
242
+ * Dispatches the topic to all participants (see module-level NOTE on
243
+ * concurrency), synthesizes responses, optionally runs additional
244
+ * rounds, writes a markdown history file, and returns a typed DiscussionResult.
245
+ *
246
+ * Unavailable participants produce `{ skipped: true, reason }` entries;
247
+ * the discussion continues with the remaining available participants.
248
+ *
249
+ * @param topic - The question or topic posed to all participants
250
+ * @param participants - Backend IDs to include in the discussion
251
+ * @param options - Optional configuration (rounds, synthesizer, timeout, paths, labels)
252
+ * @returns A DiscussionResult with all rounds, synthesis, and the path to the written file
253
+ */
254
+ function runDiscussion(topic, participants, options) {
255
+ const { rounds = 2, synthesizer = 'claude', timeout_per_round_seconds = 180, cwd = process.cwd(), phase = 'unknown', type = 'discussion', milestone = null, } = options ?? {};
256
+ const clampedRounds = Math.min(Math.max(rounds, 1), 3);
257
+ const start = Date.now();
258
+ const availability = detectAvailableBackends(cwd);
259
+ // Sanitize phase/type to prevent path traversal
260
+ const safePhase = phase.replace(/[/\\]/g, '_');
261
+ const safeType = type.replace(/[/\\]/g, '_');
262
+ const filename = `discussion-${safePhase}-${safeType}-${Date.now()}.md`;
263
+ const dir = discussionsDir(cwd, milestone);
264
+ const timeoutMs = timeout_per_round_seconds * 1000;
265
+ const dispatchOpts = { timeout_ms: timeoutMs, cwd, _availability: availability };
266
+ // Round 1: dispatch to all participants sequentially
267
+ const round1Results = dispatchRound(participants, topic, availability, dispatchOpts);
268
+ const allRounds = [round1Results];
269
+ // Synthesize after round 1, then re-synthesize after each additional round
270
+ let synthPrompt = buildSynthesisPrompt(topic, round1Results);
271
+ let synthesis = dispatchToBackend(synthesizer, synthPrompt, dispatchOpts);
272
+ for (let roundNum = 2; roundNum <= clampedRounds; roundNum++) {
273
+ const roundPrompt = [
274
+ 'You are participating in a multi-round discussion on the following topic:',
275
+ '',
276
+ '## Topic',
277
+ topic,
278
+ '',
279
+ '## Synthesis from Previous Round',
280
+ synthesis.response_text,
281
+ '',
282
+ '## Instructions',
283
+ 'Please respond to the synthesis above. Do you agree, disagree, or have additional insights to add?',
284
+ ].join('\n');
285
+ const roundResults = dispatchRound(participants, roundPrompt, availability, dispatchOpts);
286
+ allRounds.push(roundResults);
287
+ // Re-synthesize incorporating the new round's responses
288
+ synthPrompt = buildSynthesisPrompt(topic, roundResults);
289
+ synthesis = dispatchToBackend(synthesizer, synthPrompt, dispatchOpts);
290
+ }
291
+ // --- Build result ---
292
+ const duration_ms = Date.now() - start;
293
+ const filePath = path.join(dir, filename);
294
+ const result = {
295
+ topic,
296
+ participants,
297
+ rounds: allRounds,
298
+ synthesis,
299
+ duration_ms,
300
+ discussion_file: filePath,
301
+ };
302
+ // --- Write history file (BEFORE return) ---
303
+ fs.mkdirSync(dir, { recursive: true });
304
+ fs.writeFileSync(filePath, buildDiscussionMarkdown(result, phase, type), 'utf-8');
305
+ return result;
306
+ }
307
+ /**
308
+ * List all discussion filenames in the discussions directory for a milestone.
309
+ *
310
+ * @param cwd - Working directory (project root)
311
+ * @param milestone - Optional milestone version string; defaults to current milestone
312
+ * @returns Array of filenames in the discussions directory, or empty array if not found
313
+ */
314
+ function listDiscussions(cwd, milestone) {
315
+ const dir = discussionsDir(cwd, milestone);
316
+ try {
317
+ return fs.readdirSync(dir);
318
+ }
319
+ catch {
320
+ return [];
321
+ }
322
+ }
323
+ /**
324
+ * Read the content of a specific discussion file.
325
+ *
326
+ * @param filename - Filename of the discussion (not full path)
327
+ * @param cwd - Working directory (project root)
328
+ * @param milestone - Optional milestone version string; defaults to current milestone
329
+ * @returns UTF-8 content of the discussion file, or null if not found
330
+ */
331
+ function readDiscussion(filename, cwd, milestone) {
332
+ const dir = discussionsDir(cwd, milestone);
333
+ const filePath = path.join(dir, filename);
334
+ if (!filePath.startsWith(dir + path.sep) && filePath !== dir) {
335
+ throw new Error('Invalid filename: path would escape discussions directory');
336
+ }
337
+ return safeReadFile(filePath);
338
+ }
339
+ // --- Workflow Integration Functions ------------------------------------------
340
+ /**
341
+ * Extract JSON from a raw backend response string.
342
+ *
343
+ * Handles markdown code fences (```json ... ``` or ``` ... ```) and plain JSON.
344
+ * Returns null on parse failure.
345
+ *
346
+ * Internal helper — not exported.
347
+ */
348
+ function parseJSONFromResponse(raw) {
349
+ // Try to extract JSON from markdown code fences first
350
+ const fenceMatch = raw.match(/```(?:json)?\s*([\s\S]*?)```/);
351
+ const candidate = fenceMatch ? fenceMatch[1].trim() : raw.trim();
352
+ try {
353
+ const parsed = JSON.parse(candidate);
354
+ if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
355
+ return parsed;
356
+ }
357
+ return null;
358
+ }
359
+ catch {
360
+ return null;
361
+ }
362
+ }
363
+ /**
364
+ * Check if the brainstormer backend is configured and available for a
365
+ * discussion gate (before_planning or before_execution).
366
+ *
367
+ * Returns the brainstormer BackendId on success, or null to skip.
368
+ */
369
+ function resolveBrainstormer(config, gate, cwd) {
370
+ if (config.discussion?.enabled === false)
371
+ return null;
372
+ // before_planning defaults to true; before_execution must be explicitly true
373
+ if (gate === 'before_planning' && config.discussion?.before_planning === false)
374
+ return null;
375
+ if (gate === 'before_execution' && config.discussion?.before_execution !== true)
376
+ return null;
377
+ if (!config.backend_roles?.brainstormer)
378
+ return null;
379
+ const brainstormerBackend = config.backend_roles.brainstormer;
380
+ const availability = detectAvailableBackends(cwd);
381
+ if (!availability[brainstormerBackend]?.available)
382
+ return null;
383
+ return brainstormerBackend;
384
+ }
385
+ /**
386
+ * Run a pre-planning discussion with the configured brainstormer backend.
387
+ *
388
+ * Checks config flags before dispatching. Returns null (silently skips)
389
+ * when discussion is disabled, before_planning is false, or the brainstormer
390
+ * backend is not configured or unavailable.
391
+ *
392
+ * REQ-138
393
+ */
394
+ function runPrePlanningDiscussion(options) {
395
+ const { phaseGoal, requirements, cwd, phase, milestone, config } = options;
396
+ const brainstormer = resolveBrainstormer(config, 'before_planning', cwd);
397
+ if (!brainstormer)
398
+ return null;
399
+ const reqLines = requirements.map((r) => `- ${r}`).join('\n');
400
+ const topic = `Pre-planning discussion for phase goal: ${phaseGoal}\n\nRequirements:\n${reqLines}`;
401
+ return runDiscussion(topic, [brainstormer], {
402
+ rounds: 1,
403
+ phase,
404
+ type: 'pre-planning',
405
+ cwd,
406
+ milestone,
407
+ });
408
+ }
409
+ /**
410
+ * Run a pre-execution discussion with the configured brainstormer backend.
411
+ *
412
+ * Checks config flags before dispatching. Returns null when discussion is
413
+ * disabled, before_execution is not explicitly enabled, or the brainstormer
414
+ * backend is not configured or unavailable.
415
+ *
416
+ * REQ-139
417
+ */
418
+ function runPreExecutionDiscussion(options) {
419
+ const { planSummary, cwd, phase, milestone, config } = options;
420
+ const brainstormer = resolveBrainstormer(config, 'before_execution', cwd);
421
+ if (!brainstormer)
422
+ return null;
423
+ const topic = `Pre-execution discussion: surface implementation concerns for the following plan:\n\n${planSummary}`;
424
+ return runDiscussion(topic, [brainstormer], {
425
+ rounds: 1,
426
+ phase,
427
+ type: 'pre-execution',
428
+ cwd,
429
+ milestone,
430
+ });
431
+ }
432
+ /**
433
+ * Dispatch a plan text to the reviewer backend and parse the structured response.
434
+ *
435
+ * Checks that a reviewer is configured and that it differs from the primary
436
+ * backend. Returns null when reviewer is unavailable. Handles malformed JSON
437
+ * responses gracefully.
438
+ *
439
+ * REQ-140
440
+ *
441
+ * @param options - Plan text, working directory, and GRD config
442
+ * @returns A PlanReviewResult on success, null if skipped/unavailable
443
+ */
444
+ function reviewPlanViaBackend(options) {
445
+ const { planText, cwd, config } = options;
446
+ const resolved = resolveReviewer(config, cwd);
447
+ if (!resolved)
448
+ return null;
449
+ const { backend: reviewerBackend, availability } = resolved;
450
+ const prompt = [
451
+ 'Review the following plan and provide structured feedback.',
452
+ '',
453
+ '## Plan',
454
+ planText,
455
+ '',
456
+ 'Respond with JSON only (no markdown prose outside the JSON block):',
457
+ '```json',
458
+ '{ "approved": boolean, "concerns": [{"description": string, "severity": "blocker"|"warning"|"suggestion"}], "suggestions": [string] }',
459
+ '```',
460
+ ].join('\n');
461
+ const start = Date.now();
462
+ const response = dispatchToBackend(reviewerBackend, prompt, { cwd, _availability: availability });
463
+ const duration_ms = Date.now() - start;
464
+ const parsed = parseJSONFromResponse(response.response_text);
465
+ if (!parsed) {
466
+ const concerns = [
467
+ { description: reviewFallbackMessage(response), severity: 'warning' },
468
+ ];
469
+ return {
470
+ approved: false,
471
+ concerns,
472
+ suggestions: [response.response_text],
473
+ reviewer_backend: reviewerBackend,
474
+ duration_ms,
475
+ raw_response: response.response_text,
476
+ };
477
+ }
478
+ const approved = typeof parsed['approved'] === 'boolean' ? parsed['approved'] : false;
479
+ const rawConcerns = Array.isArray(parsed['concerns']) ? parsed['concerns'] : [];
480
+ const concerns = rawConcerns
481
+ .filter((c) => typeof c === 'object' && c !== null)
482
+ .map((c) => ({
483
+ description: typeof c['description'] === 'string' ? c['description'] : String(c['description'] ?? ''),
484
+ severity: coerceSeverity(c['severity']),
485
+ }));
486
+ const rawSuggestions = Array.isArray(parsed['suggestions']) ? parsed['suggestions'] : [];
487
+ const suggestions = rawSuggestions.map((s) => String(s));
488
+ return {
489
+ approved,
490
+ concerns,
491
+ suggestions,
492
+ reviewer_backend: reviewerBackend,
493
+ duration_ms,
494
+ raw_response: response.response_text,
495
+ };
496
+ }
497
+ /**
498
+ * Dispatch a code diff to the reviewer backend and parse the structured response.
499
+ *
500
+ * Same reviewer availability checks as reviewPlanViaBackend.
501
+ * Handles malformed JSON responses gracefully.
502
+ *
503
+ * REQ-141
504
+ *
505
+ * @param options - Code diff text, working directory, and GRD config
506
+ * @returns A CodeReviewResult on success, null if skipped/unavailable
507
+ */
508
+ function reviewCodeViaBackend(options) {
509
+ const { diff, cwd, config } = options;
510
+ const resolved = resolveReviewer(config, cwd);
511
+ if (!resolved)
512
+ return null;
513
+ const { backend: reviewerBackend, availability } = resolved;
514
+ const prompt = [
515
+ 'Review this code diff and provide structured feedback.',
516
+ '',
517
+ '## Diff',
518
+ diff,
519
+ '',
520
+ 'Respond with JSON only (no markdown prose outside the JSON block):',
521
+ '```json',
522
+ '{ "approved": boolean, "issues": [{"severity": "blocker"|"warning"|"suggestion", "file": string, "line_range": string, "description": string}], "summary": string }',
523
+ '```',
524
+ ].join('\n');
525
+ const start = Date.now();
526
+ const response = dispatchToBackend(reviewerBackend, prompt, { cwd, _availability: availability });
527
+ const duration_ms = Date.now() - start;
528
+ const parsed = parseJSONFromResponse(response.response_text);
529
+ if (!parsed) {
530
+ const issues = [
531
+ { severity: 'warning', file: '', line_range: '', description: reviewFallbackMessage(response) },
532
+ ];
533
+ return {
534
+ approved: false,
535
+ issues,
536
+ summary: response.response_text,
537
+ reviewer_backend: reviewerBackend,
538
+ duration_ms,
539
+ raw_response: response.response_text,
540
+ };
541
+ }
542
+ const approved = typeof parsed['approved'] === 'boolean' ? parsed['approved'] : false;
543
+ const rawIssues = Array.isArray(parsed['issues']) ? parsed['issues'] : [];
544
+ const issues = rawIssues
545
+ .filter((i) => typeof i === 'object' && i !== null)
546
+ .map((i) => ({
547
+ severity: coerceSeverity(i['severity']),
548
+ file: typeof i['file'] === 'string' ? i['file'] : '',
549
+ line_range: typeof i['line_range'] === 'string' ? i['line_range'] : '',
550
+ description: typeof i['description'] === 'string' ? i['description'] : String(i['description'] ?? ''),
551
+ }));
552
+ const summary = typeof parsed['summary'] === 'string' ? parsed['summary'] : '';
553
+ return {
554
+ approved,
555
+ issues,
556
+ summary,
557
+ reviewer_backend: reviewerBackend,
558
+ duration_ms,
559
+ raw_response: response.response_text,
560
+ };
561
+ }
562
+ /**
563
+ * Dispatch a PR diff to the reviewer backend and parse structured review comments.
564
+ *
565
+ * Checks code_review_enabled and reviewer role configuration.
566
+ * Handles malformed JSON responses gracefully.
567
+ *
568
+ * REQ-142
569
+ *
570
+ * @param options - PR diff text, PR number, working directory, and GRD config
571
+ * @returns A PRReviewResult on success, null if skipped/unavailable
572
+ */
573
+ function reviewPRViaBackend(options) {
574
+ const { diff, prNumber, cwd, config } = options;
575
+ if (!config.code_review_enabled)
576
+ return null;
577
+ // PR review allows reviewer === primary backend (no requireDifferentFromPrimary)
578
+ const resolved = resolveReviewer(config, cwd, { requireDifferentFromPrimary: false });
579
+ if (!resolved)
580
+ return null;
581
+ const { backend: reviewerBackend, availability } = resolved;
582
+ const prompt = [
583
+ `Review this PR #${prNumber} diff and provide structured review comments.`,
584
+ '',
585
+ '## Diff',
586
+ diff,
587
+ '',
588
+ 'Respond with JSON only (no markdown prose outside the JSON block):',
589
+ '```json',
590
+ '{ "comments": [{"file": string, "line": number, "body": string, "severity": "blocker"|"warning"|"suggestion"}], "summary": string }',
591
+ '```',
592
+ ].join('\n');
593
+ const start = Date.now();
594
+ const response = dispatchToBackend(reviewerBackend, prompt, { cwd, _availability: availability });
595
+ const duration_ms = Date.now() - start;
596
+ const parsed = parseJSONFromResponse(response.response_text);
597
+ if (!parsed) {
598
+ const comments = [
599
+ {
600
+ file: '',
601
+ line: 0,
602
+ body: `Reviewer returned unparseable response: ${response.response_text.slice(0, 200)}`,
603
+ severity: 'warning',
604
+ },
605
+ ];
606
+ return {
607
+ comments,
608
+ summary: response.response_text,
609
+ reviewer_backend: reviewerBackend,
610
+ duration_ms,
611
+ raw_response: response.response_text,
612
+ };
613
+ }
614
+ const rawComments = Array.isArray(parsed['comments']) ? parsed['comments'] : [];
615
+ const comments = rawComments
616
+ .filter((c) => typeof c === 'object' && c !== null)
617
+ .map((c) => ({
618
+ file: typeof c['file'] === 'string' ? c['file'] : '',
619
+ line: typeof c['line'] === 'number' ? c['line'] : 0,
620
+ body: typeof c['body'] === 'string' ? c['body'] : '',
621
+ severity: coerceSeverity(c['severity']),
622
+ }));
623
+ const summary = typeof parsed['summary'] === 'string' ? parsed['summary'] : '';
624
+ return {
625
+ comments,
626
+ summary,
627
+ reviewer_backend: reviewerBackend,
628
+ duration_ms,
629
+ raw_response: response.response_text,
630
+ };
631
+ }
632
+ // --- Elicitation Detection ---------------------------------------------------
633
+ /**
634
+ * Clarification phrases that strongly indicate a backend is asking for input.
635
+ * Matched case-insensitively.
636
+ */
637
+ const CLARIFICATION_PHRASES = [
638
+ 'please clarify',
639
+ 'which approach',
640
+ 'could you specify',
641
+ 'would you prefer',
642
+ 'do you want',
643
+ ];
644
+ /**
645
+ * Detect elicitation patterns in backend output text.
646
+ *
647
+ * Parses the output line-by-line to find questions, numbered option lists,
648
+ * or clarification phrases that indicate the backend is waiting for user input.
649
+ * False-positive filters exclude questions inside code blocks, comments,
650
+ * string literals, and markdown headers.
651
+ *
652
+ * Detection patterns (in priority order):
653
+ * 1. direct_question: Line ends with '?' (not in code/comment context)
654
+ * 2. numbered_options: 2+ consecutive lines matching /^\s*\d+[.)]\s+/
655
+ * 3. clarification_phrase: Line contains known clarification keywords
656
+ * 4. option_prompt: Line contains "Choose one", "Select an option", "Pick one"
657
+ *
658
+ * @param output - Full text output from a backend subprocess (may be multi-line)
659
+ * @returns An ElicitationDetection on match, or null if no elicitation detected
660
+ */
661
+ function detectElicitation(output) {
662
+ if (!output)
663
+ return null;
664
+ const lines = output.split('\n');
665
+ let inCodeBlock = false;
666
+ // Option-prompt phrases (case-insensitive)
667
+ const OPTION_PROMPTS = [
668
+ 'choose one',
669
+ 'select an option',
670
+ 'pick one',
671
+ ];
672
+ // Numbered option pattern: lines like "1. Foo", "2) Bar"
673
+ const NUMBERED_OPTION_RE = /^\s*\d+[.)]\s+/;
674
+ /**
675
+ * Returns true if a '?' on this line appears to be inside a string literal.
676
+ * Simple heuristic: count quote characters before the last '?'. If the
677
+ * number of quotes before the '?' is odd, the '?' is likely inside a string.
678
+ */
679
+ function questionInString(line) {
680
+ const qIdx = line.lastIndexOf('?');
681
+ if (qIdx === -1)
682
+ return false;
683
+ const before = line.slice(0, qIdx);
684
+ // Count unescaped single quotes and double quotes separately
685
+ const singleQuotes = (before.match(/(?<!\\)'/g) ?? []).length;
686
+ const doubleQuotes = (before.match(/(?<!\\)"/g) ?? []).length;
687
+ // If either count is odd, the '?' is inside a string
688
+ return singleQuotes % 2 !== 0 || doubleQuotes % 2 !== 0;
689
+ }
690
+ const SKIPPED_PREFIXES = ['//', '/*', '*', '#', 'at ', 'Error:', 'Warning:'];
691
+ function isSkippedContext(trimmed) {
692
+ return SKIPPED_PREFIXES.some((p) => trimmed.startsWith(p));
693
+ }
694
+ /**
695
+ * Returns true if a line looks like a short rhetorical question
696
+ * (single word followed by '?') that appears to be a sentence connector
697
+ * rather than a standalone question to the user.
698
+ * Pattern: one word ending with '?', not preceded by typical elicitation context.
699
+ */
700
+ function isRhetoricalQuestion(trimmed) {
701
+ // "Why?" or "Why? Because..." — single word before '?'
702
+ return /^\w+\?/.test(trimmed) && trimmed.split(/\s+/).length <= 2;
703
+ }
704
+ // Pass 1: look for numbered option blocks (need 2+ consecutive matches)
705
+ // We need to find these first to handle mixed patterns correctly.
706
+ let consecutiveNumbered = 0;
707
+ let numberedStart = -1;
708
+ let numberedEnd = -1;
709
+ {
710
+ let blockDepth = 0;
711
+ for (let i = 0; i < lines.length; i++) {
712
+ const trimmed = lines[i].trim();
713
+ if (trimmed.startsWith('```')) {
714
+ blockDepth = blockDepth === 0 ? 1 : 0;
715
+ }
716
+ if (blockDepth > 0) {
717
+ consecutiveNumbered = 0;
718
+ continue;
719
+ }
720
+ if (NUMBERED_OPTION_RE.test(lines[i])) {
721
+ if (consecutiveNumbered === 0)
722
+ numberedStart = i;
723
+ consecutiveNumbered++;
724
+ numberedEnd = i;
725
+ }
726
+ else {
727
+ if (consecutiveNumbered >= 2)
728
+ break; // found block
729
+ consecutiveNumbered = 0;
730
+ numberedStart = -1;
731
+ }
732
+ }
733
+ }
734
+ if (consecutiveNumbered >= 2 && numberedStart !== -1) {
735
+ const questionLines = lines.slice(numberedStart, numberedEnd + 1);
736
+ return {
737
+ question: questionLines.join('\n'),
738
+ patterns: ['numbered_options'],
739
+ confidence: 'high',
740
+ };
741
+ }
742
+ // Pass 2: scan line-by-line for other patterns
743
+ for (const line of lines) {
744
+ const trimmed = line.trim();
745
+ // Track code block fences
746
+ if (trimmed.startsWith('```')) {
747
+ inCodeBlock = !inCodeBlock;
748
+ continue;
749
+ }
750
+ if (inCodeBlock)
751
+ continue;
752
+ // Skip commented/header/trace lines
753
+ if (isSkippedContext(trimmed))
754
+ continue;
755
+ // Skip empty lines
756
+ if (trimmed.length === 0)
757
+ continue;
758
+ const lower = trimmed.toLowerCase();
759
+ // Pattern: clarification_phrase
760
+ for (const phrase of CLARIFICATION_PHRASES) {
761
+ if (lower.includes(phrase)) {
762
+ return {
763
+ question: trimmed,
764
+ patterns: ['clarification_phrase'],
765
+ confidence: 'high',
766
+ };
767
+ }
768
+ }
769
+ // Pattern: direct_question — line ends with '?'
770
+ if (trimmed.endsWith('?')) {
771
+ if (questionInString(trimmed))
772
+ continue;
773
+ if (isRhetoricalQuestion(trimmed))
774
+ continue;
775
+ return {
776
+ question: trimmed,
777
+ patterns: ['direct_question'],
778
+ confidence: 'high',
779
+ };
780
+ }
781
+ // Pattern: option_prompt
782
+ for (const phrase of OPTION_PROMPTS) {
783
+ if (lower.includes(phrase)) {
784
+ return {
785
+ question: trimmed,
786
+ patterns: ['option_prompt'],
787
+ confidence: 'medium',
788
+ };
789
+ }
790
+ }
791
+ }
792
+ return null;
793
+ }
794
+ // --- Elicitation Context Builder and Resolver --------------------------------
795
+ /**
796
+ * Section budgets in characters for buildElicitationContext.
797
+ * Total: ~32000 chars (~8K tokens).
798
+ */
799
+ const ELICITATION_BUDGET = {
800
+ question: 1000,
801
+ phaseGoal: 1000,
802
+ planSummary: 2000,
803
+ recentChanges: 2000,
804
+ projectState: 1000,
805
+ };
806
+ /**
807
+ * Truncate a string to maxChars, appending a truncation notice if needed.
808
+ */
809
+ function truncate(s, maxChars) {
810
+ if (s.length <= maxChars)
811
+ return s;
812
+ return s.slice(0, maxChars) + '\n[... truncated ...]';
813
+ }
814
+ /**
815
+ * Search milestones/MILESTONE/phases/PHASE*\/*-PLAN.md for an objective tag.
816
+ * Returns the trimmed objective text or '' if not found.
817
+ */
818
+ function findPlanObjective(milestonesDir, phase, milestone) {
819
+ try {
820
+ const milestones = milestone ? [milestone] : fs.readdirSync(milestonesDir);
821
+ for (const ms of milestones) {
822
+ const phasesPath = path.join(milestonesDir, ms, 'phases');
823
+ let phaseEntries;
824
+ try {
825
+ phaseEntries = fs.readdirSync(phasesPath);
826
+ }
827
+ catch {
828
+ continue;
829
+ }
830
+ for (const entry of phaseEntries) {
831
+ if (!entry.startsWith(phase))
832
+ continue;
833
+ let planFiles;
834
+ try {
835
+ planFiles = fs.readdirSync(path.join(phasesPath, entry));
836
+ }
837
+ catch {
838
+ continue;
839
+ }
840
+ const planFile = planFiles.find((f) => f.endsWith('-PLAN.md'));
841
+ if (!planFile)
842
+ continue;
843
+ const content = fs.readFileSync(path.join(phasesPath, entry, planFile), 'utf-8');
844
+ const objMatch = content.match(/<objective>([\s\S]*?)<\/objective>/);
845
+ return objMatch ? objMatch[1].trim() : '';
846
+ }
847
+ }
848
+ }
849
+ catch { /* milestonesDir not readable */ }
850
+ return '';
851
+ }
852
+ /**
853
+ * Build a context string for elicitation resolution.
854
+ *
855
+ * Assembles up to 5 sections:
856
+ * - ## Question: the detected elicitation text
857
+ * - ## Phase Goal: extracted from ROADMAP.md for the given phase
858
+ * - ## Plan Summary: extracted from the active PLAN.md objective element
859
+ * - ## Recent Changes: git diff --stat HEAD~3..HEAD
860
+ * - ## Project State: current position from STATE.md
861
+ *
862
+ * All file reads are wrapped in try/catch — missing files silently omit the section.
863
+ * Total output is kept under 32000 chars (~8K tokens).
864
+ *
865
+ * @param question - The elicitation question text to answer
866
+ * @param options - Configuration: cwd (project root), optional phase identifier
867
+ * @returns A context string with clearly labeled sections
868
+ */
869
+ function buildElicitationContext(question, options) {
870
+ const { cwd, phase, milestone } = options;
871
+ const planningDir = path.join(cwd, '.planning');
872
+ const sections = [];
873
+ // ## Question
874
+ sections.push('## Question\n' + truncate(question, ELICITATION_BUDGET.question));
875
+ // ## Phase Goal — read from ROADMAP.md
876
+ try {
877
+ const roadmapPath = path.join(planningDir, 'ROADMAP.md');
878
+ const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
879
+ let goalText = '';
880
+ if (phase) {
881
+ // Find the phase section and extract the goal line
882
+ const lines = roadmapContent.split('\n');
883
+ let inPhaseSection = false;
884
+ const phaseRe = new RegExp(`Phase\\s+${phase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i');
885
+ for (const line of lines) {
886
+ // Match headings containing the phase number
887
+ if (line.match(phaseRe)) {
888
+ inPhaseSection = true;
889
+ continue;
890
+ }
891
+ if (inPhaseSection) {
892
+ // Stop at next heading of same or higher level
893
+ if (line.match(/^#{1,3}\s/))
894
+ break;
895
+ const trimmed = line.trim();
896
+ if (trimmed.length > 0) {
897
+ goalText += (goalText ? '\n' : '') + trimmed;
898
+ if (goalText.length >= ELICITATION_BUDGET.phaseGoal)
899
+ break;
900
+ }
901
+ }
902
+ }
903
+ }
904
+ if (!goalText && roadmapContent.length > 0) {
905
+ // Fallback: first non-empty paragraph from ROADMAP
906
+ const paras = roadmapContent.split(/\n{2,}/);
907
+ for (const para of paras) {
908
+ const trimmed = para.trim();
909
+ if (trimmed.length > 10) {
910
+ goalText = trimmed;
911
+ break;
912
+ }
913
+ }
914
+ }
915
+ if (goalText) {
916
+ sections.push('## Phase Goal\n' + truncate(goalText, ELICITATION_BUDGET.phaseGoal));
917
+ }
918
+ }
919
+ catch {
920
+ // Missing ROADMAP.md — omit section
921
+ }
922
+ // Section: Plan Summary — read active PLAN.md objective
923
+ if (phase) {
924
+ const planText = findPlanObjective(path.join(planningDir, 'milestones'), String(phase), milestone);
925
+ if (planText) {
926
+ sections.push('## Plan Summary\n' + truncate(planText, ELICITATION_BUDGET.planSummary));
927
+ }
928
+ }
929
+ // ## Recent Changes — git diff --stat HEAD~3..HEAD
930
+ try {
931
+ const _gitCfg = (() => {
932
+ try {
933
+ return JSON.parse(safeReadFile(path.join(cwd, '.planning', 'config.json')) ?? '{}');
934
+ }
935
+ catch {
936
+ return {};
937
+ }
938
+ })();
939
+ const _gitTimeouts = _gitCfg.timeouts;
940
+ const gitTimeout = typeof _gitTimeouts?.discussion_git_ms === 'number' ? _gitTimeouts.discussion_git_ms : 10000;
941
+ const diffStat = execFileSync('git', ['diff', '--stat', 'HEAD~3..HEAD'], {
942
+ timeout: gitTimeout,
943
+ encoding: 'utf-8',
944
+ cwd,
945
+ stdio: ['pipe', 'pipe', 'pipe'],
946
+ maxBuffer: 1024 * 1024,
947
+ });
948
+ if (diffStat && diffStat.trim()) {
949
+ sections.push('## Recent Changes\n' + truncate(diffStat.trim(), ELICITATION_BUDGET.recentChanges));
950
+ }
951
+ }
952
+ catch {
953
+ // Git not available or no history — omit section
954
+ }
955
+ // ## Project State — read STATE.md current position section
956
+ try {
957
+ const statePath = path.join(planningDir, 'STATE.md');
958
+ const stateContent = fs.readFileSync(statePath, 'utf-8');
959
+ // Extract up to the first 1K chars; focus on position/current status
960
+ const posMatch = stateContent.match(/(?:## Current Position|## Status|Current Plan:)[^\n]*([\s\S]{0,800})/i);
961
+ const stateSnippet = posMatch
962
+ ? posMatch[0].trim()
963
+ : stateContent.slice(0, ELICITATION_BUDGET.projectState);
964
+ sections.push('## Project State\n' + truncate(stateSnippet, ELICITATION_BUDGET.projectState));
965
+ }
966
+ catch {
967
+ // Missing STATE.md — omit section
968
+ }
969
+ return sections.join('\n\n');
970
+ }
971
+ /**
972
+ * Route an elicitation question through a single-round multi-backend discussion
973
+ * and return the consensus answer text.
974
+ *
975
+ * Calls runDiscussion() with rounds=1 for speed. Falls back to the first
976
+ * non-skipped participant response if synthesis fails. Returns '' when all
977
+ * participants are unavailable.
978
+ *
979
+ * @param question - The elicitation question to resolve
980
+ * @param context - Context string from buildElicitationContext()
981
+ * @param options - participants, synthesizer, and cwd
982
+ * @returns The resolved answer text, or '' if resolution is not possible
983
+ */
984
+ function resolveElicitation(question, context, options) {
985
+ const { participants, synthesizer, cwd } = options;
986
+ const topic = [
987
+ context,
988
+ '',
989
+ '## Instructions',
990
+ 'Based on the context above, answer the question concisely and make a concrete decision.',
991
+ 'Do not ask clarifying questions. Provide a direct, actionable answer.',
992
+ ].join('\n');
993
+ let result;
994
+ try {
995
+ result = runDiscussion(topic, participants, {
996
+ rounds: 1,
997
+ synthesizer,
998
+ cwd,
999
+ type: 'elicitation',
1000
+ });
1001
+ }
1002
+ catch {
1003
+ return '';
1004
+ }
1005
+ // Happy path: synthesis has response text
1006
+ const synthText = result.synthesis &&
1007
+ typeof result.synthesis.response_text === 'string'
1008
+ ? result.synthesis.response_text.trim()
1009
+ : '';
1010
+ if (synthText)
1011
+ return synthText;
1012
+ // Fallback: find first non-skipped round entry
1013
+ const round0 = result.rounds[0];
1014
+ if (!round0)
1015
+ return '';
1016
+ for (const entry of round0) {
1017
+ if (!('skipped' in entry) && entry.response_text && entry.response_text.trim()) {
1018
+ return entry.response_text.trim();
1019
+ }
1020
+ }
1021
+ return '';
1022
+ }
1023
+ // --- Exports -----------------------------------------------------------------
1024
+ module.exports = {
1025
+ dispatchToBackend,
1026
+ runDiscussion,
1027
+ listDiscussions,
1028
+ readDiscussion,
1029
+ runPrePlanningDiscussion,
1030
+ runPreExecutionDiscussion,
1031
+ reviewPlanViaBackend,
1032
+ reviewCodeViaBackend,
1033
+ reviewPRViaBackend,
1034
+ detectElicitation,
1035
+ buildElicitationContext,
1036
+ resolveElicitation,
1037
+ DISCUSSION_SONNET_MODEL,
1038
+ BACKEND_CLI_MAP,
1039
+ DEFAULT_DISPATCH_TIMEOUT_MS,
1040
+ };
1041
+ //# sourceMappingURL=discussion.js.map