@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,1363 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { execFileSync } = require('child_process');
7
+ const { detectBackend, resolveBackendModel, resolveEffortLevel, getBackendCapabilities, VALID_BACKENDS, } = require('./backend');
8
+ const { phasesDir: getPhasesDirPath } = require('./paths');
9
+ // ─── Git Operation Whitelist ────────────────────────────────────────────────
10
+ const GIT_ALLOWED_COMMANDS = new Set([
11
+ 'add',
12
+ 'commit',
13
+ 'log',
14
+ 'status',
15
+ 'diff',
16
+ 'show',
17
+ 'rev-parse',
18
+ 'cat-file',
19
+ 'check-ignore',
20
+ 'ls-files',
21
+ 'branch',
22
+ 'checkout',
23
+ 'merge',
24
+ 'rebase',
25
+ 'cherry-pick',
26
+ 'tag',
27
+ 'stash',
28
+ 'remote',
29
+ 'fetch',
30
+ 'pull',
31
+ ]);
32
+ const GIT_BLOCKED_COMMANDS = new Set(['config', 'push', 'clean']);
33
+ const GIT_BLOCKED_FLAGS = new Set(['--force', '-f', '--hard', '--delete', '-D']);
34
+ // ─── Model Profile Table ─────────────────────────────────────────────────────
35
+ const MODEL_PROFILES = {
36
+ 'grd-planner': { quality: 'opus', balanced: 'opus', budget: 'sonnet' },
37
+ 'grd-roadmapper': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
38
+ 'grd-executor': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
39
+ 'grd-phase-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
40
+ 'grd-project-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
41
+ 'grd-research-synthesizer': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
42
+ 'grd-debugger': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
43
+ 'grd-codebase-mapper': { quality: 'sonnet', balanced: 'haiku', budget: 'haiku' },
44
+ 'grd-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
45
+ // NERFIFY refinement-loop critic (codex r43 P2: was routed through
46
+ // grd-verifier, which never loaded the agent definition).
47
+ 'grd-critique-agent': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
48
+ 'grd-plan-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
49
+ 'grd-integration-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
50
+ // R&D-specific agents
51
+ 'grd-surveyor': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
52
+ 'grd-deep-diver': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
53
+ 'grd-feasibility-analyst': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
54
+ 'grd-eval-planner': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
55
+ 'grd-eval-reporter': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
56
+ 'grd-product-owner': { quality: 'opus', balanced: 'opus', budget: 'sonnet' },
57
+ 'grd-baseline-assessor': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
58
+ // Development practice agents
59
+ 'grd-code-reviewer': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
60
+ };
61
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
62
+ /**
63
+ * Parse --include flag from CLI args into a Set of included items.
64
+ * @param args - CLI argument array
65
+ * @returns Set of comma-separated include values, or empty Set if not present
66
+ */
67
+ function parseIncludeFlag(args) {
68
+ const includeIndex = args.indexOf('--include');
69
+ if (includeIndex === -1)
70
+ return new Set();
71
+ const includeValue = args[includeIndex + 1];
72
+ if (!includeValue)
73
+ return new Set();
74
+ return new Set(includeValue.split(',').map((s) => s.trim()));
75
+ }
76
+ /**
77
+ * Read file returning content or null on error.
78
+ * @param filePath - Absolute path to the file
79
+ * @returns File content as UTF-8 string, or null if read fails
80
+ */
81
+ function safeReadFile(filePath) {
82
+ try {
83
+ return fs.readFileSync(filePath, 'utf-8');
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * Read a markdown file, transparently handling GRD split-format index files.
91
+ * If the file is a GRD index (contains <!-- GRD-INDEX --> marker), partials are
92
+ * automatically reassembled. Otherwise, returns the file content as-is.
93
+ * @param filePath - Absolute path to the markdown file
94
+ * @returns File content (reassembled if split), or null on error
95
+ */
96
+ function safeReadMarkdown(filePath) {
97
+ try {
98
+ // Lazy require to avoid circular dependency (markdown-split.js imports safeReadFile from utils.js)
99
+ const { readMarkdownWithPartials } = require('./markdown-split');
100
+ return readMarkdownWithPartials(filePath);
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * Read and parse a JSON file, returning the default value on any error.
108
+ * @param filePath - Absolute path to the JSON file
109
+ * @param defaultValue - Value to return if read or parse fails
110
+ * @returns Parsed JSON object, or defaultValue on error
111
+ */
112
+ function safeReadJSON(filePath, defaultValue = null) {
113
+ try {
114
+ const raw = fs.readFileSync(filePath, 'utf-8');
115
+ return JSON.parse(raw);
116
+ }
117
+ catch {
118
+ return defaultValue;
119
+ }
120
+ }
121
+ /**
122
+ * Extract content under a markdown heading (## or ### level).
123
+ * @param content - Full markdown content
124
+ * @param heading - Heading text to find (case-insensitive)
125
+ * @param level - Heading level (2 for ##, 3 for ###)
126
+ * @returns Section content (without the heading line), or null if not found
127
+ */
128
+ function extractMarkdownSection(content, heading, level = 2) {
129
+ const prefix = '#'.repeat(level);
130
+ const regex = new RegExp(`${prefix}\\s+${heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\n([\\s\\S]*?)(?=\\n${prefix}\\s|$)`, 'i');
131
+ const match = content.match(regex);
132
+ return match ? match[1] : null;
133
+ }
134
+ // ─── Levenshtein Distance ────────────────────────────────────────────────────
135
+ /**
136
+ * Compute the Levenshtein edit distance between two strings.
137
+ * @param s1 - First string
138
+ * @param s2 - Second string
139
+ * @returns Edit distance
140
+ */
141
+ function levenshteinDistance(s1, s2) {
142
+ const m = s1.length;
143
+ const n = s2.length;
144
+ const dp = [];
145
+ for (let i = 0; i <= m; i++) {
146
+ dp[i] = [i];
147
+ for (let j = 1; j <= n; j++) {
148
+ if (i === 0) {
149
+ dp[i][j] = j;
150
+ }
151
+ else {
152
+ dp[i][j] = 0;
153
+ }
154
+ }
155
+ }
156
+ for (let i = 1; i <= m; i++) {
157
+ for (let j = 1; j <= n; j++) {
158
+ if (s1[i - 1] === s2[j - 1]) {
159
+ dp[i][j] = dp[i - 1][j - 1];
160
+ }
161
+ else {
162
+ dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
163
+ }
164
+ }
165
+ }
166
+ return dp[m][n];
167
+ }
168
+ /**
169
+ * Find the closest command name from a list of commands using Levenshtein distance.
170
+ * Returns null if no command is close enough (distance > threshold) or if input is invalid.
171
+ * @param input - User input to match
172
+ * @param commands - List of valid command names
173
+ * @returns The closest command name, or null if too different
174
+ */
175
+ function findClosestCommand(input, commands) {
176
+ if (!input || !commands || commands.length === 0)
177
+ return null;
178
+ const lower = input.toLowerCase();
179
+ let best = null;
180
+ let bestDist = Infinity;
181
+ for (const cmd of commands) {
182
+ const dist = levenshteinDistance(lower, cmd.toLowerCase());
183
+ if (dist < bestDist) {
184
+ bestDist = dist;
185
+ best = cmd;
186
+ }
187
+ }
188
+ // Threshold: only suggest if distance is at most 3 (reasonable typo range)
189
+ const threshold = Math.max(3, Math.floor(best ? best.length / 3 : 3));
190
+ if (bestDist > threshold)
191
+ return null;
192
+ return best;
193
+ }
194
+ // ─── Phase Cache ─────────────────────────────────────────────────────────────
195
+ const _phaseCache = new Map();
196
+ /**
197
+ * Clear the internal phase directory cache.
198
+ */
199
+ function clearPhaseCache() {
200
+ _phaseCache.clear();
201
+ }
202
+ /**
203
+ * Known top-level config keys (used for unrecognized key warnings).
204
+ */
205
+ const KNOWN_CONFIG_KEYS = new Set([
206
+ 'model_profile',
207
+ 'commit_docs',
208
+ 'search_gitignored',
209
+ 'branching_strategy',
210
+ 'phase_branch_template',
211
+ 'milestone_branch_template',
212
+ 'base_branch',
213
+ 'research',
214
+ 'plan_checker',
215
+ 'verifier',
216
+ 'parallelization',
217
+ 'code_review_enabled',
218
+ 'code_review_timing',
219
+ 'code_review_severity_gate',
220
+ 'code_review_auto_fix_warnings',
221
+ 'use_teams',
222
+ 'team_timeout_minutes',
223
+ 'max_concurrent_teammates',
224
+ 'backend',
225
+ 'backend_models',
226
+ 'autonomous_mode',
227
+ // Nested section keys (objects)
228
+ 'code_review',
229
+ 'execution',
230
+ 'git',
231
+ 'planning',
232
+ 'workflow',
233
+ 'tracker',
234
+ 'eval_config',
235
+ 'ceremony',
236
+ 'phase_cleanup',
237
+ 'research_gates',
238
+ 'confirmation_gates',
239
+ 'timeouts',
240
+ 'evolve',
241
+ // New-project command keys
242
+ 'mode',
243
+ 'depth',
244
+ // YOLO saved state keys
245
+ '_saved_research_gates',
246
+ '_saved_confirmation_gates',
247
+ 'yolo_decision_log',
248
+ // Backend-specific keys
249
+ 'overstory',
250
+ // Scheduler config
251
+ 'scheduler',
252
+ // Superpowers config
253
+ 'superpowers',
254
+ // Discussion config
255
+ 'backend_roles',
256
+ 'discussion',
257
+ // Citation gate
258
+ 'citation_gate',
259
+ // Transitive citation gate
260
+ 'transitive_citation_gate',
261
+ // Refinement loop
262
+ 'refinement_loop',
263
+ // LLM fallback for phase completion (Spec 3B)
264
+ 'phase_complete_llm_fallback',
265
+ // LLM fallback retry count with exponential backoff
266
+ 'phase_complete_llm_fallback_retries',
267
+ // Drift score (Tier-2 #7 of Ouroboros integration)
268
+ 'drift',
269
+ // Autopilot termination knobs (Tier-3 #10 of Ouroboros integration)
270
+ 'autopilot',
271
+ // Evolve r9 + r14: surface-level keys consumed by Ouroboros r9 CLIs.
272
+ // research_staleness_days drives `gd health`'s STALE_RESEARCH blocker;
273
+ // `survey` carries staleness_days for `gd progress`'s freshness warn.
274
+ 'research_staleness_days',
275
+ 'survey',
276
+ // Plug-in / context-mode knowledge stats path
277
+ 'token_profile',
278
+ // v0.4 Phase 1: orthogonal effort axis
279
+ 'effort',
280
+ ]);
281
+ // ─── Effort Axis (v0.4 Phase 1) ─────────────────────────────────────────────
282
+ /**
283
+ * v0.4 effort-scaled knobs. Single-knob scope by design (codex r6) — only
284
+ * `candidates_per_plan_phase` is wired in v0.4. The table is structured
285
+ * (object keyed by knob name) so v0.5+ can add knobs without changing the
286
+ * `resolveEffortKnob` signature.
287
+ */
288
+ const EFFORT_PROFILES = {
289
+ thrifty: { candidates_per_plan_phase: 1 },
290
+ balanced: { candidates_per_plan_phase: 3 },
291
+ deep: { candidates_per_plan_phase: 7 },
292
+ };
293
+ /**
294
+ * Return the integer value of an effort-scaled knob for the current
295
+ * `effort` setting in config. Defaults to 'balanced' when unset.
296
+ *
297
+ * @param config - GrdConfig (effort field optional, defaults to 'balanced')
298
+ * @param knob - Name of the effort-scaled knob to resolve
299
+ * @returns Integer value for the knob under the active effort level
300
+ */
301
+ function resolveEffortKnob(config, knob) {
302
+ // Codex review P2: loadConfig preserves invalid `effort` values (warns but
303
+ // keeps them), so guard against EFFORT_PROFILES[level] being undefined.
304
+ // An unrecognized level falls back to 'balanced' rather than crashing.
305
+ const raw = config.effort;
306
+ const level = raw !== undefined && Object.prototype.hasOwnProperty.call(EFFORT_PROFILES, raw)
307
+ ? raw
308
+ : 'balanced';
309
+ return EFFORT_PROFILES[level][knob];
310
+ }
311
+ /**
312
+ * Load and merge .planning/config.json with default configuration values.
313
+ * @param cwd - Project working directory
314
+ * @returns Merged configuration object with all fields populated
315
+ */
316
+ function loadConfig(cwd) {
317
+ const configPath = path.join(cwd, '.planning', 'config.json');
318
+ const defaultTimeouts = {
319
+ jest_ms: 120000,
320
+ lint_ms: 60000,
321
+ format_ms: 60000,
322
+ consistency_ms: 30000,
323
+ tracker_gh_ms: 30000,
324
+ tracker_auth_ms: 10000,
325
+ backend_detect_ms: 10000,
326
+ autopilot_check_ms: 5000,
327
+ autoresearch_test_ms: 120000,
328
+ autoresearch_coverage_ms: 180000,
329
+ autoresearch_lint_ms: 60000,
330
+ backend_probe_ms: 5000,
331
+ discussion_git_ms: 10000,
332
+ overstory_probe_ms: 5000,
333
+ overstory_install_ms: 120000,
334
+ };
335
+ const defaults = {
336
+ model_profile: 'balanced',
337
+ commit_docs: true,
338
+ search_gitignored: false,
339
+ branching_strategy: 'none',
340
+ phase_branch_template: 'grd/{milestone}/{phase}-{slug}',
341
+ milestone_branch_template: 'grd/{milestone}-{slug}',
342
+ base_branch: 'main',
343
+ research: true,
344
+ plan_checker: true,
345
+ verifier: true,
346
+ parallelization: true,
347
+ // Code review defaults
348
+ code_review_enabled: true,
349
+ code_review_timing: 'per_wave',
350
+ code_review_severity_gate: 'blocker',
351
+ code_review_auto_fix_warnings: false,
352
+ // Execution defaults
353
+ use_teams: false,
354
+ team_timeout_minutes: 30,
355
+ max_concurrent_teammates: 4,
356
+ // Backend config (pass-through, no defaults)
357
+ backend: undefined,
358
+ backend_models: undefined,
359
+ // Autonomous mode
360
+ autonomous_mode: false,
361
+ // Ceremony config
362
+ ceremony: undefined,
363
+ // Timeout defaults (ms)
364
+ timeouts: defaultTimeouts,
365
+ // Evolve config
366
+ evolve: undefined,
367
+ };
368
+ try {
369
+ const raw = fs.readFileSync(configPath, 'utf-8');
370
+ const parsed = JSON.parse(raw);
371
+ // Warn about unrecognized top-level config keys
372
+ for (const key of Object.keys(parsed)) {
373
+ if (!KNOWN_CONFIG_KEYS.has(key)) {
374
+ process.stderr.write(`Warning: Unrecognized config key "${key}" in .planning/config.json\n`);
375
+ }
376
+ }
377
+ // Warn about invalid model_profile values
378
+ const validProfiles = ['quality', 'balanced', 'budget'];
379
+ if (parsed.model_profile !== undefined &&
380
+ !validProfiles.includes(parsed.model_profile)) {
381
+ process.stderr.write(`Warning: Invalid model_profile value "${parsed.model_profile}" in .planning/config.json. Valid values: ${validProfiles.join(', ')}\n`);
382
+ }
383
+ // Warn about invalid effort values (v0.4 Phase 1)
384
+ const validEffortLevels = ['thrifty', 'balanced', 'deep'];
385
+ if (parsed.effort !== undefined &&
386
+ !validEffortLevels.includes(parsed.effort)) {
387
+ process.stderr.write(`Warning: Invalid effort value "${parsed.effort}" in .planning/config.json. Valid values: ${validEffortLevels.join(', ')}\n`);
388
+ }
389
+ const get = (key, nested) => {
390
+ if (parsed[key] !== undefined)
391
+ return parsed[key];
392
+ if (nested) {
393
+ const section = parsed[nested.section];
394
+ if (section && section[nested.field] !== undefined) {
395
+ return section[nested.field];
396
+ }
397
+ }
398
+ return undefined;
399
+ };
400
+ const parallelization = (() => {
401
+ const val = get('parallelization');
402
+ if (typeof val === 'boolean')
403
+ return val;
404
+ if (typeof val === 'object' && val !== null && 'enabled' in val)
405
+ return val.enabled;
406
+ return defaults.parallelization;
407
+ })();
408
+ return {
409
+ model_profile: (get('model_profile') ?? defaults.model_profile),
410
+ // v0.4 Phase 1: effort axis. Invalid values pass through as warnings
411
+ // above; we keep raw here so resolveEffortKnob's default ('balanced')
412
+ // takes effect when the field is absent.
413
+ ...(parsed.effort !== undefined ? { effort: parsed.effort } : {}),
414
+ commit_docs: (get('commit_docs', { section: 'planning', field: 'commit_docs' }) ??
415
+ defaults.commit_docs),
416
+ search_gitignored: (get('search_gitignored', {
417
+ section: 'planning',
418
+ field: 'search_gitignored',
419
+ }) ?? defaults.search_gitignored),
420
+ branching_strategy: (get('branching_strategy', {
421
+ section: 'git',
422
+ field: 'branching_strategy',
423
+ }) ?? defaults.branching_strategy),
424
+ phase_branch_template: (get('phase_branch_template', {
425
+ section: 'git',
426
+ field: 'phase_branch_template',
427
+ }) ?? defaults.phase_branch_template),
428
+ milestone_branch_template: (get('milestone_branch_template', {
429
+ section: 'git',
430
+ field: 'milestone_branch_template',
431
+ }) ?? defaults.milestone_branch_template),
432
+ base_branch: (get('base_branch', { section: 'git', field: 'base_branch' }) ??
433
+ defaults.base_branch),
434
+ research: (get('research', { section: 'workflow', field: 'research' }) ??
435
+ defaults.research),
436
+ plan_checker: (get('plan_checker', { section: 'workflow', field: 'plan_check' }) ??
437
+ defaults.plan_checker),
438
+ verifier: (get('verifier', { section: 'workflow', field: 'verifier' }) ??
439
+ defaults.verifier),
440
+ parallelization,
441
+ // Code review config
442
+ code_review_enabled: (get('code_review_enabled', {
443
+ section: 'code_review',
444
+ field: 'enabled',
445
+ }) ?? defaults.code_review_enabled),
446
+ code_review_timing: (get('code_review_timing', { section: 'code_review', field: 'timing' }) ??
447
+ defaults.code_review_timing),
448
+ code_review_severity_gate: (get('code_review_severity_gate', {
449
+ section: 'code_review',
450
+ field: 'severity_gate',
451
+ }) ?? defaults.code_review_severity_gate),
452
+ code_review_auto_fix_warnings: (get('code_review_auto_fix_warnings', {
453
+ section: 'code_review',
454
+ field: 'auto_fix_warnings',
455
+ }) ?? defaults.code_review_auto_fix_warnings),
456
+ // Execution config
457
+ use_teams: (get('use_teams', { section: 'execution', field: 'use_teams' }) ??
458
+ defaults.use_teams),
459
+ team_timeout_minutes: (get('team_timeout_minutes', {
460
+ section: 'execution',
461
+ field: 'team_timeout_minutes',
462
+ }) ?? defaults.team_timeout_minutes),
463
+ max_concurrent_teammates: (get('max_concurrent_teammates', {
464
+ section: 'execution',
465
+ field: 'max_concurrent_teammates',
466
+ }) ?? defaults.max_concurrent_teammates),
467
+ // Backend config (pass-through, no defaults)
468
+ backend: (parsed.backend || undefined),
469
+ backend_models: (parsed.backend_models || undefined),
470
+ // Autonomous mode
471
+ autonomous_mode: (get('autonomous_mode') ?? false),
472
+ // Ceremony config (pass-through)
473
+ ceremony: (parsed.ceremony || undefined),
474
+ // Evolve config
475
+ evolve: (() => {
476
+ const e = parsed.evolve && typeof parsed.evolve === 'object'
477
+ ? parsed.evolve
478
+ : null;
479
+ if (!e)
480
+ return undefined;
481
+ return {
482
+ auto_commit: (e.auto_commit ?? true),
483
+ create_pr: (e.create_pr ?? true),
484
+ // Tier-2 #8 auto-genome follow-up. Default off — opt-in.
485
+ auto_genome_snapshot: (e.auto_genome_snapshot ?? false),
486
+ };
487
+ })(),
488
+ // Scheduler config (pass-through)
489
+ scheduler: (parsed.scheduler || undefined),
490
+ // Superpowers config (pass-through)
491
+ superpowers: (parsed.superpowers || undefined),
492
+ // Drift score config (pass-through; defaults applied in lib/drift.ts)
493
+ drift: (parsed.drift || undefined),
494
+ // Autopilot termination knobs (pass-through; Tier-3 #10)
495
+ autopilot: (parsed.autopilot || undefined),
496
+ // Codex r14 P2/P3: Ouroboros r9 staleness knobs need to survive
497
+ // loadConfig so cmdHealth + cmdProgress can read them. Cast the
498
+ // whole return at the bottom to widen the type.
499
+ ...(parsed.research_staleness_days !== undefined
500
+ ? { research_staleness_days: parsed.research_staleness_days }
501
+ : {}),
502
+ ...(parsed.survey !== undefined ? { survey: parsed.survey } : {}),
503
+ // Backend roles config: validate each value against VALID_BACKENDS
504
+ backend_roles: (() => {
505
+ const raw = parsed.backend_roles;
506
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
507
+ return undefined;
508
+ const rawRoles = raw;
509
+ const validRoles = ['reviewer', 'brainstormer', 'verifier', 'executor'];
510
+ const result = {};
511
+ for (const [role, backendVal] of Object.entries(rawRoles)) {
512
+ if (!validRoles.includes(role)) {
513
+ process.stderr.write(`Warning: Unrecognized backend_roles role "${role}" in .planning/config.json\n`);
514
+ continue;
515
+ }
516
+ if (typeof backendVal !== 'string' ||
517
+ !VALID_BACKENDS.includes(backendVal)) {
518
+ process.stderr.write(`Warning: Invalid backend "${String(backendVal)}" for role "${role}" in backend_roles — must be a valid BackendId\n`);
519
+ continue;
520
+ }
521
+ result[role] = backendVal;
522
+ }
523
+ return Object.keys(result).length > 0 ? result : undefined;
524
+ })(),
525
+ // Discussion config: validate fields, apply defaults
526
+ discussion: (() => {
527
+ const raw = parsed.discussion;
528
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
529
+ return undefined;
530
+ const d = raw;
531
+ const enabled = typeof d.enabled === 'boolean' ? d.enabled : true;
532
+ if (!enabled) {
533
+ return {
534
+ enabled: false,
535
+ before_planning: typeof d.before_planning === 'boolean' ? d.before_planning : true,
536
+ before_execution: typeof d.before_execution === 'boolean' ? d.before_execution : false,
537
+ max_rounds: 2,
538
+ timeout_per_round_seconds: 180,
539
+ synthesizer: 'claude',
540
+ };
541
+ }
542
+ let max_rounds = typeof d.max_rounds === 'number' ? Math.round(d.max_rounds) : 2;
543
+ if (max_rounds < 1)
544
+ max_rounds = 1;
545
+ if (max_rounds > 3)
546
+ max_rounds = 3;
547
+ const timeout_per_round_seconds = typeof d.timeout_per_round_seconds === 'number' && d.timeout_per_round_seconds > 0
548
+ ? d.timeout_per_round_seconds
549
+ : 180;
550
+ const synthRaw = d.synthesizer;
551
+ const synthesizer = typeof synthRaw === 'string' && VALID_BACKENDS.includes(synthRaw)
552
+ ? synthRaw
553
+ : 'claude';
554
+ return {
555
+ enabled,
556
+ before_planning: typeof d.before_planning === 'boolean' ? d.before_planning : true,
557
+ before_execution: typeof d.before_execution === 'boolean' ? d.before_execution : false,
558
+ max_rounds,
559
+ timeout_per_round_seconds,
560
+ synthesizer: synthesizer,
561
+ };
562
+ })(),
563
+ // Citation gate (optional boolean, default: false)
564
+ citation_gate: typeof parsed.citation_gate === 'boolean' ? parsed.citation_gate : false,
565
+ // Transitive citation gate (optional boolean, default: false)
566
+ transitive_citation_gate: typeof parsed.transitive_citation_gate === 'boolean'
567
+ ? parsed.transitive_citation_gate
568
+ : false,
569
+ // Refinement loop (optional boolean, default: false)
570
+ refinement_loop: typeof parsed.refinement_loop === 'boolean' ? parsed.refinement_loop : false,
571
+ // LLM fallback for phase completion (Spec 3B, optional boolean, default: false)
572
+ phase_complete_llm_fallback: typeof parsed.phase_complete_llm_fallback === 'boolean'
573
+ ? parsed.phase_complete_llm_fallback
574
+ : undefined,
575
+ // LLM fallback retry count (optional number, default: 0)
576
+ phase_complete_llm_fallback_retries: typeof parsed.phase_complete_llm_fallback_retries === 'number'
577
+ ? Math.max(0, parsed.phase_complete_llm_fallback_retries)
578
+ : undefined,
579
+ // Timeouts config
580
+ timeouts: (() => {
581
+ const t = parsed.timeouts && typeof parsed.timeouts === 'object'
582
+ ? parsed.timeouts
583
+ : {};
584
+ const d = defaultTimeouts;
585
+ return {
586
+ jest_ms: (t.jest_ms ?? d.jest_ms),
587
+ lint_ms: (t.lint_ms ?? d.lint_ms),
588
+ format_ms: (t.format_ms ?? d.format_ms),
589
+ consistency_ms: (t.consistency_ms ?? d.consistency_ms),
590
+ tracker_gh_ms: (t.tracker_gh_ms ?? d.tracker_gh_ms),
591
+ tracker_auth_ms: (t.tracker_auth_ms ?? d.tracker_auth_ms),
592
+ backend_detect_ms: (t.backend_detect_ms ?? d.backend_detect_ms),
593
+ autopilot_check_ms: (t.autopilot_check_ms ?? d.autopilot_check_ms),
594
+ autoresearch_test_ms: (t.autoresearch_test_ms ?? d.autoresearch_test_ms),
595
+ autoresearch_coverage_ms: (t.autoresearch_coverage_ms ?? d.autoresearch_coverage_ms),
596
+ autoresearch_lint_ms: (t.autoresearch_lint_ms ?? d.autoresearch_lint_ms),
597
+ backend_probe_ms: (t.backend_probe_ms ?? d.backend_probe_ms),
598
+ discussion_git_ms: (t.discussion_git_ms ?? d.discussion_git_ms),
599
+ overstory_probe_ms: (t.overstory_probe_ms ?? d.overstory_probe_ms),
600
+ overstory_install_ms: (t.overstory_install_ms ?? d.overstory_install_ms),
601
+ };
602
+ })(),
603
+ };
604
+ }
605
+ catch {
606
+ return defaults;
607
+ }
608
+ }
609
+ /**
610
+ * Check if a file path is git-ignored via git check-ignore.
611
+ * @param cwd - Project working directory
612
+ * @param targetPath - Path to check against .gitignore rules
613
+ * @returns True if the path is git-ignored, false otherwise
614
+ */
615
+ function isGitIgnored(cwd, targetPath) {
616
+ if (targetPath.includes('\0'))
617
+ return false;
618
+ try {
619
+ execFileSync('git', ['check-ignore', '-q', '--', targetPath], {
620
+ cwd,
621
+ stdio: 'pipe',
622
+ });
623
+ return true;
624
+ }
625
+ catch {
626
+ return false;
627
+ }
628
+ }
629
+ /**
630
+ * Execute a git command with whitelist enforcement for security.
631
+ * @param cwd - Project working directory
632
+ * @param args - Git command arguments (first element is the subcommand)
633
+ * @param opts - Options object
634
+ * @returns Command result with exit code and output
635
+ */
636
+ function execGit(cwd, args, opts = {}) {
637
+ // Git operation whitelist enforcement
638
+ const subcommand = args[0];
639
+ if (subcommand && GIT_BLOCKED_COMMANDS.has(subcommand) && !opts.allowBlocked) {
640
+ return {
641
+ exitCode: 1,
642
+ stdout: '',
643
+ stderr: `Blocked: "git ${subcommand}" is not allowed by the GRD security policy. Pass { allowBlocked: true } to override.`,
644
+ };
645
+ }
646
+ if (!opts.allowBlocked) {
647
+ for (const arg of args) {
648
+ if (GIT_BLOCKED_FLAGS.has(arg)) {
649
+ return {
650
+ exitCode: 1,
651
+ stdout: '',
652
+ stderr: `Blocked: flag "${arg}" is not allowed by the GRD security policy. Pass { allowBlocked: true } to override.`,
653
+ };
654
+ }
655
+ }
656
+ }
657
+ try {
658
+ const stdout = execFileSync('git', args, {
659
+ cwd,
660
+ stdio: 'pipe',
661
+ encoding: 'utf-8',
662
+ });
663
+ return { exitCode: 0, stdout: stdout.trim(), stderr: '' };
664
+ }
665
+ catch (err) {
666
+ const gitErr = err;
667
+ return {
668
+ exitCode: gitErr.status ?? 1,
669
+ stdout: (gitErr.stdout ?? '').toString().trim(),
670
+ stderr: (gitErr.stderr ?? '').toString().trim(),
671
+ };
672
+ }
673
+ }
674
+ /**
675
+ * Normalize a phase identifier by zero-padding and validating format.
676
+ * @param phase - Phase identifier (e.g., "7", "07", "07.1")
677
+ * @returns Normalized phase name with zero-padded number (e.g., "07", "07.1")
678
+ * @throws If phase is not a string or contains path traversal/directory separators
679
+ */
680
+ function normalizePhaseName(phase) {
681
+ if (typeof phase !== 'string')
682
+ throw new Error('Phase must be a string');
683
+ if (phase.includes('..'))
684
+ throw new Error('Phase name must not contain path traversal (..)');
685
+ if (phase.includes('/') || phase.includes('\\'))
686
+ throw new Error('Phase name must not contain directory separators');
687
+ const match = phase.match(/^(\d+(?:\.\d+)?)/);
688
+ if (!match)
689
+ return phase;
690
+ const num = match[1];
691
+ const parts = num.split('.');
692
+ const padded = parts[0].padStart(2, '0');
693
+ return parts.length > 1 ? `${padded}.${parts[1]}` : padded;
694
+ }
695
+ const CODE_EXTENSIONS = new Set([
696
+ '.ts',
697
+ '.js',
698
+ '.py',
699
+ '.go',
700
+ '.rs',
701
+ '.swift',
702
+ '.java',
703
+ ]);
704
+ /**
705
+ * Recursively find code files up to a maximum depth, capped at 5 results.
706
+ * @param dir - Directory to search in
707
+ * @param maxDepth - Maximum directory depth to recurse into
708
+ * @param found - Accumulator array of found file paths
709
+ * @param depth - Current recursion depth
710
+ * @returns Array of absolute paths to code files found
711
+ */
712
+ function findCodeFiles(dir, maxDepth, found, depth) {
713
+ if (depth > maxDepth || found.length >= 5)
714
+ return found;
715
+ let entries;
716
+ try {
717
+ entries = fs.readdirSync(dir, { withFileTypes: true });
718
+ }
719
+ catch {
720
+ return found;
721
+ }
722
+ for (const entry of entries) {
723
+ if (found.length >= 5)
724
+ break;
725
+ if (entry.name === 'node_modules' || entry.name === '.git')
726
+ continue;
727
+ const fullPath = path.join(dir, entry.name);
728
+ if (entry.isDirectory()) {
729
+ findCodeFiles(fullPath, maxDepth, found, depth + 1);
730
+ }
731
+ else if (entry.isFile()) {
732
+ const ext = path.extname(entry.name).toLowerCase();
733
+ if (CODE_EXTENSIONS.has(ext)) {
734
+ found.push(fullPath);
735
+ }
736
+ }
737
+ }
738
+ return found;
739
+ }
740
+ // ─── Input Validation ────────────────────────────────────────────────────────
741
+ /**
742
+ * Validate phase name format strictly, rejecting path traversal and invalid characters.
743
+ * @param phase - Phase name to validate
744
+ * @returns The validated phase name if valid
745
+ * @throws If phase format is invalid, contains traversal, null bytes, or separators
746
+ */
747
+ function validatePhaseName(phase) {
748
+ if (typeof phase !== 'string')
749
+ throw new Error('Phase must be a string');
750
+ if (phase.includes('..'))
751
+ throw new Error('Phase name must not contain path traversal (..)');
752
+ if (phase.includes('/') || phase.includes('\\'))
753
+ throw new Error('Phase name must not contain directory separators');
754
+ if (phase.includes('\0'))
755
+ throw new Error('Phase name must not contain null bytes');
756
+ if (!/^\d+(?:\.\d+)?(?:-[a-zA-Z0-9-]+)?$/.test(phase)) {
757
+ throw new Error('Invalid phase name format: must be digits with optional decimal and kebab-case suffix');
758
+ }
759
+ return phase;
760
+ }
761
+ /**
762
+ * Validate that a file path does not escape the project directory.
763
+ * @param filePath - File path to validate (relative or absolute)
764
+ * @param cwd - Project working directory used as security boundary
765
+ * @returns The validated file path if safe
766
+ * @throws If path is not a string, contains null bytes, or escapes project directory
767
+ */
768
+ function validateFilePath(filePath, cwd) {
769
+ if (typeof filePath !== 'string')
770
+ throw new Error('File path must be a string');
771
+ if (filePath.includes('\0'))
772
+ throw new Error('File path must not contain null bytes');
773
+ const resolved = path.resolve(cwd, filePath);
774
+ if (!resolved.startsWith(cwd))
775
+ throw new Error('File path must not escape project directory');
776
+ return filePath;
777
+ }
778
+ /**
779
+ * Validate git ref format, preventing flag injection and path traversal.
780
+ * @param ref - Git reference to validate (commit hash, branch name, tag)
781
+ * @returns The validated git ref if valid
782
+ * @throws If ref is invalid, starts with dash, contains traversal, or has invalid characters
783
+ */
784
+ function validateGitRef(ref) {
785
+ if (typeof ref !== 'string')
786
+ throw new Error('Git ref must be a string');
787
+ if (ref.startsWith('-'))
788
+ throw new Error('Git ref must not start with a dash (flag injection)');
789
+ if (ref.includes('..'))
790
+ throw new Error('Git ref must not contain path traversal (..)');
791
+ if (!/^[a-zA-Z0-9._\-/~^]+$/.test(ref))
792
+ throw new Error('Git ref contains invalid characters');
793
+ if (ref.length > 256)
794
+ throw new Error('Git ref too long');
795
+ return ref;
796
+ }
797
+ // ─── CLI Argument Validation ──────────────────────────────────────────────────
798
+ /**
799
+ * Validate CLI phase number argument, ensuring it is present and well-formed.
800
+ * @param phase - Phase number from CLI arguments
801
+ * @returns The validated phase number
802
+ * @throws If phase is missing or not in valid format (digits with optional decimal)
803
+ */
804
+ function validatePhaseArg(phase) {
805
+ if (phase == null || phase === '')
806
+ throw new Error('Phase number is required');
807
+ if (typeof phase !== 'string')
808
+ throw new Error('Phase number is required');
809
+ if (!/^\d+(?:\.\d+)?(?:-[a-zA-Z0-9-]+)?$/.test(phase)) {
810
+ throw new Error('Invalid phase number: must be digits with optional decimal (e.g., 01, 02.1)');
811
+ }
812
+ return phase;
813
+ }
814
+ /**
815
+ * Validate CLI file path argument, ensuring it is present and does not escape project.
816
+ * @param filePath - File path from CLI arguments
817
+ * @param cwd - Project working directory used as security boundary
818
+ * @returns The validated file path
819
+ * @throws If file path is missing or escapes project directory
820
+ */
821
+ function validateFileArg(filePath, cwd) {
822
+ if (filePath == null || filePath === '')
823
+ throw new Error('File path is required');
824
+ return validateFilePath(filePath, cwd);
825
+ }
826
+ /**
827
+ * Validate CLI subcommand against a list of valid subcommands.
828
+ * @param sub - Subcommand string from CLI arguments
829
+ * @param validSubs - Array of valid subcommand names
830
+ * @param parentCmd - Parent command name for error messages
831
+ * @returns The validated subcommand
832
+ * @throws If subcommand is missing or not in the valid list
833
+ */
834
+ function validateSubcommand(sub, validSubs, parentCmd) {
835
+ if (sub == null || sub === '') {
836
+ throw new Error(`Subcommand required for '${parentCmd}'. Available: ${validSubs.join(', ')}`);
837
+ }
838
+ if (!validSubs.includes(sub)) {
839
+ throw new Error(`Unknown ${parentCmd} subcommand: '${sub}'. Available: ${validSubs.join(', ')}`);
840
+ }
841
+ return sub;
842
+ }
843
+ /**
844
+ * Validate that a required CLI argument is present and non-empty.
845
+ * @param value - Argument value to check
846
+ * @param argName - Name of the argument for error messages
847
+ * @returns The validated value
848
+ * @throws If value is null, undefined, or empty string
849
+ */
850
+ function validateRequiredArg(value, argName) {
851
+ if (value == null || value === '')
852
+ throw new Error(`${argName} is required`);
853
+ return value;
854
+ }
855
+ // ─── Output ──────────────────────────────────────────────────────────────────
856
+ /**
857
+ * Write JSON or raw output to stdout and exit with code 0.
858
+ * @param result - Result object to serialize as JSON
859
+ * @param raw - If true, output rawValue as plain text instead of JSON
860
+ * @param rawValue - Plain text value to output when raw is true
861
+ */
862
+ function output(result, raw, rawValue) {
863
+ if (raw && rawValue !== undefined) {
864
+ process.stdout.write(String(rawValue));
865
+ }
866
+ else {
867
+ process.stdout.write(JSON.stringify(result, null, 2));
868
+ }
869
+ process.exit(0);
870
+ }
871
+ /**
872
+ * Write error message to stderr and exit with code 1.
873
+ * @param message - Error message to display
874
+ */
875
+ function error(message) {
876
+ process.stderr.write('Error: ' + message + '\n');
877
+ process.exit(1);
878
+ }
879
+ /**
880
+ * Write debug message to stderr when GRD_DEBUG environment variable is set.
881
+ * No-op in normal operation; enabled by setting GRD_DEBUG=1 (or any truthy value).
882
+ * @param message - Debug message to display
883
+ * @param data - Optional data to JSON-stringify alongside the message
884
+ */
885
+ function debugLog(message, data) {
886
+ if (!process.env.GRD_DEBUG)
887
+ return;
888
+ const prefix = '[grd:debug] ';
889
+ if (data !== undefined) {
890
+ process.stderr.write(prefix + message + ' ' + JSON.stringify(data) + '\n');
891
+ }
892
+ else {
893
+ process.stderr.write(prefix + message + '\n');
894
+ }
895
+ }
896
+ // ─── Shared Cache Factory ─────────────────────────────────────────────────────
897
+ /**
898
+ * Create a run-scoped file content cache.
899
+ * Returns a cache object with `get`, `init`, and `reset` methods.
900
+ * Modules can use this to avoid redundant disk reads within a single CLI invocation.
901
+ *
902
+ * Usage pattern:
903
+ * const cache = createRunCache();
904
+ * cache.init(); // activate caching for this run
905
+ * cache.get(p, reader) // returns cached value or calls reader(p) and stores result
906
+ * cache.reset(); // deactivate and clear
907
+ *
908
+ * @returns Cache object with init, reset, and get methods
909
+ */
910
+ function createRunCache() {
911
+ let _map = null;
912
+ return {
913
+ /** Activate the cache (creates a new Map). */
914
+ init() {
915
+ _map = new Map();
916
+ },
917
+ /** Deactivate and clear the cache. */
918
+ reset() {
919
+ _map = null;
920
+ },
921
+ /**
922
+ * Get a cached value, or compute it with `reader(key)` and store the result.
923
+ * Falls back to calling `reader(key)` directly if the cache is not active.
924
+ * @param key - Cache key (typically a file path)
925
+ * @param reader - Function to produce the value if not cached
926
+ * @returns The cached or freshly computed value
927
+ */
928
+ get(key, reader) {
929
+ if (!_map)
930
+ return reader(key);
931
+ if (!_map.has(key))
932
+ _map.set(key, reader(key));
933
+ return _map.get(key);
934
+ },
935
+ };
936
+ }
937
+ // ─── Phase Directory Utilities ────────────────────────────────────────────────
938
+ /**
939
+ * Find a phase directory name inside `phasesDir` that matches `phaseArg`.
940
+ * Matches by exact normalized name or by prefix `<normalized>-`.
941
+ * @param phasesDir - Absolute path to the phases directory
942
+ * @param phaseArg - Phase identifier (e.g., '1', '01', '1.1')
943
+ * @returns The matching directory name, or null if not found
944
+ */
945
+ function findPhaseDir(phasesDir, phaseArg) {
946
+ const normalized = normalizePhaseName(phaseArg);
947
+ try {
948
+ const entries = fs.readdirSync(phasesDir, {
949
+ withFileTypes: true,
950
+ });
951
+ const dirs = entries
952
+ .filter((e) => e.isDirectory())
953
+ .map((e) => e.name)
954
+ .sort();
955
+ return dirs.find((d) => d.startsWith(normalized + '-') || d === normalized) || null;
956
+ }
957
+ catch {
958
+ return null;
959
+ }
960
+ }
961
+ /**
962
+ * Parse a phase number from a directory name or plain string.
963
+ * Handles formats like '01-feature-name', '1.2-sub', '1', etc.
964
+ * @param str - Directory name or phase string to parse
965
+ * @returns The numeric phase number as a string (e.g. '1', '1.2'), or null
966
+ */
967
+ function parsePhaseNumber(str) {
968
+ if (!str)
969
+ return null;
970
+ const match = str.match(/^(\d+(?:\.\d+)?)/);
971
+ return match ? match[1] : null;
972
+ }
973
+ // ─── Directory Walking ────────────────────────────────────────────────────────
974
+ /**
975
+ * Recursively collect JavaScript file paths under `rootDir`.
976
+ * Skips `node_modules`, `.git`, `.planning`, and any paths matching `excludePatterns`.
977
+ * @param rootDir - Root directory to walk (returned paths are relative to this)
978
+ * @param excludePatterns - Substrings; any relative path containing one is skipped
979
+ * @returns Relative paths of all .js files found
980
+ */
981
+ function walkJsFiles(rootDir, excludePatterns = []) {
982
+ const results = [];
983
+ _walkJsDir(rootDir, rootDir, results, excludePatterns);
984
+ return results;
985
+ }
986
+ function _walkJsDir(rootDir, currentDir, results, excludePatterns) {
987
+ let entries;
988
+ try {
989
+ entries = fs.readdirSync(currentDir, { withFileTypes: true });
990
+ }
991
+ catch {
992
+ return;
993
+ }
994
+ for (const entry of entries) {
995
+ const fullPath = path.join(currentDir, entry.name);
996
+ const relPath = path.relative(rootDir, fullPath);
997
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.planning') {
998
+ continue;
999
+ }
1000
+ if (excludePatterns.some((p) => relPath.includes(p)))
1001
+ continue;
1002
+ if (entry.isDirectory()) {
1003
+ _walkJsDir(rootDir, fullPath, results, excludePatterns);
1004
+ }
1005
+ else if (entry.isFile() && entry.name.endsWith('.js')) {
1006
+ results.push(relPath);
1007
+ }
1008
+ }
1009
+ }
1010
+ // ─── Compound Helpers ────────────────────────────────────────────────────────
1011
+ /**
1012
+ * Resolve the model name for a given agent type from project configuration.
1013
+ * @param cwd - Project working directory
1014
+ * @param agentType - Agent type key (e.g., 'grd-executor', 'grd-planner')
1015
+ * @returns Model name (e.g., 'opus', 'sonnet', 'haiku')
1016
+ */
1017
+ function resolveModelInternal(cwd, agentType) {
1018
+ const config = loadConfig(cwd);
1019
+ const profile = config.model_profile || 'balanced';
1020
+ const agentModels = MODEL_PROFILES[agentType];
1021
+ if (!agentModels) {
1022
+ // Unknown agent type: resolve 'sonnet' tier through backend
1023
+ const backend = detectBackend(cwd);
1024
+ return resolveBackendModel(backend, 'sonnet', config, cwd);
1025
+ }
1026
+ const tier = agentModels[profile] || agentModels['balanced'] || 'sonnet';
1027
+ const backend = detectBackend(cwd);
1028
+ return resolveBackendModel(backend, tier, config, cwd);
1029
+ }
1030
+ /**
1031
+ * Find a phase directory and enumerate its plans, summaries, and metadata.
1032
+ * @param cwd - Project working directory
1033
+ * @param phase - Phase identifier to search for
1034
+ * @returns Phase info object with directory, plans, summaries, and flags, or null if not found
1035
+ */
1036
+ function findPhaseInternal(cwd, phase) {
1037
+ if (!phase)
1038
+ return null;
1039
+ const phasesDir = getPhasesDirPath(cwd);
1040
+ const normalized = normalizePhaseName(phase);
1041
+ try {
1042
+ const entries = fs.readdirSync(phasesDir, {
1043
+ withFileTypes: true,
1044
+ });
1045
+ const dirs = entries
1046
+ .filter((e) => e.isDirectory())
1047
+ .map((e) => e.name)
1048
+ .sort();
1049
+ const match = dirs.find((d) => d.startsWith(normalized + '-') || d === normalized);
1050
+ if (!match)
1051
+ return null;
1052
+ const dirMatch = match.match(/^(\d+(?:\.\d+)?)-?(.*)/);
1053
+ const phaseNumber = dirMatch ? dirMatch[1] : normalized;
1054
+ const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
1055
+ const phaseDir = path.join(phasesDir, match);
1056
+ const phaseFiles = fs.readdirSync(phaseDir);
1057
+ const plans = phaseFiles
1058
+ .filter((f) => f.endsWith('-PLAN.md') || f === 'PLAN.md')
1059
+ .sort();
1060
+ const summaries = phaseFiles
1061
+ .filter((f) => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md')
1062
+ .sort();
1063
+ const hasResearch = phaseFiles.some((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
1064
+ const hasContext = phaseFiles.some((f) => f.endsWith('-CONTEXT.md') || f === 'CONTEXT.md');
1065
+ const hasVerification = phaseFiles.some((f) => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
1066
+ // Determine incomplete plans (plans without matching summaries)
1067
+ const completedPlanIds = new Set(summaries.map((s) => s.replace('-SUMMARY.md', '').replace('SUMMARY.md', '')));
1068
+ const incompletePlans = plans.filter((p) => {
1069
+ const planId = p.replace('-PLAN.md', '').replace('PLAN.md', '');
1070
+ return !completedPlanIds.has(planId);
1071
+ });
1072
+ // Check if phase exists in ROADMAP.md (non-blocking consistency check)
1073
+ let consistencyWarning = null;
1074
+ try {
1075
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
1076
+ const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
1077
+ const unpadded = String(parseInt(phaseNumber, 10));
1078
+ const phasePattern = new RegExp(`#{2,}\\s*Phase\\s+(?:${phaseNumber}|${unpadded})\\s*:`, 'i');
1079
+ if (!phasePattern.test(roadmapContent)) {
1080
+ consistencyWarning = `Phase ${phaseNumber} found on disk but not in ROADMAP.md — may be from a previous milestone`;
1081
+ }
1082
+ }
1083
+ catch {
1084
+ // ROADMAP.md may not exist yet; skip check
1085
+ }
1086
+ return {
1087
+ found: true,
1088
+ directory: path.relative(cwd, path.join(phasesDir, match)),
1089
+ phase_number: phaseNumber,
1090
+ phase_name: phaseName,
1091
+ phase_slug: phaseName
1092
+ ? phaseName
1093
+ .toLowerCase()
1094
+ .replace(/[^a-z0-9]+/g, '-')
1095
+ .replace(/^-+|-+$/g, '')
1096
+ : null,
1097
+ plans,
1098
+ summaries,
1099
+ incomplete_plans: incompletePlans,
1100
+ has_research: hasResearch,
1101
+ has_context: hasContext,
1102
+ has_verification: hasVerification,
1103
+ consistency_warning: consistencyWarning,
1104
+ };
1105
+ }
1106
+ catch {
1107
+ return null;
1108
+ }
1109
+ }
1110
+ /**
1111
+ * Check if a path exists on the filesystem.
1112
+ * @param cwd - Project working directory (used for resolving relative paths)
1113
+ * @param targetPath - Path to check, relative or absolute
1114
+ * @returns True if the path exists, false otherwise
1115
+ */
1116
+ function pathExistsInternal(cwd, targetPath) {
1117
+ const fullPath = path.isAbsolute(targetPath) ? targetPath : path.join(cwd, targetPath);
1118
+ try {
1119
+ fs.statSync(fullPath);
1120
+ return true;
1121
+ }
1122
+ catch {
1123
+ return false;
1124
+ }
1125
+ }
1126
+ /**
1127
+ * Generate a kebab-case slug from text, stripping non-alphanumeric characters.
1128
+ * @param text - Input text to slugify
1129
+ * @returns Kebab-case slug, or null if text is falsy
1130
+ */
1131
+ function generateSlugInternal(text) {
1132
+ if (!text)
1133
+ return null;
1134
+ return text
1135
+ .toLowerCase()
1136
+ .replace(/[^a-z0-9]+/g, '-')
1137
+ .replace(/^-+|-+$/g, '');
1138
+ }
1139
+ /**
1140
+ * Strip shipped milestone sections wrapped in <details> blocks.
1141
+ * These contain archived milestone content that should not contaminate
1142
+ * active milestone parsing (phase numbers, version detection, etc.).
1143
+ * @param content - Raw ROADMAP.md content
1144
+ * @returns Content with <details>...</details> blocks removed
1145
+ */
1146
+ function stripShippedSections(content) {
1147
+ if (!content)
1148
+ return content;
1149
+ return content.replace(/<details>[\s\S]*?<\/details>/gi, '');
1150
+ }
1151
+ /**
1152
+ * Extract milestone version and name from ROADMAP.md.
1153
+ * @param cwd - Project working directory
1154
+ * @returns Milestone info with version (e.g., 'v1.0') and name
1155
+ */
1156
+ function getMilestoneInfo(cwd) {
1157
+ try {
1158
+ const roadmap = fs.readFileSync(path.join(cwd, '.planning', 'ROADMAP.md'), 'utf-8');
1159
+ const active = stripShippedSections(roadmap);
1160
+ // Strategy 1: Find "(in progress)" milestone from bullet list
1161
+ const inProgressMatch = active.match(/-\s+(v[\d.]+)\s+([^\n(]+?)\s*\(in progress\)/im);
1162
+ if (inProgressMatch) {
1163
+ return { version: inProgressMatch[1], name: inProgressMatch[2].trim() };
1164
+ }
1165
+ // Strategy 2: Last non-shipped milestone bullet
1166
+ const bulletRegex = /-\s+(v[\d.]+)\s+([^\n(]+?)(?:\s*\(([^)]*)\))?\s*$/gim;
1167
+ let lastNonShipped = null;
1168
+ let bm;
1169
+ while ((bm = bulletRegex.exec(active)) !== null) {
1170
+ const status = bm[3] || '';
1171
+ if (!/shipped/i.test(status)) {
1172
+ lastNonShipped = { version: bm[1], name: bm[2].trim() };
1173
+ }
1174
+ }
1175
+ if (lastNonShipped)
1176
+ return lastNonShipped;
1177
+ // Strategy 3: Active heading "## ... vX.Y.Z ... (In Progress)"
1178
+ const headingMatch = active.match(/##\s*.*?(v\d+\.\d+(?:\.\d+)?)\s*[:\s]+([^\n(]+)/);
1179
+ if (headingMatch) {
1180
+ return { version: headingMatch[1], name: headingMatch[2].trim() };
1181
+ }
1182
+ // Strategy 4: Fallback -- first version found (with 3-part support)
1183
+ const versionMatch = active.match(/v(\d+\.\d+(?:\.\d+)?)/);
1184
+ return {
1185
+ version: versionMatch ? versionMatch[0] : 'v1.0',
1186
+ name: 'milestone',
1187
+ };
1188
+ }
1189
+ catch {
1190
+ return { version: 'v1.0', name: 'milestone' };
1191
+ }
1192
+ }
1193
+ /**
1194
+ * Resolve model name from a config object without disk I/O.
1195
+ * When cwd is provided, resolves to backend-specific model name.
1196
+ * When cwd is omitted, returns abstract tier name (backward compatible).
1197
+ * @param config - Configuration object with model_profile field
1198
+ * @param agentType - Agent type key to look up in MODEL_PROFILES
1199
+ * @param cwd - Optional project working directory for backend-specific resolution
1200
+ * @param options - Optional overrides. When options.effectiveTierOverride is set,
1201
+ * it replaces the MODEL_PROFILES lookup entirely. Callers that omit this parameter
1202
+ * get identical behavior to before (backward compatible).
1203
+ * @returns Model name (e.g., 'opus', 'sonnet', 'haiku', or backend-specific name)
1204
+ */
1205
+ function resolveModelForAgent(config, agentType, cwd, options) {
1206
+ const profile = (config.model_profile || 'balanced').toLowerCase();
1207
+ const agentModels = MODEL_PROFILES[agentType];
1208
+ const baseTier = agentModels
1209
+ ? agentModels[profile] || agentModels['balanced'] || 'sonnet'
1210
+ : 'sonnet';
1211
+ // Use override when provided; otherwise fall back to MODEL_PROFILES lookup
1212
+ const tier = options?.effectiveTierOverride || baseTier;
1213
+ // If cwd provided, resolve to backend-specific model name
1214
+ if (cwd) {
1215
+ const backend = detectBackend(cwd);
1216
+ return resolveBackendModel(backend, tier, config, cwd);
1217
+ }
1218
+ // Backward compatible: no cwd means return tier name (existing behavior)
1219
+ return tier;
1220
+ }
1221
+ /**
1222
+ * Resolve effort level for a given agent type from project configuration.
1223
+ * Returns null if the backend does not support effort levels.
1224
+ * @param config - Project configuration
1225
+ * @param agentType - Agent type key (e.g., 'grd-executor', 'grd-planner')
1226
+ * @param cwd - Optional project working directory for backend detection
1227
+ * @returns Effort level string ('low', 'medium', 'high') or null if unsupported
1228
+ */
1229
+ function resolveEffortForAgent(config, agentType, cwd) {
1230
+ const backend = cwd ? detectBackend(cwd) : 'claude';
1231
+ const caps = getBackendCapabilities(backend);
1232
+ if (!caps.effort)
1233
+ return null;
1234
+ const profile = (config.model_profile || 'balanced');
1235
+ return resolveEffortLevel(agentType, profile);
1236
+ }
1237
+ // ─── Config Drift Validator ───────────────────────────────────────────────────
1238
+ /**
1239
+ * Known config keys with their default values, organized for drift detection.
1240
+ * Each entry has: key (dot-path for nested keys), default value, and the gd settings
1241
+ * command to fix it.
1242
+ */
1243
+ // Codex r2 P2: `gd settings` tool-mode only accepts token_profile and
1244
+ // phase_complete_llm_fallback. All other keys must use `gd config-set
1245
+ // <dot.path> <value>` (canonical route at lib/cli/index.ts:42), so emit
1246
+ // runnable commands for those instead of broken `gd settings ...` hints.
1247
+ const CONFIG_DRIFT_KEYS = [
1248
+ { key: 'token_profile', default: 'balanced', fix: 'gd settings token_profile balanced' },
1249
+ // v0.4 Phase 1: effort axis (tool-mode settings key, alongside the other two).
1250
+ { key: 'effort', default: 'balanced', fix: 'gd settings effort balanced' },
1251
+ { key: 'phase_complete_llm_fallback', default: false, fix: 'gd settings phase_complete_llm_fallback false' },
1252
+ { key: 'autonomous_mode', default: false, fix: 'gd config-set autonomous_mode false' },
1253
+ { key: 'branching_strategy', default: 'none', fix: 'gd config-set branching_strategy none' },
1254
+ { key: 'scheduler.idle_timeout_seconds', default: 900, fix: 'gd config-set scheduler.idle_timeout_seconds 900' },
1255
+ { key: 'scheduler.budget_pressure_thresholds', default: { warning: 0.6, high: 0.8, critical: 0.95 }, fix: 'gd config-set scheduler.budget_pressure_thresholds \'{"warning":0.6,"high":0.8,"critical":0.95}\'' },
1256
+ // Codex r27 P2: cmdHealth uses DEFAULT_WEIGHTS from lib/drift.ts
1257
+ // (0.5/0.3/0.2) when `drift` is missing. The fix command must
1258
+ // materialize the same runtime default so users don't accidentally
1259
+ // change drift-scoring semantics by applying it.
1260
+ { key: 'drift', default: { weights: { goal: 0.5, constraint: 0.3, ontology: 0.2 }, threshold: 0.3 }, fix: 'gd config-set drift \'{"weights":{"goal":0.5,"constraint":0.3,"ontology":0.2},"threshold":0.3}\'' },
1261
+ { key: 'autopilot', default: {}, fix: 'gd config-set autopilot \'{}\'' },
1262
+ ];
1263
+ /**
1264
+ * Validate config.json against the current schema defaults, identifying keys that
1265
+ * were added after initial `gd init` and are missing from the user's config.
1266
+ *
1267
+ * @param cwd - Project working directory
1268
+ * @returns DriftReport with missing keys, fix commands, and deprecated keys
1269
+ */
1270
+ function validateConfigDrift(cwd) {
1271
+ const configPath = path.join(cwd, '.planning', 'config.json');
1272
+ let parsed = {};
1273
+ try {
1274
+ const raw = fs.readFileSync(configPath, 'utf-8');
1275
+ parsed = JSON.parse(raw);
1276
+ }
1277
+ catch {
1278
+ // Config doesn't exist or is malformed — all keys are "missing"
1279
+ }
1280
+ const missing = [];
1281
+ for (const entry of CONFIG_DRIFT_KEYS) {
1282
+ // Support dot-path for nested keys (e.g. "scheduler.idle_timeout_seconds")
1283
+ const parts = entry.key.split('.');
1284
+ let cur = parsed;
1285
+ for (const part of parts) {
1286
+ if (cur === null || typeof cur !== 'object') {
1287
+ cur = undefined;
1288
+ break;
1289
+ }
1290
+ cur = cur[part];
1291
+ }
1292
+ if (cur === undefined) {
1293
+ missing.push({ key: entry.key, default: entry.default, fix_command: entry.fix });
1294
+ }
1295
+ }
1296
+ return {
1297
+ missing_keys: missing,
1298
+ deprecated_keys: [],
1299
+ total_checks: CONFIG_DRIFT_KEYS.length,
1300
+ };
1301
+ }
1302
+ // ─── Exports ─────────────────────────────────────────────────────────────────
1303
+ module.exports = {
1304
+ // Node built-ins (re-exported for convenience)
1305
+ fs,
1306
+ path,
1307
+ os,
1308
+ execFileSync,
1309
+ // Constants
1310
+ GIT_ALLOWED_COMMANDS,
1311
+ GIT_BLOCKED_COMMANDS,
1312
+ GIT_BLOCKED_FLAGS,
1313
+ MODEL_PROFILES,
1314
+ CODE_EXTENSIONS,
1315
+ // Helpers
1316
+ parseIncludeFlag,
1317
+ safeReadFile,
1318
+ safeReadMarkdown,
1319
+ safeReadJSON,
1320
+ extractMarkdownSection,
1321
+ loadConfig,
1322
+ isGitIgnored,
1323
+ execGit,
1324
+ normalizePhaseName,
1325
+ findCodeFiles,
1326
+ // Input validation
1327
+ validatePhaseName,
1328
+ validateFilePath,
1329
+ validateGitRef,
1330
+ // CLI argument validation
1331
+ validatePhaseArg,
1332
+ validateFileArg,
1333
+ validateSubcommand,
1334
+ validateRequiredArg,
1335
+ // Caching
1336
+ createRunCache,
1337
+ // Phase directory utilities
1338
+ findPhaseDir,
1339
+ parsePhaseNumber,
1340
+ // Directory walking
1341
+ walkJsFiles,
1342
+ // Output
1343
+ output,
1344
+ error,
1345
+ debugLog,
1346
+ // Compound helpers
1347
+ resolveModelInternal,
1348
+ findPhaseInternal,
1349
+ pathExistsInternal,
1350
+ generateSlugInternal,
1351
+ getMilestoneInfo,
1352
+ stripShippedSections,
1353
+ resolveModelForAgent,
1354
+ resolveEffortForAgent,
1355
+ levenshteinDistance,
1356
+ findClosestCommand,
1357
+ clearPhaseCache,
1358
+ validateConfigDrift,
1359
+ // v0.4 Phase 1: effort axis
1360
+ EFFORT_PROFILES,
1361
+ resolveEffortKnob,
1362
+ };
1363
+ //# sourceMappingURL=utils.js.map