@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,1147 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FREE_FALLBACK_BUDGET = exports.ENV_VAR_MAP = exports.ADAPTERS = void 0;
4
+ exports.createBackendState = createBackendState;
5
+ exports.updateEWMA = updateEWMA;
6
+ exports.evictExpiredSamples = evictExpiredSamples;
7
+ exports.recordSample = recordSample;
8
+ exports.pickBackend = pickBackend;
9
+ exports._killProcessTree = _killProcessTree;
10
+ exports._resolveIdleTimeoutSeconds = _resolveIdleTimeoutSeconds;
11
+ exports._startIdleWatchdog = _startIdleWatchdog;
12
+ exports._anyPriorityHasHeadroom = _anyPriorityHasHeadroom;
13
+ exports.computeSoonestRecovery = computeSoonestRecovery;
14
+ exports.isBudgetPressured = isBudgetPressured;
15
+ exports.logPressureTransition = logPressureTransition;
16
+ exports.computeBudgetPressureLevel = computeBudgetPressureLevel;
17
+ exports.resolveAccount = resolveAccount;
18
+ exports.markInFlight = markInFlight;
19
+ exports.markComplete = markComplete;
20
+ exports.checkBinary = checkBinary;
21
+ exports.createScheduler = createScheduler;
22
+ exports.detectSpin = detectSpin;
23
+ const { waitUntilOrAbort } = require('./scheduler-wait');
24
+ const { incrementCounter } = require('./metrics');
25
+ // ─── Per-backend CLI Adapters ─────────────────────────────────────────────────
26
+ /**
27
+ * Map of backend adapters for all supported CLI backends.
28
+ * Each adapter encapsulates binary name, argument building, token parsing,
29
+ * and rate-limit detection for a specific backend CLI.
30
+ *
31
+ * Meta-backends (superpowers, grd) are not included — they are scheduling
32
+ * strategies that resolve to one of these real adapters at spawn time.
33
+ */
34
+ const _claudeAdapter = {
35
+ binary: 'claude',
36
+ buildArgs(prompt, opts) {
37
+ const args = ['-p', prompt, '--verbose', '--dangerously-skip-permissions'];
38
+ if (opts.maxTurns) {
39
+ args.push('--max-turns', String(opts.maxTurns));
40
+ }
41
+ if (opts.model) {
42
+ args.push('--model', opts.model);
43
+ }
44
+ args.push('--output-format', 'json');
45
+ return args;
46
+ },
47
+ parseTokenUsage(stderr) {
48
+ const totalMatch = stderr.match(/[Tt]otal.tokens:\s*(\d+)/);
49
+ if (totalMatch)
50
+ return parseInt(totalMatch[1], 10);
51
+ const inputMatch = stderr.match(/input_tokens:\s*(\d+)/);
52
+ const outputMatch = stderr.match(/output_tokens:\s*(\d+)/);
53
+ if (inputMatch && outputMatch) {
54
+ return parseInt(inputMatch[1], 10) + parseInt(outputMatch[1], 10);
55
+ }
56
+ return null;
57
+ },
58
+ isRateLimited(exitCode, stderr) {
59
+ if (exitCode === 0)
60
+ return false;
61
+ return /rate.limit|429|overloaded_error|too many requests/i.test(stderr);
62
+ },
63
+ };
64
+ exports.ADAPTERS = {
65
+ claude: _claudeAdapter,
66
+ codex: {
67
+ binary: 'codex',
68
+ buildArgs(prompt, opts) {
69
+ const args = ['--prompt', prompt, '--approval-mode', 'full-auto'];
70
+ if (opts.model) {
71
+ args.push('--model', opts.model);
72
+ }
73
+ return args;
74
+ },
75
+ parseTokenUsage(stderr) {
76
+ const match = stderr.match(/"total_tokens":\s*(\d+)/);
77
+ return match ? parseInt(match[1], 10) : null;
78
+ },
79
+ isRateLimited(_exitCode, stderr) {
80
+ return /rate.limit|429|rate_limit_exceeded/i.test(stderr);
81
+ },
82
+ },
83
+ gemini: {
84
+ binary: 'gemini',
85
+ buildArgs(prompt, opts) {
86
+ const args = ['-p', prompt, '--sandbox', 'off'];
87
+ if (opts.model) {
88
+ args.push('--model', opts.model);
89
+ }
90
+ return args;
91
+ },
92
+ parseTokenUsage(stderr) {
93
+ const match = stderr.match(/tokenCount["\s:]*(\d+)/);
94
+ return match ? parseInt(match[1], 10) : null;
95
+ },
96
+ isRateLimited(_exitCode, stderr) {
97
+ return /rate.limit|429|RESOURCE_EXHAUSTED|quota/i.test(stderr);
98
+ },
99
+ },
100
+ opencode: {
101
+ binary: 'opencode',
102
+ buildArgs(prompt, opts) {
103
+ const args = ['--non-interactive', '--prompt', prompt];
104
+ if (opts.model) {
105
+ args.push('--model', opts.model);
106
+ }
107
+ return args;
108
+ },
109
+ parseTokenUsage(stderr) {
110
+ const match = stderr.match(/(?:total_tokens|tokens?.used)[\s:"]*(\d+)/i);
111
+ return match ? parseInt(match[1], 10) : null;
112
+ },
113
+ isRateLimited(_exitCode, stderr) {
114
+ return /rate.limit|429|too many requests|quota/i.test(stderr);
115
+ },
116
+ },
117
+ overstory: {
118
+ binary: 'ov',
119
+ buildArgs(prompt, opts) {
120
+ const args = ['run', '--prompt', prompt];
121
+ if (opts.model) {
122
+ args.push('--model', opts.model);
123
+ }
124
+ return args;
125
+ },
126
+ parseTokenUsage(stderr) {
127
+ const match = stderr.match(/tokens?:\s*(\d+)/i);
128
+ return match ? parseInt(match[1], 10) : null;
129
+ },
130
+ isRateLimited(_exitCode, stderr) {
131
+ return /rate.limit|429|quota/i.test(stderr);
132
+ },
133
+ },
134
+ };
135
+ /**
136
+ * Maps each adapter backend to its config-directory environment variable.
137
+ * Used by account rotation to override which account a CLI binary uses.
138
+ */
139
+ exports.ENV_VAR_MAP = {
140
+ claude: 'CLAUDE_CONFIG_DIR',
141
+ codex: 'CODEX_HOME',
142
+ gemini: 'GEMINI_CLI_HOME',
143
+ opencode: 'OPENCODE_CONFIG_DIR',
144
+ overstory: 'OVERSTORY_HOME',
145
+ };
146
+ // ─── EWMA and Rolling Window ──────────────────────────────────────────────────
147
+ /** Default token-per-minute budget for backends with no explicit limit configured. */
148
+ const DEFAULT_BUDGET_TPM = 40000;
149
+ /** Token-per-minute budget for the free-fallback backend (effectively unlimited). */
150
+ exports.FREE_FALLBACK_BUDGET = 1000000;
151
+ /**
152
+ * Creates a fresh BackendUsageState with the given token budget.
153
+ *
154
+ * @param tokenBudget - tokens-per-minute budget for this backend
155
+ * @returns initialized state with zeroed counters
156
+ */
157
+ function createBackendState(tokenBudget) {
158
+ return {
159
+ samples: [],
160
+ ewma_tokens_per_task: 0,
161
+ tokens_consumed_in_window: 0,
162
+ tokens_reserved: 0,
163
+ in_flight_count: 0,
164
+ token_budget: tokenBudget,
165
+ budget_learned: false,
166
+ budget_confidence: 0,
167
+ cooldown_until: undefined,
168
+ };
169
+ }
170
+ /**
171
+ * Updates the EWMA estimate in-place with a new token observation.
172
+ * On first observation (ewma === 0), sets directly to the observed value.
173
+ *
174
+ * @param state - backend usage state to update
175
+ * @param tokens - observed token count for the latest task
176
+ * @param alpha - EWMA smoothing factor (0 < alpha < 1)
177
+ */
178
+ function updateEWMA(state, tokens, alpha) {
179
+ if (state.ewma_tokens_per_task === 0) {
180
+ state.ewma_tokens_per_task = tokens;
181
+ }
182
+ else {
183
+ state.ewma_tokens_per_task = alpha * tokens + (1 - alpha) * state.ewma_tokens_per_task;
184
+ }
185
+ }
186
+ /**
187
+ * Removes samples older than windowMinutes from state and recalculates
188
+ * tokens_consumed_in_window from the remaining samples.
189
+ *
190
+ * @param state - backend usage state to mutate
191
+ * @param windowMinutes - rolling window duration in minutes
192
+ */
193
+ function evictExpiredSamples(state, windowMinutes) {
194
+ if (windowMinutes <= 0)
195
+ return;
196
+ const cutoff = Date.now() - windowMinutes * 60 * 1000;
197
+ state.samples = state.samples.filter((s) => s.timestamp >= cutoff);
198
+ state.tokens_consumed_in_window = state.samples.reduce((sum, s) => sum + s.tokenEstimate, 0);
199
+ }
200
+ /**
201
+ * Records a completed usage sample, evicts stale samples from the window,
202
+ * updates EWMA, and recalculates budget_confidence.
203
+ *
204
+ * @param state - backend usage state to update
205
+ * @param sample - new usage sample to record
206
+ * @param windowMinutes - rolling window duration in minutes
207
+ * @param alpha - EWMA smoothing factor
208
+ */
209
+ function recordSample(state, sample, windowMinutes, alpha) {
210
+ state.samples.push(sample);
211
+ evictExpiredSamples(state, windowMinutes);
212
+ updateEWMA(state, sample.tokenEstimate, alpha);
213
+ state.budget_confidence = 1 - 1 / (1 + state.samples.length * 0.2);
214
+ }
215
+ // ─── Backend Picker with Concurrency Accounting ───────────────────────────────
216
+ /**
217
+ * Selects the highest-priority backend that has sufficient token headroom.
218
+ * Skips backends in cooldown or without enough remaining capacity (accounting
219
+ * for in-flight reservations). Falls back to freeFallback if none qualify.
220
+ *
221
+ * @param priority - ordered list of backend IDs to try
222
+ * @param states - map of backend ID to usage state
223
+ * @param safetyMargin - minimum remaining tasks before a backend is considered full
224
+ * @param freeFallback - fallback backend used when all priority backends are exhausted
225
+ * @returns selected BackendId
226
+ */
227
+ function pickBackend(priority, states, safetyMargin, freeFallback) {
228
+ const now = Date.now();
229
+ for (const backend of priority) {
230
+ const state = states.get(backend);
231
+ if (!state)
232
+ continue;
233
+ if (state.cooldown_until && state.cooldown_until > now)
234
+ continue;
235
+ if (state.ewma_tokens_per_task === 0)
236
+ return backend;
237
+ const effective = state.tokens_consumed_in_window + state.tokens_reserved;
238
+ const remaining = state.token_budget - effective;
239
+ const tasksRemaining = remaining / state.ewma_tokens_per_task;
240
+ if (tasksRemaining >= safetyMargin)
241
+ return backend;
242
+ }
243
+ return freeFallback.backend;
244
+ }
245
+ // ─── Account Resolution Waterfall ─────────────────────────────────────────────
246
+ /**
247
+ * Checks whether a single account state key has sufficient headroom for
248
+ * scheduling, considering EWMA prediction, in-flight reservations, and cooldown.
249
+ *
250
+ * @param state - the account's usage state
251
+ * @param safetyMargin - minimum remaining tasks before considered full
252
+ * @returns true if the account has capacity or no EWMA data yet
253
+ */
254
+ function _hasHeadroom(state, safetyMargin) {
255
+ const now = Date.now();
256
+ if (state.cooldown_until && state.cooldown_until > now)
257
+ return false;
258
+ if (state.ewma_tokens_per_task === 0)
259
+ return true;
260
+ const effective = state.tokens_consumed_in_window + state.tokens_reserved;
261
+ const remaining = state.token_budget - effective;
262
+ const tasksRemaining = remaining / state.ewma_tokens_per_task;
263
+ return tasksRemaining >= safetyMargin;
264
+ }
265
+ /**
266
+ * Sends `signal` to the process group of `child` on POSIX platforms, or to
267
+ * the direct child on Windows. Using a negative PID with process.kill ensures
268
+ * grandchildren (e.g., tool-invocation forks spawned by the backend CLI) are
269
+ * also terminated.
270
+ *
271
+ * Requires the child to have been spawned with `detached: true` so that it
272
+ * gets its own process group (pgid === pid).
273
+ *
274
+ * @param child - the spawned ChildProcess whose process group to signal
275
+ * @param signal - signal to send (e.g. 'SIGTERM', 'SIGKILL')
276
+ */
277
+ function _killProcessTree(child, signal) {
278
+ if (child.pid === undefined)
279
+ return;
280
+ if (process.platform === 'win32') {
281
+ // Windows: just kill the direct child (no POSIX process groups)
282
+ try {
283
+ child.kill(signal);
284
+ }
285
+ catch {
286
+ /* already dead */
287
+ }
288
+ return;
289
+ }
290
+ // POSIX: signal the whole process group via negative pid
291
+ try {
292
+ process.kill(-child.pid, signal);
293
+ }
294
+ catch (e) {
295
+ // ESRCH (no such process) is benign — process already exited.
296
+ // Fall back to direct kill in case the group wasn't created (e.g., race).
297
+ if (e.code !== 'ESRCH') {
298
+ try {
299
+ child.kill(signal);
300
+ }
301
+ catch {
302
+ /* already dead */
303
+ }
304
+ }
305
+ }
306
+ }
307
+ /**
308
+ * Resolves the idle timeout in seconds for the given backend, applying the
309
+ * lookup order: per-backend override → global idle_timeout_seconds → default 900.
310
+ *
311
+ * @param backend - backend ID (e.g. 'claude', 'gemini')
312
+ * @param config - subset of SchedulerConfig with timeout fields
313
+ * @returns resolved idle timeout in seconds
314
+ */
315
+ function _resolveIdleTimeoutSeconds(backend, config) {
316
+ return config.idle_timeout_seconds_by_backend?.[backend] ?? config.idle_timeout_seconds ?? 900;
317
+ }
318
+ /**
319
+ * Starts an idle watchdog that invokes `onIdle` when no markActivity
320
+ * has been called for longer than `idleTimeoutMs`. Returns markActivity
321
+ * and stop functions.
322
+ *
323
+ * Polls every 1000ms. Fires at most once — subsequent ticks are no-ops.
324
+ */
325
+ function _startIdleWatchdog(idleTimeoutMs, onIdle) {
326
+ const POLL_INTERVAL_MS = 1000;
327
+ let lastActivityAt = Date.now();
328
+ let stopped = false;
329
+ const timer = setInterval(() => {
330
+ if (stopped)
331
+ return;
332
+ if (Date.now() - lastActivityAt >= idleTimeoutMs) {
333
+ stopped = true;
334
+ clearInterval(timer);
335
+ onIdle();
336
+ }
337
+ }, POLL_INTERVAL_MS);
338
+ return {
339
+ markActivity: () => {
340
+ lastActivityAt = Date.now();
341
+ },
342
+ stop: () => {
343
+ if (stopped)
344
+ return;
345
+ stopped = true;
346
+ clearInterval(timer);
347
+ },
348
+ };
349
+ }
350
+ /**
351
+ * Returns true iff at least one account in the priority list has headroom.
352
+ * Small helper used by the _spawnWithRetry wait-branch decision.
353
+ */
354
+ function _anyPriorityHasHeadroom(priority, accounts, states, safetyMargin) {
355
+ for (const backend of priority) {
356
+ const backendAccounts = accounts[backend] || [];
357
+ for (const account of backendAccounts) {
358
+ const stateKey = `${backend}/${account.config_dir}`;
359
+ const state = states.get(stateKey);
360
+ if (!state)
361
+ continue;
362
+ if (_hasHeadroom(state, safetyMargin))
363
+ return true;
364
+ }
365
+ }
366
+ return false;
367
+ }
368
+ /**
369
+ * Computes the earliest timestamp (ms since epoch) at which ANY priority
370
+ * account will regain headroom based on sample aging out of the rolling
371
+ * window. Used by the wait-loop in _spawnWithRetry when all priority
372
+ * accounts are currently exhausted.
373
+ *
374
+ * For each priority account, walks its samples oldest-first, hypothetically
375
+ * dropping each one and recomputing projected headroom. The latest-dropped
376
+ * sample's timestamp + windowMinutes is the moment that account will have
377
+ * enough headroom for one more EWMA-sized task.
378
+ *
379
+ * Returns null if:
380
+ * - No priority account has samples (nothing to wait for)
381
+ * - Soonest recovery across all accounts is beyond Date.now() + maxWaitMs
382
+ * - All considered accounts have zero ewma_tokens_per_task (no prediction data)
383
+ *
384
+ * Pattern adopted from gsd-2 v2.67 auto-timeout-recovery.ts — but
385
+ * sample-based rather than attempt-based.
386
+ *
387
+ * Note: tokens_reserved (in-flight EWMA cost) is held constant during the
388
+ * simulation because in-flight tasks are expected to complete independently
389
+ * of sample aging. This makes the estimate slightly pessimistic — actual
390
+ * headroom may return sooner.
391
+ */
392
+ function computeSoonestRecovery(states, priority, accounts, windowMinutes, maxWaitMs) {
393
+ const now = Date.now();
394
+ let soonest = Infinity;
395
+ for (const backend of priority) {
396
+ const backendAccounts = accounts[backend] || [];
397
+ for (const account of backendAccounts) {
398
+ const stateKey = `${backend}/${account.config_dir}`;
399
+ const state = states.get(stateKey);
400
+ if (!state || state.samples.length === 0)
401
+ continue;
402
+ if (state.ewma_tokens_per_task === 0)
403
+ continue;
404
+ const sortedSamples = [...state.samples].sort((a, b) => a.timestamp - b.timestamp);
405
+ const ewmaCost = state.ewma_tokens_per_task;
406
+ let consumed = state.tokens_consumed_in_window;
407
+ const reserved = state.tokens_reserved;
408
+ let latestDroppedTs = null;
409
+ for (const sample of sortedSamples) {
410
+ const projectedRemaining = state.token_budget - consumed - reserved;
411
+ if (projectedRemaining >= ewmaCost)
412
+ break;
413
+ consumed -= sample.tokenEstimate;
414
+ latestDroppedTs = sample.timestamp;
415
+ }
416
+ if (latestDroppedTs === null)
417
+ continue;
418
+ const recoveryTime = latestDroppedTs + windowMinutes * 60 * 1000;
419
+ if (recoveryTime < soonest)
420
+ soonest = recoveryTime;
421
+ }
422
+ }
423
+ if (soonest === Infinity)
424
+ return null;
425
+ if (soonest > now + maxWaitMs)
426
+ return null;
427
+ return soonest;
428
+ }
429
+ // ─── Spec 4: budget pressure detection ────────────────────────────────────
430
+ /**
431
+ * Default thresholds for budget pressure classification. Overridable
432
+ * via SchedulerConfig.budget_pressure_thresholds.
433
+ */
434
+ const DEFAULT_PRESSURE_THRESHOLDS = {
435
+ warning: 0.6,
436
+ high: 0.8,
437
+ critical: 0.95,
438
+ };
439
+ /**
440
+ * Returns true if any priority account has consumed more than the warning
441
+ * threshold (default 60%) of its rolling-window budget. Pure function.
442
+ */
443
+ function isBudgetPressured(states, priority, accounts, thresholds) {
444
+ return computeBudgetPressureLevel(states, priority, accounts, thresholds) !== 'none';
445
+ }
446
+ // Module-level state for transition-based logging
447
+ const _lastLoggedPressure = new Map();
448
+ // Monotonic counter for unique per-scheduler session keys. Each
449
+ // createScheduler call gets its own ID so _lastLoggedPressure
450
+ // transitions are tracked independently (O3).
451
+ let _nextSchedulerSessionId = 0;
452
+ /**
453
+ * Logs a single stderr line when the pressure level has changed since
454
+ * the last call with the same sessionKey. Safe to call per spawn —
455
+ * only emits on transitions. Noop when current == previous.
456
+ *
457
+ * The sessionKey lets multiple sessions in the same process have
458
+ * independent transition state. Autopilot/evolve/autoresearch
459
+ * typically pass process.pid.toString().
460
+ */
461
+ function logPressureTransition(sessionKey, current, agentType, baseTier, effectiveTier) {
462
+ const previous = _lastLoggedPressure.get(sessionKey) || 'none';
463
+ if (previous === current)
464
+ return;
465
+ _lastLoggedPressure.set(sessionKey, current);
466
+ incrementCounter(`scheduler.pressure_transitions.${current}`);
467
+ if (current === 'none')
468
+ return;
469
+ const tierNote = baseTier === effectiveTier
470
+ ? ''
471
+ : ` — downgrading ${agentType} from ${baseTier} to ${effectiveTier}`;
472
+ process.stderr.write(`[scheduler] budget pressure detected — level=${current}${tierNote}\n`);
473
+ }
474
+ /**
475
+ * Classifies the worst pressure level across all priority accounts.
476
+ * Returns 'none' | 'warning' | 'high' | 'critical'. Pure function.
477
+ *
478
+ * For each priority account, computes (consumed + reserved) / budget
479
+ * and picks the worst ratio across all accounts (i.e., the one closest
480
+ * to exhaustion determines the level for the whole session).
481
+ */
482
+ function computeBudgetPressureLevel(states, priority, accounts, thresholds) {
483
+ const t = thresholds || DEFAULT_PRESSURE_THRESHOLDS;
484
+ let worstRatio = 0;
485
+ for (const backend of priority) {
486
+ const backendAccounts = accounts[backend] || [];
487
+ for (const account of backendAccounts) {
488
+ const stateKey = `${backend}/${account.config_dir}`;
489
+ const state = states.get(stateKey);
490
+ if (!state)
491
+ continue;
492
+ if (state.token_budget <= 0)
493
+ continue;
494
+ const ratio = (state.tokens_consumed_in_window + state.tokens_reserved) / state.token_budget;
495
+ if (ratio > worstRatio)
496
+ worstRatio = ratio;
497
+ }
498
+ }
499
+ if (worstRatio >= t.critical)
500
+ return 'critical';
501
+ if (worstRatio >= t.high)
502
+ return 'high';
503
+ if (worstRatio >= t.warning)
504
+ return 'warning';
505
+ return 'none';
506
+ }
507
+ /**
508
+ * Resolves which backend and account to use for the next scheduled task.
509
+ * Walks the backend_priority list, and within each backend tries every
510
+ * configured account in order. Falls back to the free_fallback backend
511
+ * when all priority accounts are exhausted.
512
+ *
513
+ * Edge cases:
514
+ * - Empty accounts ({}) — returns default_backend with no config dir override
515
+ * - Empty account array ([]) — skips that backend
516
+ * - Backend in priority but missing from accounts — skipped
517
+ *
518
+ * @param superpowersConfig - superpowers configuration with accounts
519
+ * @param schedulerConfig - scheduler configuration with priority and fallback
520
+ * @param states - map of compound state keys to usage state
521
+ * @param safetyMargin - minimum remaining tasks before an account is considered full
522
+ * @returns resolved backend, account, and state key
523
+ */
524
+ function resolveAccount(superpowersConfig, schedulerConfig, states, safetyMargin) {
525
+ const accounts = superpowersConfig.accounts;
526
+ // Edge case: accounts is empty — use default_backend with no config dir
527
+ const hasAnyAccounts = Object.keys(accounts).some((k) => (accounts[k] || []).length > 0);
528
+ if (!hasAnyAccounts) {
529
+ return {
530
+ backend: superpowersConfig.default_backend,
531
+ account: { config_dir: '' },
532
+ stateKey: superpowersConfig.default_backend,
533
+ };
534
+ }
535
+ // Walk priority list, try each account within each backend
536
+ for (const backend of schedulerConfig.backend_priority) {
537
+ const backendAccounts = accounts[backend];
538
+ if (!backendAccounts || backendAccounts.length === 0)
539
+ continue;
540
+ for (const account of backendAccounts) {
541
+ const stateKey = `${backend}/${account.config_dir}`;
542
+ const state = states.get(stateKey);
543
+ if (!state)
544
+ continue;
545
+ if (_hasHeadroom(state, safetyMargin)) {
546
+ return { backend, account, stateKey };
547
+ }
548
+ }
549
+ }
550
+ // Exhaustion fallback: use free_fallback backend
551
+ const fallbackBackend = schedulerConfig.free_fallback.backend;
552
+ const fallbackAccounts = accounts[fallbackBackend];
553
+ if (fallbackAccounts && fallbackAccounts.length > 0) {
554
+ return {
555
+ backend: fallbackBackend,
556
+ account: fallbackAccounts[0],
557
+ stateKey: `${fallbackBackend}/${fallbackAccounts[0].config_dir}`,
558
+ };
559
+ }
560
+ // No accounts configured for fallback — use default account (empty config_dir)
561
+ return {
562
+ backend: fallbackBackend,
563
+ account: { config_dir: '' },
564
+ stateKey: fallbackBackend,
565
+ };
566
+ }
567
+ /**
568
+ * Marks one task as in-flight, incrementing the in-flight counter and
569
+ * reserving the EWMA-predicted token cost.
570
+ *
571
+ * @param state - backend usage state to mutate
572
+ */
573
+ function markInFlight(state) {
574
+ state.in_flight_count += 1;
575
+ state.tokens_reserved += state.ewma_tokens_per_task;
576
+ }
577
+ /**
578
+ * Marks one in-flight task as complete, decrementing the counter and
579
+ * recalculating tokens_reserved from the updated in-flight count.
580
+ *
581
+ * @param state - backend usage state to mutate
582
+ */
583
+ function markComplete(state) {
584
+ state.in_flight_count = Math.max(0, state.in_flight_count - 1);
585
+ state.tokens_reserved = state.ewma_tokens_per_task * state.in_flight_count;
586
+ }
587
+ // ─── Shared Helpers ──────────────────────────────────────────────────────────
588
+ /**
589
+ * Checks whether a CLI binary is available on the system PATH.
590
+ * Uses 'where' on Windows and 'which' on POSIX (I8 fix).
591
+ */
592
+ function checkBinary(binary) {
593
+ try {
594
+ const { execFileSync } = require('child_process');
595
+ const cmd = process.platform === 'win32' ? 'where' : 'which';
596
+ execFileSync(cmd, [binary], { stdio: 'ignore' });
597
+ return true;
598
+ }
599
+ catch {
600
+ return false;
601
+ }
602
+ }
603
+ /**
604
+ * Initializes per-account states when account rotation is enabled.
605
+ * Creates a BackendUsageState for each account across all priority backends
606
+ * and the fallback backend, using compound keys like "claude/~/.claude-personal".
607
+ *
608
+ * @param states - state map to populate
609
+ * @param schedulerConfig - scheduler configuration with priority and fallback
610
+ * @param superpowersConfig - superpowers configuration with accounts
611
+ */
612
+ function _initAccountStates(states, schedulerConfig, superpowersConfig) {
613
+ const accounts = superpowersConfig.accounts;
614
+ const allBackends = new Set([
615
+ ...schedulerConfig.backend_priority,
616
+ schedulerConfig.free_fallback.backend,
617
+ ]);
618
+ for (const backend of allBackends) {
619
+ const backendAccounts = accounts[backend];
620
+ if (!backendAccounts || backendAccounts.length === 0)
621
+ continue;
622
+ const limit = schedulerConfig.backend_limits?.[backend]?.tpm;
623
+ const isFallback = backend === schedulerConfig.free_fallback.backend;
624
+ const budget = limit ?? (isFallback ? exports.FREE_FALLBACK_BUDGET : DEFAULT_BUDGET_TPM);
625
+ for (const account of backendAccounts) {
626
+ const stateKey = `${backend}/${account.config_dir}`;
627
+ states.set(stateKey, createBackendState(budget));
628
+ }
629
+ }
630
+ }
631
+ /**
632
+ * Computes the maximum number of 429 retries allowed for account rotation.
633
+ * Equals total number of accounts across all priority backends.
634
+ *
635
+ * @param schedulerConfig - scheduler configuration with priority
636
+ * @param superpowersConfig - superpowers configuration with accounts
637
+ * @returns maximum retry count
638
+ */
639
+ function _computeMaxRetries(schedulerConfig, superpowersConfig) {
640
+ let maxAccountsPerBackend = 0;
641
+ for (const backend of schedulerConfig.backend_priority) {
642
+ const backendAccounts = superpowersConfig.accounts[backend];
643
+ if (backendAccounts) {
644
+ maxAccountsPerBackend = Math.max(maxAccountsPerBackend, backendAccounts.length);
645
+ }
646
+ }
647
+ return schedulerConfig.backend_priority.length * Math.max(maxAccountsPerBackend, 1);
648
+ }
649
+ /**
650
+ * Creates a Scheduler instance from the given config, or returns null
651
+ * when no config is provided (pass-through / disabled mode).
652
+ *
653
+ * When superpowersConfig is provided with account_rotation enabled, the
654
+ * scheduler tracks per-account state and uses resolveAccount() for backend
655
+ * selection. Otherwise, it uses the simple pickBackend() flow.
656
+ *
657
+ * @param config - scheduler configuration, or undefined to disable
658
+ * @param superpowersConfig - optional superpowers configuration for account rotation
659
+ * @returns Scheduler instance, or null if config is absent
660
+ */
661
+ function createScheduler(config, superpowersConfig) {
662
+ if (!config)
663
+ return null;
664
+ // Unique key for this scheduler instance, used to namespace
665
+ // _lastLoggedPressure so multiple schedulers in the same process do not
666
+ // share transition state (O3).
667
+ const sessionKey = `pid-${process.pid}-session-${_nextSchedulerSessionId++}`;
668
+ // Apply Spec 2A defaults here so the rest of the scheduler can rely on
669
+ // a fully-populated config. Spread-merge avoids mutating caller input.
670
+ const schedulerConfig = {
671
+ ...config,
672
+ max_wait_minutes: config.max_wait_minutes ?? 90,
673
+ };
674
+ const states = new Map();
675
+ const prediction = schedulerConfig.prediction;
676
+ const accountRotation = !!superpowersConfig?.account_rotation;
677
+ if (accountRotation && superpowersConfig) {
678
+ // Per-account state initialization
679
+ _initAccountStates(states, schedulerConfig, superpowersConfig);
680
+ // Also initialize fallback backend with no config_dir for the exhaustion case
681
+ const fallbackBackend = schedulerConfig.free_fallback.backend;
682
+ if (!states.has(fallbackBackend)) {
683
+ const limit = schedulerConfig.backend_limits?.[fallbackBackend]?.tpm;
684
+ const budget = limit ?? exports.FREE_FALLBACK_BUDGET;
685
+ states.set(fallbackBackend, createBackendState(budget));
686
+ }
687
+ // If no accounts at all, initialize default_backend with no config_dir
688
+ const hasAnyAccounts = Object.keys(superpowersConfig.accounts).some((k) => (superpowersConfig.accounts[k] || []).length > 0);
689
+ if (!hasAnyAccounts) {
690
+ const defaultBackend = superpowersConfig.default_backend;
691
+ if (!states.has(defaultBackend)) {
692
+ const limit = schedulerConfig.backend_limits?.[defaultBackend]?.tpm;
693
+ const budget = limit ?? DEFAULT_BUDGET_TPM;
694
+ states.set(defaultBackend, createBackendState(budget));
695
+ }
696
+ }
697
+ }
698
+ else {
699
+ // Simple per-backend state initialization (existing behavior)
700
+ const allBackends = [
701
+ ...schedulerConfig.backend_priority,
702
+ schedulerConfig.free_fallback.backend,
703
+ ];
704
+ for (const backend of new Set(allBackends)) {
705
+ const limit = schedulerConfig.backend_limits?.[backend]?.tpm;
706
+ const isFallback = backend === schedulerConfig.free_fallback.backend;
707
+ const budget = limit ?? (isFallback ? exports.FREE_FALLBACK_BUDGET : DEFAULT_BUDGET_TPM);
708
+ states.set(backend, createBackendState(budget));
709
+ }
710
+ }
711
+ // Check which backend binaries are available
712
+ const availableBackends = new Set();
713
+ const allBackendIds = new Set([
714
+ ...schedulerConfig.backend_priority,
715
+ schedulerConfig.free_fallback.backend,
716
+ ]);
717
+ for (const backend of allBackendIds) {
718
+ const adapter = exports.ADAPTERS[backend];
719
+ if (adapter && checkBinary(adapter.binary))
720
+ availableBackends.add(backend);
721
+ }
722
+ const maxRetries = accountRotation && superpowersConfig
723
+ ? _computeMaxRetries(schedulerConfig, superpowersConfig)
724
+ : schedulerConfig.backend_priority.length;
725
+ /**
726
+ * Internal spawn implementation with retry counter for 429 rate-limit retries.
727
+ * Capped at maxRetries to prevent infinite loops when all accounts are exhausted.
728
+ */
729
+ async function _spawnWithRetry(prompt, opts, retryCount, lastRecoveryTime = null) {
730
+ let backend;
731
+ let stateKey;
732
+ const envOverrides = {};
733
+ if (accountRotation && superpowersConfig) {
734
+ // Account-rotation path: resolve backend + account
735
+ const resolution = resolveAccount(superpowersConfig, schedulerConfig, states, prediction.safety_margin_tasks);
736
+ backend = resolution.backend;
737
+ stateKey = resolution.stateKey;
738
+ // Set env var for the account's config directory
739
+ if (resolution.account.config_dir) {
740
+ envOverrides[exports.ENV_VAR_MAP[backend]] = resolution.account.config_dir;
741
+ }
742
+ }
743
+ else {
744
+ // Simple backend picker path (existing behavior)
745
+ const filteredPriority = schedulerConfig.backend_priority.filter((b) => availableBackends.has(b));
746
+ backend = pickBackend(filteredPriority, states, prediction.safety_margin_tasks, schedulerConfig.free_fallback);
747
+ stateKey = backend;
748
+ }
749
+ // Spec 2A: bounded wait for soonest recovery when all priority accounts
750
+ // are exhausted and resolveAccount fell through to free_fallback.
751
+ if (accountRotation &&
752
+ superpowersConfig &&
753
+ backend === schedulerConfig.free_fallback.backend &&
754
+ schedulerConfig.backend_priority.length > 0 &&
755
+ !_anyPriorityHasHeadroom(schedulerConfig.backend_priority, superpowersConfig.accounts, states, prediction.safety_margin_tasks)) {
756
+ // Defensive: createScheduler applies 90 default, but TS can't narrow through
757
+ // the spread-merge. The ?? 90 keeps TypeScript happy and guards against
758
+ // direct construction of the SchedulerConfig bypassing createScheduler.
759
+ const maxWaitMinutes = schedulerConfig.max_wait_minutes ?? 90;
760
+ if (maxWaitMinutes > 0) {
761
+ const maxWaitMs = maxWaitMinutes * 60 * 1000;
762
+ const recoveryTime = computeSoonestRecovery(states, schedulerConfig.backend_priority, superpowersConfig.accounts, prediction.window_minutes, maxWaitMs);
763
+ if (recoveryTime !== null && recoveryTime === lastRecoveryTime) {
764
+ // Infinite-loop guard: if this is the same timestamp we already
765
+ // waited for, sample state didn't change. Fall through to
766
+ // free_fallback instead of waiting again (pre-Spec 2A behavior).
767
+ }
768
+ else if (recoveryTime !== null) {
769
+ const waitMs = recoveryTime - Date.now();
770
+ if (waitMs <= 0) {
771
+ // Recovery target already elapsed — waiting would be a no-op and
772
+ // recursing may not progress. Fall through to free_fallback (I9).
773
+ process.stderr.write(`[scheduler] recovery target already elapsed, falling through to free_fallback\n`);
774
+ // Fall through to normal spawn with the fallback backend
775
+ }
776
+ else {
777
+ const displayMinutes = Math.max(0, Math.ceil(waitMs / 60_000));
778
+ process.stderr.write(`[scheduler] all priority accounts exhausted, waiting ${displayMinutes}m for soonest recovery (target=${new Date(recoveryTime).toISOString()})\n`);
779
+ const waitResult = await waitUntilOrAbort(recoveryTime);
780
+ if (waitResult === 'aborted') {
781
+ throw new Error('scheduler: wait for account recovery interrupted by SIGINT');
782
+ }
783
+ return _spawnWithRetry(prompt, opts, retryCount, recoveryTime);
784
+ }
785
+ }
786
+ }
787
+ }
788
+ const adapter = exports.ADAPTERS[backend] || exports.ADAPTERS.claude;
789
+ let state = states.get(stateKey);
790
+ if (!state) {
791
+ // Register the new state in the shared map so markInFlight/markComplete
792
+ // mutations are visible to subsequent dispatches (previously a throw-away
793
+ // orphan object silently lost budget accounting — I1).
794
+ state = createBackendState(DEFAULT_BUDGET_TPM);
795
+ states.set(stateKey, state);
796
+ }
797
+ const args = adapter.buildArgs(prompt, opts);
798
+ const workItemId = opts.workItemId || `task-${Date.now()}`;
799
+ markInFlight(state);
800
+ const startTime = Date.now();
801
+ try {
802
+ const { spawn } = require('child_process');
803
+ // Codex r7 P2: callers can request "unlimited" total timeout by
804
+ // passing 0 explicitly (autoresearch --time-budget 0 path).
805
+ // Distinguish missing (undefined → 2hr default) from explicit 0
806
+ // (no total timeout — only idle watchdog applies).
807
+ const totalTimeoutMs = opts.timeout === 0
808
+ ? null
809
+ : (typeof opts.timeout === 'number' ? opts.timeout : 120 * 60 * 1000);
810
+ const idleTimeoutMs = _resolveIdleTimeoutSeconds(backend, schedulerConfig) * 1000;
811
+ const MAX_BUFFER_BYTES = 50 * 1024 * 1024;
812
+ const result = await new Promise((resolve) => {
813
+ const isWindows = process.platform === 'win32';
814
+ const child = spawn(adapter.binary, args, {
815
+ cwd: opts.cwd || process.cwd(),
816
+ env: { ...process.env, ...envOverrides },
817
+ stdio: ['ignore', 'pipe', 'pipe'],
818
+ // Create a new process group on POSIX so we can signal children + grandchildren.
819
+ // Windows doesn't support process groups — fall back to default.
820
+ detached: !isWindows,
821
+ });
822
+ let stdoutBuf = '';
823
+ let stderrBuf = '';
824
+ let stdoutOverflowed = false;
825
+ let idleTimedOut = false;
826
+ let totalTimedOut = false;
827
+ let resolved = false;
828
+ // Track SIGKILL escalation timers so they can be cleared when the
829
+ // child exits, preventing stale kill signals to recycled PIDs (I2).
830
+ let idleKillTimer;
831
+ let totalKillTimer;
832
+ const safeResolve = (r) => {
833
+ if (resolved)
834
+ return;
835
+ resolved = true;
836
+ resolve(r);
837
+ };
838
+ const watchdog = _startIdleWatchdog(idleTimeoutMs, () => {
839
+ idleTimedOut = true;
840
+ incrementCounter('scheduler.idle_kills_total');
841
+ process.stderr.write(`[scheduler] spawn idle ${Math.round(idleTimeoutMs / 1000)}s, killing ${adapter.binary} (stateKey=${stateKey}, workItemId=${workItemId})\n`);
842
+ _killProcessTree(child, 'SIGTERM');
843
+ idleKillTimer = setTimeout(() => {
844
+ if (child.exitCode === null && child.signalCode === null) {
845
+ _killProcessTree(child, 'SIGKILL');
846
+ }
847
+ }, 5000);
848
+ });
849
+ const totalTimer = totalTimeoutMs === null
850
+ ? null
851
+ : setTimeout(() => {
852
+ totalTimedOut = true;
853
+ _killProcessTree(child, 'SIGTERM');
854
+ totalKillTimer = setTimeout(() => {
855
+ if (child.exitCode === null && child.signalCode === null) {
856
+ _killProcessTree(child, 'SIGKILL');
857
+ }
858
+ }, 5000);
859
+ }, totalTimeoutMs);
860
+ // Codex r44 P1 #6: detect live spinning subprocesses. The
861
+ // prior implementation only ran detectSpin on the captured
862
+ // stdout buffer in the `close` handler, so an actively-
863
+ // looping subprocess that keeps printing would never trigger
864
+ // until total timeout. Keep a rolling window of recent stdout
865
+ // chunks (each chunk = ~one streamed Claude output token block),
866
+ // and run detectSpin every SPIN_CHECK_EVERY chunks. If detected
867
+ // and not yet acted on, kill the subprocess so the parent sees
868
+ // the spinEvent and can react.
869
+ const SPIN_CHECK_WINDOW = 5;
870
+ const SPIN_CHECK_EVERY = 5;
871
+ const recentChunks = [];
872
+ let liveSpinEvent = null;
873
+ let chunksSinceCheck = 0;
874
+ child.stdout?.on('data', (chunk) => {
875
+ watchdog.markActivity();
876
+ if (stdoutBuf.length + chunk.length > MAX_BUFFER_BYTES) {
877
+ stdoutOverflowed = true;
878
+ return;
879
+ }
880
+ const text = chunk.toString('utf-8');
881
+ stdoutBuf += text;
882
+ recentChunks.push(text);
883
+ if (recentChunks.length > SPIN_CHECK_WINDOW)
884
+ recentChunks.shift();
885
+ chunksSinceCheck++;
886
+ if (!liveSpinEvent &&
887
+ recentChunks.length >= SPIN_CHECK_WINDOW &&
888
+ chunksSinceCheck >= SPIN_CHECK_EVERY) {
889
+ chunksSinceCheck = 0;
890
+ const evt = detectSpin(recentChunks);
891
+ if (evt.detected) {
892
+ liveSpinEvent = evt;
893
+ _killProcessTree(child, 'SIGTERM');
894
+ }
895
+ }
896
+ });
897
+ child.stderr?.on('data', (chunk) => {
898
+ watchdog.markActivity();
899
+ if (stderrBuf.length + chunk.length > MAX_BUFFER_BYTES)
900
+ return;
901
+ stderrBuf += chunk.toString('utf-8');
902
+ });
903
+ child.on('error', (err) => {
904
+ watchdog.stop();
905
+ if (totalTimer)
906
+ clearTimeout(totalTimer);
907
+ if (idleKillTimer)
908
+ clearTimeout(idleKillTimer);
909
+ if (totalKillTimer)
910
+ clearTimeout(totalKillTimer);
911
+ markComplete(state);
912
+ safeResolve({
913
+ exitCode: 1,
914
+ stdout: undefined,
915
+ stderr: err.message,
916
+ timedOut: false,
917
+ idleTimedOut: false,
918
+ backend: backend,
919
+ tokensUsed: 0,
920
+ workItemId,
921
+ });
922
+ });
923
+ child.on('close', (code) => {
924
+ watchdog.stop();
925
+ if (totalTimer)
926
+ clearTimeout(totalTimer);
927
+ if (idleKillTimer)
928
+ clearTimeout(idleKillTimer);
929
+ if (totalKillTimer)
930
+ clearTimeout(totalKillTimer);
931
+ const duration = Date.now() - startTime;
932
+ // Codex r45 P1 #6 followup: when we killed the subprocess
933
+ // because of a live spin detection, the process exits with
934
+ // code=null (SIGTERM). The prior `code ?? 0` fallback then
935
+ // reported success, so callers wrote SPIN-REPORT.md AND
936
+ // marked the phase completed. Treat a spin-kill as a
937
+ // failure exit so autopilot does not advance on a killed
938
+ // step.
939
+ const exitCode = code ?? (idleTimedOut || totalTimedOut || liveSpinEvent ? 1 : 0);
940
+ const tokens = adapter.parseTokenUsage(stderrBuf) ?? Math.round(duration * 10);
941
+ const sample = {
942
+ backend: backend,
943
+ stateKey,
944
+ agentType: opts.agentType, // M2: record per-agent type for complexity routing
945
+ timestamp: Date.now(),
946
+ duration,
947
+ tokenEstimate: tokens,
948
+ exitCode,
949
+ workItemId,
950
+ };
951
+ markComplete(state);
952
+ recordSample(state, sample, prediction.window_minutes, prediction.ewma_alpha);
953
+ // Periodic persistence: every 10 samples across all backends
954
+ const totalSamples = Array.from(states.values()).reduce((sum, s) => sum + s.samples.length, 0);
955
+ if (totalSamples % 10 === 0 && opts.cwd) {
956
+ const { join } = require('path');
957
+ scheduler.persistState(join(opts.cwd, '.planning'));
958
+ }
959
+ // Codex r15 P2 / r44 P1 #6: prefer the live spin event if
960
+ // detected during streaming (the process was killed for it);
961
+ // otherwise scan the final buffer for post-hoc detection.
962
+ let spinEvent = liveSpinEvent ?? undefined;
963
+ if (!spinEvent && stdoutBuf.length > 500) {
964
+ const chunkSize = Math.max(200, Math.floor(stdoutBuf.length / 12));
965
+ const chunks = [];
966
+ for (let i = 0; i < stdoutBuf.length; i += chunkSize) {
967
+ chunks.push(stdoutBuf.slice(i, i + chunkSize));
968
+ }
969
+ const evt = detectSpin(chunks);
970
+ if (evt.detected)
971
+ spinEvent = evt;
972
+ }
973
+ safeResolve({
974
+ exitCode,
975
+ stdout: opts.captureOutput && !stdoutOverflowed ? stdoutBuf : undefined,
976
+ stderr: stderrBuf || undefined,
977
+ timedOut: totalTimedOut,
978
+ idleTimedOut,
979
+ backend: backend,
980
+ tokensUsed: tokens,
981
+ workItemId,
982
+ spinEvent,
983
+ });
984
+ });
985
+ });
986
+ // Rate limit retry: if rate-limited despite prediction, cooldown and retry
987
+ if (adapter.isRateLimited(result.exitCode, result.stderr || '')) {
988
+ // Enforce a minimum 5-minute cooldown so a zero/missing window_minutes never skips the guard
989
+ state.cooldown_until = Date.now() + Math.max(prediction.window_minutes || 60, 5) * 60 * 1000;
990
+ // Max retry guard: cap recursive retries
991
+ if (retryCount >= maxRetries) {
992
+ return result; // Exhausted all retries, return last result
993
+ }
994
+ return _spawnWithRetry(prompt, opts, retryCount + 1);
995
+ }
996
+ return result;
997
+ }
998
+ catch (_err) {
999
+ markComplete(state);
1000
+ return {
1001
+ exitCode: 1,
1002
+ timedOut: false,
1003
+ backend: backend,
1004
+ tokensUsed: 0,
1005
+ workItemId,
1006
+ };
1007
+ }
1008
+ }
1009
+ const scheduler = {
1010
+ sessionKey,
1011
+ getState(stateKey) {
1012
+ return states.get(stateKey);
1013
+ },
1014
+ getStates() {
1015
+ return states;
1016
+ },
1017
+ recordExternalSample(stateKey, sample) {
1018
+ let state = states.get(stateKey);
1019
+ if (!state) {
1020
+ state = createBackendState(DEFAULT_BUDGET_TPM);
1021
+ states.set(stateKey, state);
1022
+ }
1023
+ recordSample(state, sample, prediction.window_minutes, prediction.ewma_alpha);
1024
+ },
1025
+ async spawn(prompt, opts) {
1026
+ return _spawnWithRetry(prompt, opts, 0);
1027
+ },
1028
+ persistState(planningDir) {
1029
+ const { writeFileSync } = require('fs');
1030
+ const { join } = require('path');
1031
+ const data = { version: 1, backends: {} };
1032
+ const backends = data.backends;
1033
+ for (const [key, state] of states) {
1034
+ backends[key] = {
1035
+ token_budget: state.token_budget,
1036
+ ewma_tokens_per_task: state.ewma_tokens_per_task,
1037
+ budget_learned: state.budget_learned,
1038
+ budget_confidence: state.budget_confidence,
1039
+ last_updated: Date.now(),
1040
+ };
1041
+ }
1042
+ writeFileSync(join(planningDir, 'scheduler-state.json'), JSON.stringify(data, null, 2) + '\n');
1043
+ },
1044
+ loadPersistedState(planningDir) {
1045
+ const { safeReadJSON, } = require('./utils');
1046
+ const { join } = require('path');
1047
+ const raw = safeReadJSON(join(planningDir, 'scheduler-state.json'));
1048
+ if (!raw || raw.version !== 1 || !raw.backends)
1049
+ return;
1050
+ for (const [key, saved] of Object.entries(raw.backends)) {
1051
+ const state = states.get(key);
1052
+ if (!state)
1053
+ continue;
1054
+ if (saved.budget_learned)
1055
+ state.token_budget = saved.token_budget;
1056
+ state.ewma_tokens_per_task = saved.ewma_tokens_per_task;
1057
+ state.budget_learned = saved.budget_learned;
1058
+ state.budget_confidence = saved.budget_confidence;
1059
+ }
1060
+ },
1061
+ };
1062
+ return scheduler;
1063
+ }
1064
+ /**
1065
+ * Analyze the last N stdout chunks from an agent run to detect spin (looping on
1066
+ * the same error without progress). Uses bigram overlap (Jaccard similarity) to
1067
+ * compare consecutive chunks.
1068
+ *
1069
+ * Returns SpinDetectedEvent with detected=true if 3+ consecutive pairs have
1070
+ * similarity >= threshold (default 0.80).
1071
+ *
1072
+ * @param chunks - Array of stdout text chunks (ordered chronologically)
1073
+ * @param threshold - Similarity threshold for spin detection (default 0.80)
1074
+ * @param windowSize - Number of recent chunks to analyze (default 5)
1075
+ */
1076
+ function detectSpin(chunks, threshold = 0.80, windowSize = 5) {
1077
+ const NO_SPIN = { detected: false, repeated_pattern: '', consecutive_count: 0, max_similarity: 0 };
1078
+ const recent = chunks.slice(-windowSize);
1079
+ if (recent.length < 3)
1080
+ return NO_SPIN;
1081
+ /** Compute bigram set from a normalized text chunk. */
1082
+ function bigrams(text) {
1083
+ const normalized = text.toLowerCase().replace(/[^a-z0-9\n]/g, ' ').replace(/\s+/g, ' ').trim();
1084
+ const set = new Set();
1085
+ for (let i = 0; i + 1 < normalized.length; i++) {
1086
+ set.add(normalized.slice(i, i + 2));
1087
+ }
1088
+ return set;
1089
+ }
1090
+ function jaccard(a, b) {
1091
+ if (a.size === 0 && b.size === 0)
1092
+ return 1;
1093
+ let intersection = 0;
1094
+ for (const t of a) {
1095
+ if (b.has(t))
1096
+ intersection++;
1097
+ }
1098
+ const union = a.size + b.size - intersection;
1099
+ return union === 0 ? 0 : intersection / union;
1100
+ }
1101
+ let consecutiveCount = 0;
1102
+ let maxSimilarity = 0;
1103
+ let repeatedPattern = '';
1104
+ for (let i = 1; i < recent.length; i++) {
1105
+ const sim = jaccard(bigrams(recent[i - 1]), bigrams(recent[i]));
1106
+ if (sim >= threshold) {
1107
+ consecutiveCount++;
1108
+ if (sim > maxSimilarity) {
1109
+ maxSimilarity = sim;
1110
+ repeatedPattern = recent[i].slice(0, 200);
1111
+ }
1112
+ }
1113
+ else {
1114
+ consecutiveCount = 0;
1115
+ }
1116
+ }
1117
+ if (consecutiveCount >= 2) {
1118
+ return { detected: true, repeated_pattern: repeatedPattern, consecutive_count: consecutiveCount + 1, max_similarity: Math.round(maxSimilarity * 100) / 100 };
1119
+ }
1120
+ return NO_SPIN;
1121
+ }
1122
+ module.exports = {
1123
+ ADAPTERS: exports.ADAPTERS,
1124
+ ENV_VAR_MAP: exports.ENV_VAR_MAP,
1125
+ FREE_FALLBACK_BUDGET: exports.FREE_FALLBACK_BUDGET,
1126
+ checkBinary,
1127
+ _checkBinary: checkBinary,
1128
+ createBackendState,
1129
+ updateEWMA,
1130
+ evictExpiredSamples,
1131
+ recordSample,
1132
+ pickBackend,
1133
+ resolveAccount,
1134
+ markInFlight,
1135
+ markComplete,
1136
+ createScheduler,
1137
+ computeSoonestRecovery,
1138
+ _anyPriorityHasHeadroom,
1139
+ _startIdleWatchdog,
1140
+ _resolveIdleTimeoutSeconds,
1141
+ _killProcessTree,
1142
+ isBudgetPressured,
1143
+ computeBudgetPressureLevel,
1144
+ logPressureTransition,
1145
+ detectSpin,
1146
+ };
1147
+ //# sourceMappingURL=scheduler.js.map