@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
package/lib/backend.ts ADDED
@@ -0,0 +1,1252 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * GRD Backend Detection, Model Resolution & Capabilities
5
+ *
6
+ * Detects which AI coding CLI is running (Claude Code, Codex CLI, Gemini CLI,
7
+ * OpenCode) via a detection waterfall: config override > env vars > filesystem
8
+ * clues > default. Resolves abstract model tiers (opus/sonnet/haiku) to
9
+ * backend-specific model names. Provides capability flags per backend.
10
+ *
11
+ * Supported backends (March 2026):
12
+ * - Claude Code v2.1.71 — Anthropic's native CLI (opus/sonnet/haiku tiers)
13
+ * - Codex CLI v0.112.0 — OpenAI's CLI (GPT-5.4, GPT-5.3-Codex-Spark, GPT-5.4-mini)
14
+ * - Gemini CLI v0.32.1 — Google's CLI (Gemini 3.1 Pro, 3.1 Flash, 3.1 Flash-Lite)
15
+ * - OpenCode v1.2.21 — Provider-agnostic CLI by anomalyco (actively maintained, 70K+ stars)
16
+ * - Superpowers — Plugin/skill layer that orchestrates any AI CLI backend with account rotation
17
+ * - GRD — Native mode using GRD's own commands/skills with the configured AI backend
18
+ *
19
+ * This module reads config.json directly with fs.readFileSync to avoid
20
+ * circular dependency with lib/utils.js (which will later import from here).
21
+ *
22
+ * Research basis:
23
+ * - Detection waterfall: .planning/research/multi-backend-detection.md (Section 2)
24
+ * - Model mappings: .planning/research/ARCHITECTURE.md
25
+ * - Capability flags: .planning/research/ARCHITECTURE.md
26
+ * - Pitfall avoidance: .planning/research/PITFALLS.md (P5: no AGENT env var)
27
+ */
28
+
29
+ import type {
30
+ BackendId,
31
+ BackendCapabilities,
32
+ ModelTierMap,
33
+ ModelTier,
34
+ ModelProfileName,
35
+ EffortLevel,
36
+ AgentEffortProfiles,
37
+ WebMcpResult,
38
+ PlaywrightResult,
39
+ BackendAvailability,
40
+ TokenProfileName,
41
+ BudgetPressureLevel,
42
+ ComplexityLevel,
43
+ GrdConfig,
44
+ SchedulerConfig,
45
+ SuperpowersConfig,
46
+ BackendUsageState,
47
+ BudgetPressureThresholds,
48
+ AdapterBackendId,
49
+ } from './types';
50
+
51
+ const fs = require('fs');
52
+ const os = require('os');
53
+ const path = require('path');
54
+ const { execFileSync } = require('child_process');
55
+
56
+ // --- Constants ---------------------------------------------------------------
57
+
58
+ /**
59
+ * List of valid backend identifiers.
60
+ */
61
+ const VALID_BACKENDS: readonly BackendId[] = [
62
+ 'claude',
63
+ 'codex',
64
+ 'gemini',
65
+ 'opencode',
66
+ 'overstory',
67
+ 'superpowers',
68
+ 'grd',
69
+ ];
70
+
71
+ /**
72
+ * Default model name mappings per backend and tier.
73
+ * Each backend maps the abstract tiers (opus, sonnet, haiku) to concrete
74
+ * model identifiers recognized by that backend's CLI.
75
+ */
76
+ const DEFAULT_BACKEND_MODELS: Record<BackendId, ModelTierMap> = {
77
+ claude: { opus: 'opus', sonnet: 'sonnet', haiku: 'haiku' },
78
+ codex: {
79
+ opus: 'gpt-5.4',
80
+ sonnet: 'gpt-5.3-codex-spark',
81
+ haiku: 'gpt-5.4-mini',
82
+ },
83
+ gemini: {
84
+ opus: 'gemini-3.1-pro',
85
+ sonnet: 'gemini-3.1-flash',
86
+ haiku: 'gemini-3.1-flash-lite',
87
+ },
88
+ opencode: {
89
+ opus: 'anthropic/claude-opus-4-6',
90
+ sonnet: 'anthropic/claude-sonnet-4-6',
91
+ haiku: 'anthropic/claude-haiku-4-5',
92
+ },
93
+ overstory: { opus: 'opus', sonnet: 'sonnet', haiku: 'haiku' },
94
+ superpowers: { opus: 'opus', sonnet: 'sonnet', haiku: 'haiku' },
95
+ grd: { opus: 'opus', sonnet: 'sonnet', haiku: 'haiku' },
96
+ };
97
+
98
+ /**
99
+ * Capability flags per backend. Describes what orchestration features each
100
+ * backend supports. Used to degrade gracefully for backends with limited features.
101
+ */
102
+ const BACKEND_CAPABILITIES: Record<BackendId, BackendCapabilities> = {
103
+ claude: {
104
+ subagents: true,
105
+ parallel: true,
106
+ teams: true,
107
+ hooks: true,
108
+ mcp: true,
109
+ native_worktree_isolation: true,
110
+ effort: true,
111
+ http_hooks: true,
112
+ cron: true,
113
+ smart_approvals: false,
114
+ plan_mode: false,
115
+ sandbox_gvisor: false,
116
+ sandbox_lxc: false,
117
+ mcp_elicitation: true,
118
+ model_overrides: true,
119
+ max_output_tokens: { default: 64000, upper_bound: 128000 },
120
+ },
121
+ codex: {
122
+ subagents: true,
123
+ parallel: true,
124
+ teams: true,
125
+ hooks: true,
126
+ mcp: true,
127
+ native_worktree_isolation: false,
128
+ effort: false,
129
+ http_hooks: false,
130
+ cron: false,
131
+ smart_approvals: true,
132
+ plan_mode: false,
133
+ sandbox_gvisor: false,
134
+ sandbox_lxc: false,
135
+ mcp_elicitation: false,
136
+ model_overrides: true,
137
+ max_output_tokens: null,
138
+ },
139
+ gemini: {
140
+ subagents: true,
141
+ parallel: true,
142
+ teams: false,
143
+ hooks: true,
144
+ mcp: true,
145
+ native_worktree_isolation: false,
146
+ effort: false,
147
+ http_hooks: false,
148
+ cron: false,
149
+ smart_approvals: false,
150
+ plan_mode: true,
151
+ sandbox_gvisor: true,
152
+ sandbox_lxc: false,
153
+ mcp_elicitation: false,
154
+ model_overrides: true,
155
+ max_output_tokens: null,
156
+ },
157
+ opencode: {
158
+ subagents: true,
159
+ parallel: true,
160
+ teams: false,
161
+ hooks: true,
162
+ mcp: true,
163
+ native_worktree_isolation: false,
164
+ effort: false,
165
+ http_hooks: false,
166
+ cron: false,
167
+ smart_approvals: false,
168
+ plan_mode: false,
169
+ sandbox_gvisor: false,
170
+ sandbox_lxc: false,
171
+ mcp_elicitation: false,
172
+ model_overrides: true,
173
+ max_output_tokens: null,
174
+ },
175
+ overstory: {
176
+ subagents: true,
177
+ parallel: true,
178
+ teams: true,
179
+ hooks: false,
180
+ mcp: true,
181
+ native_worktree_isolation: true,
182
+ effort: false,
183
+ http_hooks: false,
184
+ cron: false,
185
+ smart_approvals: false,
186
+ plan_mode: false,
187
+ sandbox_gvisor: false,
188
+ sandbox_lxc: false,
189
+ mcp_elicitation: false,
190
+ model_overrides: true,
191
+ max_output_tokens: null,
192
+ },
193
+ superpowers: {
194
+ subagents: true,
195
+ parallel: true,
196
+ teams: true,
197
+ hooks: true,
198
+ mcp: true,
199
+ native_worktree_isolation: true,
200
+ effort: true,
201
+ http_hooks: false,
202
+ cron: false,
203
+ smart_approvals: false,
204
+ plan_mode: false,
205
+ sandbox_gvisor: false,
206
+ sandbox_lxc: false,
207
+ mcp_elicitation: false,
208
+ model_overrides: true,
209
+ max_output_tokens: null,
210
+ },
211
+ grd: {
212
+ subagents: true,
213
+ parallel: true,
214
+ teams: true,
215
+ hooks: true,
216
+ mcp: true,
217
+ native_worktree_isolation: true,
218
+ effort: false,
219
+ http_hooks: false,
220
+ cron: false,
221
+ smart_approvals: false,
222
+ plan_mode: false,
223
+ sandbox_gvisor: false,
224
+ sandbox_lxc: false,
225
+ mcp_elicitation: false,
226
+ model_overrides: false,
227
+ max_output_tokens: null,
228
+ },
229
+ };
230
+
231
+ // --- Effort Level Profiles ---------------------------------------------------
232
+
233
+ /**
234
+ * Default effort levels per agent and profile. Mirrors MODEL_PROFILES in utils.ts
235
+ * but for the effort dimension. Effort controls reasoning depth in backends that
236
+ * support it (currently Claude Code v2.1.68+).
237
+ *
238
+ * Design intent (from REQ-92):
239
+ * quality => planners/executors get high (deep reasoning), verifiers get medium
240
+ * balanced => planners get high, executors get medium, verifiers/lightweight agents get low
241
+ * budget => everything low (fast, minimal reasoning)
242
+ */
243
+ const EFFORT_PROFILES: AgentEffortProfiles = {
244
+ 'grd-planner': { quality: 'high', balanced: 'high', budget: 'low' },
245
+ 'grd-roadmapper': { quality: 'high', balanced: 'medium', budget: 'low' },
246
+ 'grd-executor': { quality: 'high', balanced: 'medium', budget: 'low' },
247
+ 'grd-phase-researcher': { quality: 'high', balanced: 'medium', budget: 'low' },
248
+ 'grd-project-researcher': { quality: 'high', balanced: 'medium', budget: 'low' },
249
+ 'grd-research-synthesizer': { quality: 'medium', balanced: 'medium', budget: 'low' },
250
+ 'grd-debugger': { quality: 'high', balanced: 'medium', budget: 'low' },
251
+ 'grd-codebase-mapper': { quality: 'medium', balanced: 'low', budget: 'low' },
252
+ 'grd-verifier': { quality: 'medium', balanced: 'low', budget: 'low' },
253
+ 'grd-critique-agent': { quality: 'medium', balanced: 'low', budget: 'low' },
254
+ 'grd-plan-checker': { quality: 'medium', balanced: 'medium', budget: 'low' },
255
+ 'grd-integration-checker': { quality: 'medium', balanced: 'medium', budget: 'low' },
256
+ 'grd-surveyor': { quality: 'medium', balanced: 'medium', budget: 'low' },
257
+ 'grd-deep-diver': { quality: 'high', balanced: 'medium', budget: 'low' },
258
+ 'grd-feasibility-analyst': { quality: 'high', balanced: 'medium', budget: 'low' },
259
+ 'grd-eval-planner': { quality: 'high', balanced: 'medium', budget: 'low' },
260
+ 'grd-eval-reporter': { quality: 'medium', balanced: 'medium', budget: 'low' },
261
+ 'grd-product-owner': { quality: 'high', balanced: 'high', budget: 'low' },
262
+ 'grd-baseline-assessor': { quality: 'medium', balanced: 'medium', budget: 'low' },
263
+ 'grd-code-reviewer': { quality: 'high', balanced: 'medium', budget: 'low' },
264
+ };
265
+
266
+ /**
267
+ * Resolve the effort level for a given agent type and model profile.
268
+ *
269
+ * Returns the effort level from EFFORT_PROFILES for the given agent and profile.
270
+ * Unknown agent types return 'medium' as a safe default. Unknown profiles
271
+ * fall back to 'balanced', then to 'medium'.
272
+ *
273
+ * @param agentType - Agent type key (e.g., 'grd-executor', 'grd-planner')
274
+ * @param profile - Model profile name ('quality', 'balanced', or 'budget')
275
+ * @returns The effort level string: 'low', 'medium', or 'high'
276
+ */
277
+ function resolveEffortLevel(agentType: string, profile: ModelProfileName): EffortLevel {
278
+ const agentEffort = EFFORT_PROFILES[agentType];
279
+ if (!agentEffort) return 'medium';
280
+ return agentEffort[profile] || agentEffort['balanced'] || 'medium';
281
+ }
282
+
283
+ // --- Internal Helpers --------------------------------------------------------
284
+
285
+ /** Detected model tier map: opus, sonnet, haiku each nullable. */
286
+ interface DetectedModels {
287
+ opus: string | null;
288
+ sonnet: string | null;
289
+ haiku: string | null;
290
+ }
291
+
292
+ /** Model cache entry with TTL tracking. */
293
+ interface ModelCacheEntry {
294
+ models: DetectedModels | null;
295
+ ts: number;
296
+ }
297
+
298
+ /**
299
+ * Read and parse .planning/config.json from cwd. Returns parsed object or null.
300
+ * Uses fs.readFileSync directly to avoid circular dependency with lib/utils.js.
301
+ */
302
+ function readConfig(cwd: string): Record<string, unknown> | null {
303
+ try {
304
+ const configPath: string = path.join(cwd, '.planning', 'config.json');
305
+ const raw: string = fs.readFileSync(configPath, 'utf-8');
306
+ return JSON.parse(raw) as Record<string, unknown>;
307
+ } catch {
308
+ return null;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Check if any environment variable starts with a given prefix.
314
+ */
315
+ function hasEnvPrefix(prefix: string): boolean {
316
+ return Object.keys(process.env).some((k) => k.startsWith(prefix));
317
+ }
318
+
319
+ /**
320
+ * Check if a file exists at a given path.
321
+ */
322
+ function fileExists(filePath: string): boolean {
323
+ try {
324
+ fs.statSync(filePath);
325
+ return true;
326
+ } catch {
327
+ return false;
328
+ }
329
+ }
330
+
331
+ // --- Exported Functions ------------------------------------------------------
332
+
333
+ /**
334
+ * Detect which AI coding CLI backend is currently running.
335
+ *
336
+ * Detection waterfall (highest to lowest priority):
337
+ * 1. Config override: .planning/config.json `backend` field
338
+ * 2. Environment variables: CLAUDE_CODE_*, CODEX_HOME, GEMINI_CLI_HOME, OPENCODE
339
+ * 3. Filesystem clues: .claude-plugin/plugin.json, .codex/config.toml, etc.
340
+ * 4. Default: 'claude' (backward compatible)
341
+ *
342
+ * Note: The AGENT env var is NOT used for OpenCode detection per PITFALLS.md P5
343
+ * (too generic, may collide with other tools).
344
+ *
345
+ * @param cwd - Absolute path to the project root directory used for config and filesystem detection
346
+ * @returns The detected backend identifier (e.g. 'claude', 'codex', 'gemini', 'opencode')
347
+ */
348
+ function detectBackend(cwd: string): BackendId {
349
+ // Step 1: Config override (highest priority)
350
+ const config = readConfig(cwd);
351
+ if (config && config.backend && VALID_BACKENDS.includes(config.backend as BackendId)) {
352
+ return config.backend as BackendId;
353
+ }
354
+
355
+ // Step 2: Environment variable detection
356
+ // Superpowers detection (highest env priority — orchestrates other backends)
357
+ if (process.env.SUPERPOWERS_HOME || process.env.SUPERPOWERS_SESSION) return 'superpowers';
358
+ // Overstory detection (before Claude — takes priority when both present)
359
+ if (process.env.OVERSTORY_HOME || process.env.OVERSTORY_SESSION) return 'overstory';
360
+ if (hasEnvPrefix('CLAUDE_CODE_')) return 'claude';
361
+ // CODEX_THREAD_ID: may be deprecated in newer Codex CLI versions (no docs mention
362
+ // as of March 2026), but kept for backward compatibility with older installations.
363
+ if (process.env.CODEX_HOME || process.env.CODEX_THREAD_ID) return 'codex';
364
+ if (process.env.GEMINI_CLI_HOME) return 'gemini';
365
+ // OpenCode: actively maintained under anomalyco/opencode (original opencode-ai
366
+ // repo archived Sept 2025). OPENCODE_PID is NOT used — it's a process management
367
+ // var, not a presence indicator.
368
+ if (process.env.OPENCODE) return 'opencode';
369
+
370
+ // Step 3: Filesystem clues
371
+ if (fileExists(path.join(cwd, '.superpowers', 'config.json'))) return 'superpowers';
372
+ if (fileExists(path.join(cwd, '.overstory', 'config.yaml'))) return 'overstory';
373
+ if (fileExists(path.join(cwd, '.claude-plugin', 'plugin.json'))) return 'claude';
374
+ if (fileExists(path.join(cwd, '.codex', 'config.toml'))) return 'codex';
375
+ if (fileExists(path.join(cwd, '.gemini', 'settings.json'))) return 'gemini';
376
+ if (fileExists(path.join(cwd, 'opencode.json'))) return 'opencode';
377
+
378
+ // Step 4: Default (backward compatible)
379
+ return 'claude';
380
+ }
381
+
382
+ // --- Dynamic Model Detection -------------------------------------------------
383
+
384
+ /**
385
+ * Parse `opencode models` stdout into tier-classified model map.
386
+ * Classifies each model ID by keyword patterns:
387
+ * opus tier: /opus/i, /pro/i (non-flash)
388
+ * sonnet tier: /sonnet/i
389
+ * haiku tier: /haiku/i, /flash/i, /mini/i, /spark/i
390
+ *
391
+ * @param stdout - Raw stdout string from the `opencode models` CLI command
392
+ * @returns A DetectedModels map with opus/sonnet/haiku slots filled where matched, or null if no models were matched
393
+ */
394
+ function parseOpenCodeModels(stdout: string): DetectedModels | null {
395
+ if (!stdout || typeof stdout !== 'string') return null;
396
+
397
+ const lines: string[] = stdout
398
+ .split('\n')
399
+ .map((l) => l.trim())
400
+ .filter((l) => l && !l.startsWith('Available') && !l.startsWith('---') && !l.startsWith('#'));
401
+
402
+ const result: DetectedModels = { opus: null, sonnet: null, haiku: null };
403
+ let matched = false;
404
+
405
+ for (const line of lines) {
406
+ const model: string | undefined = line.split(/\s+/)[0];
407
+ if (!model || !model.includes('/')) continue;
408
+
409
+ if (/opus/i.test(model)) {
410
+ if (!result.opus) {
411
+ result.opus = model;
412
+ matched = true;
413
+ }
414
+ } else if (/sonnet/i.test(model)) {
415
+ if (!result.sonnet) {
416
+ result.sonnet = model;
417
+ matched = true;
418
+ }
419
+ } else if (/haiku/i.test(model)) {
420
+ if (!result.haiku) {
421
+ result.haiku = model;
422
+ matched = true;
423
+ }
424
+ } else if (/pro/i.test(model) && !/flash/i.test(model)) {
425
+ if (!result.opus) {
426
+ result.opus = model;
427
+ matched = true;
428
+ }
429
+ } else if (/flash/i.test(model) || /mini/i.test(model) || /spark/i.test(model)) {
430
+ if (!result.haiku) {
431
+ result.haiku = model;
432
+ matched = true;
433
+ }
434
+ }
435
+ }
436
+
437
+ return matched ? result : null;
438
+ }
439
+
440
+ /**
441
+ * Run backend-specific CLI command to detect available models.
442
+ * Currently only OpenCode supports programmatic model listing.
443
+ */
444
+ function detectModels(backend: string, cwd?: string): DetectedModels | null {
445
+ if (backend !== 'opencode') return null;
446
+
447
+ const effectiveCwd: string = cwd || process.cwd();
448
+ const cfg = readConfig(effectiveCwd);
449
+ const timeouts = cfg?.timeouts as Record<string, unknown> | undefined;
450
+ const timeout: number =
451
+ typeof timeouts?.backend_detect_ms === 'number' ? timeouts.backend_detect_ms : 10000;
452
+ try {
453
+ const stdout: string = execFileSync('opencode', ['models'], {
454
+ cwd: effectiveCwd,
455
+ timeout,
456
+ encoding: 'utf-8',
457
+ stdio: ['pipe', 'pipe', 'pipe'],
458
+ });
459
+ return parseOpenCodeModels(stdout);
460
+ } catch {
461
+ return null;
462
+ }
463
+ }
464
+
465
+ const _modelCache: Map<string, ModelCacheEntry> = new Map();
466
+ const MODEL_CACHE_TTL_MS: number = 5 * 60 * 1000;
467
+
468
+ /**
469
+ * Get cached detected models for a backend, refreshing if TTL expired.
470
+ */
471
+ function getCachedModels(backend: string, cwd?: string): DetectedModels | null {
472
+ const entry: ModelCacheEntry | undefined = _modelCache.get(backend);
473
+ const now: number = Date.now();
474
+ if (entry && now - entry.ts < MODEL_CACHE_TTL_MS) {
475
+ return entry.models;
476
+ }
477
+ const models: DetectedModels | null = detectModels(backend, cwd);
478
+ _modelCache.set(backend, { models, ts: now });
479
+ return models;
480
+ }
481
+
482
+ /**
483
+ * Clear the model detection cache. Exported for testing.
484
+ */
485
+ function clearModelCache(): void {
486
+ _modelCache.clear();
487
+ }
488
+
489
+ /**
490
+ * Resolve an abstract model tier to a backend-specific model name.
491
+ *
492
+ * Checks config.backend_models for user overrides first, then falls back
493
+ * to DEFAULT_BACKEND_MODELS. Unknown backends fall back to claude mappings.
494
+ * Unknown tiers return undefined.
495
+ *
496
+ * @param backend - The backend identifier (e.g. 'claude', 'codex', 'gemini', 'opencode')
497
+ * @param tier - The abstract model tier to resolve ('opus', 'sonnet', or 'haiku')
498
+ * @param config - Optional parsed config.json object used for user-defined backend_models overrides
499
+ * @param cwd - Optional project root path used for dynamic model detection (opencode only)
500
+ * @returns The backend-specific model name string, or undefined if the tier is not mapped
501
+ */
502
+ function resolveBackendModel(
503
+ backend: string,
504
+ tier: ModelTier,
505
+ config?: Record<string, unknown>,
506
+ cwd?: string
507
+ ): string | undefined {
508
+ // Check user override from config (highest priority)
509
+ if (config && config.backend_models) {
510
+ const backendModelsConfig = config.backend_models as Record<string, Record<string, string>>;
511
+ const backendOverrides = backendModelsConfig[backend];
512
+ if (backendOverrides && backendOverrides[tier] !== undefined) {
513
+ return backendOverrides[tier];
514
+ }
515
+ }
516
+
517
+ // Check dynamically detected models (middle priority)
518
+ if (cwd) {
519
+ const detected: DetectedModels | null = getCachedModels(backend, cwd);
520
+ if (detected && detected[tier]) {
521
+ return detected[tier];
522
+ }
523
+ }
524
+
525
+ // Use built-in defaults, falling back to claude for unknown backends
526
+ const backendModels: ModelTierMap =
527
+ DEFAULT_BACKEND_MODELS[backend as BackendId] || DEFAULT_BACKEND_MODELS.claude;
528
+ return backendModels[tier];
529
+ }
530
+
531
+ /**
532
+ * Get capability flags for a backend.
533
+ *
534
+ * Returns an object describing what orchestration features the backend supports.
535
+ * Unknown backends get minimal capabilities (all false) to prevent
536
+ * accidentally enabling features like native_worktree_isolation or effort.
537
+ *
538
+ * @param backend - The backend identifier (e.g. 'claude', 'codex', 'gemini', 'opencode')
539
+ * @returns A BackendCapabilities object describing which orchestration features are supported
540
+ */
541
+ function getBackendCapabilities(backend: string): BackendCapabilities {
542
+ if (BACKEND_CAPABILITIES[backend as BackendId]) {
543
+ return BACKEND_CAPABILITIES[backend as BackendId];
544
+ }
545
+ // Unknown backend: warn and return minimal capabilities
546
+ process.stderr.write(`[grd] WARNING: unknown backend "${backend}", using minimal capabilities\n`);
547
+ return {
548
+ subagents: true,
549
+ parallel: false,
550
+ teams: false,
551
+ hooks: false,
552
+ mcp: false,
553
+ native_worktree_isolation: false,
554
+ effort: false,
555
+ http_hooks: false,
556
+ cron: false,
557
+ smart_approvals: false,
558
+ plan_mode: false,
559
+ sandbox_gvisor: false,
560
+ sandbox_lxc: false,
561
+ mcp_elicitation: false,
562
+ model_overrides: false,
563
+ max_output_tokens: null,
564
+ };
565
+ }
566
+
567
+ // --- WebMCP Detection --------------------------------------------------------
568
+
569
+ /**
570
+ * Detect whether Chrome DevTools MCP is available.
571
+ *
572
+ * Detection waterfall (highest to lowest priority):
573
+ * 1. Config override: .planning/config.json `webmcp.enabled` field
574
+ * 2. Environment variables: CHROME_DEVTOOLS_MCP, WEBMCP_AVAILABLE
575
+ * 3. Claude Code MCP settings: ~/.claude.json `mcpServers` key
576
+ * 4. Default: not available
577
+ *
578
+ * @param cwd - Absolute path to the project root directory used for config-based detection
579
+ * @returns A WebMcpResult indicating availability, the detection source, and an optional reason when unavailable
580
+ */
581
+ function detectWebMcp(cwd: string): WebMcpResult {
582
+ // Step 1: Config override (highest priority)
583
+ const config = readConfig(cwd);
584
+ if (config && config.webmcp && typeof config.webmcp === 'object') {
585
+ const webmcp = config.webmcp as Record<string, unknown>;
586
+ if (typeof webmcp.enabled === 'boolean') {
587
+ if (webmcp.enabled) {
588
+ return { available: true, source: 'config' };
589
+ }
590
+ return {
591
+ available: false,
592
+ source: 'config',
593
+ reason: 'Disabled via config',
594
+ };
595
+ }
596
+ }
597
+
598
+ // Step 2: Environment variable check
599
+ const chromeDevToolsMcp: string | undefined = process.env.CHROME_DEVTOOLS_MCP;
600
+ const webmcpAvailable: string | undefined = process.env.WEBMCP_AVAILABLE;
601
+
602
+ if (chromeDevToolsMcp !== undefined) {
603
+ if (chromeDevToolsMcp === 'true' || chromeDevToolsMcp === '1') {
604
+ return { available: true, source: 'env' };
605
+ }
606
+ if (chromeDevToolsMcp === 'false' || chromeDevToolsMcp === '0') {
607
+ return {
608
+ available: false,
609
+ source: 'env',
610
+ reason: 'Disabled via environment variable',
611
+ };
612
+ }
613
+ }
614
+
615
+ if (webmcpAvailable !== undefined) {
616
+ if (webmcpAvailable === 'true' || webmcpAvailable === '1') {
617
+ return { available: true, source: 'env' };
618
+ }
619
+ if (webmcpAvailable === 'false' || webmcpAvailable === '0') {
620
+ return {
621
+ available: false,
622
+ source: 'env',
623
+ reason: 'Disabled via environment variable',
624
+ };
625
+ }
626
+ }
627
+
628
+ // Step 3: Claude Code MCP settings check (~/.claude.json)
629
+ try {
630
+ const homeDir: string = os.homedir();
631
+ const claudeConfigPath: string = path.join(homeDir, '.claude.json');
632
+ const raw: string = fs.readFileSync(claudeConfigPath, 'utf-8');
633
+ const claudeConfig = JSON.parse(raw) as Record<string, unknown>;
634
+ if (claudeConfig && claudeConfig.mcpServers) {
635
+ const serverNames: string[] = Object.keys(claudeConfig.mcpServers as Record<string, unknown>);
636
+ const hasBrowserMcp: boolean = serverNames.some((name) =>
637
+ /chrome|devtools|playwright|browser/i.test(name)
638
+ );
639
+ if (hasBrowserMcp) {
640
+ return { available: true, source: 'mcp-config' };
641
+ }
642
+ }
643
+ } catch {
644
+ // ~/.claude.json not found or malformed -- continue to default
645
+ }
646
+
647
+ // Step 4: Default
648
+ return {
649
+ available: false,
650
+ source: 'default',
651
+ reason: 'Chrome DevTools MCP not detected in config, environment, or MCP server settings',
652
+ };
653
+ }
654
+
655
+ // --- Playwright Detection ----------------------------------------------------
656
+
657
+ /**
658
+ * Detect whether Playwright MCP is available.
659
+ *
660
+ * Detection waterfall (highest to lowest priority):
661
+ * 1. Config override: .planning/config.json `playwright.enabled` field
662
+ * 2. Environment variable: PLAYWRIGHT_AVAILABLE
663
+ * 3. Claude Code MCP settings: ~/.claude.json `mcpServers` key (playwright name match)
664
+ * 4. Default: not available
665
+ *
666
+ * Mirrors the detectWebMcp() pattern exactly — same try/catch around ~/.claude.json,
667
+ * same config reading via readConfig(cwd), same env var parsing.
668
+ *
669
+ * @param cwd - Absolute path to the project root directory used for config-based detection
670
+ * @returns A PlaywrightResult indicating availability, the detection source, and an optional reason when unavailable
671
+ */
672
+ function detectPlaywright(cwd: string): PlaywrightResult {
673
+ // Step 1: Config override (highest priority)
674
+ const config = readConfig(cwd);
675
+ if (config && config.playwright && typeof config.playwright === 'object') {
676
+ const playwright = config.playwright as Record<string, unknown>;
677
+ if (typeof playwright.enabled === 'boolean') {
678
+ if (playwright.enabled) {
679
+ return { available: true, source: 'config' };
680
+ }
681
+ return {
682
+ available: false,
683
+ source: 'config',
684
+ reason: 'Disabled via config',
685
+ };
686
+ }
687
+ }
688
+
689
+ // Step 2: Environment variable check
690
+ const playwrightAvailable: string | undefined = process.env.PLAYWRIGHT_AVAILABLE;
691
+
692
+ if (playwrightAvailable !== undefined) {
693
+ if (playwrightAvailable === 'true' || playwrightAvailable === '1') {
694
+ return { available: true, source: 'env' };
695
+ }
696
+ if (playwrightAvailable === 'false' || playwrightAvailable === '0') {
697
+ return {
698
+ available: false,
699
+ source: 'env',
700
+ reason: 'Disabled via environment variable',
701
+ };
702
+ }
703
+ }
704
+
705
+ // Step 3: Claude Code MCP settings check (~/.claude.json)
706
+ try {
707
+ const homeDir: string = os.homedir();
708
+ const claudeConfigPath: string = path.join(homeDir, '.claude.json');
709
+ const raw: string = fs.readFileSync(claudeConfigPath, 'utf-8');
710
+ const claudeConfig = JSON.parse(raw) as Record<string, unknown>;
711
+ if (claudeConfig && claudeConfig.mcpServers) {
712
+ const serverNames: string[] = Object.keys(claudeConfig.mcpServers as Record<string, unknown>);
713
+ const hasPlaywrightMcp: boolean = serverNames.some((name) => /playwright/i.test(name));
714
+ if (hasPlaywrightMcp) {
715
+ return { available: true, source: 'mcp-config' };
716
+ }
717
+ }
718
+ } catch {
719
+ // ~/.claude.json not found or malformed -- continue to default
720
+ }
721
+
722
+ // Step 4: Default
723
+ return {
724
+ available: false,
725
+ source: 'default',
726
+ reason: 'Playwright MCP not detected in config, environment, or MCP server settings',
727
+ };
728
+ }
729
+
730
+ // --- Backend Availability Detection ------------------------------------------
731
+
732
+ /** Cache entry for detectAvailableBackends result. */
733
+ interface AvailabilityCacheEntry {
734
+ result: Record<BackendId, BackendAvailability>;
735
+ ts: number;
736
+ }
737
+
738
+ let _availabilityCache: AvailabilityCacheEntry | null = null;
739
+ const AVAILABILITY_CACHE_TTL_MS: number = 5 * 60 * 1000;
740
+
741
+ /**
742
+ * Dispatchable backends — the four CLIs that discussion.ts can spawn directly.
743
+ * Meta-backends (overstory, superpowers, grd) are probed as unavailable.
744
+ */
745
+ const DISPATCHABLE_BACKENDS: readonly string[] = ['claude', 'codex', 'gemini', 'opencode'];
746
+
747
+ /**
748
+ * Environment variable that controls the config directory for each backend CLI.
749
+ * When set, the CLI uses the specified directory for auth/credentials instead of default.
750
+ */
751
+ const BACKEND_CONFIG_ENV: Record<string, string> = {
752
+ claude: 'CLAUDE_CONFIG_DIR',
753
+ codex: 'CODEX_HOME',
754
+ gemini: 'GEMINI_CLI_HOME',
755
+ opencode: 'OPENCODE_CONFIG_DIR',
756
+ };
757
+
758
+ /**
759
+ * Files that prove a config directory has valid auth/credentials for a backend.
760
+ * Must be actual credential files, not files created by a bare first-run.
761
+ *
762
+ * Paths are relative to the config dir. Use nested paths for backends that
763
+ * store auth in a subdirectory (e.g. gemini stores creds in <dir>/.gemini/).
764
+ */
765
+ const BACKEND_AUTH_MARKERS: Record<string, string[]> = {
766
+ claude: ['credentials.json', '.credentials.json', 'settings.json'],
767
+ codex: ['auth.json'],
768
+ gemini: ['.gemini/oauth_creds.json', '.gemini/google_accounts.json'],
769
+ opencode: ['auth.json', 'config.json'],
770
+ };
771
+
772
+ /** Cached config dir discovery result. */
773
+ let _configDirCache: Record<string, string | null> | null = null;
774
+
775
+ /**
776
+ * Discover the actual config directory for each backend by scanning the home
777
+ * directory for directories matching ~/.<backend>* that contain auth marker files.
778
+ *
779
+ * Priority:
780
+ * 1. Current env var (e.g. CLAUDE_CONFIG_DIR already set)
781
+ * 2. First ~/.<backend>-* directory containing an auth marker file
782
+ * 3. Default ~/.<backend> if it contains an auth marker file
783
+ * 4. null (no config dir found — use backend's default)
784
+ */
785
+ function discoverBackendConfigDirs(): Record<string, string | null> {
786
+ if (_configDirCache) return _configDirCache;
787
+
788
+ const homeDir: string = os.homedir();
789
+ const result: Record<string, string | null> = {};
790
+
791
+ for (const backend of DISPATCHABLE_BACKENDS) {
792
+ const envVar = BACKEND_CONFIG_ENV[backend];
793
+ const markers = BACKEND_AUTH_MARKERS[backend];
794
+
795
+ // 1. Check if env var is already set
796
+ if (envVar && process.env[envVar]) {
797
+ result[backend] = process.env[envVar] as string;
798
+ continue;
799
+ }
800
+
801
+ // 2. Scan home directory for matching config dirs
802
+ let found: string | null = null;
803
+ try {
804
+ const entries: string[] = fs.readdirSync(homeDir);
805
+ // Collect candidates: ~/.<backend>-* first (custom profiles), then ~/.<backend> (default)
806
+ const profileDirs: string[] = entries
807
+ .filter((e: string) => e.startsWith(`.${backend}-`))
808
+ .sort();
809
+ const defaultDir: string[] = entries.filter((e: string) => e === `.${backend}`);
810
+ const candidates: string[] = [...profileDirs, ...defaultDir]
811
+ .map((e: string) => path.join(homeDir, e))
812
+ .filter((p: string) => {
813
+ try {
814
+ return fs.statSync(p).isDirectory();
815
+ } catch {
816
+ return false;
817
+ }
818
+ });
819
+
820
+ // Check each candidate for auth marker files
821
+ for (const candidate of candidates) {
822
+ const hasAuth = markers.some((marker: string) => {
823
+ try {
824
+ return fs.statSync(path.join(candidate, marker)).isFile();
825
+ } catch {
826
+ return false;
827
+ }
828
+ });
829
+ if (hasAuth) {
830
+ found = candidate;
831
+ break;
832
+ }
833
+ }
834
+ } catch {
835
+ // Home dir not readable — skip
836
+ }
837
+
838
+ result[backend] = found;
839
+ }
840
+
841
+ _configDirCache = result;
842
+ return result;
843
+ }
844
+
845
+ /**
846
+ * Clear the config dir discovery cache. Exported for testing.
847
+ */
848
+ function clearConfigDirCache(): void {
849
+ _configDirCache = null;
850
+ }
851
+
852
+ /**
853
+ * Build the environment variables needed to run a backend CLI with the correct
854
+ * config directory. Returns a copy of process.env with the override applied.
855
+ */
856
+ function buildBackendEnv(backend: string): Record<string, string | undefined> {
857
+ const env: Record<string, string | undefined> = { ...process.env };
858
+
859
+ // Strip Claude session env vars so subprocess doesn't detect nested invocation
860
+ for (const key of Object.keys(env)) {
861
+ if (key === 'CLAUDECODE' || key.startsWith('CLAUDE_CODE_') || key.startsWith('CLAUDECODE_')) {
862
+ delete env[key];
863
+ }
864
+ }
865
+
866
+ const configDirs = discoverBackendConfigDirs();
867
+ const configDir = configDirs[backend];
868
+ if (!configDir) return env;
869
+
870
+ const envVar = BACKEND_CONFIG_ENV[backend];
871
+ if (!envVar) return env;
872
+
873
+ return { ...env, [envVar]: configDir };
874
+ }
875
+
876
+ /**
877
+ * Probe which AI CLI backends are available on PATH.
878
+ *
879
+ * For each of the four dispatchable backends (claude, codex, gemini, opencode),
880
+ * runs `<binary> --version` with a 5-second timeout. Success means available.
881
+ * Meta-backends (overstory, superpowers, grd) are always marked unavailable here.
882
+ *
883
+ * Result is cached for 5 minutes (AVAILABILITY_CACHE_TTL_MS). Call
884
+ * clearAvailabilityCache() to force re-detection in tests.
885
+ *
886
+ * @param cwd - Optional working directory for subprocess (defaults to process.cwd())
887
+ * @returns A map of BackendId to BackendAvailability for all known backends
888
+ */
889
+ function detectAvailableBackends(cwd?: string): Record<BackendId, BackendAvailability> {
890
+ const now: number = Date.now();
891
+ if (_availabilityCache && now - _availabilityCache.ts < AVAILABILITY_CACHE_TTL_MS) {
892
+ return _availabilityCache.result;
893
+ }
894
+
895
+ const effectiveCwd: string = cwd || process.cwd();
896
+ const unavailable: BackendAvailability = { available: false, version: null };
897
+
898
+ const probeCfg = readConfig(effectiveCwd);
899
+ const probeTimeouts = probeCfg?.timeouts as Record<string, unknown> | undefined;
900
+ const probeTimeout: number =
901
+ typeof probeTimeouts?.backend_probe_ms === 'number' ? probeTimeouts.backend_probe_ms : 5000;
902
+
903
+ const result: Record<BackendId, BackendAvailability> = {
904
+ claude: unavailable,
905
+ codex: unavailable,
906
+ gemini: unavailable,
907
+ opencode: unavailable,
908
+ overstory: unavailable,
909
+ superpowers: unavailable,
910
+ grd: unavailable,
911
+ };
912
+
913
+ for (const backend of DISPATCHABLE_BACKENDS) {
914
+ try {
915
+ const stdout: string = execFileSync(backend, ['--version'], {
916
+ cwd: effectiveCwd,
917
+ timeout: probeTimeout,
918
+ encoding: 'utf-8',
919
+ stdio: ['pipe', 'pipe', 'pipe'],
920
+ env: buildBackendEnv(backend),
921
+ });
922
+ result[backend as BackendId] = {
923
+ available: true,
924
+ version: stdout.trim().split('\n')[0] || null,
925
+ };
926
+ } catch {
927
+ result[backend as BackendId] = { available: false, version: null };
928
+ }
929
+ }
930
+
931
+ _availabilityCache = { result, ts: now };
932
+ return result;
933
+ }
934
+
935
+ /**
936
+ * Clear the availability detection cache. Exported for testing.
937
+ */
938
+ function clearAvailabilityCache(): void {
939
+ _availabilityCache = null;
940
+ }
941
+
942
+ // ─── Spec 4: adaptive model-tier routing ──────────────────────────────────
943
+
944
+ type _ModelTier = 'opus' | 'sonnet' | 'haiku';
945
+ const _TIER_ORDER: _ModelTier[] = ['opus', 'sonnet', 'haiku'];
946
+
947
+ /**
948
+ * Looks up how many tiers to downgrade given the profile, pressure,
949
+ * and complexity. Returns 0, 1, or 2. Pure function — table-driven.
950
+ */
951
+ function _lookupDowngradeCount(
952
+ profile: TokenProfileName,
953
+ pressure: BudgetPressureLevel,
954
+ complexity: ComplexityLevel
955
+ ): number {
956
+ // quality: only downgrade on critical pressure
957
+ if (profile === 'quality') {
958
+ if (pressure === 'critical') return 1;
959
+ return 0;
960
+ }
961
+
962
+ // balanced: moderate adaptive downgrade
963
+ if (profile === 'balanced') {
964
+ if (pressure === 'none') {
965
+ if (complexity === 'low') return 1;
966
+ return 0;
967
+ }
968
+ if (pressure === 'warning') {
969
+ if (complexity === 'high') return 0;
970
+ return 1;
971
+ }
972
+ if (pressure === 'high') {
973
+ if (complexity === 'high') return 0;
974
+ if (complexity === 'medium') return 1;
975
+ return 2; // low
976
+ }
977
+ if (pressure === 'critical') {
978
+ if (complexity === 'high') return 1;
979
+ return 2;
980
+ }
981
+ }
982
+
983
+ // frugal: aggressive downgrade
984
+ if (profile === 'frugal') {
985
+ if (pressure === 'none') {
986
+ if (complexity === 'high') return 0;
987
+ return 1; // medium or low
988
+ }
989
+ if (pressure === 'warning') {
990
+ if (complexity === 'low') return 2;
991
+ return 1;
992
+ }
993
+ // high or critical
994
+ return 2;
995
+ }
996
+
997
+ return 0;
998
+ }
999
+
1000
+ /**
1001
+ * Applies a downgrade count to a base tier, floored at the lowest tier.
1002
+ * Returns the base tier unchanged if it's not in _TIER_ORDER (passthrough).
1003
+ */
1004
+ function _applyDowngrade(baseTier: _ModelTier, count: number): _ModelTier {
1005
+ const baseIndex = _TIER_ORDER.indexOf(baseTier);
1006
+ if (baseIndex === -1) return baseTier;
1007
+ const targetIndex = Math.min(baseIndex + count, _TIER_ORDER.length - 1);
1008
+ return _TIER_ORDER[targetIndex];
1009
+ }
1010
+
1011
+ /**
1012
+ * Applies an upgrade count to a base tier, capped at the strongest tier.
1013
+ * Symmetric to _applyDowngrade. Used for verify-fail retry escalation
1014
+ * (Tier-2 #5 of the Ouroboros integration) — when a dispatch is a retry
1015
+ * after a verification failure, the agent runs at a stronger tier than
1016
+ * the base, capped at opus. Returns the base tier unchanged if it's not
1017
+ * in _TIER_ORDER (passthrough).
1018
+ *
1019
+ * Note: _TIER_ORDER is ordered strongest-to-weakest (opus, sonnet, haiku),
1020
+ * so "upgrade" decreases the index.
1021
+ */
1022
+ function _applyUpgrade(baseTier: _ModelTier, count: number): _ModelTier {
1023
+ const baseIndex = _TIER_ORDER.indexOf(baseTier);
1024
+ if (baseIndex === -1) return baseTier;
1025
+ const targetIndex = Math.max(baseIndex - count, 0);
1026
+ return _TIER_ORDER[targetIndex];
1027
+ }
1028
+
1029
+ /**
1030
+ * Computes the effective model tier for an agent dispatch given the
1031
+ * base tier (from MODEL_PROFILES), the user's token_profile preference,
1032
+ * the current budget pressure level, and the task's complexity level.
1033
+ *
1034
+ * Pure function. Returns a possibly-downgraded ModelTier. The decision
1035
+ * matrix is documented in the spec and implemented in _lookupDowngradeCount.
1036
+ */
1037
+ function computeEffectiveModelTier(opts: {
1038
+ baseTier: _ModelTier;
1039
+ tokenProfile: TokenProfileName;
1040
+ pressure: BudgetPressureLevel;
1041
+ complexity: ComplexityLevel;
1042
+ }): _ModelTier {
1043
+ const count = _lookupDowngradeCount(opts.tokenProfile, opts.pressure, opts.complexity);
1044
+ return _applyDowngrade(opts.baseTier, count);
1045
+ }
1046
+
1047
+ // --- Adaptive tier dispatch helper -------------------------------------------
1048
+
1049
+ /**
1050
+ * Structural interface for the scheduler's state accessor.
1051
+ * Using a structural interface avoids circular imports between
1052
+ * scheduler.ts (which imports from types.ts) and backend.ts.
1053
+ */
1054
+ interface _SchedulerLike {
1055
+ getStates(): Map<string, BackendUsageState>;
1056
+ readonly sessionKey: string;
1057
+ }
1058
+
1059
+ const { estimateComplexity } = require('./complexity') as {
1060
+ estimateComplexity: (opts: {
1061
+ agentType: string;
1062
+ promptLength?: number;
1063
+ recentSamples?: { duration: number; tokenEstimate: number }[];
1064
+ baselineOverride?: ComplexityLevel;
1065
+ heuristics?: {
1066
+ prompt_length_high_threshold?: number;
1067
+ sample_demote_high_to_medium?: number;
1068
+ sample_demote_medium_to_low?: number;
1069
+ min_samples_for_demotion?: number;
1070
+ };
1071
+ }) => ComplexityLevel;
1072
+ };
1073
+
1074
+ const { computeBudgetPressureLevel, logPressureTransition } = require('./scheduler') as {
1075
+ computeBudgetPressureLevel: (
1076
+ states: Map<string, BackendUsageState>,
1077
+ priority: BackendId[],
1078
+ accounts: SuperpowersConfig['accounts'],
1079
+ thresholds?: BudgetPressureThresholds
1080
+ ) => BudgetPressureLevel;
1081
+ logPressureTransition: (
1082
+ sessionKey: string,
1083
+ current: BudgetPressureLevel,
1084
+ agentType: string,
1085
+ baseTier: string,
1086
+ effectiveTier: string
1087
+ ) => void;
1088
+ };
1089
+
1090
+ /**
1091
+ * Computes the effective model tier for an agent dispatch by running
1092
+ * the Spec 4 chain: estimateComplexity → computeBudgetPressureLevel →
1093
+ * computeEffectiveModelTier. Returns the tier to pass to
1094
+ * resolveModelForAgent as effectiveTierOverride.
1095
+ *
1096
+ * When scheduler/schedulerConfig/superpowersConfig are null/undefined,
1097
+ * returns the base tier unchanged (preserving pre-Spec-4 behavior).
1098
+ *
1099
+ * @param opts.agentType - Agent type key (e.g. 'grd-executor')
1100
+ * @param opts.prompt - The prompt string (used for promptLength)
1101
+ * @param opts.config - GrdConfig with model_profile and token_profile fields
1102
+ * @param opts.scheduler - Scheduler instance or null when not configured
1103
+ * @param opts.schedulerConfig - Scheduler configuration from config.scheduler
1104
+ * @param opts.superpowersConfig - Superpowers config from config.superpowers
1105
+ * @param opts.modelProfiles - MODEL_PROFILES table (passed in to avoid circular dep)
1106
+ * @returns Effective model tier for this dispatch
1107
+ */
1108
+ function getEffectiveTierForDispatch(opts: {
1109
+ agentType: string;
1110
+ prompt: string;
1111
+ config: GrdConfig;
1112
+ scheduler: _SchedulerLike | null;
1113
+ schedulerConfig?: SchedulerConfig;
1114
+ superpowersConfig?: SuperpowersConfig;
1115
+ modelProfiles: Record<string, Record<string, string>>;
1116
+ /**
1117
+ * Verify-fail retry escalation (Tier-2 #5). Per-dispatch metadata: when
1118
+ * this dispatch is a retry after a previous verification failed, set to
1119
+ * the retry count (1 for the first retry, 2 for the second, etc.). The
1120
+ * effective tier is escalated by this many notches, capped at the
1121
+ * strongest tier (opus). 0 or omitted means first attempt — no escalation.
1122
+ *
1123
+ * This is per-work-item metadata only; it does NOT mutate global model
1124
+ * preferences. The caller (e.g. the refinement loop in autopilot-pipeline)
1125
+ * supplies its own retry counter.
1126
+ */
1127
+ retry_attempt?: number;
1128
+ }): _ModelTier {
1129
+ const profile = opts.config.model_profile || 'balanced';
1130
+ const agentEntry = opts.modelProfiles[opts.agentType];
1131
+ const baseTier = ((agentEntry && agentEntry[profile]) || 'sonnet') as _ModelTier;
1132
+ const retryAttempt = opts.retry_attempt && opts.retry_attempt > 0 ? opts.retry_attempt : 0;
1133
+
1134
+ if (!opts.scheduler || !opts.schedulerConfig || !opts.superpowersConfig) {
1135
+ // No adaptive chain available — apply retry escalation directly to
1136
+ // the base tier so retry_attempt still has an effect.
1137
+ return retryAttempt > 0 ? _applyUpgrade(baseTier, retryAttempt) : baseTier;
1138
+ }
1139
+
1140
+ const states = opts.scheduler.getStates();
1141
+
1142
+ // Collect recent samples from all priority accounts (most recent first).
1143
+ // Spec 4 M2: collect agentType so we can prefer same-agent samples for
1144
+ // complexity estimation. Old samples without agentType participate in the
1145
+ // global pool only.
1146
+ let recentSamples: { duration: number; tokenEstimate: number }[] | undefined;
1147
+ const allSamples: {
1148
+ duration: number;
1149
+ tokenEstimate: number;
1150
+ timestamp: number;
1151
+ agentType?: string;
1152
+ }[] = [];
1153
+ for (const backend of opts.schedulerConfig.backend_priority) {
1154
+ const backendAccounts = opts.superpowersConfig.accounts[backend as AdapterBackendId] || [];
1155
+ for (const account of backendAccounts) {
1156
+ const stateKey = `${backend}/${account.config_dir}`;
1157
+ const state = states.get(stateKey);
1158
+ if (!state) continue;
1159
+ for (const sample of state.samples) {
1160
+ allSamples.push({
1161
+ duration: sample.duration,
1162
+ tokenEstimate: sample.tokenEstimate,
1163
+ timestamp: sample.timestamp,
1164
+ agentType: sample.agentType,
1165
+ });
1166
+ }
1167
+ }
1168
+ }
1169
+ if (allSamples.length >= 3) {
1170
+ // Spec 4 M2: prefer per-agent samples if we have enough, else fall back
1171
+ // to the global tail. Old samples without agentType participate in the
1172
+ // global pool only.
1173
+ const ownAgentSamples = allSamples.filter((s) => s.agentType === opts.agentType);
1174
+ const samplesToUse = ownAgentSamples.length >= 3 ? ownAgentSamples : allSamples;
1175
+ // Sort by timestamp descending, take up to 10 most recent
1176
+ samplesToUse.sort((a, b) => b.timestamp - a.timestamp);
1177
+ recentSamples = samplesToUse.slice(0, 10).map((s) => ({
1178
+ duration: s.duration,
1179
+ tokenEstimate: s.tokenEstimate,
1180
+ }));
1181
+ }
1182
+
1183
+ const complexity = estimateComplexity({
1184
+ agentType: opts.agentType,
1185
+ promptLength: opts.prompt.length,
1186
+ recentSamples,
1187
+ baselineOverride: opts.config.agent_complexity_overrides?.[opts.agentType],
1188
+ heuristics: opts.config.complexity_heuristics,
1189
+ });
1190
+ const pressure = computeBudgetPressureLevel(
1191
+ states,
1192
+ opts.schedulerConfig.backend_priority as BackendId[],
1193
+ opts.superpowersConfig.accounts,
1194
+ opts.schedulerConfig.budget_pressure_thresholds
1195
+ );
1196
+ const tokenProfile: TokenProfileName = opts.config.token_profile || 'balanced';
1197
+
1198
+ const adaptiveTier = computeEffectiveModelTier({
1199
+ baseTier,
1200
+ tokenProfile,
1201
+ pressure,
1202
+ complexity,
1203
+ });
1204
+
1205
+ // Verify-fail retry escalation (Tier-2 #5). Applied AFTER adaptive
1206
+ // downgrade so that a retry escalates from whatever tier the adaptive
1207
+ // chain landed on, not from the original baseTier. Capped at opus.
1208
+ const effectiveTier =
1209
+ retryAttempt > 0 ? _applyUpgrade(adaptiveTier, retryAttempt) : adaptiveTier;
1210
+
1211
+ // Spec 4 Goal #7: log on pressure transitions only (O3: use per-scheduler
1212
+ // sessionKey instead of process.pid to avoid shared state across multiple
1213
+ // createScheduler calls in the same process).
1214
+ logPressureTransition(
1215
+ opts.scheduler.sessionKey,
1216
+ pressure,
1217
+ opts.agentType,
1218
+ baseTier,
1219
+ effectiveTier
1220
+ );
1221
+
1222
+ return effectiveTier;
1223
+ }
1224
+
1225
+ // --- Exports -----------------------------------------------------------------
1226
+
1227
+ module.exports = {
1228
+ VALID_BACKENDS,
1229
+ DEFAULT_BACKEND_MODELS,
1230
+ BACKEND_CAPABILITIES,
1231
+ EFFORT_PROFILES,
1232
+ _applyUpgrade,
1233
+ detectBackend,
1234
+ resolveBackendModel,
1235
+ resolveEffortLevel,
1236
+ getBackendCapabilities,
1237
+ parseOpenCodeModels,
1238
+ detectModels,
1239
+ getCachedModels,
1240
+ clearModelCache,
1241
+ detectWebMcp,
1242
+ detectPlaywright,
1243
+ detectAvailableBackends,
1244
+ clearAvailabilityCache,
1245
+ discoverBackendConfigDirs,
1246
+ clearConfigDirCache,
1247
+ buildBackendEnv,
1248
+ BACKEND_CONFIG_ENV,
1249
+ readConfig,
1250
+ computeEffectiveModelTier,
1251
+ getEffectiveTierForDispatch,
1252
+ };