hungry-ghost-hive 0.3.0

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 (467) hide show
  1. package/LICENSE +111 -0
  2. package/README.md +352 -0
  3. package/dist/agents/base-agent.d.ts +63 -0
  4. package/dist/agents/base-agent.d.ts.map +1 -0
  5. package/dist/agents/base-agent.js +189 -0
  6. package/dist/agents/base-agent.js.map +1 -0
  7. package/dist/agents/index.d.ts +7 -0
  8. package/dist/agents/index.d.ts.map +1 -0
  9. package/dist/agents/index.js +7 -0
  10. package/dist/agents/index.js.map +1 -0
  11. package/dist/agents/intermediate.d.ts +15 -0
  12. package/dist/agents/intermediate.d.ts.map +1 -0
  13. package/dist/agents/intermediate.js +142 -0
  14. package/dist/agents/intermediate.js.map +1 -0
  15. package/dist/agents/junior.d.ts +15 -0
  16. package/dist/agents/junior.d.ts.map +1 -0
  17. package/dist/agents/junior.js +147 -0
  18. package/dist/agents/junior.js.map +1 -0
  19. package/dist/agents/qa.d.ts +23 -0
  20. package/dist/agents/qa.d.ts.map +1 -0
  21. package/dist/agents/qa.js +238 -0
  22. package/dist/agents/qa.js.map +1 -0
  23. package/dist/agents/senior.d.ts +18 -0
  24. package/dist/agents/senior.d.ts.map +1 -0
  25. package/dist/agents/senior.js +267 -0
  26. package/dist/agents/senior.js.map +1 -0
  27. package/dist/agents/tech-lead.d.ts +17 -0
  28. package/dist/agents/tech-lead.d.ts.map +1 -0
  29. package/dist/agents/tech-lead.js +274 -0
  30. package/dist/agents/tech-lead.js.map +1 -0
  31. package/dist/cli/commands/add-repo.d.ts +3 -0
  32. package/dist/cli/commands/add-repo.d.ts.map +1 -0
  33. package/dist/cli/commands/add-repo.js +84 -0
  34. package/dist/cli/commands/add-repo.js.map +1 -0
  35. package/dist/cli/commands/agents.d.ts +3 -0
  36. package/dist/cli/commands/agents.d.ts.map +1 -0
  37. package/dist/cli/commands/agents.js +214 -0
  38. package/dist/cli/commands/agents.js.map +1 -0
  39. package/dist/cli/commands/assign.d.ts +3 -0
  40. package/dist/cli/commands/assign.d.ts.map +1 -0
  41. package/dist/cli/commands/assign.js +81 -0
  42. package/dist/cli/commands/assign.js.map +1 -0
  43. package/dist/cli/commands/config.d.ts +3 -0
  44. package/dist/cli/commands/config.d.ts.map +1 -0
  45. package/dist/cli/commands/config.js +118 -0
  46. package/dist/cli/commands/config.js.map +1 -0
  47. package/dist/cli/commands/escalations.d.ts +3 -0
  48. package/dist/cli/commands/escalations.d.ts.map +1 -0
  49. package/dist/cli/commands/escalations.js +157 -0
  50. package/dist/cli/commands/escalations.js.map +1 -0
  51. package/dist/cli/commands/index.d.ts +17 -0
  52. package/dist/cli/commands/index.d.ts.map +1 -0
  53. package/dist/cli/commands/index.js +17 -0
  54. package/dist/cli/commands/index.js.map +1 -0
  55. package/dist/cli/commands/init.d.ts +3 -0
  56. package/dist/cli/commands/init.d.ts.map +1 -0
  57. package/dist/cli/commands/init.js +59 -0
  58. package/dist/cli/commands/init.js.map +1 -0
  59. package/dist/cli/commands/manager.d.ts +3 -0
  60. package/dist/cli/commands/manager.d.ts.map +1 -0
  61. package/dist/cli/commands/manager.js +775 -0
  62. package/dist/cli/commands/manager.js.map +1 -0
  63. package/dist/cli/commands/manager.test.d.ts +2 -0
  64. package/dist/cli/commands/manager.test.d.ts.map +1 -0
  65. package/dist/cli/commands/manager.test.js +45 -0
  66. package/dist/cli/commands/manager.test.js.map +1 -0
  67. package/dist/cli/commands/msg.d.ts +3 -0
  68. package/dist/cli/commands/msg.d.ts.map +1 -0
  69. package/dist/cli/commands/msg.js +190 -0
  70. package/dist/cli/commands/msg.js.map +1 -0
  71. package/dist/cli/commands/my-stories.d.ts +3 -0
  72. package/dist/cli/commands/my-stories.d.ts.map +1 -0
  73. package/dist/cli/commands/my-stories.js +174 -0
  74. package/dist/cli/commands/my-stories.js.map +1 -0
  75. package/dist/cli/commands/nuke.d.ts +3 -0
  76. package/dist/cli/commands/nuke.d.ts.map +1 -0
  77. package/dist/cli/commands/nuke.js +189 -0
  78. package/dist/cli/commands/nuke.js.map +1 -0
  79. package/dist/cli/commands/pr.d.ts +3 -0
  80. package/dist/cli/commands/pr.d.ts.map +1 -0
  81. package/dist/cli/commands/pr.js +488 -0
  82. package/dist/cli/commands/pr.js.map +1 -0
  83. package/dist/cli/commands/req.d.ts +3 -0
  84. package/dist/cli/commands/req.d.ts.map +1 -0
  85. package/dist/cli/commands/req.js +212 -0
  86. package/dist/cli/commands/req.js.map +1 -0
  87. package/dist/cli/commands/resume.d.ts +3 -0
  88. package/dist/cli/commands/resume.d.ts.map +1 -0
  89. package/dist/cli/commands/resume.js +114 -0
  90. package/dist/cli/commands/resume.js.map +1 -0
  91. package/dist/cli/commands/status.d.ts +3 -0
  92. package/dist/cli/commands/status.d.ts.map +1 -0
  93. package/dist/cli/commands/status.js +259 -0
  94. package/dist/cli/commands/status.js.map +1 -0
  95. package/dist/cli/commands/stories.d.ts +3 -0
  96. package/dist/cli/commands/stories.d.ts.map +1 -0
  97. package/dist/cli/commands/stories.js +111 -0
  98. package/dist/cli/commands/stories.js.map +1 -0
  99. package/dist/cli/commands/teams.d.ts +3 -0
  100. package/dist/cli/commands/teams.d.ts.map +1 -0
  101. package/dist/cli/commands/teams.js +137 -0
  102. package/dist/cli/commands/teams.js.map +1 -0
  103. package/dist/cli/dashboard/index.d.ts +5 -0
  104. package/dist/cli/dashboard/index.d.ts.map +1 -0
  105. package/dist/cli/dashboard/index.js +128 -0
  106. package/dist/cli/dashboard/index.js.map +1 -0
  107. package/dist/cli/dashboard/panels/activity.d.ts +5 -0
  108. package/dist/cli/dashboard/panels/activity.d.ts.map +1 -0
  109. package/dist/cli/dashboard/panels/activity.js +64 -0
  110. package/dist/cli/dashboard/panels/activity.js.map +1 -0
  111. package/dist/cli/dashboard/panels/agents.d.ts +5 -0
  112. package/dist/cli/dashboard/panels/agents.d.ts.map +1 -0
  113. package/dist/cli/dashboard/panels/agents.js +196 -0
  114. package/dist/cli/dashboard/panels/agents.js.map +1 -0
  115. package/dist/cli/dashboard/panels/escalations.d.ts +5 -0
  116. package/dist/cli/dashboard/panels/escalations.d.ts.map +1 -0
  117. package/dist/cli/dashboard/panels/escalations.js +93 -0
  118. package/dist/cli/dashboard/panels/escalations.js.map +1 -0
  119. package/dist/cli/dashboard/panels/merge-queue.d.ts +5 -0
  120. package/dist/cli/dashboard/panels/merge-queue.d.ts.map +1 -0
  121. package/dist/cli/dashboard/panels/merge-queue.js +57 -0
  122. package/dist/cli/dashboard/panels/merge-queue.js.map +1 -0
  123. package/dist/cli/dashboard/panels/pipeline.d.ts +5 -0
  124. package/dist/cli/dashboard/panels/pipeline.d.ts.map +1 -0
  125. package/dist/cli/dashboard/panels/pipeline.js +54 -0
  126. package/dist/cli/dashboard/panels/pipeline.js.map +1 -0
  127. package/dist/cli/dashboard/panels/stories.d.ts +5 -0
  128. package/dist/cli/dashboard/panels/stories.d.ts.map +1 -0
  129. package/dist/cli/dashboard/panels/stories.js +79 -0
  130. package/dist/cli/dashboard/panels/stories.js.map +1 -0
  131. package/dist/cli-runtimes/claude.d.ts +8 -0
  132. package/dist/cli-runtimes/claude.d.ts.map +1 -0
  133. package/dist/cli-runtimes/claude.js +27 -0
  134. package/dist/cli-runtimes/claude.js.map +1 -0
  135. package/dist/cli-runtimes/codex.d.ts +8 -0
  136. package/dist/cli-runtimes/codex.d.ts.map +1 -0
  137. package/dist/cli-runtimes/codex.js +27 -0
  138. package/dist/cli-runtimes/codex.js.map +1 -0
  139. package/dist/cli-runtimes/gemini.d.ts +8 -0
  140. package/dist/cli-runtimes/gemini.d.ts.map +1 -0
  141. package/dist/cli-runtimes/gemini.js +29 -0
  142. package/dist/cli-runtimes/gemini.js.map +1 -0
  143. package/dist/cli-runtimes/index.d.ts +25 -0
  144. package/dist/cli-runtimes/index.d.ts.map +1 -0
  145. package/dist/cli-runtimes/index.js +48 -0
  146. package/dist/cli-runtimes/index.js.map +1 -0
  147. package/dist/cli-runtimes/index.test.d.ts +2 -0
  148. package/dist/cli-runtimes/index.test.d.ts.map +1 -0
  149. package/dist/cli-runtimes/index.test.js +216 -0
  150. package/dist/cli-runtimes/index.test.js.map +1 -0
  151. package/dist/cli-runtimes/types.d.ts +27 -0
  152. package/dist/cli-runtimes/types.d.ts.map +1 -0
  153. package/dist/cli-runtimes/types.js +2 -0
  154. package/dist/cli-runtimes/types.js.map +1 -0
  155. package/dist/config/index.d.ts +3 -0
  156. package/dist/config/index.d.ts.map +1 -0
  157. package/dist/config/index.js +3 -0
  158. package/dist/config/index.js.map +1 -0
  159. package/dist/config/loader.d.ts +11 -0
  160. package/dist/config/loader.d.ts.map +1 -0
  161. package/dist/config/loader.js +72 -0
  162. package/dist/config/loader.js.map +1 -0
  163. package/dist/config/schema.d.ts +660 -0
  164. package/dist/config/schema.d.ts.map +1 -0
  165. package/dist/config/schema.js +217 -0
  166. package/dist/config/schema.js.map +1 -0
  167. package/dist/config/schema.test.d.ts +2 -0
  168. package/dist/config/schema.test.d.ts.map +1 -0
  169. package/dist/config/schema.test.js +123 -0
  170. package/dist/config/schema.test.js.map +1 -0
  171. package/dist/context-files/generator.d.ts +32 -0
  172. package/dist/context-files/generator.d.ts.map +1 -0
  173. package/dist/context-files/generator.js +120 -0
  174. package/dist/context-files/generator.js.map +1 -0
  175. package/dist/context-files/index.d.ts +38 -0
  176. package/dist/context-files/index.d.ts.map +1 -0
  177. package/dist/context-files/index.js +76 -0
  178. package/dist/context-files/index.js.map +1 -0
  179. package/dist/context-files/index.test.d.ts +2 -0
  180. package/dist/context-files/index.test.d.ts.map +1 -0
  181. package/dist/context-files/index.test.js +265 -0
  182. package/dist/context-files/index.test.js.map +1 -0
  183. package/dist/context-files/templates.d.ts +19 -0
  184. package/dist/context-files/templates.d.ts.map +1 -0
  185. package/dist/context-files/templates.js +266 -0
  186. package/dist/context-files/templates.js.map +1 -0
  187. package/dist/db/client.d.ts +95 -0
  188. package/dist/db/client.d.ts.map +1 -0
  189. package/dist/db/client.js +343 -0
  190. package/dist/db/client.js.map +1 -0
  191. package/dist/db/lock.d.ts +25 -0
  192. package/dist/db/lock.d.ts.map +1 -0
  193. package/dist/db/lock.js +56 -0
  194. package/dist/db/lock.js.map +1 -0
  195. package/dist/db/lock.test.d.ts +2 -0
  196. package/dist/db/lock.test.d.ts.map +1 -0
  197. package/dist/db/lock.test.js +73 -0
  198. package/dist/db/lock.test.js.map +1 -0
  199. package/dist/db/queries/agents.d.ts +31 -0
  200. package/dist/db/queries/agents.d.ts.map +1 -0
  201. package/dist/db/queries/agents.js +76 -0
  202. package/dist/db/queries/agents.js.map +1 -0
  203. package/dist/db/queries/escalations.d.ts +29 -0
  204. package/dist/db/queries/escalations.d.ts.map +1 -0
  205. package/dist/db/queries/escalations.js +105 -0
  206. package/dist/db/queries/escalations.js.map +1 -0
  207. package/dist/db/queries/heartbeat.d.ts +20 -0
  208. package/dist/db/queries/heartbeat.d.ts.map +1 -0
  209. package/dist/db/queries/heartbeat.js +61 -0
  210. package/dist/db/queries/heartbeat.js.map +1 -0
  211. package/dist/db/queries/index.d.ts +8 -0
  212. package/dist/db/queries/index.d.ts.map +1 -0
  213. package/dist/db/queries/index.js +8 -0
  214. package/dist/db/queries/index.js.map +1 -0
  215. package/dist/db/queries/logs.d.ts +21 -0
  216. package/dist/db/queries/logs.d.ts.map +1 -0
  217. package/dist/db/queries/logs.js +72 -0
  218. package/dist/db/queries/logs.js.map +1 -0
  219. package/dist/db/queries/messages.d.ts +17 -0
  220. package/dist/db/queries/messages.d.ts.map +1 -0
  221. package/dist/db/queries/messages.js +22 -0
  222. package/dist/db/queries/messages.js.map +1 -0
  223. package/dist/db/queries/pull-requests.d.ts +33 -0
  224. package/dist/db/queries/pull-requests.d.ts.map +1 -0
  225. package/dist/db/queries/pull-requests.js +130 -0
  226. package/dist/db/queries/pull-requests.js.map +1 -0
  227. package/dist/db/queries/requirements.d.ts +22 -0
  228. package/dist/db/queries/requirements.d.ts.map +1 -0
  229. package/dist/db/queries/requirements.js +53 -0
  230. package/dist/db/queries/requirements.js.map +1 -0
  231. package/dist/db/queries/stories.d.ts +42 -0
  232. package/dist/db/queries/stories.d.ts.map +1 -0
  233. package/dist/db/queries/stories.js +163 -0
  234. package/dist/db/queries/stories.js.map +1 -0
  235. package/dist/db/queries/teams.d.ts +14 -0
  236. package/dist/db/queries/teams.d.ts.map +1 -0
  237. package/dist/db/queries/teams.js +24 -0
  238. package/dist/db/queries/teams.js.map +1 -0
  239. package/dist/git/branches.d.ts +52 -0
  240. package/dist/git/branches.d.ts.map +1 -0
  241. package/dist/git/branches.js +133 -0
  242. package/dist/git/branches.js.map +1 -0
  243. package/dist/git/github.d.ts +75 -0
  244. package/dist/git/github.d.ts.map +1 -0
  245. package/dist/git/github.js +162 -0
  246. package/dist/git/github.js.map +1 -0
  247. package/dist/git/index.d.ts +4 -0
  248. package/dist/git/index.d.ts.map +1 -0
  249. package/dist/git/index.js +4 -0
  250. package/dist/git/index.js.map +1 -0
  251. package/dist/git/submodules.d.ts +47 -0
  252. package/dist/git/submodules.d.ts.map +1 -0
  253. package/dist/git/submodules.js +115 -0
  254. package/dist/git/submodules.js.map +1 -0
  255. package/dist/index.d.ts +3 -0
  256. package/dist/index.d.ts.map +1 -0
  257. package/dist/index.js +62 -0
  258. package/dist/index.js.map +1 -0
  259. package/dist/llm/anthropic.d.ts +18 -0
  260. package/dist/llm/anthropic.d.ts.map +1 -0
  261. package/dist/llm/anthropic.js +111 -0
  262. package/dist/llm/anthropic.js.map +1 -0
  263. package/dist/llm/index.d.ts +6 -0
  264. package/dist/llm/index.d.ts.map +1 -0
  265. package/dist/llm/index.js +24 -0
  266. package/dist/llm/index.js.map +1 -0
  267. package/dist/llm/openai.d.ts +18 -0
  268. package/dist/llm/openai.d.ts.map +1 -0
  269. package/dist/llm/openai.js +103 -0
  270. package/dist/llm/openai.js.map +1 -0
  271. package/dist/llm/provider.d.ts +38 -0
  272. package/dist/llm/provider.d.ts.map +1 -0
  273. package/dist/llm/provider.js +17 -0
  274. package/dist/llm/provider.js.map +1 -0
  275. package/dist/orchestrator/index.d.ts +4 -0
  276. package/dist/orchestrator/index.d.ts.map +1 -0
  277. package/dist/orchestrator/index.js +4 -0
  278. package/dist/orchestrator/index.js.map +1 -0
  279. package/dist/orchestrator/scaler.d.ts +42 -0
  280. package/dist/orchestrator/scaler.d.ts.map +1 -0
  281. package/dist/orchestrator/scaler.js +154 -0
  282. package/dist/orchestrator/scaler.js.map +1 -0
  283. package/dist/orchestrator/scheduler.d.ts +90 -0
  284. package/dist/orchestrator/scheduler.d.ts.map +1 -0
  285. package/dist/orchestrator/scheduler.js +1003 -0
  286. package/dist/orchestrator/scheduler.js.map +1 -0
  287. package/dist/orchestrator/scheduler.test.d.ts +2 -0
  288. package/dist/orchestrator/scheduler.test.d.ts.map +1 -0
  289. package/dist/orchestrator/scheduler.test.js +242 -0
  290. package/dist/orchestrator/scheduler.test.js.map +1 -0
  291. package/dist/orchestrator/workflow.d.ts +18 -0
  292. package/dist/orchestrator/workflow.d.ts.map +1 -0
  293. package/dist/orchestrator/workflow.js +106 -0
  294. package/dist/orchestrator/workflow.js.map +1 -0
  295. package/dist/state-detectors/claude.d.ts +33 -0
  296. package/dist/state-detectors/claude.d.ts.map +1 -0
  297. package/dist/state-detectors/claude.js +237 -0
  298. package/dist/state-detectors/claude.js.map +1 -0
  299. package/dist/state-detectors/claude.test.d.ts +2 -0
  300. package/dist/state-detectors/claude.test.d.ts.map +1 -0
  301. package/dist/state-detectors/claude.test.js +127 -0
  302. package/dist/state-detectors/claude.test.js.map +1 -0
  303. package/dist/state-detectors/codex.d.ts +34 -0
  304. package/dist/state-detectors/codex.d.ts.map +1 -0
  305. package/dist/state-detectors/codex.js +233 -0
  306. package/dist/state-detectors/codex.js.map +1 -0
  307. package/dist/state-detectors/codex.test.d.ts +2 -0
  308. package/dist/state-detectors/codex.test.d.ts.map +1 -0
  309. package/dist/state-detectors/codex.test.js +85 -0
  310. package/dist/state-detectors/codex.test.js.map +1 -0
  311. package/dist/state-detectors/factory.d.ts +22 -0
  312. package/dist/state-detectors/factory.d.ts.map +1 -0
  313. package/dist/state-detectors/factory.js +37 -0
  314. package/dist/state-detectors/factory.js.map +1 -0
  315. package/dist/state-detectors/factory.test.d.ts +2 -0
  316. package/dist/state-detectors/factory.test.d.ts.map +1 -0
  317. package/dist/state-detectors/factory.test.js +44 -0
  318. package/dist/state-detectors/factory.test.js.map +1 -0
  319. package/dist/state-detectors/gemini.d.ts +34 -0
  320. package/dist/state-detectors/gemini.d.ts.map +1 -0
  321. package/dist/state-detectors/gemini.js +236 -0
  322. package/dist/state-detectors/gemini.js.map +1 -0
  323. package/dist/state-detectors/gemini.test.d.ts +2 -0
  324. package/dist/state-detectors/gemini.test.d.ts.map +1 -0
  325. package/dist/state-detectors/gemini.test.js +93 -0
  326. package/dist/state-detectors/gemini.test.js.map +1 -0
  327. package/dist/state-detectors/index.d.ts +20 -0
  328. package/dist/state-detectors/index.d.ts.map +1 -0
  329. package/dist/state-detectors/index.js +21 -0
  330. package/dist/state-detectors/index.js.map +1 -0
  331. package/dist/state-detectors/types.d.ts +67 -0
  332. package/dist/state-detectors/types.d.ts.map +1 -0
  333. package/dist/state-detectors/types.js +28 -0
  334. package/dist/state-detectors/types.js.map +1 -0
  335. package/dist/tmux/index.d.ts +2 -0
  336. package/dist/tmux/index.d.ts.map +1 -0
  337. package/dist/tmux/index.js +2 -0
  338. package/dist/tmux/index.js.map +1 -0
  339. package/dist/tmux/manager.d.ts +45 -0
  340. package/dist/tmux/manager.d.ts.map +1 -0
  341. package/dist/tmux/manager.js +252 -0
  342. package/dist/tmux/manager.js.map +1 -0
  343. package/dist/utils/claude-code-state.d.ts +46 -0
  344. package/dist/utils/claude-code-state.d.ts.map +1 -0
  345. package/dist/utils/claude-code-state.js +252 -0
  346. package/dist/utils/claude-code-state.js.map +1 -0
  347. package/dist/utils/cli-builder.d.ts +19 -0
  348. package/dist/utils/cli-builder.d.ts.map +1 -0
  349. package/dist/utils/cli-builder.js +58 -0
  350. package/dist/utils/cli-builder.js.map +1 -0
  351. package/dist/utils/cli-commands.d.ts +27 -0
  352. package/dist/utils/cli-commands.d.ts.map +1 -0
  353. package/dist/utils/cli-commands.js +69 -0
  354. package/dist/utils/cli-commands.js.map +1 -0
  355. package/dist/utils/index.d.ts +3 -0
  356. package/dist/utils/index.d.ts.map +1 -0
  357. package/dist/utils/index.js +3 -0
  358. package/dist/utils/index.js.map +1 -0
  359. package/dist/utils/logger.d.ts +13 -0
  360. package/dist/utils/logger.d.ts.map +1 -0
  361. package/dist/utils/logger.js +77 -0
  362. package/dist/utils/logger.js.map +1 -0
  363. package/dist/utils/paths.d.ts +17 -0
  364. package/dist/utils/paths.d.ts.map +1 -0
  365. package/dist/utils/paths.js +33 -0
  366. package/dist/utils/paths.js.map +1 -0
  367. package/dist/utils/timeout.d.ts +25 -0
  368. package/dist/utils/timeout.d.ts.map +1 -0
  369. package/dist/utils/timeout.js +57 -0
  370. package/dist/utils/timeout.js.map +1 -0
  371. package/package.json +78 -0
  372. package/src/agents/base-agent.ts +255 -0
  373. package/src/agents/index.ts +6 -0
  374. package/src/agents/intermediate.ts +161 -0
  375. package/src/agents/junior.ts +166 -0
  376. package/src/agents/qa.ts +272 -0
  377. package/src/agents/senior.ts +307 -0
  378. package/src/agents/tech-lead.ts +324 -0
  379. package/src/cli/commands/add-repo.ts +89 -0
  380. package/src/cli/commands/agents.ts +247 -0
  381. package/src/cli/commands/assign.ts +86 -0
  382. package/src/cli/commands/config.ts +121 -0
  383. package/src/cli/commands/escalations.ts +179 -0
  384. package/src/cli/commands/index.ts +16 -0
  385. package/src/cli/commands/init.ts +66 -0
  386. package/src/cli/commands/manager.test.ts +52 -0
  387. package/src/cli/commands/manager.ts +916 -0
  388. package/src/cli/commands/msg.ts +232 -0
  389. package/src/cli/commands/my-stories.ts +198 -0
  390. package/src/cli/commands/nuke.ts +223 -0
  391. package/src/cli/commands/pr.ts +559 -0
  392. package/src/cli/commands/req.ts +231 -0
  393. package/src/cli/commands/resume.ts +129 -0
  394. package/src/cli/commands/status.ts +284 -0
  395. package/src/cli/commands/stories.ts +131 -0
  396. package/src/cli/commands/teams.ts +158 -0
  397. package/src/cli/dashboard/index.ts +141 -0
  398. package/src/cli/dashboard/panels/activity.ts +77 -0
  399. package/src/cli/dashboard/panels/agents.ts +244 -0
  400. package/src/cli/dashboard/panels/escalations.ts +109 -0
  401. package/src/cli/dashboard/panels/merge-queue.ts +65 -0
  402. package/src/cli/dashboard/panels/pipeline.ts +65 -0
  403. package/src/cli/dashboard/panels/stories.ts +87 -0
  404. package/src/cli-runtimes/claude.ts +31 -0
  405. package/src/cli-runtimes/codex.ts +31 -0
  406. package/src/cli-runtimes/gemini.ts +33 -0
  407. package/src/cli-runtimes/index.test.ts +261 -0
  408. package/src/cli-runtimes/index.ts +52 -0
  409. package/src/cli-runtimes/types.ts +30 -0
  410. package/src/config/index.ts +2 -0
  411. package/src/config/loader.ts +89 -0
  412. package/src/config/schema.test.ts +135 -0
  413. package/src/config/schema.ts +238 -0
  414. package/src/context-files/generator.ts +132 -0
  415. package/src/context-files/index.test.ts +323 -0
  416. package/src/context-files/index.ts +102 -0
  417. package/src/context-files/templates.ts +279 -0
  418. package/src/db/client.ts +475 -0
  419. package/src/db/lock.test.ts +93 -0
  420. package/src/db/lock.ts +74 -0
  421. package/src/db/migrations/001-initial.sql +121 -0
  422. package/src/db/migrations/005-add-agent-heartbeat.sql +4 -0
  423. package/src/db/queries/agents.ts +113 -0
  424. package/src/db/queries/escalations.ts +140 -0
  425. package/src/db/queries/heartbeat.ts +92 -0
  426. package/src/db/queries/index.ts +7 -0
  427. package/src/db/queries/logs.ts +136 -0
  428. package/src/db/queries/messages.ts +38 -0
  429. package/src/db/queries/pull-requests.ts +170 -0
  430. package/src/db/queries/requirements.ts +81 -0
  431. package/src/db/queries/stories.ts +223 -0
  432. package/src/db/queries/teams.ts +39 -0
  433. package/src/git/branches.ts +186 -0
  434. package/src/git/github.ts +247 -0
  435. package/src/git/index.ts +3 -0
  436. package/src/git/submodules.ts +141 -0
  437. package/src/index.ts +93 -0
  438. package/src/llm/anthropic.ts +134 -0
  439. package/src/llm/index.ts +26 -0
  440. package/src/llm/openai.ts +125 -0
  441. package/src/llm/provider.ts +60 -0
  442. package/src/orchestrator/index.ts +3 -0
  443. package/src/orchestrator/scaler.ts +201 -0
  444. package/src/orchestrator/scheduler.test.ts +288 -0
  445. package/src/orchestrator/scheduler.ts +1130 -0
  446. package/src/orchestrator/workflow.ts +137 -0
  447. package/src/state-detectors/claude.test.ts +149 -0
  448. package/src/state-detectors/claude.ts +256 -0
  449. package/src/state-detectors/codex.test.ts +100 -0
  450. package/src/state-detectors/codex.ts +252 -0
  451. package/src/state-detectors/factory.test.ts +51 -0
  452. package/src/state-detectors/factory.ts +40 -0
  453. package/src/state-detectors/gemini.test.ts +110 -0
  454. package/src/state-detectors/gemini.ts +255 -0
  455. package/src/state-detectors/index.ts +25 -0
  456. package/src/state-detectors/types.ts +80 -0
  457. package/src/tmux/index.ts +1 -0
  458. package/src/tmux/manager.ts +310 -0
  459. package/src/types/sql.js.d.ts +34 -0
  460. package/src/utils/claude-code-state.ts +281 -0
  461. package/src/utils/cli-builder.ts +78 -0
  462. package/src/utils/cli-commands.ts +84 -0
  463. package/src/utils/index.ts +2 -0
  464. package/src/utils/logger.ts +93 -0
  465. package/src/utils/paths.ts +49 -0
  466. package/src/utils/timeout.ts +84 -0
  467. package/tsconfig.json +25 -0
@@ -0,0 +1,1003 @@
1
+ import { getPlannedStories, updateStory, getStoryPointsByTeam, getStoryDependencies } from '../db/queries/stories.js';
2
+ import { getAgentsByTeam, getAgentById, createAgent, updateAgent } from '../db/queries/agents.js';
3
+ import { getTeamById, getAllTeams } from '../db/queries/teams.js';
4
+ import { queryOne, queryAll } from '../db/client.js';
5
+ import { createLog } from '../db/queries/logs.js';
6
+ import { spawnTmuxSession, generateSessionName, isTmuxSessionRunning, sendToTmuxSession, startManager, isManagerRunning, getHiveSessions, waitForTmuxSessionReady, forceBypassMode, killTmuxSession } from '../tmux/manager.js';
7
+ import { getCliRuntimeBuilder } from '../cli-runtimes/index.js';
8
+ export class Scheduler {
9
+ db;
10
+ config;
11
+ constructor(db, config) {
12
+ this.db = db;
13
+ this.config = config;
14
+ }
15
+ /**
16
+ * Create a git worktree for an agent
17
+ * Returns the worktree path
18
+ */
19
+ async createWorktree(agentId, teamId, repoPath) {
20
+ const { execSync } = await import('child_process');
21
+ // Construct worktree path: repos/<team-id>-<agent-id>/
22
+ const worktreePath = `repos/${teamId}-${agentId}`;
23
+ const fullWorktreePath = `${this.config.rootDir}/${worktreePath}`;
24
+ const fullRepoPath = `${this.config.rootDir}/${repoPath}`;
25
+ // Branch name: agent/<agent-id>
26
+ const branchName = `agent/${agentId}`;
27
+ try {
28
+ // Create worktree from main branch
29
+ execSync(`git worktree add "${fullWorktreePath}" -b "${branchName}"`, {
30
+ cwd: fullRepoPath,
31
+ stdio: 'pipe',
32
+ });
33
+ }
34
+ catch (err) {
35
+ // If worktree or branch already exists, try to add without creating branch
36
+ try {
37
+ execSync(`git worktree add "${fullWorktreePath}" "${branchName}"`, {
38
+ cwd: fullRepoPath,
39
+ stdio: 'pipe',
40
+ });
41
+ }
42
+ catch {
43
+ // If that fails too, log and throw
44
+ throw new Error(`Failed to create worktree at ${fullWorktreePath}: ${err instanceof Error ? err.message : 'Unknown error'}`);
45
+ }
46
+ }
47
+ return worktreePath;
48
+ }
49
+ /**
50
+ * Remove a git worktree for an agent
51
+ */
52
+ async removeWorktree(worktreePath) {
53
+ if (!worktreePath)
54
+ return;
55
+ const { execSync } = await import('child_process');
56
+ const fullWorktreePath = `${this.config.rootDir}/${worktreePath}`;
57
+ try {
58
+ execSync(`git worktree remove "${fullWorktreePath}" --force`, {
59
+ cwd: this.config.rootDir,
60
+ stdio: 'pipe',
61
+ });
62
+ }
63
+ catch (err) {
64
+ // Log error but don't throw - worktree might already be removed
65
+ console.error(`Warning: Failed to remove worktree at ${fullWorktreePath}: ${err instanceof Error ? err.message : 'Unknown error'}`);
66
+ }
67
+ }
68
+ /**
69
+ * Build a dependency graph for stories
70
+ * Returns a map of story ID to its direct dependencies
71
+ */
72
+ buildDependencyGraph(stories) {
73
+ const graph = new Map();
74
+ const storyIds = new Set(stories.map(s => s.id));
75
+ // Initialize all stories in the graph
76
+ for (const story of stories) {
77
+ if (!graph.has(story.id)) {
78
+ graph.set(story.id, new Set());
79
+ }
80
+ }
81
+ // Add dependencies (only within the planned set; external deps handled by areDependenciesSatisfied)
82
+ for (const story of stories) {
83
+ const dependencies = getStoryDependencies(this.db, story.id);
84
+ for (const dep of dependencies) {
85
+ if (graph.has(story.id) && storyIds.has(dep.id)) {
86
+ graph.get(story.id).add(dep.id);
87
+ }
88
+ }
89
+ }
90
+ return graph;
91
+ }
92
+ /**
93
+ * Topological sort of stories based on dependencies
94
+ * Returns stories in order where dependencies come before dependents
95
+ * Returns null if circular dependency is detected
96
+ */
97
+ topologicalSort(stories) {
98
+ const graph = this.buildDependencyGraph(stories);
99
+ const storyMap = new Map(stories.map(s => [s.id, s]));
100
+ // Kahn's algorithm for topological sort
101
+ const inDegree = new Map();
102
+ const result = [];
103
+ // Calculate in-degrees: count how many dependencies each story has
104
+ for (const [storyId, dependencies] of graph.entries()) {
105
+ inDegree.set(storyId, dependencies.size);
106
+ }
107
+ // Find all nodes with in-degree 0 (no dependencies)
108
+ const queue = [];
109
+ for (const [storyId, degree] of inDegree.entries()) {
110
+ if (degree === 0) {
111
+ queue.push(storyId);
112
+ }
113
+ }
114
+ // Process queue using Kahn's algorithm
115
+ while (queue.length > 0) {
116
+ const storyId = queue.shift();
117
+ const story = storyMap.get(storyId);
118
+ if (story) {
119
+ result.push(story);
120
+ }
121
+ // For each story that depends on this one, reduce in-degree
122
+ for (const [otherStoryId, dependencies] of graph.entries()) {
123
+ if (dependencies.has(storyId)) {
124
+ const newDegree = (inDegree.get(otherStoryId) || 0) - 1;
125
+ inDegree.set(otherStoryId, newDegree);
126
+ if (newDegree === 0) {
127
+ queue.push(otherStoryId);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ // Check for circular dependencies
133
+ if (result.length !== stories.length) {
134
+ return null;
135
+ }
136
+ return result;
137
+ }
138
+ /**
139
+ * Check if a story's dependencies are satisfied
140
+ * A dependency is satisfied if it's completed (merged) or in progress (being worked on)
141
+ */
142
+ areDependenciesSatisfied(storyId) {
143
+ const dependencies = getStoryDependencies(this.db, storyId);
144
+ for (const dep of dependencies) {
145
+ // Check if dependency is in a terminal or in-progress state
146
+ if (dep.status !== 'merged' && dep.status !== 'pr_submitted' && dep.status !== 'in_progress' && dep.status !== 'review' && dep.status !== 'qa' && dep.status !== 'qa_failed') {
147
+ return false;
148
+ }
149
+ }
150
+ return true;
151
+ }
152
+ /**
153
+ * Select the agent with the least workload (queue-depth aware)
154
+ * Returns the agent with fewest active stories; breaks ties by creation order
155
+ */
156
+ selectAgentWithLeastWorkload(agents) {
157
+ let selectedAgent = agents[0];
158
+ let minWorkload = this.getAgentWorkload(selectedAgent.id);
159
+ for (let i = 1; i < agents.length; i++) {
160
+ const workload = this.getAgentWorkload(agents[i].id);
161
+ if (workload < minWorkload) {
162
+ minWorkload = workload;
163
+ selectedAgent = agents[i];
164
+ }
165
+ }
166
+ return selectedAgent;
167
+ }
168
+ /**
169
+ * Calculate queue depth for an agent (number of active stories)
170
+ */
171
+ getAgentWorkload(agentId) {
172
+ const activeStories = queryAll(this.db, `
173
+ SELECT * FROM stories
174
+ WHERE assigned_agent_id = ?
175
+ AND status IN ('in_progress', 'review', 'qa', 'qa_failed')
176
+ `, [agentId]);
177
+ return activeStories.length;
178
+ }
179
+ /**
180
+ * Assign planned stories to available agents
181
+ */
182
+ async assignStories() {
183
+ const plannedStories = getPlannedStories(this.db);
184
+ const errors = [];
185
+ let assigned = 0;
186
+ // Topological sort stories to respect dependencies
187
+ const sortedStories = this.topologicalSort(plannedStories);
188
+ if (sortedStories === null) {
189
+ errors.push('Circular dependency detected in planned stories');
190
+ return { assigned, errors };
191
+ }
192
+ // Group stories by team
193
+ const storiesByTeam = new Map();
194
+ for (const story of sortedStories) {
195
+ if (!story.team_id)
196
+ continue;
197
+ const existing = storiesByTeam.get(story.team_id) || [];
198
+ existing.push(story);
199
+ storiesByTeam.set(story.team_id, existing);
200
+ }
201
+ // Process each team
202
+ for (const [teamId, stories] of storiesByTeam) {
203
+ const team = getTeamById(this.db, teamId);
204
+ if (!team)
205
+ continue;
206
+ // Get available agents for this team
207
+ const agents = getAgentsByTeam(this.db, teamId)
208
+ .filter(a => a.status === 'idle' && a.type !== 'qa');
209
+ // Find or create a Senior for delegation
210
+ let senior = agents.find(a => a.type === 'senior');
211
+ if (!senior) {
212
+ try {
213
+ senior = await this.spawnSenior(teamId, team.name, team.repo_path);
214
+ }
215
+ catch (err) {
216
+ errors.push(`Failed to spawn Senior for team ${team.name}: ${err instanceof Error ? err.message : 'Unknown error'}`);
217
+ continue;
218
+ }
219
+ }
220
+ // Assign stories based on complexity
221
+ for (const story of stories) {
222
+ // Check if dependencies are satisfied before assigning
223
+ if (!this.areDependenciesSatisfied(story.id)) {
224
+ continue;
225
+ }
226
+ const complexity = story.complexity_score || 5;
227
+ let targetAgent;
228
+ if (complexity <= this.config.scaling.junior_max_complexity) {
229
+ // Assign to Junior with least workload
230
+ const juniors = agents.filter(a => a.type === 'junior' && a.status === 'idle');
231
+ targetAgent = juniors.length > 0 ? this.selectAgentWithLeastWorkload(juniors) : undefined;
232
+ if (!targetAgent) {
233
+ try {
234
+ targetAgent = await this.spawnJunior(teamId, team.name, team.repo_path);
235
+ }
236
+ catch {
237
+ // Fall back to Intermediate or Senior
238
+ const intermediates = agents.filter(a => a.type === 'intermediate' && a.status === 'idle');
239
+ targetAgent = intermediates.length > 0 ? this.selectAgentWithLeastWorkload(intermediates) : senior;
240
+ }
241
+ }
242
+ }
243
+ else if (complexity <= this.config.scaling.intermediate_max_complexity) {
244
+ // Assign to Intermediate with least workload
245
+ const intermediates = agents.filter(a => a.type === 'intermediate' && a.status === 'idle');
246
+ targetAgent = intermediates.length > 0 ? this.selectAgentWithLeastWorkload(intermediates) : undefined;
247
+ if (!targetAgent) {
248
+ try {
249
+ targetAgent = await this.spawnIntermediate(teamId, team.name, team.repo_path);
250
+ }
251
+ catch {
252
+ // Fall back to Senior
253
+ targetAgent = senior;
254
+ }
255
+ }
256
+ }
257
+ else {
258
+ // Senior handles directly
259
+ targetAgent = senior;
260
+ }
261
+ if (!targetAgent) {
262
+ errors.push(`No available agent for story ${story.id}`);
263
+ continue;
264
+ }
265
+ // Assign the story
266
+ updateStory(this.db, story.id, {
267
+ assignedAgentId: targetAgent.id,
268
+ status: 'in_progress',
269
+ });
270
+ updateAgent(this.db, targetAgent.id, {
271
+ status: 'working',
272
+ currentStoryId: story.id,
273
+ });
274
+ createLog(this.db, {
275
+ agentId: targetAgent.id,
276
+ storyId: story.id,
277
+ eventType: 'STORY_ASSIGNED',
278
+ message: `Assigned to ${targetAgent.type}`,
279
+ });
280
+ assigned++;
281
+ }
282
+ }
283
+ return { assigned, errors };
284
+ }
285
+ /**
286
+ * Get the next story to work on for a specific agent
287
+ */
288
+ getNextStoryForAgent(agentId) {
289
+ const agent = getAgentById(this.db, agentId);
290
+ if (!agent || !agent.team_id)
291
+ return null;
292
+ // Find an unassigned planned story for this team
293
+ const story = queryOne(this.db, `
294
+ SELECT * FROM stories
295
+ WHERE team_id = ?
296
+ AND status = 'planned'
297
+ AND assigned_agent_id IS NULL
298
+ ORDER BY story_points DESC, created_at
299
+ LIMIT 1
300
+ `, [agent.team_id]);
301
+ return story || null;
302
+ }
303
+ /**
304
+ * Check if scaling is needed based on workload
305
+ */
306
+ async checkScaling() {
307
+ const teams = getAllTeams(this.db);
308
+ for (const team of teams) {
309
+ const storyPoints = getStoryPointsByTeam(this.db, team.id);
310
+ const seniors = getAgentsByTeam(this.db, team.id).filter(a => a.type === 'senior' && a.status !== 'terminated');
311
+ // Calculate needed seniors
312
+ const seniorCapacity = this.config.scaling.senior_capacity;
313
+ const neededSeniors = Math.ceil(storyPoints / seniorCapacity);
314
+ const currentSeniors = seniors.length;
315
+ if (neededSeniors > currentSeniors) {
316
+ // Scale up
317
+ const toSpawn = neededSeniors - currentSeniors;
318
+ for (let i = 0; i < toSpawn; i++) {
319
+ try {
320
+ await this.spawnSenior(team.id, team.name, team.repo_path, currentSeniors + i + 1);
321
+ createLog(this.db, {
322
+ agentId: 'scheduler',
323
+ eventType: 'TEAM_SCALED_UP',
324
+ message: `Spawned additional Senior for team ${team.name}`,
325
+ metadata: { teamId: team.id, totalSeniors: currentSeniors + i + 1 },
326
+ });
327
+ }
328
+ catch {
329
+ // Log error but continue
330
+ }
331
+ }
332
+ }
333
+ }
334
+ }
335
+ /**
336
+ * Health check: sync agent status with actual tmux sessions
337
+ * Returns number of agents whose status was corrected
338
+ */
339
+ async healthCheck() {
340
+ const allAgents = queryAll(this.db, `
341
+ SELECT * FROM agents WHERE status != 'terminated'
342
+ `);
343
+ const liveSessions = await getHiveSessions();
344
+ const liveSessionNames = new Set(liveSessions.map(s => s.name));
345
+ let terminated = 0;
346
+ const revived = [];
347
+ for (const agent of allAgents) {
348
+ if (!agent.tmux_session)
349
+ continue;
350
+ const sessionAlive = liveSessionNames.has(agent.tmux_session);
351
+ // Only terminate if tmux session is actually dead
352
+ // Heartbeat staleness alone is not sufficient since Claude Code sessions
353
+ // don't have a mechanism to send heartbeats
354
+ if (!sessionAlive && agent.status !== 'terminated') {
355
+ // Remove worktree if exists
356
+ if (agent.worktree_path) {
357
+ await this.removeWorktree(agent.worktree_path);
358
+ }
359
+ updateAgent(this.db, agent.id, { status: 'terminated', currentStoryId: null, worktreePath: null });
360
+ createLog(this.db, {
361
+ agentId: agent.id,
362
+ eventType: 'AGENT_TERMINATED',
363
+ message: `Session ${agent.tmux_session} no longer running`,
364
+ });
365
+ terminated++;
366
+ // If agent was working on a story, mark it for reassignment
367
+ if (agent.current_story_id) {
368
+ updateStory(this.db, agent.current_story_id, {
369
+ status: 'planned',
370
+ assignedAgentId: null,
371
+ });
372
+ revived.push(agent.current_story_id);
373
+ }
374
+ }
375
+ }
376
+ return { terminated, revived };
377
+ }
378
+ /**
379
+ * Check merge queue and spawn QA agents if needed
380
+ * Scales QA agents based on pending work: 1 QA per 2-3 pending PRs, max 5
381
+ */
382
+ async checkMergeQueue() {
383
+ const teams = getAllTeams(this.db);
384
+ for (const team of teams) {
385
+ await this.scaleQAAgents(team.id, team.name, team.repo_path);
386
+ }
387
+ }
388
+ /**
389
+ * Scale QA agents based on pending work
390
+ * - Count stories with status 'pr_submitted' or 'qa'
391
+ * - Calculate needed QA agents: 1 QA per 2-3 pending PRs, max 5
392
+ * - Spawn QA agents in parallel with unique session names
393
+ * - Scale down excess QA agents when queue shrinks
394
+ */
395
+ async scaleQAAgents(teamId, teamName, repoPath) {
396
+ // Count pending QA work: stories in 'qa' or 'pr_submitted' status
397
+ const qaStories = queryAll(this.db, `
398
+ SELECT * FROM stories
399
+ WHERE team_id = ? AND status IN ('qa', 'pr_submitted')
400
+ `, [teamId]);
401
+ const pendingCount = qaStories.length;
402
+ // Calculate needed QA agents: 1 per 2-3 pending PRs, max 5
403
+ // If no pending work, scale down to 0 agents
404
+ const neededQAs = pendingCount > 0 ? Math.min(Math.ceil(pendingCount / 2.5), 5) : 0;
405
+ // Get currently active QA agents for this team
406
+ const activeQAs = getAgentsByTeam(this.db, teamId)
407
+ .filter(a => a.type === 'qa' && a.status !== 'terminated');
408
+ const currentQACount = activeQAs.length;
409
+ if (neededQAs > currentQACount) {
410
+ // Scale up: spawn additional QA agents in parallel
411
+ const toSpawn = neededQAs - currentQACount;
412
+ const spawnPromises = [];
413
+ for (let i = 0; i < toSpawn; i++) {
414
+ const index = currentQACount + i + 1;
415
+ spawnPromises.push(this.spawnQA(teamId, teamName, repoPath, index));
416
+ }
417
+ try {
418
+ await Promise.all(spawnPromises);
419
+ createLog(this.db, {
420
+ agentId: 'scheduler',
421
+ eventType: 'TEAM_SCALED_UP',
422
+ message: `Scaled QA agents for team ${teamName}: ${currentQACount} → ${neededQAs} (${pendingCount} pending stories)`,
423
+ metadata: { teamId, agentType: 'qa', previousCount: currentQACount, newCount: neededQAs, pendingCount },
424
+ });
425
+ }
426
+ catch (err) {
427
+ createLog(this.db, {
428
+ agentId: 'scheduler',
429
+ eventType: 'AGENT_SPAWNED',
430
+ status: 'error',
431
+ message: `Failed to scale QA agents for team ${teamName}: ${err instanceof Error ? err.message : 'Unknown error'}`,
432
+ metadata: { teamId, agentType: 'qa', error: String(err) },
433
+ });
434
+ }
435
+ }
436
+ else if (neededQAs < currentQACount) {
437
+ // Scale down: terminate excess QA agents
438
+ const toTerminate = currentQACount - neededQAs;
439
+ // Identify QA agents to terminate (remove the ones with highest indices first)
440
+ const sortedAgents = activeQAs.sort((a, b) => {
441
+ const aIndex = parseInt(a.id.split('-').pop() || '0', 10);
442
+ const bIndex = parseInt(b.id.split('-').pop() || '0', 10);
443
+ return bIndex - aIndex; // Descending order to remove highest indices first
444
+ });
445
+ const qaAgentsToTerminate = sortedAgents.slice(0, toTerminate);
446
+ try {
447
+ for (const agent of qaAgentsToTerminate) {
448
+ // Kill tmux session
449
+ if (agent.tmux_session) {
450
+ await killTmuxSession(agent.tmux_session);
451
+ }
452
+ // Remove worktree
453
+ if (agent.worktree_path) {
454
+ await this.removeWorktree(agent.worktree_path);
455
+ }
456
+ // Update database
457
+ updateAgent(this.db, agent.id, {
458
+ status: 'terminated',
459
+ currentStoryId: null,
460
+ worktreePath: null,
461
+ });
462
+ // Log the event
463
+ createLog(this.db, {
464
+ agentId: agent.id,
465
+ eventType: 'AGENT_TERMINATED',
466
+ message: 'QA agent scaled down due to reduced PR queue',
467
+ metadata: { teamId, agentType: 'qa', reason: 'queue_shrink', pendingCount },
468
+ });
469
+ }
470
+ createLog(this.db, {
471
+ agentId: 'scheduler',
472
+ eventType: 'TEAM_SCALED_DOWN',
473
+ message: `Scaled down QA agents for team ${teamName}: ${currentQACount} → ${neededQAs} (${pendingCount} pending stories)`,
474
+ metadata: { teamId, agentType: 'qa', previousCount: currentQACount, newCount: neededQAs, pendingCount },
475
+ });
476
+ }
477
+ catch (err) {
478
+ createLog(this.db, {
479
+ agentId: 'scheduler',
480
+ eventType: 'AGENT_TERMINATED',
481
+ status: 'error',
482
+ message: `Failed to scale down QA agents for team ${teamName}: ${err instanceof Error ? err.message : 'Unknown error'}`,
483
+ metadata: { teamId, agentType: 'qa', error: String(err) },
484
+ });
485
+ }
486
+ }
487
+ }
488
+ async spawnQA(teamId, teamName, repoPath, index = 1) {
489
+ const agent = createAgent(this.db, {
490
+ type: 'qa',
491
+ teamId,
492
+ model: 'sonnet',
493
+ });
494
+ // Create git worktree for this agent
495
+ const worktreePath = await this.createWorktree(agent.id, teamId, repoPath);
496
+ const workDir = `${this.config.rootDir}/${worktreePath}`;
497
+ const sessionName = generateSessionName('qa', teamName, index);
498
+ if (!await isTmuxSessionRunning(sessionName)) {
499
+ // Build CLI command using the configured runtime for QA agents
500
+ const cliTool = this.config.models.qa.cli_tool;
501
+ const model = 'sonnet';
502
+ const commandArgs = getCliRuntimeBuilder(cliTool).buildSpawnCommand(model);
503
+ const command = commandArgs.join(' ');
504
+ await spawnTmuxSession({
505
+ sessionName,
506
+ workDir,
507
+ command,
508
+ });
509
+ // Wait for Claude to be ready before sending prompt
510
+ await waitForTmuxSessionReady(sessionName);
511
+ // Force bypass permissions mode to enable autonomous work
512
+ await forceBypassMode(sessionName, 'claude');
513
+ const team = getTeamById(this.db, teamId);
514
+ const prompt = generateQAPrompt(teamName, team?.repo_url || '', worktreePath, sessionName);
515
+ await sendToTmuxSession(sessionName, prompt);
516
+ // Auto-start manager when spawning agents
517
+ await this.ensureManagerRunning();
518
+ }
519
+ updateAgent(this.db, agent.id, {
520
+ tmuxSession: sessionName,
521
+ status: 'working',
522
+ worktreePath,
523
+ });
524
+ return agent;
525
+ }
526
+ async ensureManagerRunning() {
527
+ if (!await isManagerRunning()) {
528
+ await startManager(60);
529
+ }
530
+ }
531
+ async spawnSenior(teamId, teamName, repoPath, index) {
532
+ const sessionName = generateSessionName('senior', teamName, index);
533
+ // Prevent creating duplicate agents on same tmux session
534
+ const existingSeniors = getAgentsByTeam(this.db, teamId).filter(a => a.type === 'senior');
535
+ const existingOnSession = existingSeniors.find(a => a.tmux_session === sessionName && a.status !== 'terminated');
536
+ if (existingOnSession && await isTmuxSessionRunning(sessionName)) {
537
+ return existingOnSession;
538
+ }
539
+ const agent = createAgent(this.db, {
540
+ type: 'senior',
541
+ teamId,
542
+ model: 'sonnet',
543
+ });
544
+ // Create git worktree for this agent
545
+ const worktreePath = await this.createWorktree(agent.id, teamId, repoPath);
546
+ const workDir = `${this.config.rootDir}/${worktreePath}`;
547
+ if (!await isTmuxSessionRunning(sessionName)) {
548
+ // Build CLI command using the configured runtime for Senior agents
549
+ const cliTool = this.config.models.senior.cli_tool;
550
+ const model = 'sonnet';
551
+ const commandArgs = getCliRuntimeBuilder(cliTool).buildSpawnCommand(model);
552
+ const command = commandArgs.join(' ');
553
+ await spawnTmuxSession({
554
+ sessionName,
555
+ workDir,
556
+ command,
557
+ });
558
+ // Wait for Claude to be ready before sending prompt
559
+ await waitForTmuxSessionReady(sessionName);
560
+ // Force bypass permissions mode to enable autonomous work
561
+ await forceBypassMode(sessionName, 'claude');
562
+ const team = getTeamById(this.db, teamId);
563
+ const stories = this.getTeamStories(teamId);
564
+ const prompt = generateSeniorPrompt(teamName, team?.repo_url || '', worktreePath, stories);
565
+ await sendToTmuxSession(sessionName, prompt);
566
+ // Auto-start manager when spawning agents
567
+ await this.ensureManagerRunning();
568
+ }
569
+ updateAgent(this.db, agent.id, {
570
+ tmuxSession: sessionName,
571
+ status: 'working',
572
+ worktreePath,
573
+ });
574
+ return agent;
575
+ }
576
+ async spawnIntermediate(teamId, teamName, repoPath) {
577
+ const existing = getAgentsByTeam(this.db, teamId).filter(a => a.type === 'intermediate');
578
+ const index = existing.length + 1;
579
+ const sessionName = generateSessionName('intermediate', teamName, index);
580
+ // Prevent creating duplicate agents on same tmux session
581
+ const existingOnSession = existing.find(a => a.tmux_session === sessionName && a.status !== 'terminated');
582
+ if (existingOnSession && await isTmuxSessionRunning(sessionName)) {
583
+ return existingOnSession;
584
+ }
585
+ const agent = createAgent(this.db, {
586
+ type: 'intermediate',
587
+ teamId,
588
+ model: 'haiku',
589
+ });
590
+ // Create git worktree for this agent
591
+ const worktreePath = await this.createWorktree(agent.id, teamId, repoPath);
592
+ const workDir = `${this.config.rootDir}/${worktreePath}`;
593
+ if (!await isTmuxSessionRunning(sessionName)) {
594
+ // Build CLI command using the configured runtime for Intermediate agents
595
+ const cliTool = this.config.models.intermediate.cli_tool;
596
+ const model = 'haiku';
597
+ const commandArgs = getCliRuntimeBuilder(cliTool).buildSpawnCommand(model);
598
+ const command = commandArgs.join(' ');
599
+ await spawnTmuxSession({
600
+ sessionName,
601
+ workDir,
602
+ command,
603
+ });
604
+ // Wait for Claude to be ready before sending prompt
605
+ await waitForTmuxSessionReady(sessionName);
606
+ // Force bypass permissions mode to enable autonomous work
607
+ await forceBypassMode(sessionName, 'claude');
608
+ const team = getTeamById(this.db, teamId);
609
+ const prompt = generateIntermediatePrompt(teamName, team?.repo_url || '', worktreePath, sessionName);
610
+ await sendToTmuxSession(sessionName, prompt);
611
+ // Auto-start manager when spawning agents
612
+ await this.ensureManagerRunning();
613
+ }
614
+ updateAgent(this.db, agent.id, {
615
+ tmuxSession: sessionName,
616
+ status: 'working',
617
+ worktreePath,
618
+ });
619
+ return agent;
620
+ }
621
+ async spawnJunior(teamId, teamName, repoPath) {
622
+ const existing = getAgentsByTeam(this.db, teamId).filter(a => a.type === 'junior');
623
+ const index = existing.length + 1;
624
+ const sessionName = generateSessionName('junior', teamName, index);
625
+ // Prevent creating duplicate agents on same tmux session
626
+ const existingOnSession = existing.find(a => a.tmux_session === sessionName && a.status !== 'terminated');
627
+ if (existingOnSession && await isTmuxSessionRunning(sessionName)) {
628
+ return existingOnSession;
629
+ }
630
+ const agent = createAgent(this.db, {
631
+ type: 'junior',
632
+ teamId,
633
+ model: 'haiku',
634
+ });
635
+ // Create git worktree for this agent
636
+ const worktreePath = await this.createWorktree(agent.id, teamId, repoPath);
637
+ const workDir = `${this.config.rootDir}/${worktreePath}`;
638
+ if (!await isTmuxSessionRunning(sessionName)) {
639
+ // Build CLI command using the configured runtime for Junior agents
640
+ // Note: Spec calls for gpt-4o-mini but using haiku until OpenAI integration is added
641
+ const cliTool = this.config.models.junior.cli_tool;
642
+ const model = 'haiku';
643
+ const commandArgs = getCliRuntimeBuilder(cliTool).buildSpawnCommand(model);
644
+ const command = commandArgs.join(' ');
645
+ await spawnTmuxSession({
646
+ sessionName,
647
+ workDir,
648
+ command,
649
+ });
650
+ // Wait for Claude to be ready before sending prompt
651
+ await waitForTmuxSessionReady(sessionName);
652
+ // Force bypass permissions mode to enable autonomous work
653
+ await forceBypassMode(sessionName, 'claude');
654
+ const team = getTeamById(this.db, teamId);
655
+ const prompt = generateJuniorPrompt(teamName, team?.repo_url || '', worktreePath, sessionName);
656
+ await sendToTmuxSession(sessionName, prompt);
657
+ // Auto-start manager when spawning agents
658
+ await this.ensureManagerRunning();
659
+ }
660
+ updateAgent(this.db, agent.id, {
661
+ tmuxSession: sessionName,
662
+ status: 'working',
663
+ worktreePath,
664
+ });
665
+ return agent;
666
+ }
667
+ getTeamStories(teamId) {
668
+ return queryAll(this.db, `
669
+ SELECT * FROM stories
670
+ WHERE team_id = ? AND status IN ('planned', 'estimated')
671
+ ORDER BY complexity_score DESC
672
+ `, [teamId]);
673
+ }
674
+ }
675
+ // Prompt generation functions
676
+ function generateSeniorPrompt(teamName, repoUrl, repoPath, stories) {
677
+ const storyList = stories.map(s => `- [${s.id}] ${s.title} (complexity: ${s.complexity_score || '?'})\n ${s.description}`).join('\n\n');
678
+ const sessionName = `hive-senior-${teamName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
679
+ return `You are a Senior Developer on Team ${teamName}.
680
+ Your tmux session: ${sessionName}
681
+
682
+ ## Your Repository
683
+ - Local path: ${repoPath}
684
+ - Remote: ${repoUrl}
685
+
686
+ ## Your Responsibilities
687
+ 1. Implement assigned stories
688
+ 2. Review code quality
689
+ 3. Delegate simpler tasks to Intermediate/Junior developers
690
+ 4. Ensure tests pass and code meets standards
691
+
692
+ ## Pending Stories for Your Team
693
+ ${storyList || 'No stories assigned yet.'}
694
+
695
+ ## Finding Your Stories
696
+ Check your assigned stories:
697
+ \`\`\`bash
698
+ hive my-stories ${sessionName}
699
+ \`\`\`
700
+
701
+ See all team stories:
702
+ \`\`\`bash
703
+ hive my-stories ${sessionName} --all
704
+ \`\`\`
705
+
706
+ Claim a story:
707
+ \`\`\`bash
708
+ hive my-stories claim <story-id> --session ${sessionName}
709
+ \`\`\`
710
+
711
+ Mark story complete:
712
+ \`\`\`bash
713
+ hive my-stories complete <story-id>
714
+ \`\`\`
715
+
716
+ ## Workflow
717
+ 1. Run \`hive my-stories ${sessionName}\` to see your assigned work
718
+ 2. Create a feature branch: \`git checkout -b feature/<story-id>-<short-description>\`
719
+ 3. Implement the changes
720
+ 4. Run tests and linting
721
+ 5. Commit with a clear message referencing the story ID
722
+ 6. Create a PR using \`gh pr create\`
723
+ 7. Submit to merge queue for QA review:
724
+ \`\`\`bash
725
+ hive pr submit -b feature/<story-id>-<description> -s <story-id> --from ${sessionName}
726
+ \`\`\`
727
+
728
+ ## Submitting PRs
729
+ After creating your GitHub PR, submit it to the merge queue:
730
+ \`\`\`bash
731
+ gh pr create --title "Story <story-id>: <title>" --body "..."
732
+ hive pr submit -b <branch-name> -s <story-id> --pr-url <github-pr-url> --from ${sessionName}
733
+ \`\`\`
734
+
735
+ Check your PR status:
736
+ \`\`\`bash
737
+ hive pr queue
738
+ \`\`\`
739
+
740
+ ## Communication with Tech Lead
741
+ If you have questions or need guidance, message the Tech Lead:
742
+ \`\`\`bash
743
+ hive msg send hive-tech-lead "Your question here" --from ${sessionName}
744
+ \`\`\`
745
+
746
+ Check for replies:
747
+ \`\`\`bash
748
+ hive msg outbox ${sessionName}
749
+ \`\`\`
750
+
751
+ ## Guidelines
752
+ - Follow existing code patterns in the repository
753
+ - Write tests for new functionality
754
+ - Keep commits atomic and well-documented
755
+ - Message the Tech Lead if blocked or need clarification
756
+
757
+ ## IMPORTANT: Autonomous Workflow
758
+ You are an autonomous agent. DO NOT ask "Is there anything else?" or wait for instructions.
759
+ After completing a story:
760
+ 1. Run \`hive my-stories ${sessionName}\` to get your next assignment
761
+ 2. If no stories assigned, run \`hive my-stories ${sessionName} --all\` to see available work
762
+ 3. Claim available work with \`hive my-stories claim <story-id> --session ${sessionName}\`
763
+ 4. ALWAYS submit PRs to hive after creating them on GitHub:
764
+ \`hive pr submit -b <branch> -s <story-id> --pr-url <github-url> --from ${sessionName}\`
765
+
766
+ Start by exploring the codebase to understand its structure, then begin working on the highest priority story.`;
767
+ }
768
+ function generateIntermediatePrompt(teamName, repoUrl, repoPath, sessionName) {
769
+ const seniorSession = `hive-senior-${teamName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
770
+ return `You are an Intermediate Developer on Team ${teamName}.
771
+ Your tmux session: ${sessionName}
772
+
773
+ ## Your Repository
774
+ - Local path: ${repoPath}
775
+ - Remote: ${repoUrl}
776
+
777
+ ## Your Responsibilities
778
+ 1. Implement assigned stories (moderate complexity)
779
+ 2. Write clean, tested code
780
+ 3. Follow team coding standards
781
+ 4. Ask Senior for help if stuck
782
+
783
+ ## Finding Your Stories
784
+ Check your assigned stories:
785
+ \`\`\`bash
786
+ hive my-stories ${sessionName}
787
+ \`\`\`
788
+
789
+ Claim a story:
790
+ \`\`\`bash
791
+ hive my-stories claim <story-id> --session ${sessionName}
792
+ \`\`\`
793
+
794
+ Mark story complete:
795
+ \`\`\`bash
796
+ hive my-stories complete <story-id>
797
+ \`\`\`
798
+
799
+ ## Workflow
800
+ 1. Run \`hive my-stories ${sessionName}\` to see your assigned work
801
+ 2. Create a feature branch: \`git checkout -b feature/<story-id>-<description>\`
802
+ 3. Implement the changes
803
+ 4. Run tests and linting
804
+ 5. Commit and create a PR using \`gh pr create\`
805
+ 6. Submit to merge queue:
806
+ \`\`\`bash
807
+ hive pr submit -b <branch-name> -s <story-id> --from ${sessionName}
808
+ \`\`\`
809
+
810
+ ## Submitting PRs
811
+ After creating your GitHub PR:
812
+ \`\`\`bash
813
+ gh pr create --title "Story <story-id>: <title>" --body "..."
814
+ hive pr submit -b <branch-name> -s <story-id> --pr-url <github-pr-url> --from ${sessionName}
815
+ \`\`\`
816
+
817
+ ## Communication
818
+ If you have questions, message your Senior or the Tech Lead:
819
+ \`\`\`bash
820
+ hive msg send ${seniorSession} "Your question" --from ${sessionName}
821
+ hive msg send hive-tech-lead "Your question" --from ${sessionName}
822
+ \`\`\`
823
+
824
+ Check for replies:
825
+ \`\`\`bash
826
+ hive msg outbox ${sessionName}
827
+ \`\`\`
828
+
829
+ ## Guidelines
830
+ - Follow existing code patterns
831
+ - Write tests for your changes
832
+ - Keep commits focused and clear
833
+ - Message Senior or Tech Lead if blocked
834
+
835
+ ## IMPORTANT: Autonomous Workflow
836
+ You are an autonomous agent. DO NOT ask "Is there anything else?" or wait for instructions.
837
+ After completing a story:
838
+ 1. Run \`hive my-stories ${sessionName}\` to get your next assignment
839
+ 2. If no stories assigned, run \`hive my-stories ${sessionName} --all\` to see available work
840
+ 3. Claim available work with \`hive my-stories claim <story-id> --session ${sessionName}\`
841
+ 4. ALWAYS submit PRs to hive after creating them on GitHub:
842
+ \`hive pr submit -b <branch> -s <story-id> --pr-url <github-url> --from ${sessionName}\`
843
+
844
+ Start by exploring the codebase, then run \`hive my-stories ${sessionName}\` to see your assignments.`;
845
+ }
846
+ function generateJuniorPrompt(teamName, repoUrl, repoPath, sessionName) {
847
+ const seniorSession = `hive-senior-${teamName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
848
+ return `You are a Junior Developer on Team ${teamName}.
849
+ Your tmux session: ${sessionName}
850
+
851
+ ## Your Repository
852
+ - Local path: ${repoPath}
853
+ - Remote: ${repoUrl}
854
+
855
+ ## Your Responsibilities
856
+ 1. Implement simple, well-defined stories
857
+ 2. Learn the codebase patterns
858
+ 3. Write tests for your changes
859
+ 4. Ask for help when needed
860
+
861
+ ## Finding Your Stories
862
+ Check your assigned stories:
863
+ \`\`\`bash
864
+ hive my-stories ${sessionName}
865
+ \`\`\`
866
+
867
+ Claim a story:
868
+ \`\`\`bash
869
+ hive my-stories claim <story-id> --session ${sessionName}
870
+ \`\`\`
871
+
872
+ Mark story complete:
873
+ \`\`\`bash
874
+ hive my-stories complete <story-id>
875
+ \`\`\`
876
+
877
+ ## Workflow
878
+ 1. Run \`hive my-stories ${sessionName}\` to see your assigned work
879
+ 2. Create a feature branch: \`git checkout -b feature/<story-id>-<description>\`
880
+ 3. Implement the changes carefully
881
+ 4. Run tests before committing
882
+ 5. Commit and create a PR using \`gh pr create\`
883
+ 6. Submit to merge queue:
884
+ \`\`\`bash
885
+ hive pr submit -b <branch-name> -s <story-id> --from ${sessionName}
886
+ \`\`\`
887
+
888
+ ## Submitting PRs
889
+ After creating your GitHub PR:
890
+ \`\`\`bash
891
+ gh pr create --title "Story <story-id>: <title>" --body "..."
892
+ hive pr submit -b <branch-name> -s <story-id> --pr-url <github-pr-url> --from ${sessionName}
893
+ \`\`\`
894
+
895
+ ## Communication
896
+ If you have questions, message your Senior or the Tech Lead:
897
+ \`\`\`bash
898
+ hive msg send ${seniorSession} "Your question" --from ${sessionName}
899
+ hive msg send hive-tech-lead "Your question" --from ${sessionName}
900
+ \`\`\`
901
+
902
+ Check for replies:
903
+ \`\`\`bash
904
+ hive msg outbox ${sessionName}
905
+ \`\`\`
906
+
907
+ ## Guidelines
908
+ - Follow existing patterns exactly
909
+ - Ask questions if requirements are unclear
910
+ - Test thoroughly before submitting
911
+ - Keep changes small and focused
912
+
913
+ ## IMPORTANT: Autonomous Workflow
914
+ You are an autonomous agent. DO NOT ask "Is there anything else?" or wait for instructions.
915
+ After completing a story:
916
+ 1. Run \`hive my-stories ${sessionName}\` to get your next assignment
917
+ 2. If no stories assigned, run \`hive my-stories ${sessionName} --all\` to see available work
918
+ 3. Claim available work with \`hive my-stories claim <story-id> --session ${sessionName}\`
919
+ 4. ALWAYS submit PRs to hive after creating them on GitHub:
920
+ \`hive pr submit -b <branch> -s <story-id> --pr-url <github-url> --from ${sessionName}\`
921
+
922
+ Start by exploring the codebase to understand how things work, then run \`hive my-stories ${sessionName}\` to see your assignments.`;
923
+ }
924
+ function generateQAPrompt(teamName, repoUrl, repoPath, sessionName) {
925
+ return `You are a QA Engineer on Team ${teamName}.
926
+ Your tmux session: ${sessionName}
927
+
928
+ ## Your Repository
929
+ - Local path: ${repoPath}
930
+ - Remote: ${repoUrl}
931
+
932
+ ## Your Responsibilities
933
+ 1. Review PRs in the merge queue
934
+ 2. Check for merge conflicts
935
+ 3. Run tests and verify functionality
936
+ 4. Check code quality and standards
937
+ 5. Approve and merge good PRs
938
+ 6. Reject PRs that need fixes
939
+
940
+ ## Merge Queue Workflow
941
+
942
+ ### Check the merge queue:
943
+ \`\`\`bash
944
+ hive pr queue
945
+ \`\`\`
946
+
947
+ ### Claim the next PR for review:
948
+ \`\`\`bash
949
+ hive pr review --from ${sessionName}
950
+ \`\`\`
951
+
952
+ ### View PR details:
953
+ \`\`\`bash
954
+ hive pr show <pr-id>
955
+ \`\`\`
956
+
957
+ ### After reviewing:
958
+
959
+ **If the PR is good - approve and merge:**
960
+ \`\`\`bash
961
+ # First, merge via GitHub CLI
962
+ gh pr merge <pr-number> --merge
963
+
964
+ # Then mark as merged in Hive
965
+ hive pr approve <pr-id> --from ${sessionName}
966
+ \`\`\`
967
+
968
+ **If the PR has issues - reject with feedback:**
969
+ \`\`\`bash
970
+ hive pr reject <pr-id> --reason "Description of issues" --from ${sessionName}
971
+
972
+ # Notify the developer
973
+ hive msg send <developer-session> "Your PR was rejected: <reason>" --from ${sessionName}
974
+ \`\`\`
975
+
976
+ ## Review Checklist
977
+ For each PR, verify:
978
+ 1. **No merge conflicts** - Check with \`git fetch && git merge --no-commit origin/main\`
979
+ 2. **Tests pass** - Run the project's test suite
980
+ 3. **Code quality** - Check for code standards, no obvious bugs
981
+ 4. **Functionality** - Test that the changes work as expected
982
+ 5. **Story requirements** - Verify acceptance criteria are met
983
+
984
+ ## Communication
985
+ If you need clarification from a developer:
986
+ \`\`\`bash
987
+ hive msg send <developer-session> "Your question" --from ${sessionName}
988
+ \`\`\`
989
+
990
+ Check for replies:
991
+ \`\`\`bash
992
+ hive msg outbox ${sessionName}
993
+ \`\`\`
994
+
995
+ ## Guidelines
996
+ - Review PRs in queue order (first in, first out)
997
+ - Be thorough but efficient
998
+ - Provide clear feedback when rejecting
999
+ - Ensure main branch stays stable
1000
+
1001
+ Start by running \`hive pr queue\` to see PRs waiting for review.`;
1002
+ }
1003
+ //# sourceMappingURL=scheduler.js.map