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,775 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { findHiveRoot, getHivePaths } from '../../utils/paths.js';
4
+ import { getDatabase } from '../../db/client.js';
5
+ import { loadConfig } from '../../config/loader.js';
6
+ import { Scheduler } from '../../orchestrator/scheduler.js';
7
+ import { getHiveSessions, sendToTmuxSession, sendEnterToTmuxSession, captureTmuxPane, isManagerRunning, stopManager as stopManagerSession, killTmuxSession } from '../../tmux/manager.js';
8
+ import { getMergeQueue, getPullRequestsByStatus, getApprovedPullRequests, updatePullRequest } from '../../db/queries/pull-requests.js';
9
+ import { getUnreadMessages, markMessageRead } from '../../db/queries/messages.js';
10
+ import { createEscalation, getPendingEscalations } from '../../db/queries/escalations.js';
11
+ import { getAgentById, updateAgent } from '../../db/queries/agents.js';
12
+ import { createLog } from '../../db/queries/logs.js';
13
+ import { queryAll } from '../../db/client.js';
14
+ import { getAllTeams } from '../../db/queries/teams.js';
15
+ import { updateStory, getStoriesByStatus } from '../../db/queries/stories.js';
16
+ import { execa } from 'execa';
17
+ import { createPullRequest } from '../../db/queries/pull-requests.js';
18
+ import { acquireLock } from '../../db/lock.js';
19
+ import { join } from 'path';
20
+ import { detectClaudeCodeState, getStateDescription, ClaudeCodeState } from '../../utils/claude-code-state.js';
21
+ import { getAvailableCommands, buildAutoRecoveryReminder } from '../../utils/cli-commands.js';
22
+ // In-memory state tracking per agent session
23
+ const agentStates = new Map();
24
+ // Constants for nudge behavior
25
+ const STATE_STUCK_THRESHOLD_MS = 120000; // 120 seconds = 2 minutes
26
+ const NUDGE_COOLDOWN_MS = 300000; // 300 seconds = 5 minutes
27
+ export const managerCommand = new Command('manager')
28
+ .description('Micromanager daemon that keeps agents productive');
29
+ // Start the manager daemon
30
+ managerCommand
31
+ .command('start')
32
+ .description('Start the manager daemon (runs every 60s)')
33
+ .option('-i, --interval <seconds>', 'Check interval in seconds', '60')
34
+ .option('--once', 'Run once and exit')
35
+ .action(async (options) => {
36
+ const root = findHiveRoot();
37
+ if (!root) {
38
+ console.error(chalk.red('Not in a Hive workspace.'));
39
+ process.exit(1);
40
+ }
41
+ const paths = getHivePaths(root);
42
+ const lockPath = join(paths.hiveDir, 'manager.lock');
43
+ // Acquire manager lock to ensure singleton
44
+ let releaseLock = null;
45
+ try {
46
+ releaseLock = await acquireLock(lockPath, { stale: 120000 }); // 2 min stale threshold
47
+ console.log(chalk.gray('Manager lock acquired'));
48
+ }
49
+ catch (err) {
50
+ console.error(chalk.red('Failed to acquire manager lock - another manager instance may be running.'), err);
51
+ console.error(chalk.gray('If you are sure no other manager is running, remove:'), lockPath + '.lock');
52
+ process.exit(1);
53
+ }
54
+ // Release lock on exit
55
+ const cleanup = async () => {
56
+ if (releaseLock) {
57
+ await releaseLock();
58
+ console.log(chalk.gray('\nManager lock released'));
59
+ }
60
+ process.exit(0);
61
+ };
62
+ process.on('SIGINT', cleanup);
63
+ process.on('SIGTERM', cleanup);
64
+ // Load config to get polling intervals (for two-tier polling)
65
+ const hivePaths = getHivePaths(root);
66
+ const config = loadConfig(hivePaths.hiveDir);
67
+ // Support two modes: legacy single-interval and new two-tier polling
68
+ const useTwoTier = options.interval === '60' && config.manager;
69
+ if (useTwoTier) {
70
+ // Two-tier polling - use slow interval (60s) by default to reduce interruptions
71
+ const slowInterval = config.manager.slow_poll_interval;
72
+ console.log(chalk.cyan(`Manager started (polling every ${slowInterval / 1000}s)`));
73
+ console.log(chalk.gray('Press Ctrl+C to stop\n'));
74
+ const runCheck = async () => {
75
+ try {
76
+ await managerCheck(root);
77
+ }
78
+ catch (err) {
79
+ console.error(chalk.red('Manager error:'), err);
80
+ }
81
+ };
82
+ await runCheck();
83
+ if (!options.once) {
84
+ setInterval(runCheck, slowInterval);
85
+ }
86
+ else if (releaseLock) {
87
+ await releaseLock();
88
+ }
89
+ }
90
+ else {
91
+ // Legacy mode: single interval
92
+ const interval = parseInt(options.interval, 10) * 1000;
93
+ console.log(chalk.cyan(`Manager started (checking every ${options.interval}s)`));
94
+ console.log(chalk.gray('Press Ctrl+C to stop\n'));
95
+ const runCheck = async () => {
96
+ try {
97
+ await managerCheck(root);
98
+ }
99
+ catch (err) {
100
+ console.error(chalk.red('Manager error:'), err);
101
+ }
102
+ };
103
+ await runCheck();
104
+ if (!options.once) {
105
+ setInterval(runCheck, interval);
106
+ }
107
+ else if (releaseLock) {
108
+ await releaseLock();
109
+ }
110
+ }
111
+ });
112
+ // Run a single check
113
+ managerCommand
114
+ .command('check')
115
+ .description('Run a single manager check')
116
+ .action(async () => {
117
+ const root = findHiveRoot();
118
+ if (!root) {
119
+ console.error(chalk.red('Not in a Hive workspace.'));
120
+ process.exit(1);
121
+ }
122
+ await managerCheck(root);
123
+ });
124
+ // Run health check to sync agents with tmux
125
+ managerCommand
126
+ .command('health')
127
+ .description('Sync agent status with actual tmux sessions')
128
+ .action(async () => {
129
+ const root = findHiveRoot();
130
+ if (!root) {
131
+ console.error(chalk.red('Not in a Hive workspace.'));
132
+ process.exit(1);
133
+ }
134
+ const paths = getHivePaths(root);
135
+ const db = await getDatabase(paths.hiveDir);
136
+ try {
137
+ const config = loadConfig(paths.hiveDir);
138
+ const scheduler = new Scheduler(db.db, {
139
+ scaling: config.scaling,
140
+ models: config.models,
141
+ rootDir: root,
142
+ });
143
+ console.log(chalk.cyan('Running health check...'));
144
+ const result = await scheduler.healthCheck();
145
+ db.save();
146
+ if (result.terminated === 0) {
147
+ console.log(chalk.green('All agents healthy - tmux sessions match database'));
148
+ }
149
+ else {
150
+ console.log(chalk.yellow(`Cleaned up ${result.terminated} dead agent(s)`));
151
+ if (result.revived.length > 0) {
152
+ console.log(chalk.yellow(`Stories returned to queue: ${result.revived.join(', ')}`));
153
+ }
154
+ }
155
+ // Also check merge queue
156
+ console.log(chalk.cyan('Checking merge queue...'));
157
+ await scheduler.checkMergeQueue();
158
+ db.save();
159
+ console.log(chalk.green('Done'));
160
+ }
161
+ finally {
162
+ db.close();
163
+ }
164
+ });
165
+ // Check manager status
166
+ managerCommand
167
+ .command('status')
168
+ .description('Check if the manager daemon is running')
169
+ .action(async () => {
170
+ const running = await isManagerRunning();
171
+ if (running) {
172
+ console.log(chalk.green('Manager daemon is running (hive-manager tmux session)'));
173
+ console.log(chalk.gray('To view: tmux attach -t hive-manager'));
174
+ console.log(chalk.gray('To stop: hive manager stop'));
175
+ }
176
+ else {
177
+ console.log(chalk.yellow('Manager daemon is not running'));
178
+ console.log(chalk.gray('To start: hive manager start'));
179
+ }
180
+ });
181
+ // Stop the manager daemon
182
+ managerCommand
183
+ .command('stop')
184
+ .description('Stop the manager daemon')
185
+ .action(async () => {
186
+ const stopped = await stopManagerSession();
187
+ if (stopped) {
188
+ console.log(chalk.green('Manager daemon stopped'));
189
+ }
190
+ else {
191
+ console.log(chalk.yellow('Manager daemon was not running'));
192
+ }
193
+ });
194
+ // Nudge a specific agent
195
+ managerCommand
196
+ .command('nudge <session>')
197
+ .description('Nudge an agent to check for work')
198
+ .option('-m, --message <msg>', 'Custom message to send')
199
+ .action(async (session, options) => {
200
+ const root = findHiveRoot();
201
+ if (!root) {
202
+ console.error(chalk.red('Not in a Hive workspace.'));
203
+ process.exit(1);
204
+ }
205
+ const paths = getHivePaths(root);
206
+ const db = await getDatabase(paths.hiveDir);
207
+ try {
208
+ const agent = getAgentById(db.db, session.replace('hive-', ''));
209
+ const cliTool = (agent?.cli_tool || 'claude');
210
+ await nudgeAgent(root, session, options.message, undefined, undefined, cliTool);
211
+ console.log(chalk.green(`Nudged ${session}`));
212
+ }
213
+ finally {
214
+ db.close();
215
+ }
216
+ });
217
+ async function managerCheck(root) {
218
+ const timestamp = new Date().toLocaleTimeString();
219
+ console.log(chalk.gray(`[${timestamp}] Manager checking...`));
220
+ const paths = getHivePaths(root);
221
+ const db = await getDatabase(paths.hiveDir);
222
+ try {
223
+ // First, run health check to sync agent status with tmux
224
+ const config = loadConfig(paths.hiveDir);
225
+ const scheduler = new Scheduler(db.db, {
226
+ scaling: config.scaling,
227
+ models: config.models,
228
+ rootDir: root,
229
+ });
230
+ const healthResult = await scheduler.healthCheck();
231
+ if (healthResult.terminated > 0) {
232
+ console.log(chalk.yellow(` Health check: ${healthResult.terminated} dead agent(s) cleaned up`));
233
+ if (healthResult.revived.length > 0) {
234
+ console.log(chalk.yellow(` Stories returned to queue: ${healthResult.revived.join(', ')}`));
235
+ }
236
+ db.save();
237
+ }
238
+ // Check merge queue for QA spawning
239
+ await scheduler.checkMergeQueue();
240
+ db.save();
241
+ // Auto-merge approved PRs
242
+ const autoMerged = await autoMergeApprovedPRs(root, db);
243
+ if (autoMerged > 0) {
244
+ console.log(chalk.green(` Auto-merged ${autoMerged} approved PR(s)`));
245
+ db.save();
246
+ }
247
+ // Sync merged PRs from GitHub to keep story statuses in sync
248
+ const mergedSynced = await syncMergedPRsFromGitHub(root, db);
249
+ if (mergedSynced > 0) {
250
+ console.log(chalk.green(` Synced ${mergedSynced} merged story(ies) from GitHub`));
251
+ }
252
+ // Sync GitHub PRs that might not be in queue
253
+ const syncedPRs = await syncGitHubPRs(root, db, paths.hiveDir);
254
+ if (syncedPRs > 0) {
255
+ console.log(chalk.yellow(` Synced ${syncedPRs} GitHub PR(s) into merge queue`));
256
+ // Recheck merge queue after syncing
257
+ await scheduler.checkMergeQueue();
258
+ db.save();
259
+ }
260
+ const sessions = await getHiveSessions();
261
+ const hiveSessions = sessions.filter(s => s.name.startsWith('hive-'));
262
+ if (hiveSessions.length === 0) {
263
+ console.log(chalk.gray(' No agent sessions found'));
264
+ return;
265
+ }
266
+ let nudged = 0;
267
+ let messagesForwarded = 0;
268
+ let escalationsCreated = 0;
269
+ // Get existing pending escalations to avoid duplicates
270
+ const existingEscalations = getPendingEscalations(db.db);
271
+ const escalatedSessions = new Set(existingEscalations
272
+ .filter(e => e.from_agent_id)
273
+ .map(e => e.from_agent_id));
274
+ for (const session of hiveSessions) {
275
+ // Skip manager itself
276
+ if (session.name === 'hive-manager')
277
+ continue;
278
+ // Get agent information for CLI-aware messaging
279
+ const agent = getAgentById(db.db, session.name.replace('hive-', ''));
280
+ const agentCliTool = (agent?.cli_tool || 'claude');
281
+ // Check if agent has unread messages
282
+ const unread = getUnreadMessages(db.db, session.name);
283
+ if (unread.length > 0) {
284
+ await forwardMessages(session.name, unread, agentCliTool);
285
+ messagesForwarded += unread.length;
286
+ // Mark as read
287
+ for (const msg of unread) {
288
+ markMessageRead(db.db, msg.id);
289
+ }
290
+ db.save();
291
+ }
292
+ // Check if agent appears stuck (capture last output)
293
+ const output = await captureTmuxPane(session.name, 50);
294
+ const stateResult = detectClaudeCodeState(output);
295
+ // Track state changes for this agent
296
+ const now = Date.now();
297
+ const trackedState = agentStates.get(session.name);
298
+ if (!trackedState) {
299
+ // First time seeing this agent - initialize tracking
300
+ agentStates.set(session.name, {
301
+ lastState: stateResult.state,
302
+ lastStateChangeTime: now,
303
+ lastNudgeTime: 0,
304
+ });
305
+ }
306
+ else if (trackedState.lastState !== stateResult.state) {
307
+ // State changed - update tracking
308
+ trackedState.lastState = stateResult.state;
309
+ trackedState.lastStateChangeTime = now;
310
+ }
311
+ // Convert to legacy format for escalation logic
312
+ const waitingInfo = {
313
+ isWaiting: stateResult.isWaiting,
314
+ needsHuman: stateResult.needsHuman,
315
+ reason: stateResult.needsHuman ? getStateDescription(stateResult.state) : undefined,
316
+ };
317
+ if (waitingInfo.needsHuman && !escalatedSessions.has(session.name)) {
318
+ // Create escalation for human attention
319
+ const storyId = agent?.current_story_id || null;
320
+ createEscalation(db.db, {
321
+ storyId,
322
+ fromAgentId: session.name,
323
+ toAgentId: null, // Escalate to human
324
+ reason: `Agent waiting for input: ${waitingInfo.reason || 'Unknown question'}`,
325
+ });
326
+ db.save();
327
+ escalationsCreated++;
328
+ escalatedSessions.add(session.name);
329
+ // Also remind the agent to continue autonomously if they can
330
+ const reminder = buildAutoRecoveryReminder(session.name, agentCliTool);
331
+ await sendToTmuxSession(session.name, reminder);
332
+ console.log(chalk.red(` ESCALATION: ${session.name} needs human input`));
333
+ }
334
+ else if (waitingInfo.isWaiting && stateResult.state !== ClaudeCodeState.THINKING) {
335
+ // Agent is idle/waiting but doesn't need human
336
+ // Check if we should nudge based on state duration and cooldown
337
+ const currentTrackedState = agentStates.get(session.name);
338
+ if (currentTrackedState) {
339
+ const timeSinceStateChange = now - currentTrackedState.lastStateChangeTime;
340
+ const timeSinceLastNudge = now - currentTrackedState.lastNudgeTime;
341
+ // Only nudge if:
342
+ // 1. Agent stuck in same state for > 120 seconds
343
+ // 2. Haven't nudged in last 5 minutes
344
+ // 3. Not in THINKING state (extended thinking is normal)
345
+ if (timeSinceStateChange > STATE_STUCK_THRESHOLD_MS &&
346
+ timeSinceLastNudge > NUDGE_COOLDOWN_MS) {
347
+ // Re-check state immediately before nudging to avoid interrupting active work
348
+ const recheckOutput = await captureTmuxPane(session.name, 50);
349
+ const recheckState = detectClaudeCodeState(recheckOutput);
350
+ // Only proceed if still in a waiting state and not THINKING
351
+ if (recheckState.isWaiting && !recheckState.needsHuman && recheckState.state !== ClaudeCodeState.THINKING) {
352
+ const agentType = getAgentType(session.name);
353
+ await nudgeAgent(root, session.name, undefined, agentType, waitingInfo.reason, agentCliTool);
354
+ currentTrackedState.lastNudgeTime = now;
355
+ nudged++;
356
+ }
357
+ }
358
+ }
359
+ }
360
+ // If not waiting (actively working), do nothing - let them work
361
+ }
362
+ // Check for PRs needing QA attention
363
+ const queuedPRs = getMergeQueue(db.db);
364
+ if (queuedPRs.length > 0) {
365
+ const qaSessions = hiveSessions.filter(s => s.name.includes('-qa-'));
366
+ for (const qa of qaSessions) {
367
+ await sendToTmuxSession(qa.name, `# ${queuedPRs.length} PR(s) waiting in queue. Run: hive pr queue`);
368
+ }
369
+ }
370
+ // Check for rejected PRs that need developer attention
371
+ // Only notify once by updating status to 'closed' after notification (prevents spam)
372
+ const rejectedPRs = getPullRequestsByStatus(db.db, 'rejected');
373
+ let rejectionNotified = 0;
374
+ for (const pr of rejectedPRs) {
375
+ // Update the story status to qa_failed so developer knows they need to act
376
+ if (pr.story_id) {
377
+ updateStory(db.db, pr.story_id, { status: 'qa_failed' });
378
+ createLog(db.db, {
379
+ agentId: 'manager',
380
+ eventType: 'STORY_QA_FAILED',
381
+ message: `Story ${pr.story_id} QA failed: ${pr.review_notes || 'See review comments'}`,
382
+ storyId: pr.story_id,
383
+ });
384
+ }
385
+ if (pr.submitted_by) {
386
+ const devSession = hiveSessions.find(s => s.name === pr.submitted_by);
387
+ if (devSession) {
388
+ await sendToTmuxSession(devSession.name, `# ⚠️ PR REJECTED - ACTION REQUIRED ⚠️
389
+ # Story: ${pr.story_id || 'Unknown'}
390
+ # Reason: ${pr.review_notes || 'See review comments'}
391
+ #
392
+ # You MUST fix this issue before doing anything else.
393
+ # Fix the issues and resubmit: hive pr submit -b ${pr.branch_name} -s ${pr.story_id || 'STORY-ID'} --from ${devSession.name}`);
394
+ await sendEnterToTmuxSession(devSession.name);
395
+ rejectionNotified++;
396
+ }
397
+ }
398
+ // Mark as closed to prevent re-notification spam
399
+ // Developer will create a new PR when they resubmit
400
+ db.db.run("UPDATE pull_requests SET status = 'closed' WHERE id = ?", [pr.id]);
401
+ }
402
+ if (rejectedPRs.length > 0) {
403
+ db.save();
404
+ console.log(chalk.yellow(` Notified ${rejectionNotified} developer(s) of PR rejection(s)`));
405
+ }
406
+ // Nudge developers who have qa_failed stories - they need to fix and resubmit
407
+ // Filter out merged/completed stories to avoid nudging about finished work
408
+ const qaFailedStories = getStoriesByStatus(db.db, 'qa_failed').filter(story => !['merged', 'completed'].includes(story.status));
409
+ for (const story of qaFailedStories) {
410
+ if (story.assigned_agent_id) {
411
+ const agent = getAgentById(db.db, story.assigned_agent_id);
412
+ if (agent && agent.status === 'working') {
413
+ const agentSession = hiveSessions.find(s => s.name === agent.tmux_session || s.name.includes(agent.id));
414
+ if (agentSession) {
415
+ // Check if agent is idle before nudging
416
+ const output = await captureTmuxPane(agentSession.name, 30);
417
+ const stateResult = detectClaudeCodeState(output);
418
+ // Only nudge if idle and not thinking (thinking is productive work)
419
+ if (stateResult.isWaiting && !stateResult.needsHuman && stateResult.state !== ClaudeCodeState.THINKING) {
420
+ await sendToTmuxSession(agentSession.name, `# REMINDER: Story ${story.id} failed QA review!
421
+ # You must fix the issues and resubmit the PR.
422
+ # Check the QA feedback and address all concerns.
423
+ hive pr queue`);
424
+ await sendEnterToTmuxSession(agentSession.name);
425
+ }
426
+ }
427
+ }
428
+ }
429
+ }
430
+ // Spin down agents whose stories have been merged
431
+ // Find merged stories that still have assigned agents
432
+ const mergedStoriesWithAgents = queryAll(db.db, `SELECT * FROM stories WHERE status = 'merged' AND assigned_agent_id IS NOT NULL`);
433
+ let agentsSpunDown = 0;
434
+ for (const story of mergedStoriesWithAgents) {
435
+ if (story.assigned_agent_id) {
436
+ const agent = getAgentById(db.db, story.assigned_agent_id);
437
+ if (agent && agent.status !== 'terminated') {
438
+ // Find and kill the agent's tmux session
439
+ const agentSession = hiveSessions.find(s => s.name === agent.tmux_session || s.name.includes(agent.id));
440
+ if (agentSession) {
441
+ // Thank the agent and terminate
442
+ await sendToTmuxSession(agentSession.name, `# Congratulations! Your story ${story.id} has been merged.
443
+ # Your work is complete. Spinning down...`);
444
+ await new Promise(resolve => setTimeout(resolve, 1000));
445
+ await killTmuxSession(agentSession.name);
446
+ }
447
+ // Mark agent as terminated
448
+ updateAgent(db.db, agent.id, { status: 'terminated', currentStoryId: null });
449
+ // Log the termination
450
+ createLog(db.db, {
451
+ agentId: agent.id,
452
+ storyId: story.id,
453
+ eventType: 'AGENT_TERMINATED',
454
+ message: `Agent spun down after story ${story.id} was merged`,
455
+ });
456
+ // Clear the story assignment
457
+ db.db.run("UPDATE stories SET assigned_agent_id = NULL WHERE id = ?", [story.id]);
458
+ agentsSpunDown++;
459
+ }
460
+ }
461
+ }
462
+ if (agentsSpunDown > 0) {
463
+ db.save();
464
+ console.log(chalk.green(` Spun down ${agentsSpunDown} agent(s) after successful merge`));
465
+ }
466
+ // Spin down all non-tech-lead agents when no work remains in the pipeline
467
+ const activeStories = queryAll(db.db, `SELECT * FROM stories WHERE status IN ('planned', 'in_progress', 'review', 'qa', 'qa_failed', 'pr_submitted')`);
468
+ if (activeStories.length === 0) {
469
+ // No active work - spin down all agents except tech lead
470
+ const workingAgents = queryAll(db.db, `SELECT id, tmux_session, type FROM agents WHERE status = 'working' AND type != 'tech_lead'`);
471
+ let idleSpunDown = 0;
472
+ for (const agent of workingAgents) {
473
+ const agentSession = hiveSessions.find(s => s.name === agent.tmux_session);
474
+ if (agentSession) {
475
+ await sendToTmuxSession(agentSession.name, `# All work complete. No stories in pipeline. Spinning down...`);
476
+ await new Promise(resolve => setTimeout(resolve, 500));
477
+ await killTmuxSession(agentSession.name);
478
+ }
479
+ updateAgent(db.db, agent.id, { status: 'terminated', currentStoryId: null });
480
+ createLog(db.db, {
481
+ agentId: agent.id,
482
+ eventType: 'AGENT_TERMINATED',
483
+ message: 'Agent spun down - no work remaining in pipeline',
484
+ });
485
+ idleSpunDown++;
486
+ }
487
+ if (idleSpunDown > 0) {
488
+ db.save();
489
+ console.log(chalk.green(` Spun down ${idleSpunDown} idle agent(s) - pipeline empty`));
490
+ }
491
+ }
492
+ // Check for stories stuck in "in_progress" for too long (> 30 min without activity)
493
+ // Only nudge stories that haven't been merged or completed
494
+ const stuckStories = queryAll(db.db, `SELECT * FROM stories
495
+ WHERE status = 'in_progress'
496
+ AND updated_at < datetime('now', '-30 minutes')`).filter(story => !['merged', 'completed'].includes(story.status));
497
+ for (const story of stuckStories) {
498
+ if (story.assigned_agent_id) {
499
+ const agentSession = hiveSessions.find(s => s.name.includes(story.assigned_agent_id?.replace(/^hive-/, '') || ''));
500
+ if (agentSession) {
501
+ await sendToTmuxSession(agentSession.name, `# REMINDER: Story ${story.id} has been in progress for a while.
502
+ # If stuck, escalate to your Senior or Tech Lead.
503
+ # If done, submit your PR: hive pr submit -b <branch> -s ${story.id} --from ${agentSession.name}
504
+ # Then mark complete: hive my-stories complete ${story.id}`);
505
+ }
506
+ }
507
+ }
508
+ // Check for unassigned planned stories
509
+ const plannedStories = queryAll(db.db, "SELECT * FROM stories WHERE status = 'planned' AND assigned_agent_id IS NULL");
510
+ if (plannedStories.length > 0) {
511
+ // Notify seniors about unassigned work
512
+ const seniorSessions = hiveSessions.filter(s => s.name.includes('-senior-'));
513
+ for (const senior of seniorSessions) {
514
+ await sendToTmuxSession(senior.name, `# ${plannedStories.length} unassigned story(ies). Run: hive my-stories ${senior.name} --all`);
515
+ }
516
+ }
517
+ // Summary
518
+ const summary = [];
519
+ if (escalationsCreated > 0)
520
+ summary.push(`${escalationsCreated} escalations created`);
521
+ if (nudged > 0)
522
+ summary.push(`${nudged} nudged`);
523
+ if (messagesForwarded > 0)
524
+ summary.push(`${messagesForwarded} messages forwarded`);
525
+ if (queuedPRs.length > 0)
526
+ summary.push(`${queuedPRs.length} PRs queued`);
527
+ if (summary.length > 0) {
528
+ console.log(chalk.yellow(` ${summary.join(', ')}`));
529
+ }
530
+ else {
531
+ console.log(chalk.green(' All agents productive'));
532
+ }
533
+ }
534
+ finally {
535
+ db.close();
536
+ }
537
+ }
538
+ function getAgentType(sessionName) {
539
+ if (sessionName.includes('-senior-'))
540
+ return 'senior';
541
+ if (sessionName.includes('-intermediate-'))
542
+ return 'intermediate';
543
+ if (sessionName.includes('-junior-'))
544
+ return 'junior';
545
+ if (sessionName.includes('-qa-'))
546
+ return 'qa';
547
+ return 'unknown';
548
+ }
549
+ async function nudgeAgent(_root, sessionName, customMessage, agentType, reason, agentCliTool) {
550
+ if (customMessage) {
551
+ await sendToTmuxSession(sessionName, customMessage);
552
+ return;
553
+ }
554
+ const type = agentType || getAgentType(sessionName);
555
+ const cliTool = agentCliTool || 'claude';
556
+ const commands = getAvailableCommands(cliTool);
557
+ // Build contextual nudge message based on agent type and reason
558
+ let nudge;
559
+ switch (type) {
560
+ case 'qa':
561
+ nudge = `# You are a QA agent. Check for PRs to review:
562
+ # ${commands.queueCheck()}
563
+ # If there are PRs, review them with: hive pr review <pr-id>`;
564
+ break;
565
+ case 'senior':
566
+ nudge = `# You are a Senior developer. Continue with your assigned stories.
567
+ # Check your work: # ${commands.getMyStories(sessionName)}
568
+ # If no active stories, check for available work: hive stories list --status planned`;
569
+ break;
570
+ case 'intermediate':
571
+ case 'junior':
572
+ nudge = `# Continue with your assigned story. Check status:
573
+ # ${commands.getMyStories(sessionName)}
574
+ # If stuck, ask your Senior for help via: hive msg send hive-senior-<team> "your question"
575
+ # If done, submit PR: hive pr submit -b <branch> -s <story-id> --from ${sessionName}`;
576
+ break;
577
+ default:
578
+ nudge = `# Check current status and continue working:
579
+ hive status`;
580
+ }
581
+ // Add reason context if provided
582
+ if (reason) {
583
+ nudge = `# Manager detected: ${reason}\n${nudge}`;
584
+ }
585
+ await sendToTmuxSession(sessionName, nudge);
586
+ // Also send Enter to ensure prompt is activated
587
+ await new Promise(resolve => setTimeout(resolve, 100));
588
+ await sendEnterToTmuxSession(sessionName);
589
+ }
590
+ async function forwardMessages(sessionName, messages, cliTool = 'claude') {
591
+ const commands = getAvailableCommands(cliTool);
592
+ for (const msg of messages) {
593
+ const notification = `# New message from ${msg.from_session}${msg.subject ? ` - ${msg.subject}` : ''}
594
+ # ${msg.body}
595
+ # Reply with: # ${commands.msgReply(msg.id, 'your response', sessionName)}`;
596
+ await sendToTmuxSession(sessionName, notification);
597
+ // Small delay between messages
598
+ await new Promise(resolve => setTimeout(resolve, 500));
599
+ }
600
+ }
601
+ async function syncMergedPRsFromGitHub(root, db) {
602
+ const teams = getAllTeams(db.db);
603
+ if (teams.length === 0)
604
+ return 0;
605
+ let storiesUpdated = 0;
606
+ for (const team of teams) {
607
+ if (!team.repo_path)
608
+ continue;
609
+ const repoDir = `${root}/${team.repo_path}`;
610
+ try {
611
+ // Get recently merged PRs from GitHub
612
+ const result = await execa('gh', ['pr', 'list', '--json', 'number,headRefName,mergedAt', '--state', 'merged', '--limit', '20'], {
613
+ cwd: repoDir,
614
+ });
615
+ const mergedPRs = JSON.parse(result.stdout);
616
+ for (const pr of mergedPRs) {
617
+ // Extract story ID from branch name - match STORY-XXX-NAME pattern
618
+ // Use a more specific pattern to avoid matching extra suffixes
619
+ const storyMatch = pr.headRefName.match(/STORY-\d+-[A-Z]+/i);
620
+ if (!storyMatch)
621
+ continue;
622
+ const storyId = storyMatch[0].toUpperCase();
623
+ // Check if story exists and isn't already merged
624
+ const story = queryAll(db.db, "SELECT * FROM stories WHERE id = ? AND status != 'merged'", [storyId]);
625
+ if (story.length > 0) {
626
+ // Update story to merged
627
+ db.db.run("UPDATE stories SET status = 'merged', assigned_agent_id = NULL, updated_at = datetime('now') WHERE id = ?", [storyId]);
628
+ // Log the sync
629
+ createLog(db.db, {
630
+ agentId: 'manager',
631
+ storyId: storyId,
632
+ eventType: 'STORY_MERGED',
633
+ message: `Story synced to merged from GitHub PR #${pr.number}`,
634
+ });
635
+ storiesUpdated++;
636
+ }
637
+ }
638
+ }
639
+ catch {
640
+ // gh CLI might not be authenticated or repo might not have remote
641
+ continue;
642
+ }
643
+ }
644
+ if (storiesUpdated > 0) {
645
+ db.save();
646
+ }
647
+ return storiesUpdated;
648
+ }
649
+ async function syncGitHubPRs(root, db, _hiveDir) {
650
+ const teams = getAllTeams(db.db);
651
+ if (teams.length === 0)
652
+ return 0;
653
+ // Get ALL existing PRs (including merged/closed) to prevent duplicate imports
654
+ const existingPRs = queryAll(db.db, "SELECT * FROM pull_requests");
655
+ // Include ALL branch names to prevent duplicate entries for merged/closed PRs
656
+ const existingBranches = new Set(existingPRs.map(pr => pr.branch_name));
657
+ const existingPrNumbers = new Set(existingPRs.map(pr => pr.github_pr_number).filter(Boolean));
658
+ let synced = 0;
659
+ for (const team of teams) {
660
+ if (!team.repo_path)
661
+ continue;
662
+ const repoDir = `${root}/${team.repo_path}`;
663
+ try {
664
+ const result = await execa('gh', ['pr', 'list', '--json', 'number,headRefName,url,title', '--state', 'open'], {
665
+ cwd: repoDir,
666
+ });
667
+ const ghPRs = JSON.parse(result.stdout);
668
+ for (const ghPR of ghPRs) {
669
+ // Skip if already in queue
670
+ if (existingBranches.has(ghPR.headRefName) || existingPrNumbers.has(ghPR.number)) {
671
+ continue;
672
+ }
673
+ // Try to match to a story by parsing branch name
674
+ const storyMatch = ghPR.headRefName.match(/STORY-\d+/i);
675
+ const storyId = storyMatch ? storyMatch[0].toUpperCase() : null;
676
+ createPullRequest(db.db, {
677
+ storyId,
678
+ teamId: team.id,
679
+ branchName: ghPR.headRefName,
680
+ githubPrNumber: ghPR.number,
681
+ githubPrUrl: ghPR.url,
682
+ submittedBy: null,
683
+ });
684
+ synced++;
685
+ }
686
+ }
687
+ catch {
688
+ // gh CLI might not be authenticated or repo might not have remote
689
+ continue;
690
+ }
691
+ }
692
+ if (synced > 0) {
693
+ db.save();
694
+ }
695
+ return synced;
696
+ }
697
+ async function autoMergeApprovedPRs(root, db) {
698
+ const approvedPRs = getApprovedPullRequests(db.db);
699
+ if (approvedPRs.length === 0)
700
+ return 0;
701
+ let mergedCount = 0;
702
+ for (const pr of approvedPRs) {
703
+ // Skip PRs without GitHub PR numbers
704
+ if (!pr.github_pr_number)
705
+ continue;
706
+ try {
707
+ // Get team to find repo path
708
+ let teamId = pr.team_id;
709
+ let repoCwd = root;
710
+ if (teamId) {
711
+ const team = getAllTeams(db.db).find(t => t.id === teamId);
712
+ if (team?.repo_path) {
713
+ repoCwd = join(root, team.repo_path);
714
+ }
715
+ }
716
+ else if (pr.branch_name) {
717
+ // Try to find team by matching branch name pattern
718
+ const teams = getAllTeams(db.db);
719
+ for (const team of teams) {
720
+ if (team.repo_path) {
721
+ repoCwd = join(root, team.repo_path);
722
+ teamId = team.id;
723
+ break;
724
+ }
725
+ }
726
+ }
727
+ // Attempt to merge on GitHub
728
+ const { execSync } = await import('child_process');
729
+ try {
730
+ execSync(`gh pr merge ${pr.github_pr_number} --auto --squash --delete-branch`, { stdio: 'pipe', cwd: repoCwd });
731
+ // Update PR status to merged
732
+ updatePullRequest(db.db, pr.id, { status: 'merged' });
733
+ // Update story status if linked
734
+ if (pr.story_id) {
735
+ updateStory(db.db, pr.story_id, { status: 'merged' });
736
+ createLog(db.db, {
737
+ agentId: 'manager',
738
+ storyId: pr.story_id,
739
+ eventType: 'STORY_MERGED',
740
+ message: `Story auto-merged from GitHub PR #${pr.github_pr_number}`,
741
+ });
742
+ }
743
+ else {
744
+ createLog(db.db, {
745
+ agentId: 'manager',
746
+ eventType: 'PR_MERGED',
747
+ message: `PR ${pr.id} auto-merged (GitHub PR #${pr.github_pr_number})`,
748
+ metadata: { pr_id: pr.id },
749
+ });
750
+ }
751
+ mergedCount++;
752
+ }
753
+ catch (mergeErr) {
754
+ // Log merge failure but continue with other PRs
755
+ createLog(db.db, {
756
+ agentId: 'manager',
757
+ storyId: pr.story_id || undefined,
758
+ eventType: 'PR_MERGE_FAILED',
759
+ status: 'error',
760
+ message: `Failed to auto-merge PR ${pr.id} (GitHub PR #${pr.github_pr_number}): ${mergeErr instanceof Error ? mergeErr.message : 'Unknown error'}`,
761
+ metadata: { pr_id: pr.id },
762
+ });
763
+ }
764
+ }
765
+ catch {
766
+ // Non-fatal - continue with other PRs
767
+ continue;
768
+ }
769
+ }
770
+ if (mergedCount > 0) {
771
+ db.save();
772
+ }
773
+ return mergedCount;
774
+ }
775
+ //# sourceMappingURL=manager.js.map