hivemind-pipeline 0.1.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 (788) hide show
  1. package/LICENSE +63 -0
  2. package/README.md +352 -0
  3. package/dist/bin/swarm.d.ts +3 -0
  4. package/dist/bin/swarm.d.ts.map +1 -0
  5. package/dist/bin/swarm.js +293 -0
  6. package/dist/bin/swarm.js.map +1 -0
  7. package/dist/dashboard/assets/index-Cf_KwDES.js +359 -0
  8. package/dist/dashboard/assets/index-SatEQz0u.css +1 -0
  9. package/dist/dashboard/index.html +16 -0
  10. package/dist/prompts/Architect-Go.md +455 -0
  11. package/dist/prompts/Architect-Node.md +379 -0
  12. package/dist/prompts/Architect-Python.md +63 -0
  13. package/dist/prompts/Architect-React.md +352 -0
  14. package/dist/prompts/Architect-Rust.md +63 -0
  15. package/dist/prompts/Architect-Swift.md +61 -0
  16. package/dist/prompts/Software-engineer-go.md +254 -0
  17. package/dist/prompts/Software-engineer-node.md +241 -0
  18. package/dist/prompts/Software-engineer-python.md +70 -0
  19. package/dist/prompts/Software-engineer-react.md +167 -0
  20. package/dist/prompts/Software-engineer-rust.md +69 -0
  21. package/dist/prompts/Software-engineer-swift.md +69 -0
  22. package/dist/prompts/Software-lead-go.md +247 -0
  23. package/dist/prompts/Software-lead-node.md +238 -0
  24. package/dist/prompts/Software-lead-python.md +53 -0
  25. package/dist/prompts/Software-lead-react.md +229 -0
  26. package/dist/prompts/Software-lead-rust.md +53 -0
  27. package/dist/prompts/Software-lead-swift.md +53 -0
  28. package/dist/prompts/analyst-go.md +351 -0
  29. package/dist/prompts/analyst-node.md +317 -0
  30. package/dist/prompts/analyst-python.md +139 -0
  31. package/dist/prompts/analyst-react.md +428 -0
  32. package/dist/prompts/analyst-rust.md +125 -0
  33. package/dist/prompts/analyst-swift.md +125 -0
  34. package/dist/prompts/test-engineer-go.md +99 -0
  35. package/dist/prompts/test-engineer-node.md +97 -0
  36. package/dist/prompts/test-engineer-python.md +61 -0
  37. package/dist/prompts/test-engineer-react.md +104 -0
  38. package/dist/prompts/test-engineer-rust.md +62 -0
  39. package/dist/prompts/test-engineer-swift.md +66 -0
  40. package/dist/src/__tests__/commands/analyze.test.d.ts +2 -0
  41. package/dist/src/__tests__/commands/analyze.test.d.ts.map +1 -0
  42. package/dist/src/__tests__/commands/analyze.test.js +61 -0
  43. package/dist/src/__tests__/commands/analyze.test.js.map +1 -0
  44. package/dist/src/__tests__/commands/architect.test.d.ts +2 -0
  45. package/dist/src/__tests__/commands/architect.test.d.ts.map +1 -0
  46. package/dist/src/__tests__/commands/architect.test.js +46 -0
  47. package/dist/src/__tests__/commands/architect.test.js.map +1 -0
  48. package/dist/src/__tests__/commands/build.test.d.ts +2 -0
  49. package/dist/src/__tests__/commands/build.test.d.ts.map +1 -0
  50. package/dist/src/__tests__/commands/build.test.js +69 -0
  51. package/dist/src/__tests__/commands/build.test.js.map +1 -0
  52. package/dist/src/__tests__/commands/dashboard.test.d.ts +2 -0
  53. package/dist/src/__tests__/commands/dashboard.test.d.ts.map +1 -0
  54. package/dist/src/__tests__/commands/dashboard.test.js +95 -0
  55. package/dist/src/__tests__/commands/dashboard.test.js.map +1 -0
  56. package/dist/src/__tests__/commands/doctor.test.d.ts +2 -0
  57. package/dist/src/__tests__/commands/doctor.test.d.ts.map +1 -0
  58. package/dist/src/__tests__/commands/doctor.test.js +69 -0
  59. package/dist/src/__tests__/commands/doctor.test.js.map +1 -0
  60. package/dist/src/__tests__/commands/fix.test.d.ts +2 -0
  61. package/dist/src/__tests__/commands/fix.test.d.ts.map +1 -0
  62. package/dist/src/__tests__/commands/fix.test.js +147 -0
  63. package/dist/src/__tests__/commands/fix.test.js.map +1 -0
  64. package/dist/src/__tests__/commands/init.test.d.ts +2 -0
  65. package/dist/src/__tests__/commands/init.test.d.ts.map +1 -0
  66. package/dist/src/__tests__/commands/init.test.js +79 -0
  67. package/dist/src/__tests__/commands/init.test.js.map +1 -0
  68. package/dist/src/__tests__/commands/learn.test.d.ts +2 -0
  69. package/dist/src/__tests__/commands/learn.test.d.ts.map +1 -0
  70. package/dist/src/__tests__/commands/learn.test.js +64 -0
  71. package/dist/src/__tests__/commands/learn.test.js.map +1 -0
  72. package/dist/src/__tests__/commands/mayday.test.d.ts +2 -0
  73. package/dist/src/__tests__/commands/mayday.test.d.ts.map +1 -0
  74. package/dist/src/__tests__/commands/mayday.test.js +115 -0
  75. package/dist/src/__tests__/commands/mayday.test.js.map +1 -0
  76. package/dist/src/__tests__/commands/memory.test.d.ts +2 -0
  77. package/dist/src/__tests__/commands/memory.test.d.ts.map +1 -0
  78. package/dist/src/__tests__/commands/memory.test.js +80 -0
  79. package/dist/src/__tests__/commands/memory.test.js.map +1 -0
  80. package/dist/src/__tests__/commands/plan.test.d.ts +2 -0
  81. package/dist/src/__tests__/commands/plan.test.d.ts.map +1 -0
  82. package/dist/src/__tests__/commands/plan.test.js +46 -0
  83. package/dist/src/__tests__/commands/plan.test.js.map +1 -0
  84. package/dist/src/__tests__/commands/pr.test.d.ts +2 -0
  85. package/dist/src/__tests__/commands/pr.test.d.ts.map +1 -0
  86. package/dist/src/__tests__/commands/pr.test.js +170 -0
  87. package/dist/src/__tests__/commands/pr.test.js.map +1 -0
  88. package/dist/src/__tests__/commands/refactor.test.d.ts +2 -0
  89. package/dist/src/__tests__/commands/refactor.test.d.ts.map +1 -0
  90. package/dist/src/__tests__/commands/refactor.test.js +107 -0
  91. package/dist/src/__tests__/commands/refactor.test.js.map +1 -0
  92. package/dist/src/__tests__/commands/review.test.d.ts +2 -0
  93. package/dist/src/__tests__/commands/review.test.d.ts.map +1 -0
  94. package/dist/src/__tests__/commands/review.test.js +125 -0
  95. package/dist/src/__tests__/commands/review.test.js.map +1 -0
  96. package/dist/src/__tests__/commands/shared.test.d.ts +2 -0
  97. package/dist/src/__tests__/commands/shared.test.d.ts.map +1 -0
  98. package/dist/src/__tests__/commands/shared.test.js +112 -0
  99. package/dist/src/__tests__/commands/shared.test.js.map +1 -0
  100. package/dist/src/__tests__/commands/spike.test.d.ts +2 -0
  101. package/dist/src/__tests__/commands/spike.test.d.ts.map +1 -0
  102. package/dist/src/__tests__/commands/spike.test.js +75 -0
  103. package/dist/src/__tests__/commands/spike.test.js.map +1 -0
  104. package/dist/src/__tests__/commands/stats.test.d.ts +2 -0
  105. package/dist/src/__tests__/commands/stats.test.d.ts.map +1 -0
  106. package/dist/src/__tests__/commands/stats.test.js +82 -0
  107. package/dist/src/__tests__/commands/stats.test.js.map +1 -0
  108. package/dist/src/__tests__/commands/status.test.d.ts +2 -0
  109. package/dist/src/__tests__/commands/status.test.d.ts.map +1 -0
  110. package/dist/src/__tests__/commands/status.test.js +60 -0
  111. package/dist/src/__tests__/commands/status.test.js.map +1 -0
  112. package/dist/src/__tests__/commands/test-cmd.test.d.ts +2 -0
  113. package/dist/src/__tests__/commands/test-cmd.test.d.ts.map +1 -0
  114. package/dist/src/__tests__/commands/test-cmd.test.js +76 -0
  115. package/dist/src/__tests__/commands/test-cmd.test.js.map +1 -0
  116. package/dist/src/__tests__/commands/test-gen.test.d.ts +2 -0
  117. package/dist/src/__tests__/commands/test-gen.test.d.ts.map +1 -0
  118. package/dist/src/__tests__/commands/test-gen.test.js +151 -0
  119. package/dist/src/__tests__/commands/test-gen.test.js.map +1 -0
  120. package/dist/src/__tests__/core/agent-manager.test.d.ts +2 -0
  121. package/dist/src/__tests__/core/agent-manager.test.d.ts.map +1 -0
  122. package/dist/src/__tests__/core/agent-manager.test.js +167 -0
  123. package/dist/src/__tests__/core/agent-manager.test.js.map +1 -0
  124. package/dist/src/__tests__/core/agent-process.test.d.ts +2 -0
  125. package/dist/src/__tests__/core/agent-process.test.d.ts.map +1 -0
  126. package/dist/src/__tests__/core/agent-process.test.js +178 -0
  127. package/dist/src/__tests__/core/agent-process.test.js.map +1 -0
  128. package/dist/src/__tests__/core/guardrails.test.d.ts +2 -0
  129. package/dist/src/__tests__/core/guardrails.test.d.ts.map +1 -0
  130. package/dist/src/__tests__/core/guardrails.test.js +249 -0
  131. package/dist/src/__tests__/core/guardrails.test.js.map +1 -0
  132. package/dist/src/__tests__/core/pipeline.test.d.ts +2 -0
  133. package/dist/src/__tests__/core/pipeline.test.d.ts.map +1 -0
  134. package/dist/src/__tests__/core/pipeline.test.js +331 -0
  135. package/dist/src/__tests__/core/pipeline.test.js.map +1 -0
  136. package/dist/src/__tests__/core/state.test.d.ts +2 -0
  137. package/dist/src/__tests__/core/state.test.d.ts.map +1 -0
  138. package/dist/src/__tests__/core/state.test.js +132 -0
  139. package/dist/src/__tests__/core/state.test.js.map +1 -0
  140. package/dist/src/__tests__/core/ws-server.test.d.ts +2 -0
  141. package/dist/src/__tests__/core/ws-server.test.d.ts.map +1 -0
  142. package/dist/src/__tests__/core/ws-server.test.js +177 -0
  143. package/dist/src/__tests__/core/ws-server.test.js.map +1 -0
  144. package/dist/src/__tests__/helpers/temp-dir.d.ts +8 -0
  145. package/dist/src/__tests__/helpers/temp-dir.d.ts.map +1 -0
  146. package/dist/src/__tests__/helpers/temp-dir.js +25 -0
  147. package/dist/src/__tests__/helpers/temp-dir.js.map +1 -0
  148. package/dist/src/commands/agent.d.ts +3 -0
  149. package/dist/src/commands/agent.d.ts.map +1 -0
  150. package/dist/src/commands/agent.js +103 -0
  151. package/dist/src/commands/agent.js.map +1 -0
  152. package/dist/src/commands/allocate.d.ts +3 -0
  153. package/dist/src/commands/allocate.d.ts.map +1 -0
  154. package/dist/src/commands/allocate.js +229 -0
  155. package/dist/src/commands/allocate.js.map +1 -0
  156. package/dist/src/commands/analyze.d.ts +3 -0
  157. package/dist/src/commands/analyze.d.ts.map +1 -0
  158. package/dist/src/commands/analyze.js +63 -0
  159. package/dist/src/commands/analyze.js.map +1 -0
  160. package/dist/src/commands/architect-review.d.ts +3 -0
  161. package/dist/src/commands/architect-review.d.ts.map +1 -0
  162. package/dist/src/commands/architect-review.js +695 -0
  163. package/dist/src/commands/architect-review.js.map +1 -0
  164. package/dist/src/commands/architect.d.ts +3 -0
  165. package/dist/src/commands/architect.d.ts.map +1 -0
  166. package/dist/src/commands/architect.js +49 -0
  167. package/dist/src/commands/architect.js.map +1 -0
  168. package/dist/src/commands/audit.d.ts +3 -0
  169. package/dist/src/commands/audit.d.ts.map +1 -0
  170. package/dist/src/commands/audit.js +55 -0
  171. package/dist/src/commands/audit.js.map +1 -0
  172. package/dist/src/commands/autopilot.d.ts +7 -0
  173. package/dist/src/commands/autopilot.d.ts.map +1 -0
  174. package/dist/src/commands/autopilot.js +377 -0
  175. package/dist/src/commands/autopilot.js.map +1 -0
  176. package/dist/src/commands/babysit-prs.d.ts +13 -0
  177. package/dist/src/commands/babysit-prs.d.ts.map +1 -0
  178. package/dist/src/commands/babysit-prs.js +283 -0
  179. package/dist/src/commands/babysit-prs.js.map +1 -0
  180. package/dist/src/commands/benchmark.d.ts +34 -0
  181. package/dist/src/commands/benchmark.d.ts.map +1 -0
  182. package/dist/src/commands/benchmark.js +534 -0
  183. package/dist/src/commands/benchmark.js.map +1 -0
  184. package/dist/src/commands/build.d.ts +3 -0
  185. package/dist/src/commands/build.d.ts.map +1 -0
  186. package/dist/src/commands/build.js +63 -0
  187. package/dist/src/commands/build.js.map +1 -0
  188. package/dist/src/commands/check.d.ts +3 -0
  189. package/dist/src/commands/check.d.ts.map +1 -0
  190. package/dist/src/commands/check.js +102 -0
  191. package/dist/src/commands/check.js.map +1 -0
  192. package/dist/src/commands/ci.d.ts +3 -0
  193. package/dist/src/commands/ci.d.ts.map +1 -0
  194. package/dist/src/commands/ci.js +124 -0
  195. package/dist/src/commands/ci.js.map +1 -0
  196. package/dist/src/commands/compete.d.ts +3 -0
  197. package/dist/src/commands/compete.d.ts.map +1 -0
  198. package/dist/src/commands/compete.js +164 -0
  199. package/dist/src/commands/compete.js.map +1 -0
  200. package/dist/src/commands/compliance.d.ts +3 -0
  201. package/dist/src/commands/compliance.d.ts.map +1 -0
  202. package/dist/src/commands/compliance.js +722 -0
  203. package/dist/src/commands/compliance.js.map +1 -0
  204. package/dist/src/commands/context.d.ts +3 -0
  205. package/dist/src/commands/context.d.ts.map +1 -0
  206. package/dist/src/commands/context.js +171 -0
  207. package/dist/src/commands/context.js.map +1 -0
  208. package/dist/src/commands/contract.d.ts +3 -0
  209. package/dist/src/commands/contract.d.ts.map +1 -0
  210. package/dist/src/commands/contract.js +384 -0
  211. package/dist/src/commands/contract.js.map +1 -0
  212. package/dist/src/commands/dashboard.d.ts +3 -0
  213. package/dist/src/commands/dashboard.d.ts.map +1 -0
  214. package/dist/src/commands/dashboard.js +114 -0
  215. package/dist/src/commands/dashboard.js.map +1 -0
  216. package/dist/src/commands/delegate.d.ts +3 -0
  217. package/dist/src/commands/delegate.d.ts.map +1 -0
  218. package/dist/src/commands/delegate.js +474 -0
  219. package/dist/src/commands/delegate.js.map +1 -0
  220. package/dist/src/commands/deploy.d.ts +17 -0
  221. package/dist/src/commands/deploy.d.ts.map +1 -0
  222. package/dist/src/commands/deploy.js +163 -0
  223. package/dist/src/commands/deploy.js.map +1 -0
  224. package/dist/src/commands/deps.d.ts +3 -0
  225. package/dist/src/commands/deps.d.ts.map +1 -0
  226. package/dist/src/commands/deps.js +548 -0
  227. package/dist/src/commands/deps.js.map +1 -0
  228. package/dist/src/commands/doctor.d.ts +3 -0
  229. package/dist/src/commands/doctor.d.ts.map +1 -0
  230. package/dist/src/commands/doctor.js +120 -0
  231. package/dist/src/commands/doctor.js.map +1 -0
  232. package/dist/src/commands/empathize.d.ts +3 -0
  233. package/dist/src/commands/empathize.d.ts.map +1 -0
  234. package/dist/src/commands/empathize.js +253 -0
  235. package/dist/src/commands/empathize.js.map +1 -0
  236. package/dist/src/commands/evaluate.d.ts +3 -0
  237. package/dist/src/commands/evaluate.d.ts.map +1 -0
  238. package/dist/src/commands/evaluate.js +49 -0
  239. package/dist/src/commands/evaluate.js.map +1 -0
  240. package/dist/src/commands/evolve.d.ts +3 -0
  241. package/dist/src/commands/evolve.d.ts.map +1 -0
  242. package/dist/src/commands/evolve.js +841 -0
  243. package/dist/src/commands/evolve.js.map +1 -0
  244. package/dist/src/commands/experiment.d.ts +3 -0
  245. package/dist/src/commands/experiment.d.ts.map +1 -0
  246. package/dist/src/commands/experiment.js +286 -0
  247. package/dist/src/commands/experiment.js.map +1 -0
  248. package/dist/src/commands/explain.d.ts +3 -0
  249. package/dist/src/commands/explain.d.ts.map +1 -0
  250. package/dist/src/commands/explain.js +159 -0
  251. package/dist/src/commands/explain.js.map +1 -0
  252. package/dist/src/commands/federate.d.ts +3 -0
  253. package/dist/src/commands/federate.d.ts.map +1 -0
  254. package/dist/src/commands/federate.js +148 -0
  255. package/dist/src/commands/federate.js.map +1 -0
  256. package/dist/src/commands/fingerprint.d.ts +3 -0
  257. package/dist/src/commands/fingerprint.d.ts.map +1 -0
  258. package/dist/src/commands/fingerprint.js +65 -0
  259. package/dist/src/commands/fingerprint.js.map +1 -0
  260. package/dist/src/commands/fix.d.ts +3 -0
  261. package/dist/src/commands/fix.d.ts.map +1 -0
  262. package/dist/src/commands/fix.js +160 -0
  263. package/dist/src/commands/fix.js.map +1 -0
  264. package/dist/src/commands/fleet.d.ts +3 -0
  265. package/dist/src/commands/fleet.d.ts.map +1 -0
  266. package/dist/src/commands/fleet.js +311 -0
  267. package/dist/src/commands/fleet.js.map +1 -0
  268. package/dist/src/commands/forecast.d.ts +3 -0
  269. package/dist/src/commands/forecast.d.ts.map +1 -0
  270. package/dist/src/commands/forecast.js +522 -0
  271. package/dist/src/commands/forecast.js.map +1 -0
  272. package/dist/src/commands/govern.d.ts +3 -0
  273. package/dist/src/commands/govern.d.ts.map +1 -0
  274. package/dist/src/commands/govern.js +280 -0
  275. package/dist/src/commands/govern.js.map +1 -0
  276. package/dist/src/commands/health.d.ts +17 -0
  277. package/dist/src/commands/health.d.ts.map +1 -0
  278. package/dist/src/commands/health.js +491 -0
  279. package/dist/src/commands/health.js.map +1 -0
  280. package/dist/src/commands/impact.d.ts +3 -0
  281. package/dist/src/commands/impact.d.ts.map +1 -0
  282. package/dist/src/commands/impact.js +186 -0
  283. package/dist/src/commands/impact.js.map +1 -0
  284. package/dist/src/commands/improve.d.ts +3 -0
  285. package/dist/src/commands/improve.d.ts.map +1 -0
  286. package/dist/src/commands/improve.js +240 -0
  287. package/dist/src/commands/improve.js.map +1 -0
  288. package/dist/src/commands/inbox.d.ts +29 -0
  289. package/dist/src/commands/inbox.d.ts.map +1 -0
  290. package/dist/src/commands/inbox.js +538 -0
  291. package/dist/src/commands/inbox.js.map +1 -0
  292. package/dist/src/commands/incident.d.ts +15 -0
  293. package/dist/src/commands/incident.d.ts.map +1 -0
  294. package/dist/src/commands/incident.js +318 -0
  295. package/dist/src/commands/incident.js.map +1 -0
  296. package/dist/src/commands/init.d.ts +3 -0
  297. package/dist/src/commands/init.d.ts.map +1 -0
  298. package/dist/src/commands/init.js +97 -0
  299. package/dist/src/commands/init.js.map +1 -0
  300. package/dist/src/commands/journal.d.ts +3 -0
  301. package/dist/src/commands/journal.d.ts.map +1 -0
  302. package/dist/src/commands/journal.js +146 -0
  303. package/dist/src/commands/journal.js.map +1 -0
  304. package/dist/src/commands/learn.d.ts +9 -0
  305. package/dist/src/commands/learn.d.ts.map +1 -0
  306. package/dist/src/commands/learn.js +107 -0
  307. package/dist/src/commands/learn.js.map +1 -0
  308. package/dist/src/commands/mayday.d.ts +3 -0
  309. package/dist/src/commands/mayday.d.ts.map +1 -0
  310. package/dist/src/commands/mayday.js +145 -0
  311. package/dist/src/commands/mayday.js.map +1 -0
  312. package/dist/src/commands/memory.d.ts +3 -0
  313. package/dist/src/commands/memory.d.ts.map +1 -0
  314. package/dist/src/commands/memory.js +113 -0
  315. package/dist/src/commands/memory.js.map +1 -0
  316. package/dist/src/commands/mentor.d.ts +3 -0
  317. package/dist/src/commands/mentor.d.ts.map +1 -0
  318. package/dist/src/commands/mentor.js +225 -0
  319. package/dist/src/commands/mentor.js.map +1 -0
  320. package/dist/src/commands/migrate.d.ts +3 -0
  321. package/dist/src/commands/migrate.d.ts.map +1 -0
  322. package/dist/src/commands/migrate.js +169 -0
  323. package/dist/src/commands/migrate.js.map +1 -0
  324. package/dist/src/commands/models.d.ts +3 -0
  325. package/dist/src/commands/models.d.ts.map +1 -0
  326. package/dist/src/commands/models.js +396 -0
  327. package/dist/src/commands/models.js.map +1 -0
  328. package/dist/src/commands/multi-repo.d.ts +18 -0
  329. package/dist/src/commands/multi-repo.d.ts.map +1 -0
  330. package/dist/src/commands/multi-repo.js +423 -0
  331. package/dist/src/commands/multi-repo.js.map +1 -0
  332. package/dist/src/commands/negotiate.d.ts +3 -0
  333. package/dist/src/commands/negotiate.d.ts.map +1 -0
  334. package/dist/src/commands/negotiate.js +239 -0
  335. package/dist/src/commands/negotiate.js.map +1 -0
  336. package/dist/src/commands/observe.d.ts +3 -0
  337. package/dist/src/commands/observe.d.ts.map +1 -0
  338. package/dist/src/commands/observe.js +445 -0
  339. package/dist/src/commands/observe.js.map +1 -0
  340. package/dist/src/commands/onboard.d.ts +3 -0
  341. package/dist/src/commands/onboard.d.ts.map +1 -0
  342. package/dist/src/commands/onboard.js +263 -0
  343. package/dist/src/commands/onboard.js.map +1 -0
  344. package/dist/src/commands/optimize.d.ts +3 -0
  345. package/dist/src/commands/optimize.d.ts.map +1 -0
  346. package/dist/src/commands/optimize.js +340 -0
  347. package/dist/src/commands/optimize.js.map +1 -0
  348. package/dist/src/commands/own.d.ts +3 -0
  349. package/dist/src/commands/own.d.ts.map +1 -0
  350. package/dist/src/commands/own.js +499 -0
  351. package/dist/src/commands/own.js.map +1 -0
  352. package/dist/src/commands/pair.d.ts +3 -0
  353. package/dist/src/commands/pair.d.ts.map +1 -0
  354. package/dist/src/commands/pair.js +206 -0
  355. package/dist/src/commands/pair.js.map +1 -0
  356. package/dist/src/commands/pipeline.d.ts +3 -0
  357. package/dist/src/commands/pipeline.d.ts.map +1 -0
  358. package/dist/src/commands/pipeline.js +143 -0
  359. package/dist/src/commands/pipeline.js.map +1 -0
  360. package/dist/src/commands/plan.d.ts +3 -0
  361. package/dist/src/commands/plan.d.ts.map +1 -0
  362. package/dist/src/commands/plan.js +49 -0
  363. package/dist/src/commands/plan.js.map +1 -0
  364. package/dist/src/commands/plugin.d.ts +3 -0
  365. package/dist/src/commands/plugin.d.ts.map +1 -0
  366. package/dist/src/commands/plugin.js +114 -0
  367. package/dist/src/commands/plugin.js.map +1 -0
  368. package/dist/src/commands/pm.d.ts +38 -0
  369. package/dist/src/commands/pm.d.ts.map +1 -0
  370. package/dist/src/commands/pm.js +664 -0
  371. package/dist/src/commands/pm.js.map +1 -0
  372. package/dist/src/commands/pr.d.ts +3 -0
  373. package/dist/src/commands/pr.d.ts.map +1 -0
  374. package/dist/src/commands/pr.js +225 -0
  375. package/dist/src/commands/pr.js.map +1 -0
  376. package/dist/src/commands/prompt-guard.d.ts +3 -0
  377. package/dist/src/commands/prompt-guard.d.ts.map +1 -0
  378. package/dist/src/commands/prompt-guard.js +54 -0
  379. package/dist/src/commands/prompt-guard.js.map +1 -0
  380. package/dist/src/commands/provenance.d.ts +3 -0
  381. package/dist/src/commands/provenance.d.ts.map +1 -0
  382. package/dist/src/commands/provenance.js +96 -0
  383. package/dist/src/commands/provenance.js.map +1 -0
  384. package/dist/src/commands/recover.d.ts +3 -0
  385. package/dist/src/commands/recover.d.ts.map +1 -0
  386. package/dist/src/commands/recover.js +32 -0
  387. package/dist/src/commands/recover.js.map +1 -0
  388. package/dist/src/commands/refactor.d.ts +3 -0
  389. package/dist/src/commands/refactor.d.ts.map +1 -0
  390. package/dist/src/commands/refactor.js +143 -0
  391. package/dist/src/commands/refactor.js.map +1 -0
  392. package/dist/src/commands/report.d.ts +66 -0
  393. package/dist/src/commands/report.d.ts.map +1 -0
  394. package/dist/src/commands/report.js +493 -0
  395. package/dist/src/commands/report.js.map +1 -0
  396. package/dist/src/commands/retro.d.ts +71 -0
  397. package/dist/src/commands/retro.d.ts.map +1 -0
  398. package/dist/src/commands/retro.js +449 -0
  399. package/dist/src/commands/retro.js.map +1 -0
  400. package/dist/src/commands/review.d.ts +3 -0
  401. package/dist/src/commands/review.d.ts.map +1 -0
  402. package/dist/src/commands/review.js +202 -0
  403. package/dist/src/commands/review.js.map +1 -0
  404. package/dist/src/commands/risk.d.ts +3 -0
  405. package/dist/src/commands/risk.d.ts.map +1 -0
  406. package/dist/src/commands/risk.js +110 -0
  407. package/dist/src/commands/risk.js.map +1 -0
  408. package/dist/src/commands/roadmap.d.ts +3 -0
  409. package/dist/src/commands/roadmap.d.ts.map +1 -0
  410. package/dist/src/commands/roadmap.js +506 -0
  411. package/dist/src/commands/roadmap.js.map +1 -0
  412. package/dist/src/commands/runtime-monitor.d.ts +3 -0
  413. package/dist/src/commands/runtime-monitor.d.ts.map +1 -0
  414. package/dist/src/commands/runtime-monitor.js +154 -0
  415. package/dist/src/commands/runtime-monitor.js.map +1 -0
  416. package/dist/src/commands/sandbox.d.ts +3 -0
  417. package/dist/src/commands/sandbox.d.ts.map +1 -0
  418. package/dist/src/commands/sandbox.js +201 -0
  419. package/dist/src/commands/sandbox.js.map +1 -0
  420. package/dist/src/commands/scope.d.ts +3 -0
  421. package/dist/src/commands/scope.d.ts.map +1 -0
  422. package/dist/src/commands/scope.js +192 -0
  423. package/dist/src/commands/scope.js.map +1 -0
  424. package/dist/src/commands/secrets.d.ts +3 -0
  425. package/dist/src/commands/secrets.d.ts.map +1 -0
  426. package/dist/src/commands/secrets.js +99 -0
  427. package/dist/src/commands/secrets.js.map +1 -0
  428. package/dist/src/commands/secure.d.ts +3 -0
  429. package/dist/src/commands/secure.d.ts.map +1 -0
  430. package/dist/src/commands/secure.js +215 -0
  431. package/dist/src/commands/secure.js.map +1 -0
  432. package/dist/src/commands/server.d.ts +3 -0
  433. package/dist/src/commands/server.d.ts.map +1 -0
  434. package/dist/src/commands/server.js +228 -0
  435. package/dist/src/commands/server.js.map +1 -0
  436. package/dist/src/commands/shared.d.ts +18 -0
  437. package/dist/src/commands/shared.d.ts.map +1 -0
  438. package/dist/src/commands/shared.js +61 -0
  439. package/dist/src/commands/shared.js.map +1 -0
  440. package/dist/src/commands/simplify.d.ts +3 -0
  441. package/dist/src/commands/simplify.d.ts.map +1 -0
  442. package/dist/src/commands/simplify.js +194 -0
  443. package/dist/src/commands/simplify.js.map +1 -0
  444. package/dist/src/commands/simulate.d.ts +3 -0
  445. package/dist/src/commands/simulate.d.ts.map +1 -0
  446. package/dist/src/commands/simulate.js +275 -0
  447. package/dist/src/commands/simulate.js.map +1 -0
  448. package/dist/src/commands/slo.d.ts +3 -0
  449. package/dist/src/commands/slo.d.ts.map +1 -0
  450. package/dist/src/commands/slo.js +341 -0
  451. package/dist/src/commands/slo.js.map +1 -0
  452. package/dist/src/commands/spawn-capability.d.ts +3 -0
  453. package/dist/src/commands/spawn-capability.d.ts.map +1 -0
  454. package/dist/src/commands/spawn-capability.js +153 -0
  455. package/dist/src/commands/spawn-capability.js.map +1 -0
  456. package/dist/src/commands/specialize.d.ts +3 -0
  457. package/dist/src/commands/specialize.d.ts.map +1 -0
  458. package/dist/src/commands/specialize.js +266 -0
  459. package/dist/src/commands/specialize.js.map +1 -0
  460. package/dist/src/commands/spike.d.ts +3 -0
  461. package/dist/src/commands/spike.d.ts.map +1 -0
  462. package/dist/src/commands/spike.js +109 -0
  463. package/dist/src/commands/spike.js.map +1 -0
  464. package/dist/src/commands/standup.d.ts +3 -0
  465. package/dist/src/commands/standup.d.ts.map +1 -0
  466. package/dist/src/commands/standup.js +76 -0
  467. package/dist/src/commands/standup.js.map +1 -0
  468. package/dist/src/commands/stats.d.ts +39 -0
  469. package/dist/src/commands/stats.d.ts.map +1 -0
  470. package/dist/src/commands/stats.js +185 -0
  471. package/dist/src/commands/stats.js.map +1 -0
  472. package/dist/src/commands/status.d.ts +3 -0
  473. package/dist/src/commands/status.d.ts.map +1 -0
  474. package/dist/src/commands/status.js +65 -0
  475. package/dist/src/commands/status.js.map +1 -0
  476. package/dist/src/commands/supply-chain.d.ts +3 -0
  477. package/dist/src/commands/supply-chain.d.ts.map +1 -0
  478. package/dist/src/commands/supply-chain.js +110 -0
  479. package/dist/src/commands/supply-chain.js.map +1 -0
  480. package/dist/src/commands/system.d.ts +3 -0
  481. package/dist/src/commands/system.d.ts.map +1 -0
  482. package/dist/src/commands/system.js +582 -0
  483. package/dist/src/commands/system.js.map +1 -0
  484. package/dist/src/commands/teach.d.ts +3 -0
  485. package/dist/src/commands/teach.d.ts.map +1 -0
  486. package/dist/src/commands/teach.js +420 -0
  487. package/dist/src/commands/teach.js.map +1 -0
  488. package/dist/src/commands/team.d.ts +44 -0
  489. package/dist/src/commands/team.d.ts.map +1 -0
  490. package/dist/src/commands/team.js +355 -0
  491. package/dist/src/commands/team.js.map +1 -0
  492. package/dist/src/commands/telemetry.d.ts +3 -0
  493. package/dist/src/commands/telemetry.d.ts.map +1 -0
  494. package/dist/src/commands/telemetry.js +87 -0
  495. package/dist/src/commands/telemetry.js.map +1 -0
  496. package/dist/src/commands/test-gen.d.ts +3 -0
  497. package/dist/src/commands/test-gen.d.ts.map +1 -0
  498. package/dist/src/commands/test-gen.js +541 -0
  499. package/dist/src/commands/test-gen.js.map +1 -0
  500. package/dist/src/commands/test.d.ts +3 -0
  501. package/dist/src/commands/test.d.ts.map +1 -0
  502. package/dist/src/commands/test.js +84 -0
  503. package/dist/src/commands/test.js.map +1 -0
  504. package/dist/src/commands/watch.d.ts +11 -0
  505. package/dist/src/commands/watch.d.ts.map +1 -0
  506. package/dist/src/commands/watch.js +317 -0
  507. package/dist/src/commands/watch.js.map +1 -0
  508. package/dist/src/core/activity-tracker.d.ts +70 -0
  509. package/dist/src/core/activity-tracker.d.ts.map +1 -0
  510. package/dist/src/core/activity-tracker.js +294 -0
  511. package/dist/src/core/activity-tracker.js.map +1 -0
  512. package/dist/src/core/agent-bus.d.ts +38 -0
  513. package/dist/src/core/agent-bus.d.ts.map +1 -0
  514. package/dist/src/core/agent-bus.js +128 -0
  515. package/dist/src/core/agent-bus.js.map +1 -0
  516. package/dist/src/core/agent-loop.d.ts +79 -0
  517. package/dist/src/core/agent-loop.d.ts.map +1 -0
  518. package/dist/src/core/agent-loop.js +225 -0
  519. package/dist/src/core/agent-loop.js.map +1 -0
  520. package/dist/src/core/agent-manager.d.ts +90 -0
  521. package/dist/src/core/agent-manager.d.ts.map +1 -0
  522. package/dist/src/core/agent-manager.js +549 -0
  523. package/dist/src/core/agent-manager.js.map +1 -0
  524. package/dist/src/core/agent-process.d.ts +95 -0
  525. package/dist/src/core/agent-process.d.ts.map +1 -0
  526. package/dist/src/core/agent-process.js +428 -0
  527. package/dist/src/core/agent-process.js.map +1 -0
  528. package/dist/src/core/ambiguity-detector.d.ts +31 -0
  529. package/dist/src/core/ambiguity-detector.d.ts.map +1 -0
  530. package/dist/src/core/ambiguity-detector.js +286 -0
  531. package/dist/src/core/ambiguity-detector.js.map +1 -0
  532. package/dist/src/core/anomaly-detector.d.ts +30 -0
  533. package/dist/src/core/anomaly-detector.d.ts.map +1 -0
  534. package/dist/src/core/anomaly-detector.js +300 -0
  535. package/dist/src/core/anomaly-detector.js.map +1 -0
  536. package/dist/src/core/api-lifecycle.d.ts +11 -0
  537. package/dist/src/core/api-lifecycle.d.ts.map +1 -0
  538. package/dist/src/core/api-lifecycle.js +178 -0
  539. package/dist/src/core/api-lifecycle.js.map +1 -0
  540. package/dist/src/core/audit.d.ts +36 -0
  541. package/dist/src/core/audit.d.ts.map +1 -0
  542. package/dist/src/core/audit.js +88 -0
  543. package/dist/src/core/audit.js.map +1 -0
  544. package/dist/src/core/capability-spawner.d.ts +17 -0
  545. package/dist/src/core/capability-spawner.d.ts.map +1 -0
  546. package/dist/src/core/capability-spawner.js +170 -0
  547. package/dist/src/core/capability-spawner.js.map +1 -0
  548. package/dist/src/core/codebase-index.d.ts +53 -0
  549. package/dist/src/core/codebase-index.d.ts.map +1 -0
  550. package/dist/src/core/codebase-index.js +540 -0
  551. package/dist/src/core/codebase-index.js.map +1 -0
  552. package/dist/src/core/codebase-scanner.d.ts +3 -0
  553. package/dist/src/core/codebase-scanner.d.ts.map +1 -0
  554. package/dist/src/core/codebase-scanner.js +179 -0
  555. package/dist/src/core/codebase-scanner.js.map +1 -0
  556. package/dist/src/core/competitive-intel.d.ts +18 -0
  557. package/dist/src/core/competitive-intel.d.ts.map +1 -0
  558. package/dist/src/core/competitive-intel.js +167 -0
  559. package/dist/src/core/competitive-intel.js.map +1 -0
  560. package/dist/src/core/config.d.ts +14 -0
  561. package/dist/src/core/config.d.ts.map +1 -0
  562. package/dist/src/core/config.js +116 -0
  563. package/dist/src/core/config.js.map +1 -0
  564. package/dist/src/core/convention-extractor.d.ts +8 -0
  565. package/dist/src/core/convention-extractor.d.ts.map +1 -0
  566. package/dist/src/core/convention-extractor.js +382 -0
  567. package/dist/src/core/convention-extractor.js.map +1 -0
  568. package/dist/src/core/cost-tracker.d.ts +23 -0
  569. package/dist/src/core/cost-tracker.d.ts.map +1 -0
  570. package/dist/src/core/cost-tracker.js +83 -0
  571. package/dist/src/core/cost-tracker.js.map +1 -0
  572. package/dist/src/core/decision-journal.d.ts +41 -0
  573. package/dist/src/core/decision-journal.d.ts.map +1 -0
  574. package/dist/src/core/decision-journal.js +204 -0
  575. package/dist/src/core/decision-journal.js.map +1 -0
  576. package/dist/src/core/experiment-engine.d.ts +31 -0
  577. package/dist/src/core/experiment-engine.d.ts.map +1 -0
  578. package/dist/src/core/experiment-engine.js +248 -0
  579. package/dist/src/core/experiment-engine.js.map +1 -0
  580. package/dist/src/core/federation.d.ts +18 -0
  581. package/dist/src/core/federation.d.ts.map +1 -0
  582. package/dist/src/core/federation.js +202 -0
  583. package/dist/src/core/federation.js.map +1 -0
  584. package/dist/src/core/fingerprint.d.ts +42 -0
  585. package/dist/src/core/fingerprint.d.ts.map +1 -0
  586. package/dist/src/core/fingerprint.js +316 -0
  587. package/dist/src/core/fingerprint.js.map +1 -0
  588. package/dist/src/core/git.d.ts +50 -0
  589. package/dist/src/core/git.d.ts.map +1 -0
  590. package/dist/src/core/git.js +342 -0
  591. package/dist/src/core/git.js.map +1 -0
  592. package/dist/src/core/governance.d.ts +53 -0
  593. package/dist/src/core/governance.d.ts.map +1 -0
  594. package/dist/src/core/governance.js +276 -0
  595. package/dist/src/core/governance.js.map +1 -0
  596. package/dist/src/core/guardrails.d.ts +34 -0
  597. package/dist/src/core/guardrails.d.ts.map +1 -0
  598. package/dist/src/core/guardrails.js +575 -0
  599. package/dist/src/core/guardrails.js.map +1 -0
  600. package/dist/src/core/impact-analyzer.d.ts +56 -0
  601. package/dist/src/core/impact-analyzer.d.ts.map +1 -0
  602. package/dist/src/core/impact-analyzer.js +309 -0
  603. package/dist/src/core/impact-analyzer.js.map +1 -0
  604. package/dist/src/core/input-listener.d.ts +45 -0
  605. package/dist/src/core/input-listener.d.ts.map +1 -0
  606. package/dist/src/core/input-listener.js +116 -0
  607. package/dist/src/core/input-listener.js.map +1 -0
  608. package/dist/src/core/memory-store.d.ts +61 -0
  609. package/dist/src/core/memory-store.d.ts.map +1 -0
  610. package/dist/src/core/memory-store.js +195 -0
  611. package/dist/src/core/memory-store.js.map +1 -0
  612. package/dist/src/core/observability.d.ts +56 -0
  613. package/dist/src/core/observability.d.ts.map +1 -0
  614. package/dist/src/core/observability.js +305 -0
  615. package/dist/src/core/observability.js.map +1 -0
  616. package/dist/src/core/pair-engine.d.ts +47 -0
  617. package/dist/src/core/pair-engine.d.ts.map +1 -0
  618. package/dist/src/core/pair-engine.js +355 -0
  619. package/dist/src/core/pair-engine.js.map +1 -0
  620. package/dist/src/core/perf-analyzer.d.ts +35 -0
  621. package/dist/src/core/perf-analyzer.d.ts.map +1 -0
  622. package/dist/src/core/perf-analyzer.js +598 -0
  623. package/dist/src/core/perf-analyzer.js.map +1 -0
  624. package/dist/src/core/pipeline-loader.d.ts +46 -0
  625. package/dist/src/core/pipeline-loader.d.ts.map +1 -0
  626. package/dist/src/core/pipeline-loader.js +182 -0
  627. package/dist/src/core/pipeline-loader.js.map +1 -0
  628. package/dist/src/core/pipeline.d.ts +183 -0
  629. package/dist/src/core/pipeline.d.ts.map +1 -0
  630. package/dist/src/core/pipeline.js +2264 -0
  631. package/dist/src/core/pipeline.js.map +1 -0
  632. package/dist/src/core/plugins.d.ts +61 -0
  633. package/dist/src/core/plugins.d.ts.map +1 -0
  634. package/dist/src/core/plugins.js +114 -0
  635. package/dist/src/core/plugins.js.map +1 -0
  636. package/dist/src/core/preflight.d.ts +7 -0
  637. package/dist/src/core/preflight.d.ts.map +1 -0
  638. package/dist/src/core/preflight.js +35 -0
  639. package/dist/src/core/preflight.js.map +1 -0
  640. package/dist/src/core/prompt-guard.d.ts +44 -0
  641. package/dist/src/core/prompt-guard.d.ts.map +1 -0
  642. package/dist/src/core/prompt-guard.js +274 -0
  643. package/dist/src/core/prompt-guard.js.map +1 -0
  644. package/dist/src/core/provenance.d.ts +39 -0
  645. package/dist/src/core/provenance.d.ts.map +1 -0
  646. package/dist/src/core/provenance.js +115 -0
  647. package/dist/src/core/provenance.js.map +1 -0
  648. package/dist/src/core/providers/anthropic-api.d.ts +34 -0
  649. package/dist/src/core/providers/anthropic-api.d.ts.map +1 -0
  650. package/dist/src/core/providers/anthropic-api.js +522 -0
  651. package/dist/src/core/providers/anthropic-api.js.map +1 -0
  652. package/dist/src/core/providers/api-agent-backend.d.ts +31 -0
  653. package/dist/src/core/providers/api-agent-backend.d.ts.map +1 -0
  654. package/dist/src/core/providers/api-agent-backend.js +127 -0
  655. package/dist/src/core/providers/api-agent-backend.js.map +1 -0
  656. package/dist/src/core/providers/api-text-backend.d.ts +35 -0
  657. package/dist/src/core/providers/api-text-backend.d.ts.map +1 -0
  658. package/dist/src/core/providers/api-text-backend.js +145 -0
  659. package/dist/src/core/providers/api-text-backend.js.map +1 -0
  660. package/dist/src/core/providers/claude-cli.d.ts +80 -0
  661. package/dist/src/core/providers/claude-cli.d.ts.map +1 -0
  662. package/dist/src/core/providers/claude-cli.js +489 -0
  663. package/dist/src/core/providers/claude-cli.js.map +1 -0
  664. package/dist/src/core/providers/cost-table.d.ts +46 -0
  665. package/dist/src/core/providers/cost-table.d.ts.map +1 -0
  666. package/dist/src/core/providers/cost-table.js +136 -0
  667. package/dist/src/core/providers/cost-table.js.map +1 -0
  668. package/dist/src/core/providers/google.d.ts +26 -0
  669. package/dist/src/core/providers/google.d.ts.map +1 -0
  670. package/dist/src/core/providers/google.js +165 -0
  671. package/dist/src/core/providers/google.js.map +1 -0
  672. package/dist/src/core/providers/model-catalog.d.ts +72 -0
  673. package/dist/src/core/providers/model-catalog.d.ts.map +1 -0
  674. package/dist/src/core/providers/model-catalog.js +290 -0
  675. package/dist/src/core/providers/model-catalog.js.map +1 -0
  676. package/dist/src/core/providers/openai-compat.d.ts +35 -0
  677. package/dist/src/core/providers/openai-compat.d.ts.map +1 -0
  678. package/dist/src/core/providers/openai-compat.js +479 -0
  679. package/dist/src/core/providers/openai-compat.js.map +1 -0
  680. package/dist/src/core/providers/prompt-adapter.d.ts +42 -0
  681. package/dist/src/core/providers/prompt-adapter.d.ts.map +1 -0
  682. package/dist/src/core/providers/prompt-adapter.js +282 -0
  683. package/dist/src/core/providers/prompt-adapter.js.map +1 -0
  684. package/dist/src/core/providers/registry.d.ts +93 -0
  685. package/dist/src/core/providers/registry.d.ts.map +1 -0
  686. package/dist/src/core/providers/registry.js +191 -0
  687. package/dist/src/core/providers/registry.js.map +1 -0
  688. package/dist/src/core/providers/types.d.ts +165 -0
  689. package/dist/src/core/providers/types.d.ts.map +1 -0
  690. package/dist/src/core/providers/types.js +13 -0
  691. package/dist/src/core/providers/types.js.map +1 -0
  692. package/dist/src/core/quality.d.ts +22 -0
  693. package/dist/src/core/quality.d.ts.map +1 -0
  694. package/dist/src/core/quality.js +209 -0
  695. package/dist/src/core/quality.js.map +1 -0
  696. package/dist/src/core/resource-allocator.d.ts +37 -0
  697. package/dist/src/core/resource-allocator.d.ts.map +1 -0
  698. package/dist/src/core/resource-allocator.js +480 -0
  699. package/dist/src/core/resource-allocator.js.map +1 -0
  700. package/dist/src/core/risk-scorer.d.ts +35 -0
  701. package/dist/src/core/risk-scorer.d.ts.map +1 -0
  702. package/dist/src/core/risk-scorer.js +226 -0
  703. package/dist/src/core/risk-scorer.js.map +1 -0
  704. package/dist/src/core/runtime-monitor.d.ts +49 -0
  705. package/dist/src/core/runtime-monitor.d.ts.map +1 -0
  706. package/dist/src/core/runtime-monitor.js +235 -0
  707. package/dist/src/core/runtime-monitor.js.map +1 -0
  708. package/dist/src/core/sandbox.d.ts +47 -0
  709. package/dist/src/core/sandbox.d.ts.map +1 -0
  710. package/dist/src/core/sandbox.js +277 -0
  711. package/dist/src/core/sandbox.js.map +1 -0
  712. package/dist/src/core/secret-detector.d.ts +25 -0
  713. package/dist/src/core/secret-detector.d.ts.map +1 -0
  714. package/dist/src/core/secret-detector.js +307 -0
  715. package/dist/src/core/secret-detector.js.map +1 -0
  716. package/dist/src/core/security-scanner.d.ts +36 -0
  717. package/dist/src/core/security-scanner.d.ts.map +1 -0
  718. package/dist/src/core/security-scanner.js +366 -0
  719. package/dist/src/core/security-scanner.js.map +1 -0
  720. package/dist/src/core/self-improvement.d.ts +26 -0
  721. package/dist/src/core/self-improvement.d.ts.map +1 -0
  722. package/dist/src/core/self-improvement.js +356 -0
  723. package/dist/src/core/self-improvement.js.map +1 -0
  724. package/dist/src/core/simulator.d.ts +28 -0
  725. package/dist/src/core/simulator.d.ts.map +1 -0
  726. package/dist/src/core/simulator.js +334 -0
  727. package/dist/src/core/simulator.js.map +1 -0
  728. package/dist/src/core/specialization.d.ts +47 -0
  729. package/dist/src/core/specialization.d.ts.map +1 -0
  730. package/dist/src/core/specialization.js +239 -0
  731. package/dist/src/core/specialization.js.map +1 -0
  732. package/dist/src/core/stakeholder-engine.d.ts +47 -0
  733. package/dist/src/core/stakeholder-engine.d.ts.map +1 -0
  734. package/dist/src/core/stakeholder-engine.js +380 -0
  735. package/dist/src/core/stakeholder-engine.js.map +1 -0
  736. package/dist/src/core/state.d.ts +99 -0
  737. package/dist/src/core/state.d.ts.map +1 -0
  738. package/dist/src/core/state.js +545 -0
  739. package/dist/src/core/state.js.map +1 -0
  740. package/dist/src/core/supply-chain.d.ts +26 -0
  741. package/dist/src/core/supply-chain.d.ts.map +1 -0
  742. package/dist/src/core/supply-chain.js +332 -0
  743. package/dist/src/core/supply-chain.js.map +1 -0
  744. package/dist/src/core/telemetry.d.ts +110 -0
  745. package/dist/src/core/telemetry.d.ts.map +1 -0
  746. package/dist/src/core/telemetry.js +276 -0
  747. package/dist/src/core/telemetry.js.map +1 -0
  748. package/dist/src/core/tools/definitions.d.ts +10 -0
  749. package/dist/src/core/tools/definitions.d.ts.map +1 -0
  750. package/dist/src/core/tools/definitions.js +165 -0
  751. package/dist/src/core/tools/definitions.js.map +1 -0
  752. package/dist/src/core/tools/executor.d.ts +27 -0
  753. package/dist/src/core/tools/executor.d.ts.map +1 -0
  754. package/dist/src/core/tools/executor.js +306 -0
  755. package/dist/src/core/tools/executor.js.map +1 -0
  756. package/dist/src/core/tools/sandbox.d.ts +41 -0
  757. package/dist/src/core/tools/sandbox.d.ts.map +1 -0
  758. package/dist/src/core/tools/sandbox.js +106 -0
  759. package/dist/src/core/tools/sandbox.js.map +1 -0
  760. package/dist/src/core/training-pipeline.d.ts +66 -0
  761. package/dist/src/core/training-pipeline.d.ts.map +1 -0
  762. package/dist/src/core/training-pipeline.js +267 -0
  763. package/dist/src/core/training-pipeline.js.map +1 -0
  764. package/dist/src/core/triage.d.ts +56 -0
  765. package/dist/src/core/triage.d.ts.map +1 -0
  766. package/dist/src/core/triage.js +227 -0
  767. package/dist/src/core/triage.js.map +1 -0
  768. package/dist/src/core/user-intelligence.d.ts +29 -0
  769. package/dist/src/core/user-intelligence.d.ts.map +1 -0
  770. package/dist/src/core/user-intelligence.js +172 -0
  771. package/dist/src/core/user-intelligence.js.map +1 -0
  772. package/dist/src/core/webhooks.d.ts +32 -0
  773. package/dist/src/core/webhooks.d.ts.map +1 -0
  774. package/dist/src/core/webhooks.js +126 -0
  775. package/dist/src/core/webhooks.js.map +1 -0
  776. package/dist/src/core/ws-server.d.ts +43 -0
  777. package/dist/src/core/ws-server.d.ts.map +1 -0
  778. package/dist/src/core/ws-server.js +3278 -0
  779. package/dist/src/core/ws-server.js.map +1 -0
  780. package/dist/src/prompts/loader.d.ts +36 -0
  781. package/dist/src/prompts/loader.d.ts.map +1 -0
  782. package/dist/src/prompts/loader.js +156 -0
  783. package/dist/src/prompts/loader.js.map +1 -0
  784. package/dist/src/types.d.ts +2539 -0
  785. package/dist/src/types.d.ts.map +1 -0
  786. package/dist/src/types.js +62 -0
  787. package/dist/src/types.js.map +1 -0
  788. package/package.json +66 -0
@@ -0,0 +1,2264 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import chalk from 'chalk';
5
+ import { STAGE_ARTIFACT_MAP } from '../types.js';
6
+ import { parse as parseYaml } from 'yaml';
7
+ import { isGhInstalled, createPR, buildPRBody, getCurrentBranch, hasUncommittedChanges } from './git.js';
8
+ import { stageTransitionPause, fixLoopPause } from './input-listener.js';
9
+ import { WebhookManager } from './webhooks.js';
10
+ import { QualityScorer } from './quality.js';
11
+ import { GuardrailsEngine } from './guardrails.js';
12
+ import { scanCodebase } from './codebase-scanner.js';
13
+ import { loadConventions } from '../commands/learn.js';
14
+ import { MemoryStore, recordPipelineSuccess, recordPipelineFailure } from './memory-store.js';
15
+ import { loadPipelineDefinition, stageNameForDefinition } from './pipeline-loader.js';
16
+ // Plugin integration point: custom stages from PluginLoader.getCustomStages()
17
+ // can be injected into the pipeline by converting CustomStagePlugin entries
18
+ // into PipelineStageDefinition objects and merging them based on their
19
+ // before/after positioning. The PluginLoader is instantiated in the command
20
+ // layer (see commands/plugin.ts) and can be wired here when ready.
21
+ // Plugin type used by PluginLoader (see ./plugins.ts)
22
+ // import type { CustomStagePlugin } from './plugins.js';
23
+ // Non-engineer personas: block dangerous tools (Bash, Edit, NotebookEdit)
24
+ // They can only use Read, Glob, Grep, Write. Filename is enforced via prompt + system prompt.
25
+ const NON_ENGINEER_DISALLOWED_TOOLS = ['Bash', 'Edit', 'NotebookEdit'];
26
+ // System-level enforcement appended via --append-system-prompt.
27
+ // This is a system instruction that takes highest priority — the agent cannot override it.
28
+ const ANALYST_SYSTEM_ENFORCEMENT = [
29
+ 'SYSTEM ENFORCEMENT: Your output file MUST be named exactly REQUIREMENTS.md.',
30
+ 'SYSTEM ENFORCEMENT: REQUIREMENTS.md MUST contain these sections in order: ## 0. Original Requirement, ## 1. Summary, ## 2. Scope, ## 3. Functional Requirements, ## 4. Data Requirements, ## 5. UI/UX, ## 6. Non-Functional Requirements, ## 7. Integration, ## 8. Testing, ## 9. Rollout, ## 10. Open Questions, ## 11. Change Tracking, ## 12. Appendix.',
31
+ 'SYSTEM ENFORCEMENT: Section 3 MUST contain user stories in "As a [user] I want [thing] So that [reason]" format with Given/When/Then acceptance criteria.',
32
+ 'SYSTEM ENFORCEMENT: Do NOT write migration plans, decision tables, or free-form documents. Follow the template exactly.',
33
+ ].join('\n');
34
+ const ARCHITECT_SYSTEM_ENFORCEMENT = [
35
+ 'SYSTEM ENFORCEMENT: Your output file MUST be named exactly SPEC.md.',
36
+ 'SYSTEM ENFORCEMENT: SPEC.md MUST contain these sections: ## Overview, ## Requirements Summary, ## Architecture (with Mermaid diagrams), ## Architecture Decision Records, ## Component/Service Architecture, ## Data Model Design, ## API Specification, ## Performance Strategy, ## Testing Strategy, ## Security, ## Implementation Checklist, ## File Structure, ## Open Questions.',
37
+ 'SYSTEM ENFORCEMENT: Include ADR entries (ADR-1, ADR-2, etc.) and Mermaid diagrams. Follow the template exactly.',
38
+ ].join('\n');
39
+ const LEAD_SYSTEM_ENFORCEMENT = [
40
+ 'SYSTEM ENFORCEMENT: Your output file MUST be named exactly TASKS.md.',
41
+ 'SYSTEM ENFORCEMENT: Every task MUST follow this format: - [ ] T001 [P] [US1] Description — `file/path.ext`',
42
+ 'SYSTEM ENFORCEMENT: One task = one file. Every task has [P] if parallelizable, [USn] user story label, AC: acceptance criteria, and an exact file path.',
43
+ 'SYSTEM ENFORCEMENT: Organize into phases: Setup → Foundational (GATE) → User Stories (parallel after gate) → E2E Tests (after stories) → Polish.',
44
+ 'SYSTEM ENFORCEMENT: Do NOT write free-form documents. Follow the spec-kit task format exactly.',
45
+ ].join('\n');
46
+ const TESTER_SYSTEM_ENFORCEMENT = [
47
+ 'SYSTEM ENFORCEMENT: Your output file MUST be named exactly TESTPLAN.md.',
48
+ 'SYSTEM ENFORCEMENT: TESTPLAN.md MUST contain these sections: ## Overview, ## Test Strategy, ## E2E Test Cases, ## Authentication, ## Test Data, ## Acceptance Criteria.',
49
+ 'SYSTEM ENFORCEMENT: Every E2E test case MUST have: ID (TC-001), title, user flow steps, expected assertions, and the target test file path under e2e/.',
50
+ 'SYSTEM ENFORCEMENT: Do NOT write implementation code. Do NOT modify application source. Only produce TESTPLAN.md.',
51
+ 'SYSTEM ENFORCEMENT: If Figma designs are provided, derive visual test cases (layout, responsiveness, component states) from the designs.',
52
+ ].join('\n');
53
+ /** Non-interactive agents (dashboard/headless) need 'auto' permission — they can't prompt the user. */
54
+ function headlessPermission(interactive) {
55
+ return interactive ? undefined : 'auto';
56
+ }
57
+ function sleep(ms) {
58
+ return new Promise(resolve => setTimeout(resolve, ms));
59
+ }
60
+ /** Default test framework configurations per stack */
61
+ const TEST_FRAMEWORKS = {
62
+ // Frontend stacks → Playwright E2E + Vitest unit
63
+ 'react-e2e': {
64
+ kind: 'playwright', name: 'Playwright', testDir: 'e2e', testFilePattern: '*.spec.ts',
65
+ installCmd: 'npm init playwright@latest -- --quiet',
66
+ runCmd: 'PLAYWRIGHT_JSON_OUTPUT_NAME=.swarm/test-results.json npx playwright test --reporter=json,list',
67
+ runCmdHuman: 'npx playwright test',
68
+ category: 'e2e',
69
+ },
70
+ 'react-unit': {
71
+ kind: 'vitest', name: 'Vitest', testDir: 'src/__tests__', testFilePattern: '*.test.ts{,x}',
72
+ installCmd: 'npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom',
73
+ runCmd: 'npx vitest run --reporter=json --outputFile=.swarm/test-results.json',
74
+ runCmdHuman: 'npx vitest run',
75
+ category: 'unit',
76
+ },
77
+ // Node backend → Vitest unit/integration
78
+ 'node-unit': {
79
+ kind: 'vitest', name: 'Vitest', testDir: 'src/__tests__', testFilePattern: '*.test.ts',
80
+ installCmd: 'npm install -D vitest',
81
+ runCmd: 'npx vitest run --reporter=json --outputFile=.swarm/test-results.json',
82
+ runCmdHuman: 'npx vitest run',
83
+ category: 'integration',
84
+ },
85
+ 'node-api': {
86
+ kind: 'vitest', name: 'Vitest + Supertest', testDir: 'test', testFilePattern: '*.test.ts',
87
+ installCmd: 'npm install -D vitest supertest @types/supertest',
88
+ runCmd: 'npx vitest run --reporter=json --outputFile=.swarm/test-results.json',
89
+ runCmdHuman: 'npx vitest run',
90
+ category: 'api',
91
+ },
92
+ // Go → go test
93
+ 'go-unit': {
94
+ kind: 'go-test', name: 'go test', testDir: '.', testFilePattern: '*_test.go',
95
+ installCmd: '', // go test is built-in
96
+ runCmd: 'go test -json ./... > .swarm/test-results.json 2>&1',
97
+ runCmdHuman: 'go test -v ./...',
98
+ category: 'unit',
99
+ },
100
+ // Python → pytest
101
+ 'python-unit': {
102
+ kind: 'pytest', name: 'pytest', testDir: 'tests', testFilePattern: 'test_*.py',
103
+ installCmd: 'pip install pytest pytest-json-report',
104
+ runCmd: 'pytest --json-report --json-report-file=.swarm/test-results.json -v',
105
+ runCmdHuman: 'pytest -v',
106
+ category: 'unit',
107
+ },
108
+ // Rust → cargo test
109
+ 'rust-unit': {
110
+ kind: 'cargo-test', name: 'cargo test', testDir: 'src', testFilePattern: '*.rs',
111
+ installCmd: '', // built-in
112
+ runCmd: 'cargo test -- -Z unstable-options --format json > .swarm/test-results.json 2>&1 || cargo test 2>&1 | tee .swarm/test-results.txt',
113
+ runCmdHuman: 'cargo test',
114
+ category: 'unit',
115
+ },
116
+ // Swift → swift test
117
+ 'swift-unit': {
118
+ kind: 'swift-test', name: 'swift test', testDir: 'Tests', testFilePattern: '*Tests.swift',
119
+ installCmd: '', // built-in
120
+ runCmd: 'swift test 2>&1 | tee .swarm/test-results.txt',
121
+ runCmdHuman: 'swift test',
122
+ category: 'unit',
123
+ },
124
+ };
125
+ /** Resolve which test frameworks to use for a given stack */
126
+ function getTestFrameworks(stack) {
127
+ switch (stack) {
128
+ case 'react':
129
+ return [TEST_FRAMEWORKS['react-unit'], TEST_FRAMEWORKS['react-e2e']];
130
+ case 'node':
131
+ return [TEST_FRAMEWORKS['node-unit'], TEST_FRAMEWORKS['node-api']];
132
+ case 'go':
133
+ return [TEST_FRAMEWORKS['go-unit']];
134
+ case 'python':
135
+ return [TEST_FRAMEWORKS['python-unit']];
136
+ case 'rust':
137
+ return [TEST_FRAMEWORKS['rust-unit']];
138
+ case 'swift':
139
+ return [TEST_FRAMEWORKS['swift-unit']];
140
+ case 'custom':
141
+ // Default to vitest for custom stacks
142
+ return [TEST_FRAMEWORKS['node-unit']];
143
+ default:
144
+ return [TEST_FRAMEWORKS['react-e2e']];
145
+ }
146
+ }
147
+ export class Pipeline {
148
+ agentManager;
149
+ state;
150
+ config;
151
+ budgetExceeded = false;
152
+ gitEnabled = true;
153
+ autoPR = true;
154
+ webhooks;
155
+ quality;
156
+ guardrails;
157
+ _codebaseContext = null;
158
+ _conventionPrompt = null;
159
+ _memoryContext = null;
160
+ memoryStore;
161
+ constructor(agentManager, state, config) {
162
+ this.agentManager = agentManager;
163
+ this.state = state;
164
+ this.config = config;
165
+ this.webhooks = new WebhookManager((config.webhooks ?? []));
166
+ this.quality = new QualityScorer();
167
+ this.guardrails = new GuardrailsEngine(join(this.state.getProjectCwd(), '.swarm'));
168
+ this.memoryStore = new MemoryStore(join(this.state.getProjectCwd(), '.swarm'));
169
+ // Track budget exceeded so we can surface a clear error from waitForAgent rejections
170
+ this.agentManager.on('budget-exceeded', () => {
171
+ this.budgetExceeded = true;
172
+ });
173
+ // Graceful budget degradation: downgrade models when budget is running low
174
+ const costTracker = agentManager.getCostTracker?.();
175
+ if (costTracker) {
176
+ costTracker.on('budget-warning', (info) => {
177
+ if (info.level >= 90 && this.config.model !== 'haiku') {
178
+ console.log(chalk.yellow(`[budget] 90% budget used — switching all remaining agents to haiku`));
179
+ this.config.model = 'haiku';
180
+ this.config.models = { analyst: 'haiku', architect: 'haiku', lead: 'haiku', engineer: 'haiku', tester: 'haiku' };
181
+ }
182
+ else if (info.level >= 80 && this.config.model === 'opus') {
183
+ console.log(chalk.yellow(`[budget] 80% budget used — downgrading from opus to sonnet`));
184
+ this.config.model = 'sonnet';
185
+ if (this.config.models?.engineer === 'opus')
186
+ this.config.models.engineer = 'sonnet';
187
+ }
188
+ });
189
+ }
190
+ }
191
+ /**
192
+ * Get the working directory for the current pipeline.
193
+ * Uses worktree path if available, otherwise falls back to this.projectCwd.
194
+ */
195
+ get projectCwd() {
196
+ return this.state.getProjectCwd();
197
+ }
198
+ /** Scan and cache the codebase context for injecting into stage prompts. */
199
+ getCodebaseContext() {
200
+ if (this._codebaseContext === null) {
201
+ this._codebaseContext = scanCodebase(this.projectCwd, this.config.packages);
202
+ }
203
+ return this._codebaseContext;
204
+ }
205
+ /** Load and cache project conventions for injection into all agent system prompts. */
206
+ getConventionPrompt() {
207
+ if (this._conventionPrompt === null) {
208
+ const swarmDir = join(this.projectCwd, '.swarm');
209
+ this._conventionPrompt = loadConventions(swarmDir);
210
+ }
211
+ return this._conventionPrompt;
212
+ }
213
+ /** Load and cache cross-run memory context for injection into agent prompts. */
214
+ getMemoryContext(tags = []) {
215
+ if (this._memoryContext === null) {
216
+ this._memoryContext = this.memoryStore.buildMemoryContext(tags);
217
+ }
218
+ return this._memoryContext;
219
+ }
220
+ /** Build the full appendSystemPrompt for an agent: enforcement rules + conventions + memory. */
221
+ buildAppendSystemPrompt(enforcement) {
222
+ const parts = [enforcement];
223
+ const conventions = this.getConventionPrompt();
224
+ if (conventions)
225
+ parts.push(conventions);
226
+ const memory = this.getMemoryContext();
227
+ if (memory)
228
+ parts.push(memory);
229
+ return parts.join('\n\n');
230
+ }
231
+ /** Get conventions + memory system prompt for engineers (no enforcement rules needed). */
232
+ getEngineerConventions() {
233
+ const parts = [];
234
+ const conventions = this.getConventionPrompt();
235
+ if (conventions)
236
+ parts.push(conventions);
237
+ const memory = this.getMemoryContext();
238
+ if (memory)
239
+ parts.push(memory);
240
+ return parts.length > 0 ? parts.join('\n\n') : undefined;
241
+ }
242
+ /**
243
+ * Wraps agentManager.waitForAgent to surface a clear budget error
244
+ * when agents are killed due to budget enforcement.
245
+ */
246
+ async waitForAgentWithBudgetCheck(agentId) {
247
+ try {
248
+ return await this.agentManager.waitForAgent(agentId);
249
+ }
250
+ catch (err) {
251
+ if (this.budgetExceeded) {
252
+ const budget = this.config.maxBudgetUsd ?? 0;
253
+ throw new Error(`Budget limit of $${budget} exceeded. All agents killed. ` +
254
+ `Use maxBudgetUsd in .swarm/config.yaml to adjust the limit.`);
255
+ }
256
+ throw err;
257
+ }
258
+ }
259
+ /** Resolve model for a persona — checks per-persona overrides, then falls back to default */
260
+ modelFor(persona) {
261
+ return this.config.models?.[persona] ?? this.config.model;
262
+ }
263
+ slugify(text) {
264
+ return text
265
+ .toLowerCase()
266
+ .replace(/[^a-z0-9]+/g, '-')
267
+ .replace(/^-+|-+$/g, '')
268
+ .slice(0, 40);
269
+ }
270
+ createFeatureBranch(featureRequest) {
271
+ if (!this.gitEnabled)
272
+ return;
273
+ const slug = this.slugify(featureRequest);
274
+ const branch = `swarm/${slug}`;
275
+ try {
276
+ execSync(`git checkout -b ${branch}`, { stdio: 'pipe', cwd: this.projectCwd });
277
+ console.log(chalk.dim(`[git] Created branch: ${branch}`));
278
+ }
279
+ catch {
280
+ // Branch may already exist — try checking it out
281
+ try {
282
+ execSync(`git checkout ${branch}`, { stdio: 'pipe', cwd: this.projectCwd });
283
+ console.log(chalk.dim(`[git] Switched to existing branch: ${branch}`));
284
+ }
285
+ catch {
286
+ console.log(chalk.dim(`[git] Could not create/switch branch: ${branch} — continuing on current branch`));
287
+ }
288
+ }
289
+ }
290
+ autoCommitStage(stage, _artifact) {
291
+ if (!this.gitEnabled)
292
+ return;
293
+ const messages = {
294
+ analyze: 'Generated REQUIREMENTS.md',
295
+ architect: 'Generated SPEC.md',
296
+ plan: 'Generated TASKS.md',
297
+ build: 'Built implementation',
298
+ test: 'Generated TESTPLAN.md',
299
+ evaluate: 'Completed evaluation',
300
+ };
301
+ const msg = messages[stage] ?? `Completed ${stage}`;
302
+ try {
303
+ execSync('git add -A', { stdio: 'pipe', cwd: this.projectCwd });
304
+ execSync(`git commit -m "[swarm:${stage}] ${msg}"`, { stdio: 'pipe', cwd: this.projectCwd });
305
+ console.log(chalk.dim(`[git] Committed: [swarm:${stage}] ${msg}`));
306
+ }
307
+ catch {
308
+ // Nothing to commit is OK
309
+ }
310
+ }
311
+ /**
312
+ * Attempt to auto-create a GitHub PR after a successful MayDay run.
313
+ * Failures are logged as warnings — they never crash the pipeline.
314
+ */
315
+ attemptAutoCreatePR() {
316
+ if (!this.autoPR || !this.gitEnabled)
317
+ return;
318
+ try {
319
+ if (!isGhInstalled()) {
320
+ console.log(chalk.dim('[pr] gh CLI not found — skipping PR creation'));
321
+ return;
322
+ }
323
+ const branch = getCurrentBranch();
324
+ if (branch === 'main' || branch === 'master') {
325
+ console.log(chalk.dim(`[pr] On ${branch} branch — skipping PR creation`));
326
+ return;
327
+ }
328
+ // Auto-commit any remaining uncommitted changes
329
+ if (hasUncommittedChanges()) {
330
+ try {
331
+ execSync('git add -A', { stdio: 'pipe', cwd: this.projectCwd });
332
+ execSync('git commit -m "[swarm] Final changes before PR"', { stdio: 'pipe', cwd: this.projectCwd });
333
+ console.log(chalk.dim('[git] Committed remaining changes'));
334
+ }
335
+ catch {
336
+ // Nothing to commit or commit failed — continue anyway
337
+ }
338
+ }
339
+ // Push the branch
340
+ try {
341
+ execSync(`git push -u origin ${branch}`, { stdio: 'pipe', cwd: this.projectCwd });
342
+ console.log(chalk.dim(`[git] Pushed branch: ${branch}`));
343
+ }
344
+ catch {
345
+ console.log(chalk.yellow('[pr] Could not push branch — skipping PR creation'));
346
+ return;
347
+ }
348
+ const pipelineState = this.state.getState();
349
+ const mayday = pipelineState.mayday;
350
+ const featureRequest = mayday?.featureRequest ?? 'Swarm pipeline run';
351
+ const truncatedTitle = featureRequest.length > 50
352
+ ? featureRequest.slice(0, 50).trim() + '...'
353
+ : featureRequest;
354
+ const title = `feat: ${truncatedTitle}`;
355
+ const body = buildPRBody(pipelineState);
356
+ const prUrl = createPR({ title, body });
357
+ console.log(chalk.green.bold(`[pr] Pull request created: ${prUrl}`));
358
+ // Persist the PR URL in mayday state
359
+ if (mayday) {
360
+ this.state.updateMayday({ prUrl });
361
+ }
362
+ }
363
+ catch (err) {
364
+ console.log(chalk.yellow(`[pr] Could not create PR: ${err instanceof Error ? err.message : err}`));
365
+ }
366
+ }
367
+ async runAnalyze(featureRequest, opts) {
368
+ if (this.state.getState().stages.analyze?.status === 'running') {
369
+ throw new Error('Stage "analyze" is already running');
370
+ }
371
+ const s = opts?.stack ?? this.config.stack;
372
+ const interactive = opts?.interactive ?? true;
373
+ const figmaUrl = opts?.figmaUrl;
374
+ const codebaseCtx = this.getCodebaseContext();
375
+ const promptParts = [
376
+ ...(codebaseCtx ? [codebaseCtx, ''] : []),
377
+ `Feature request: ${featureRequest}`,
378
+ '',
379
+ '⚠️ CRITICAL CONSTRAINTS — VIOLATION WILL CAUSE PIPELINE FAILURE:',
380
+ '- Your ONLY deliverable is REQUIREMENTS.md. Do NOT create any other file.',
381
+ '- Do NOT write implementation code under ANY circumstances. No source files, no scripts.',
382
+ '- Do NOT design architecture, write specs, or create task breakdowns.',
383
+ ...(interactive
384
+ ? ['- Ask clarifying questions, then write REQUIREMENTS.md.']
385
+ : ['- Do NOT ask clarifying questions. You have all the context needed.',
386
+ '- Proceed DIRECTLY to writing REQUIREMENTS.md based on the information provided.',
387
+ '- Make reasonable assumptions where details are missing — document them in Section 10 (Open Questions).']),
388
+ '- Once REQUIREMENTS.md is written, STOP IMMEDIATELY. Do not proceed to any other stage.',
389
+ '',
390
+ '⚠️ FILENAME — The file MUST be named exactly `REQUIREMENTS.md` in the project root.',
391
+ '- Do NOT add suffixes, prefixes, or descriptions to the filename.',
392
+ '- WRONG: REQUIREMENTS-migration.md, REQUIREMENTS-auth.md, requirements.md',
393
+ '- CORRECT: REQUIREMENTS.md',
394
+ '',
395
+ '⚠️ OUTPUT FORMAT — REQUIREMENTS.md MUST follow the EXACT template from your system prompt:',
396
+ '- Sections 0-12 in order: Original Requirement, Summary, Scope, Functional Requirements,',
397
+ ' Data Requirements, UI/UX, Non-Functional Requirements, Integration, Testing, Rollout,',
398
+ ' Open Questions, Change Tracking, Appendix.',
399
+ '- Every user story MUST have Given/When/Then acceptance criteria.',
400
+ '- Do NOT deviate from the template structure. Do NOT skip sections — write "N/A" if not applicable.',
401
+ '- Do NOT write migration plans, decision tables, or free-form documents.',
402
+ '- This is a REQUIREMENTS document with user stories and acceptance criteria, not a technical plan.',
403
+ ];
404
+ if (figmaUrl) {
405
+ promptParts.push('', `Figma design URL: ${figmaUrl}`, 'Use Figma MCP tools (get_design_context, get_screenshot) to extract UI details.', 'Include design insights in Section 5 (UI/UX) and derive E2E scenarios for Section 8 (Testing).');
406
+ }
407
+ const prompt = promptParts.join('\n');
408
+ this.printStageHeader('analyze', s, interactive);
409
+ const agent = await this.agentManager.spawn({
410
+ name: `analyst-${s}`,
411
+ persona: 'analyst',
412
+ stack: s,
413
+ prompt,
414
+ model: this.modelFor('analyst'),
415
+ cwd: this.projectCwd,
416
+ interactive,
417
+ permissionMode: headlessPermission(interactive),
418
+ disallowedTools: figmaUrl ? undefined : NON_ENGINEER_DISALLOWED_TOOLS,
419
+ appendSystemPrompt: this.buildAppendSystemPrompt(ANALYST_SYSTEM_ENFORCEMENT),
420
+ });
421
+ this.state.updateStage('analyze', { status: 'running', startedAt: Date.now() });
422
+ await this.waitForAgentWithBudgetCheck(agent.id);
423
+ await this.finishStage('analyze', 'REQUIREMENTS.md');
424
+ }
425
+ async runArchitect(opts) {
426
+ if (this.state.getState().stages.architect?.status === 'running') {
427
+ throw new Error('Stage "architect" is already running');
428
+ }
429
+ const s = opts?.stack ?? this.config.stack;
430
+ const interactive = opts?.interactive ?? true;
431
+ const reqPath = join(this.projectCwd, 'REQUIREMENTS.md');
432
+ if (!existsSync(reqPath)) {
433
+ throw new Error('REQUIREMENTS.md not found. Run `hivemind analyze` first.');
434
+ }
435
+ const requirements = readFileSync(reqPath, 'utf-8');
436
+ const codebaseCtx = this.getCodebaseContext();
437
+ const prompt = [
438
+ ...(codebaseCtx ? [codebaseCtx, '', 'Do NOT redesign existing architecture. Extend it.', ''] : []),
439
+ 'Read the REQUIREMENTS.md below and produce SPEC.md.',
440
+ '',
441
+ '⚠️ CRITICAL CONSTRAINTS — VIOLATION WILL CAUSE PIPELINE FAILURE:',
442
+ '- Your ONLY deliverable is SPEC.md. Do NOT create any other file.',
443
+ '- Do NOT write implementation code under ANY circumstances. No source files, no scripts.',
444
+ '- Do NOT break work into tasks — that is the Lead\'s job.',
445
+ '- Code snippets in SPEC.md are for illustration only (interfaces, type signatures) — NOT implementation.',
446
+ '- Once SPEC.md is written, STOP IMMEDIATELY. Do not proceed to any other stage.',
447
+ '',
448
+ '⚠️ FILENAME — The file MUST be named exactly `SPEC.md` in the project root.',
449
+ '- Do NOT add suffixes or prefixes to the filename. WRONG: SPEC-auth.md. CORRECT: SPEC.md',
450
+ '',
451
+ '⚠️ OUTPUT FORMAT — SPEC.md MUST follow the EXACT template from your system prompt:',
452
+ '- All required sections: Overview, Requirements Summary, Architecture Diagrams, ADRs,',
453
+ ' Component/Service Architecture, Data Model, API Specification, Performance Strategy,',
454
+ ' Testing Strategy, Security, Implementation Checklist, File Structure, Open Questions.',
455
+ '- Include Mermaid diagrams. Include ADR entries (ADR-1, ADR-2, etc.).',
456
+ '- Do NOT deviate from the template structure. Do NOT skip sections — write "N/A" if not applicable.',
457
+ '',
458
+ '---',
459
+ '',
460
+ requirements,
461
+ ].join('\n');
462
+ this.printStageHeader('architect', s, interactive);
463
+ const agent = await this.agentManager.spawn({
464
+ name: `architect-${s}`,
465
+ persona: 'architect',
466
+ stack: s,
467
+ prompt,
468
+ model: this.modelFor('architect'),
469
+ cwd: this.projectCwd,
470
+ interactive,
471
+ permissionMode: headlessPermission(interactive),
472
+ disallowedTools: NON_ENGINEER_DISALLOWED_TOOLS,
473
+ appendSystemPrompt: this.buildAppendSystemPrompt(ARCHITECT_SYSTEM_ENFORCEMENT),
474
+ });
475
+ this.state.updateStage('architect', { status: 'running', startedAt: Date.now() });
476
+ await this.waitForAgentWithBudgetCheck(agent.id);
477
+ await this.finishStage('architect', 'SPEC.md');
478
+ }
479
+ async runPlan(opts) {
480
+ if (this.state.getState().stages.plan?.status === 'running') {
481
+ throw new Error('Stage "plan" is already running');
482
+ }
483
+ const s = opts?.stack ?? this.config.stack;
484
+ const interactive = opts?.interactive ?? true;
485
+ const specPath = join(this.projectCwd, 'SPEC.md');
486
+ if (!existsSync(specPath)) {
487
+ throw new Error('SPEC.md not found. Run `hivemind architect` first.');
488
+ }
489
+ const spec = readFileSync(specPath, 'utf-8');
490
+ const userGuidance = opts?.prompt;
491
+ const codebaseCtx = this.getCodebaseContext();
492
+ const prompt = [
493
+ ...(codebaseCtx ? [codebaseCtx, '', 'Reference existing files when assigning task file paths.', ''] : []),
494
+ 'Read the SPEC.md below and produce TASKS.md.',
495
+ '',
496
+ ...(userGuidance ? [`User guidance: ${userGuidance}`, ''] : []),
497
+ '⚠️ CRITICAL CONSTRAINTS — VIOLATION WILL CAUSE PIPELINE FAILURE:',
498
+ '- Your ONLY deliverable is TASKS.md. Do NOT create any other file.',
499
+ '- Do NOT write implementation code under ANY circumstances. No source files, no scripts.',
500
+ '- Do NOT redesign the architecture — that is the Architect\'s job.',
501
+ '- Once TASKS.md is written, STOP IMMEDIATELY. Do not proceed to any other stage.',
502
+ '',
503
+ '⚠️ FILENAME — The file MUST be named exactly `TASKS.md` in the project root.',
504
+ '- Do NOT add suffixes or prefixes to the filename. WRONG: TASKS-migration.md. CORRECT: TASKS.md',
505
+ '',
506
+ '⚠️ OUTPUT FORMAT — TASKS.md MUST follow the EXACT template from your system prompt:',
507
+ '- One task = one file. Every task touches exactly one file.',
508
+ '- Format: `- [ ] T001 [P] [US1] Description — \\`file/path.ts\\``',
509
+ '- [P] marker on every parallelizable task (different files, no blocking deps).',
510
+ '- Phases: Setup → Foundational (GATE) → User Stories (parallel) → E2E Tests (after stories) → Polish.',
511
+ '- Every task MUST have AC: acceptance criteria and an exact file path.',
512
+ '- Do NOT deviate from the template structure.',
513
+ '',
514
+ '---',
515
+ '',
516
+ spec,
517
+ ].join('\n');
518
+ this.printStageHeader('plan', s, interactive);
519
+ const agent = await this.agentManager.spawn({
520
+ name: `lead-${s}`,
521
+ persona: 'lead',
522
+ stack: s,
523
+ prompt,
524
+ model: this.modelFor('lead'),
525
+ cwd: this.projectCwd,
526
+ interactive,
527
+ permissionMode: headlessPermission(interactive),
528
+ disallowedTools: NON_ENGINEER_DISALLOWED_TOOLS,
529
+ appendSystemPrompt: this.buildAppendSystemPrompt(LEAD_SYSTEM_ENFORCEMENT),
530
+ });
531
+ this.state.updateStage('plan', { status: 'running', startedAt: Date.now() });
532
+ await this.waitForAgentWithBudgetCheck(agent.id);
533
+ await this.finishStage('plan', 'TASKS.md');
534
+ }
535
+ async runBuild(opts = {}) {
536
+ if (this.state.getState().stages.build?.status === 'running') {
537
+ throw new Error('Stage "build" is already running');
538
+ }
539
+ const s = opts.stack ?? this.config.stack;
540
+ const tasksPath = join(this.projectCwd, 'TASKS.md');
541
+ if (!existsSync(tasksPath)) {
542
+ throw new Error('TASKS.md not found. Run `hivemind plan` first.');
543
+ }
544
+ const tasks = readFileSync(tasksPath, 'utf-8');
545
+ const maxParallel = opts.parallel ?? 3;
546
+ this.state.updateStage('build', { status: 'running', startedAt: Date.now() });
547
+ // Single task mode — no orchestrator needed
548
+ if (opts.taskId) {
549
+ const prompt = `Read TASKS.md and implement ONLY task ${opts.taskId}. Mark it complete when done.\n\nTASKS.md:\n${tasks}`;
550
+ console.log(chalk.cyan(`\n[build] Running task ${opts.taskId}...\n`));
551
+ const agent = await this.agentManager.spawn({
552
+ name: `engineer-${opts.taskId}`,
553
+ persona: 'engineer',
554
+ stack: s,
555
+ prompt,
556
+ model: this.modelFor('engineer'),
557
+ cwd: this.projectCwd,
558
+ interactive: false,
559
+ permissionMode: 'auto',
560
+ appendSystemPrompt: this.getEngineerConventions(),
561
+ });
562
+ await this.waitForAgentWithBudgetCheck(agent.id);
563
+ console.log(chalk.green(`\n[build] Task ${opts.taskId} complete. Cost: $${agent.cost.totalUsd.toFixed(4)}`));
564
+ }
565
+ else {
566
+ const groups = this.parseTaskGroups(tasks);
567
+ const allTaskIds = groups.flatMap((g) => g.taskIds);
568
+ // If only 1 task or no parseable groups, run a single engineer
569
+ if (groups.length === 0 || allTaskIds.length <= 1) {
570
+ const prompt = `Read TASKS.md and implement all pending tasks. Mark each complete when done.\n\nTASKS.md:\n${tasks}`;
571
+ console.log(chalk.cyan(`\n[build] Running all tasks with single engineer...\n`));
572
+ const agent = await this.agentManager.spawn({
573
+ name: `engineer-${s}`,
574
+ persona: 'engineer',
575
+ stack: s,
576
+ prompt,
577
+ model: this.modelFor('engineer'),
578
+ cwd: this.projectCwd,
579
+ interactive: false,
580
+ permissionMode: 'auto',
581
+ appendSystemPrompt: this.getEngineerConventions(),
582
+ });
583
+ await this.waitForAgentWithBudgetCheck(agent.id);
584
+ console.log(chalk.green(`\n[build] Complete. Cost: $${agent.cost.totalUsd.toFixed(4)}`));
585
+ }
586
+ else {
587
+ // === Orchestrator pattern ===
588
+ // 1. Spawn orchestrator engineer who plans and tracks sub-engineers
589
+ const orchestratorPrompt = [
590
+ 'You are the ORCHESTRATOR ENGINEER. You coordinate parallel sub-engineers working on TASKS.md.',
591
+ '',
592
+ 'Your responsibilities:',
593
+ '1. Analyze TASKS.md and confirm the execution plan below',
594
+ '2. You will receive status updates as sub-engineers complete their tasks',
595
+ '3. After ALL sub-engineers finish, do a final integration review:',
596
+ ' - Check for conflicts between parallel implementations',
597
+ ' - Verify shared interfaces/types are consistent',
598
+ ' - Fix any integration issues (imports, type mismatches, missing glue code)',
599
+ ' - Run any available tests or linting',
600
+ '4. Mark tasks as complete in TASKS.md',
601
+ '',
602
+ `Execution plan: ${groups.length} phase(s), max ${maxParallel} parallel engineers`,
603
+ ...groups.map((g, i) => ` Phase ${i + 1}: ${g.name} — tasks: ${g.taskIds.join(', ')}`),
604
+ '',
605
+ 'Acknowledge the plan. Sub-engineers will be spawned now. Wait for status updates before reviewing.',
606
+ '',
607
+ '---',
608
+ '',
609
+ tasks,
610
+ ].join('\n');
611
+ console.log(chalk.cyan(`\n[build] Spawning orchestrator engineer...\n`));
612
+ const orchestrator = await this.agentManager.spawn({
613
+ name: 'engineer-orchestrator',
614
+ persona: 'engineer',
615
+ stack: s,
616
+ prompt: orchestratorPrompt,
617
+ model: this.modelFor('engineer'),
618
+ cwd: this.projectCwd,
619
+ interactive: false,
620
+ permissionMode: 'auto',
621
+ appendSystemPrompt: this.getEngineerConventions(),
622
+ });
623
+ // Wait for orchestrator to acknowledge the plan
624
+ await this.waitForAgentWithBudgetCheck(orchestrator.id);
625
+ console.log(chalk.green(`[build] Orchestrator ready. Spawning sub-engineers...\n`));
626
+ // 2. Spawn sub-engineers phase by phase
627
+ const completedTasks = [];
628
+ const failedTasks = [];
629
+ for (let i = 0; i < groups.length; i++) {
630
+ const group = groups[i];
631
+ console.log(chalk.cyan(`[build] Phase ${i + 1}/${groups.length}: ${group.name} (${group.taskIds.length} tasks, max ${maxParallel} parallel)\n`));
632
+ const batches = this.chunk(group.taskIds, maxParallel);
633
+ for (const batch of batches) {
634
+ // Spawn batch of sub-engineers
635
+ const subEngineers = await Promise.all(batch.map((taskId) => this.agentManager.spawn({
636
+ name: `engineer-${taskId}`,
637
+ persona: 'engineer',
638
+ stack: s,
639
+ model: this.modelFor('engineer'),
640
+ prompt: [
641
+ `You are a SUB-ENGINEER. Implement ONLY task ${taskId}. Do not touch other tasks.`,
642
+ `Focus exclusively on ${taskId}. When complete, stop.`,
643
+ '',
644
+ 'TASKS.md:',
645
+ tasks,
646
+ ].join('\n'),
647
+ cwd: this.projectCwd,
648
+ interactive: false,
649
+ permissionMode: 'auto',
650
+ appendSystemPrompt: this.getEngineerConventions(),
651
+ parentId: orchestrator.id,
652
+ })));
653
+ // Link children to orchestrator
654
+ for (const sub of subEngineers) {
655
+ this.agentManager.addChild(orchestrator.id, sub.id);
656
+ }
657
+ // Wait for batch to complete
658
+ const results = await Promise.allSettled(subEngineers.map((a) => this.waitForAgentWithBudgetCheck(a.id)));
659
+ // Collect results
660
+ for (let j = 0; j < results.length; j++) {
661
+ const taskId = batch[j];
662
+ const result = results[j];
663
+ if (result.status === 'fulfilled') {
664
+ completedTasks.push({
665
+ taskId,
666
+ status: 'done',
667
+ cost: `$${result.value.cost.totalUsd.toFixed(4)}`,
668
+ });
669
+ console.log(chalk.green(` ✓ ${taskId} complete ($${result.value.cost.totalUsd.toFixed(4)})`));
670
+ }
671
+ else {
672
+ failedTasks.push({ taskId, error: result.reason?.message || 'Unknown error' });
673
+ console.log(chalk.red(` ✗ ${taskId} failed: ${result.reason?.message}`));
674
+ }
675
+ }
676
+ }
677
+ // Send phase completion update to orchestrator
678
+ const phaseReport = [
679
+ `Phase ${i + 1}/${groups.length} "${group.name}" complete.`,
680
+ `Tasks done: ${completedTasks.filter((t) => group.taskIds.includes(t.taskId)).map((t) => t.taskId).join(', ')}`,
681
+ failedTasks.filter((t) => group.taskIds.includes(t.taskId)).length > 0
682
+ ? `Tasks failed: ${failedTasks.filter((t) => group.taskIds.includes(t.taskId)).map((t) => `${t.taskId}: ${t.error}`).join(', ')}`
683
+ : '',
684
+ i < groups.length - 1
685
+ ? `Next phase: ${groups[i + 1].name} (${groups[i + 1].taskIds.join(', ')})`
686
+ : 'All phases complete. Please do final integration review now.',
687
+ ].filter(Boolean).join('\n');
688
+ await this.agentManager.sendInput(orchestrator.id, phaseReport);
689
+ console.log(chalk.green(` Phase ${i + 1} complete.\n`));
690
+ }
691
+ // 3. Wait for orchestrator to finish integration review
692
+ console.log(chalk.cyan(`[build] Waiting for orchestrator integration review...\n`));
693
+ // Send final summary if orchestrator is still running
694
+ const finalSummary = [
695
+ 'ALL SUB-ENGINEERS COMPLETE. Final summary:',
696
+ `Total tasks: ${allTaskIds.length} | Done: ${completedTasks.length} | Failed: ${failedTasks.length}`,
697
+ '',
698
+ 'Completed:',
699
+ ...completedTasks.map((t) => ` ✓ ${t.taskId} (${t.cost})`),
700
+ ...(failedTasks.length > 0 ? [
701
+ '',
702
+ 'Failed:',
703
+ ...failedTasks.map((t) => ` ✗ ${t.taskId}: ${t.error}`),
704
+ ] : []),
705
+ '',
706
+ 'Now do your final integration review:',
707
+ '1. Check for conflicts between parallel implementations',
708
+ '2. Verify shared interfaces/types are consistent across tasks',
709
+ '3. Fix any integration issues (imports, type mismatches, missing glue code)',
710
+ '4. Run tests/linting if available',
711
+ '5. Update TASKS.md to mark completed tasks',
712
+ ].join('\n');
713
+ await this.agentManager.sendInput(orchestrator.id, finalSummary);
714
+ await this.waitForAgentWithBudgetCheck(orchestrator.id);
715
+ console.log(chalk.green(`[build] Orchestrator integration review complete.`));
716
+ if (failedTasks.length > 0) {
717
+ console.log(chalk.yellow(`\n[build] Warning: ${failedTasks.length} task(s) failed:`));
718
+ for (const t of failedTasks) {
719
+ console.log(chalk.yellow(` - ${t.taskId}: ${t.error}`));
720
+ }
721
+ }
722
+ }
723
+ }
724
+ this.state.updateStage('build', { status: 'done' });
725
+ console.log(chalk.green(`\n[build] All tasks complete.`));
726
+ }
727
+ async runTest(opts = {}) {
728
+ if (this.state.getState().stages.test?.status === 'running') {
729
+ throw new Error('Stage "test" is already running');
730
+ }
731
+ const s = opts.stack ?? this.config.stack;
732
+ const interactive = opts.interactive ?? false;
733
+ const frameworks = getTestFrameworks(s);
734
+ const primaryFramework = frameworks[0];
735
+ this.state.updateStage('test', { status: 'running', startedAt: Date.now() });
736
+ const testplanPath = join(this.projectCwd, 'TESTPLAN.md');
737
+ const pwConfig = this.buildPlaywrightContext();
738
+ let testerCost = 0;
739
+ console.log(chalk.dim(`[test] Stack: ${s} → frameworks: ${frameworks.map(f => f.name).join(', ')}`));
740
+ // ── Phase 1: Tester persona → generate TESTPLAN.md (skip if already exists) ──
741
+ if (existsSync(testplanPath)) {
742
+ console.log(chalk.green(`\n[test] TESTPLAN.md already exists — skipping Phase 1, proceeding to test runner.\n`));
743
+ }
744
+ else {
745
+ console.log(chalk.cyan(`\n[test] Phase 1: Generating test plan...\n`));
746
+ const contextParts = [];
747
+ const reqPath = join(this.projectCwd, 'REQUIREMENTS.md');
748
+ const specPath = join(this.projectCwd, 'SPEC.md');
749
+ const tasksPath = join(this.projectCwd, 'TASKS.md');
750
+ if (existsSync(reqPath)) {
751
+ contextParts.push('--- REQUIREMENTS.md ---', readFileSync(reqPath, 'utf-8'), '');
752
+ }
753
+ if (existsSync(specPath)) {
754
+ contextParts.push('--- SPEC.md ---', readFileSync(specPath, 'utf-8'), '');
755
+ }
756
+ if (existsSync(tasksPath)) {
757
+ contextParts.push('--- TASKS.md ---', readFileSync(tasksPath, 'utf-8'), '');
758
+ }
759
+ if (contextParts.length === 0) {
760
+ throw new Error('No pipeline artifacts found. Run at least `hivemind analyze` first.');
761
+ }
762
+ const testerPromptParts = this.buildTesterPrompt(s, frameworks, pwConfig, opts.figmaUrl);
763
+ testerPromptParts.push('', '---', '', ...contextParts);
764
+ if (interactive) {
765
+ this.printStageHeader('test', s, true);
766
+ }
767
+ const testerAgent = await this.agentManager.spawn({
768
+ name: `tester-${s}`,
769
+ persona: 'tester',
770
+ stack: s,
771
+ prompt: testerPromptParts.join('\n'),
772
+ model: this.modelFor('tester'),
773
+ cwd: this.projectCwd,
774
+ interactive,
775
+ permissionMode: headlessPermission(interactive),
776
+ disallowedTools: opts.figmaUrl ? undefined : NON_ENGINEER_DISALLOWED_TOOLS,
777
+ appendSystemPrompt: this.buildAppendSystemPrompt(TESTER_SYSTEM_ENFORCEMENT),
778
+ });
779
+ await this.waitForAgentWithBudgetCheck(testerAgent.id);
780
+ testerCost = testerAgent.cost.totalUsd;
781
+ if (existsSync(testplanPath)) {
782
+ console.log(chalk.green(`\n[test] Phase 1 complete. TESTPLAN.md created.`));
783
+ }
784
+ else {
785
+ console.log(chalk.yellow(`\n[test] Phase 1 complete. TESTPLAN.md not found — skipping execution phase.`));
786
+ await this.finishStage('test', 'TESTPLAN.md');
787
+ return;
788
+ }
789
+ }
790
+ // ── Phase 2: Engineer → implement and run tests ──
791
+ const testplan = readFileSync(testplanPath, 'utf-8');
792
+ if (frameworks.length > 1) {
793
+ // Parallel test execution: spawn one agent per framework
794
+ console.log(chalk.cyan(`\n[test] Phase 2: Running ${frameworks.length} test frameworks in parallel...\n`));
795
+ const runners = await Promise.all(frameworks.map((fw, idx) => {
796
+ const fwPrompt = this.buildRunnerPrompt(s, [fw], pwConfig, testplan);
797
+ return this.agentManager.spawn({
798
+ name: `test-runner-${fw.kind}-${idx}`,
799
+ persona: 'engineer',
800
+ stack: s,
801
+ prompt: fwPrompt.join('\n'),
802
+ model: this.modelFor('engineer'),
803
+ cwd: this.projectCwd,
804
+ interactive: false,
805
+ permissionMode: 'auto',
806
+ appendSystemPrompt: this.getEngineerConventions(),
807
+ });
808
+ }));
809
+ await Promise.allSettled(runners.map(a => this.waitForAgentWithBudgetCheck(a.id)));
810
+ const runnerCost = runners.reduce((sum, a) => sum + a.cost.totalUsd, 0);
811
+ await this.finishStage('test', 'TESTPLAN.md');
812
+ console.log(chalk.green(`\n[test] ${frameworks.length} test suites complete. Cost: $${(testerCost + runnerCost).toFixed(4)}`));
813
+ }
814
+ else {
815
+ // Single framework: original behavior
816
+ console.log(chalk.cyan(`\n[test] Phase 2: Implementing and running ${primaryFramework.name} tests...\n`));
817
+ const runnerPromptParts = this.buildRunnerPrompt(s, frameworks, pwConfig, testplan);
818
+ const runnerAgent = await this.agentManager.spawn({
819
+ name: `test-runner-${s}`,
820
+ persona: 'engineer',
821
+ stack: s,
822
+ prompt: runnerPromptParts.join('\n'),
823
+ model: this.modelFor('engineer'),
824
+ cwd: this.projectCwd,
825
+ interactive: false,
826
+ permissionMode: 'auto',
827
+ appendSystemPrompt: this.getEngineerConventions(),
828
+ });
829
+ await this.waitForAgentWithBudgetCheck(runnerAgent.id);
830
+ await this.finishStage('test', 'TESTPLAN.md');
831
+ console.log(chalk.green(`\n[test] Tests complete. Cost: $${(testerCost + runnerAgent.cost.totalUsd).toFixed(4)}`));
832
+ }
833
+ }
834
+ /** Build the Phase 1 tester prompt — framework-aware */
835
+ buildTesterPrompt(stack, frameworks, pwConfig, figmaUrl) {
836
+ const primary = frameworks[0];
837
+ const isE2E = primary.category === 'e2e';
838
+ const parts = [
839
+ `Read the pipeline artifacts below and produce TESTPLAN.md — a comprehensive test plan for a ${stack} project.`,
840
+ '',
841
+ `## Test Frameworks`,
842
+ ...frameworks.map((f, i) => `${i + 1}. **${f.name}** (${f.category}) — test dir: \`${f.testDir}/\`, pattern: \`${f.testFilePattern}\``),
843
+ '',
844
+ '⚠️ CRITICAL CONSTRAINTS — VIOLATION WILL CAUSE PIPELINE FAILURE:',
845
+ '- Your ONLY deliverable is TESTPLAN.md. Do NOT create any other file.',
846
+ '- Do NOT write implementation code — no test files, no scripts, no source changes.',
847
+ '- Do NOT modify existing artifacts (REQUIREMENTS.md, SPEC.md, TASKS.md).',
848
+ '- Once TESTPLAN.md is written, STOP IMMEDIATELY.',
849
+ '',
850
+ '⚠️ FILENAME — The file MUST be named exactly `TESTPLAN.md` in the project root.',
851
+ '',
852
+ '⚠️ OUTPUT FORMAT — TESTPLAN.md MUST follow this structure:',
853
+ '- ## Overview — what is being tested, scope, tech stack',
854
+ `- ## Test Strategy — frameworks: ${frameworks.map(f => f.name).join(', ')}; categories: ${frameworks.map(f => f.category).join(', ')}`,
855
+ ];
856
+ if (isE2E) {
857
+ parts.push('- ## Authentication — login method, storageState pattern, global setup needs');
858
+ }
859
+ parts.push('- ## Test Data — required fixtures, seed data, mock APIs', '- ## Test Cases — each test case with:', ' - **ID**: TC-001, TC-002, etc.', ' - **Title**: descriptive name', ` - **Framework**: which framework (${frameworks.map(f => f.name).join(' / ')})`, ' - **Category**: unit / integration / api / e2e', ' - **User Story**: which US-n / user flow this covers', ' - **Preconditions**: setup needed');
860
+ if (isE2E) {
861
+ parts.push(' - **Steps**: numbered user actions (navigate, click, fill, etc.)', ' - **Expected**: specific assertions (element visible, text matches, URL changes, etc.)');
862
+ }
863
+ else {
864
+ parts.push(' - **Input**: function/endpoint being tested with input data', ' - **Expected**: expected return value, status code, side effects, or error');
865
+ }
866
+ parts.push(` - **File**: target test file path (e.g. \`${primary.testDir}/example${primary.testFilePattern.replace('*', '')}\`)`, '- ## Acceptance Criteria — overall pass/fail criteria for the test suite');
867
+ // Stack-specific guidance
868
+ switch (stack) {
869
+ case 'node':
870
+ parts.push('', '## Node.js-Specific Guidance:', '- Test API endpoints with Supertest (HTTP assertions)', '- Test service/business logic with unit tests', '- Mock external dependencies (databases, third-party APIs)', '- Test error handling and edge cases (invalid input, auth failures)', '- Test middleware (auth, validation, rate limiting)');
871
+ break;
872
+ case 'go':
873
+ parts.push('', '## Go-Specific Guidance:', '- Use table-driven tests for multiple input/output cases', '- Test HTTP handlers with httptest.NewServer or httptest.NewRecorder', '- Use testify/assert for cleaner assertions (if available)', '- Test error paths and edge cases', '- Test interfaces with mock implementations');
874
+ break;
875
+ case 'python':
876
+ parts.push('', '## Python-Specific Guidance:', '- Use pytest fixtures for test setup/teardown', '- Test API endpoints with the test client (FastAPI: TestClient, Django: Client, Flask: test_client)', '- Use unittest.mock or pytest-mock for mocking external services', '- Test edge cases, error handling, and validation', '- Parametrize tests for multiple input scenarios');
877
+ break;
878
+ case 'rust':
879
+ parts.push('', '## Rust-Specific Guidance:', '- Use #[test] functions in the same file or in a tests/ directory', '- Use assert!, assert_eq!, assert_ne! macros', '- Test error types with Result and matches! macro', '- Test async code with #[tokio::test] if using tokio');
880
+ break;
881
+ }
882
+ if (isE2E) {
883
+ parts.push('', `Playwright config: testDir=${pwConfig.testDir}`);
884
+ if (pwConfig.baseUrl)
885
+ parts.push(`Base URL: ${pwConfig.baseUrl}`);
886
+ if (pwConfig.authStorageState)
887
+ parts.push(`Auth storageState path: ${pwConfig.authStorageState}`);
888
+ }
889
+ if (figmaUrl) {
890
+ parts.push('', `Figma design URL: ${figmaUrl}`, 'Use Figma MCP tools (get_design_context, get_screenshot) to extract UI details.', 'Derive visual E2E test cases from the designs: verify layout, component states,', 'responsiveness, and visual accuracy.');
891
+ }
892
+ return parts;
893
+ }
894
+ /** Build the Phase 2 runner prompt — framework-aware */
895
+ buildRunnerPrompt(stack, frameworks, pwConfig, testplan) {
896
+ const primary = frameworks[0];
897
+ const isE2E = primary.category === 'e2e';
898
+ const parts = [
899
+ `You are a TEST ENGINEER. Implement and run tests based on TESTPLAN.md for a ${stack} project.`,
900
+ '',
901
+ '## Test Frameworks to Use:',
902
+ ...frameworks.map((f, i) => [
903
+ `### ${i + 1}. ${f.name} (${f.category})`,
904
+ `- Test dir: \`${f.testDir}/\``,
905
+ `- File pattern: \`${f.testFilePattern}\``,
906
+ f.installCmd ? `- Install: \`${f.installCmd}\`` : '- (built-in, no install needed)',
907
+ `- Run: \`${f.runCmd}\``,
908
+ ].join('\n')),
909
+ '',
910
+ '⚠️ CRITICAL CONSTRAINTS:',
911
+ '- Do NOT modify application source code. Only create/modify test files and test config.',
912
+ '- If a test fails, fix the TEST, not the application.',
913
+ ];
914
+ if (isE2E) {
915
+ parts.push(`- If Playwright is not installed, run: \`${primary.installCmd}\``);
916
+ if (pwConfig.baseUrl) {
917
+ parts.push(`- Set baseURL to: ${pwConfig.baseUrl}`);
918
+ }
919
+ if (pwConfig.authStorageState) {
920
+ parts.push(`- Configure storageState: ${pwConfig.authStorageState}`, ' If storageState file does not exist, create a global setup script that performs login.');
921
+ }
922
+ }
923
+ parts.push('', 'Your job:', '1. Read TESTPLAN.md and implement each test case', `2. Place test files in the correct directories (${frameworks.map(f => `\`${f.testDir}/\` for ${f.name}`).join(', ')})`, `3. Create/update test config files as needed`, '', 'After implementing all tests:', ...frameworks.map(f => `- Run ${f.name} tests: \`${f.runCmd}\``), '- If any test fails, read the error, fix the test, and re-run', '- Keep iterating until all tests pass or you have exhausted debugging', '- Always re-run with JSON output so structured results are captured in `.swarm/test-results.json`', '- Report final results summary', '', '---', '', testplan);
924
+ return parts;
925
+ }
926
+ async runFull(featureRequest, opts) {
927
+ const stack = opts?.stack;
928
+ await this.runAnalyze(featureRequest, { stack, interactive: opts?.interactive ?? true, figmaUrl: opts?.figmaUrl });
929
+ await this.runArchitect({ stack, interactive: opts?.interactive ?? true });
930
+ await this.runPlan({ stack, interactive: opts?.interactive ?? true });
931
+ await this.runBuild({ stack });
932
+ await this.runTest({ stack, figmaUrl: opts?.figmaUrl });
933
+ }
934
+ /**
935
+ * Run a custom pipeline defined by a PipelineDefinition.
936
+ * Iterates through stages respecting dependsOn and condition fields.
937
+ */
938
+ async runCustomPipeline(definition, featureRequest, opts = {}) {
939
+ const stack = opts.stack ?? this.config.stack;
940
+ const stageOpts = {
941
+ stack,
942
+ interactive: opts.interactive ?? false,
943
+ figmaUrl: opts.figmaUrl,
944
+ };
945
+ const completed = new Set();
946
+ const stageMap = new Map();
947
+ for (const s of definition.stages) {
948
+ stageMap.set(s.name, s);
949
+ }
950
+ console.log(chalk.cyan(`\n[pipeline] Running custom pipeline (${definition.stages.length} stages)\n`));
951
+ for (const stageDef of definition.stages) {
952
+ // Check if mayday was stopped (if running within mayday)
953
+ if (this.state.getMayday()?.active === false) {
954
+ console.log(chalk.yellow(`\n[pipeline] Stopped by user.`));
955
+ return;
956
+ }
957
+ // Check dependencies
958
+ if (stageDef.dependsOn) {
959
+ const unmet = stageDef.dependsOn.filter(dep => !completed.has(dep));
960
+ if (unmet.length > 0) {
961
+ console.log(chalk.yellow(`[pipeline] Skipping "${stageDef.name}": unmet dependencies: ${unmet.join(', ')}`));
962
+ const mappedStage = stageNameForDefinition(stageDef);
963
+ this.state.updateStage(mappedStage, { status: 'skipped' });
964
+ continue;
965
+ }
966
+ }
967
+ // Check condition
968
+ if (stageDef.condition) {
969
+ const skip = this.evaluateCondition(stageDef.condition);
970
+ if (skip) {
971
+ console.log(chalk.yellow(`[pipeline] Skipping "${stageDef.name}": condition not met (${stageDef.condition})`));
972
+ const mappedStage = stageNameForDefinition(stageDef);
973
+ this.state.updateStage(mappedStage, { status: 'skipped' });
974
+ completed.add(stageDef.name); // Treat as completed for dependency resolution
975
+ continue;
976
+ }
977
+ }
978
+ // Dispatch to the appropriate run method based on persona
979
+ const customPrompt = stageDef.prompt;
980
+ const stageOptsWithPrompt = { ...stageOpts, prompt: customPrompt ?? opts.prompt };
981
+ console.log(chalk.cyan(`[pipeline] ▸ ${stageDef.name} (${stageDef.persona})`));
982
+ try {
983
+ switch (stageDef.persona) {
984
+ case 'analyst':
985
+ await this.runAnalyze(featureRequest, stageOptsWithPrompt);
986
+ break;
987
+ case 'architect':
988
+ await this.runArchitect(stageOptsWithPrompt);
989
+ break;
990
+ case 'lead':
991
+ await this.runPlan(stageOptsWithPrompt);
992
+ break;
993
+ case 'engineer':
994
+ await this.runBuild({ stack, parallel: opts.parallel ?? 3 });
995
+ break;
996
+ case 'tester':
997
+ await this.runTest({ stack, figmaUrl: opts.figmaUrl });
998
+ break;
999
+ }
1000
+ // Auto-commit after each stage
1001
+ const mappedStage = stageNameForDefinition(stageDef);
1002
+ if (stageDef.artifact) {
1003
+ this.autoCommitStage(mappedStage, stageDef.artifact);
1004
+ }
1005
+ completed.add(stageDef.name);
1006
+ }
1007
+ catch (err) {
1008
+ const mappedStage = stageNameForDefinition(stageDef);
1009
+ this.state.updateStage(mappedStage, { status: 'error' });
1010
+ throw err;
1011
+ }
1012
+ }
1013
+ console.log(chalk.green(`\n[pipeline] Custom pipeline complete (${completed.size}/${definition.stages.length} stages).`));
1014
+ }
1015
+ /**
1016
+ * Run a stage with retry. On first failure: log, wait 5s, retry.
1017
+ * On second failure: throw (caller decides whether to abort or skip).
1018
+ */
1019
+ /** Tracks failure reasons per stage so downstream stages can reference them */
1020
+ stageFailureContext = new Map();
1021
+ /** Get failure context from a previous stage attempt (for backward context flow) */
1022
+ getStageFailureContext(stage) {
1023
+ return this.stageFailureContext.get(stage);
1024
+ }
1025
+ async runStageWithRetry(stage, fn, maxAttempts = 2) {
1026
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1027
+ try {
1028
+ await fn();
1029
+ return;
1030
+ }
1031
+ catch (err) {
1032
+ const msg = err instanceof Error ? err.message : String(err);
1033
+ // Record failure context for backward flow
1034
+ this.stageFailureContext.set(stage, `Stage "${stage}" failed (attempt ${attempt}): ${msg}`);
1035
+ if (attempt < maxAttempts) {
1036
+ console.log(chalk.yellow(`[mayday] Stage "${stage}" failed (attempt ${attempt}/${maxAttempts}): ${msg}`));
1037
+ console.log(chalk.yellow(`[mayday] Retrying in 5s with failure context...`));
1038
+ // Reset stage status for retry
1039
+ this.state.updateStage(stage, { status: 'pending' });
1040
+ await sleep(5000);
1041
+ }
1042
+ else {
1043
+ throw err;
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+ /**
1049
+ * Evaluate a stage condition. Returns true if the stage should be SKIPPED.
1050
+ */
1051
+ evaluateCondition(condition) {
1052
+ if (condition.startsWith('file-exists:')) {
1053
+ const filename = condition.slice('file-exists:'.length).trim();
1054
+ const filePath = join(this.projectCwd, filename);
1055
+ // Skip if file does NOT exist
1056
+ return !existsSync(filePath);
1057
+ }
1058
+ // Unknown condition type — don't skip
1059
+ return false;
1060
+ }
1061
+ printStageHeader(stage, stack, interactive) {
1062
+ if (interactive) {
1063
+ console.log(chalk.cyan(`\n[${stage}] Starting interactive session (${stack})...`));
1064
+ console.log(chalk.dim('You can converse with the agent. It will produce the artifact and exit when done.\n'));
1065
+ }
1066
+ else {
1067
+ console.log(chalk.cyan(`\n[${stage}] Starting agent (${stack})...\n`));
1068
+ }
1069
+ }
1070
+ /**
1071
+ * Generate a concise context summary from an artifact file (~500 chars).
1072
+ * Extracts key structural info depending on artifact type.
1073
+ */
1074
+ generateContextSummary(artifactPath, stage) {
1075
+ try {
1076
+ const content = readFileSync(artifactPath, 'utf-8');
1077
+ const headings = content.match(/^##\s+.+$/gm) || [];
1078
+ switch (stage) {
1079
+ case 'analyze':
1080
+ // Extract section headings from REQUIREMENTS.md
1081
+ return `Sections: ${headings.map(h => h.replace(/^##\s+/, '')).join(', ')}`.slice(0, 500);
1082
+ case 'architect':
1083
+ // Extract ADR titles from SPEC.md
1084
+ const adrs = content.match(/^###\s+ADR-\d+.+$/gm) || [];
1085
+ return `Architecture: ${headings.length} sections. ADRs: ${adrs.map(a => a.replace(/^###\s+/, '')).join('; ')}`.slice(0, 500);
1086
+ case 'plan': {
1087
+ // Count tasks from TASKS.md
1088
+ const tasks = content.match(/^- \[[ x]\] T\d+/gm) || [];
1089
+ const parallel = content.match(/\[P\]/g) || [];
1090
+ return `Tasks: ${tasks.length} total, ${parallel.length} parallelizable. ${headings.map(h => h.replace(/^##\s+/, '')).join(', ')}`.slice(0, 500);
1091
+ }
1092
+ case 'test':
1093
+ // Count test cases from TESTPLAN.md
1094
+ const testCases = content.match(/^###?\s+TC-\d+/gm) || [];
1095
+ return `Test plan: ${testCases.length} test cases. ${headings.map(h => h.replace(/^##\s+/, '')).join(', ')}`.slice(0, 500);
1096
+ default:
1097
+ return headings.map(h => h.replace(/^##\s+/, '')).join(', ').slice(0, 500);
1098
+ }
1099
+ }
1100
+ catch {
1101
+ return '';
1102
+ }
1103
+ }
1104
+ async finishStage(stage, expectedArtifact) {
1105
+ const artifactFullPath = join(this.projectCwd, expectedArtifact);
1106
+ const artifact = existsSync(artifactFullPath) ? expectedArtifact : null;
1107
+ // Find the primary agent's sessionId for this stage
1108
+ const stageState = this.state.getState().stages[stage];
1109
+ const primaryAgentId = stageState.agentIds[0];
1110
+ const primaryAgent = primaryAgentId ? this.state.getAgent(primaryAgentId) : undefined;
1111
+ // Generate context summary from the artifact
1112
+ const contextSummary = artifact ? this.generateContextSummary(artifactFullPath, stage) : undefined;
1113
+ this.state.updateStage(stage, {
1114
+ status: 'done',
1115
+ artifact,
1116
+ sessionId: primaryAgent?.sessionId,
1117
+ contextSummary,
1118
+ finishedAt: Date.now(),
1119
+ });
1120
+ if (artifact) {
1121
+ console.log(chalk.green(`\n[${stage}] Complete. ${expectedArtifact} created.`));
1122
+ }
1123
+ else {
1124
+ console.log(chalk.green(`\n[${stage}] Session complete.`));
1125
+ }
1126
+ // Run blocking guardrails on the artifact
1127
+ if (artifact) {
1128
+ const violations = this.guardrails.evaluateArtifact(this.projectCwd, expectedArtifact);
1129
+ const errors = violations.filter(v => v.severity === 'error');
1130
+ const warnings = violations.filter(v => v.severity === 'warning');
1131
+ // Persist violations in state for dashboard
1132
+ const pipelineState = this.state.getState();
1133
+ pipelineState.violations = [...(pipelineState.violations || []), ...violations];
1134
+ this.state.scheduleSavePublic();
1135
+ if (warnings.length > 0) {
1136
+ for (const w of warnings) {
1137
+ console.log(chalk.yellow(`[guardrail] warning: ${w.message}`));
1138
+ }
1139
+ }
1140
+ if (errors.length > 0) {
1141
+ for (const e of errors) {
1142
+ console.log(chalk.red(`[guardrail] error: ${e.message}`));
1143
+ }
1144
+ console.log(chalk.red(`[guardrail] ${errors.length} error(s) in ${expectedArtifact} — stage will be retried.`));
1145
+ // Mark stage as error so runStageWithRetry can retry it
1146
+ this.state.updateStage(stage, { status: 'error' });
1147
+ throw new Error(`Guardrail errors in ${expectedArtifact}: ${errors.map(e => e.message).join('; ')}`);
1148
+ }
1149
+ }
1150
+ // Run quality scoring on the artifact
1151
+ const score = this.quality.scoreArtifact(this.projectCwd, stage);
1152
+ if (score) {
1153
+ const pipelineState = this.state.getState();
1154
+ const scores = pipelineState.qualityScores ?? [];
1155
+ // Replace existing score for this stage
1156
+ const filtered = scores.filter(s => s.stage !== stage);
1157
+ filtered.push(score);
1158
+ pipelineState.qualityScores = filtered;
1159
+ this.state.scheduleSavePublic();
1160
+ const color = score.overall >= 80 ? chalk.green : score.overall >= 50 ? chalk.yellow : chalk.red;
1161
+ console.log(color(`[quality] ${expectedArtifact}: ${score.overall}/100`));
1162
+ for (const d of score.dimensions) {
1163
+ console.log(chalk.dim(` ${d.name}: ${d.score}/100 — ${d.detail}`));
1164
+ }
1165
+ // LLM-powered quality gate (optional, runs haiku for semantic evaluation)
1166
+ if (this.config.llmQualityGate && artifact) {
1167
+ const threshold = this.config.llmQualityThreshold ?? 60;
1168
+ try {
1169
+ const llmDimension = await this.quality.scoreLLM(this.projectCwd, expectedArtifact, 'haiku');
1170
+ if (llmDimension) {
1171
+ // Blend heuristic + LLM: 40% heuristic, 60% LLM
1172
+ const blendedScore = Math.round(score.overall * 0.4 + llmDimension.score * 0.6);
1173
+ const llmColor = llmDimension.score >= 80 ? chalk.green : llmDimension.score >= 50 ? chalk.yellow : chalk.red;
1174
+ console.log(llmColor(`[quality:llm] ${expectedArtifact}: ${llmDimension.score}/100 — ${llmDimension.detail}`));
1175
+ console.log(chalk.dim(`[quality] Blended: ${blendedScore}/100 (heuristic: ${score.overall}, llm: ${llmDimension.score})`));
1176
+ if (blendedScore < threshold) {
1177
+ console.log(chalk.red(`[quality] Blended score ${blendedScore} below threshold ${threshold} — stage will be retried.`));
1178
+ this.state.updateStage(stage, { status: 'error' });
1179
+ throw new Error(`Quality gate failed for ${expectedArtifact}: blended score ${blendedScore}/${threshold}`);
1180
+ }
1181
+ }
1182
+ }
1183
+ catch (err) {
1184
+ // Only re-throw quality gate failures, not LLM errors
1185
+ if (err instanceof Error && err.message.includes('Quality gate failed'))
1186
+ throw err;
1187
+ // LLM eval failed — non-critical, continue with heuristic only
1188
+ }
1189
+ }
1190
+ }
1191
+ this.webhooks.stageComplete(this.config.projectName, stage, this.state.getState().totalCost).catch(() => { });
1192
+ }
1193
+ /**
1194
+ * Generate FAILURE-REPORT.md when MayDay fails, preserving what was attempted and what succeeded.
1195
+ */
1196
+ generateFailureReport() {
1197
+ try {
1198
+ const pipelineState = this.state.getState();
1199
+ const mayday = pipelineState.mayday;
1200
+ if (!mayday)
1201
+ return;
1202
+ const stages = ['analyze', 'architect', 'plan', 'build', 'test'];
1203
+ const stageLines = [];
1204
+ for (const stage of stages) {
1205
+ const s = pipelineState.stages[stage];
1206
+ const status = s.status === 'done' ? 'done' : s.status === 'skipped' ? 'skipped' : s.status === 'error' ? 'FAILED' : s.status;
1207
+ const artifact = s.artifact ? `(${s.artifact})` : '';
1208
+ stageLines.push(`| ${stage} | ${status} | ${artifact} |`);
1209
+ }
1210
+ const fixHistoryLines = [];
1211
+ if (mayday.fixHistory && mayday.fixHistory.length > 0) {
1212
+ for (const entry of mayday.fixHistory) {
1213
+ fixHistoryLines.push(`### Iteration ${entry.iteration}`);
1214
+ fixHistoryLines.push(`- **Approach:** ${entry.approach}`);
1215
+ fixHistoryLines.push(`- **Failed tests:** ${entry.failedTests.length} — ${entry.failedTests.slice(0, 5).join(', ')}${entry.failedTests.length > 5 ? '...' : ''}`);
1216
+ fixHistoryLines.push(`- **Fixed:** ${entry.fixedTests.length > 0 ? entry.fixedTests.join(', ') : 'none'}`);
1217
+ fixHistoryLines.push(`- **New regressions:** ${entry.newFailures.length > 0 ? entry.newFailures.join(', ') : 'none'}`);
1218
+ fixHistoryLines.push(`- **Cost:** $${entry.cost.toFixed(2)}`);
1219
+ fixHistoryLines.push('');
1220
+ }
1221
+ }
1222
+ const artifactsProduced = [];
1223
+ for (const stage of stages) {
1224
+ const artifact = STAGE_ARTIFACT_MAP[stage];
1225
+ if (artifact && existsSync(join(this.projectCwd, artifact))) {
1226
+ artifactsProduced.push(`- ${artifact}`);
1227
+ }
1228
+ }
1229
+ const report = [
1230
+ '# Failure Report',
1231
+ '',
1232
+ `**Feature:** ${mayday.featureRequest}`,
1233
+ `**Error:** ${mayday.error || 'Unknown error'}`,
1234
+ `**Total cost:** $${pipelineState.totalCost.totalUsd.toFixed(2)}`,
1235
+ `**Duration:** ${mayday.startedAt ? ((Date.now() - mayday.startedAt) / 60000).toFixed(1) + 'm' : 'unknown'}`,
1236
+ '',
1237
+ '## Stage Results',
1238
+ '',
1239
+ '| Stage | Status | Artifact |',
1240
+ '|-------|--------|----------|',
1241
+ ...stageLines,
1242
+ '',
1243
+ '## Artifacts Produced',
1244
+ '',
1245
+ artifactsProduced.length > 0 ? artifactsProduced.join('\n') : '_No artifacts produced._',
1246
+ '',
1247
+ ...(fixHistoryLines.length > 0
1248
+ ? ['## Fix Loop History', '', ...fixHistoryLines]
1249
+ : []),
1250
+ ...(mayday.lastTestOutput
1251
+ ? ['## Last Test Output', '', '```', mayday.lastTestOutput.slice(0, 5000), '```', '']
1252
+ : []),
1253
+ '## Suggested Next Steps',
1254
+ '',
1255
+ mayday.error?.includes('Stuck')
1256
+ ? '1. Review the failing tests manually — the fix loop tried the same approach repeatedly.\n2. Consider a different implementation approach.\n3. Run `hivemind mayday --resume --from build` after making manual fixes.'
1257
+ : mayday.error?.includes('Budget')
1258
+ ? '1. Increase budget: `hivemind mayday --resume --budget 50`\n2. Or use lean mode: `hivemind mayday --lean --resume`'
1259
+ : mayday.error?.includes('Max fix iterations')
1260
+ ? '1. Review test failures and fix manually.\n2. Resume with more iterations: `hivemind mayday --resume --max-iterations 10`'
1261
+ : '1. Check the error above and the stage that failed.\n2. Fix the issue manually, then `hivemind mayday --resume`.',
1262
+ '',
1263
+ ].join('\n');
1264
+ const reportPath = join(this.projectCwd, 'FAILURE-REPORT.md');
1265
+ writeFileSync(reportPath, report);
1266
+ console.log(chalk.yellow(`[mayday] Failure report saved to FAILURE-REPORT.md`));
1267
+ }
1268
+ catch {
1269
+ // Non-critical — don't crash on report generation failure
1270
+ }
1271
+ }
1272
+ /**
1273
+ * Validate that required artifacts exist when skipping stages via --from.
1274
+ * Each stage depends on artifacts from prior stages.
1275
+ */
1276
+ validateSkippedArtifacts(fromStage) {
1277
+ const stages = ['analyze', 'architect', 'plan', 'build', 'test'];
1278
+ const fromIdx = stages.indexOf(fromStage);
1279
+ // Check that all artifacts from stages before fromStage exist
1280
+ for (let i = 0; i < fromIdx; i++) {
1281
+ const stage = stages[i];
1282
+ const artifact = STAGE_ARTIFACT_MAP[stage];
1283
+ if (artifact && !existsSync(join(this.projectCwd, artifact))) {
1284
+ throw new Error(`Cannot skip to "${fromStage}": required artifact "${artifact}" from "${stage}" stage not found. ` +
1285
+ `Run the earlier stages first or provide the artifact manually.`);
1286
+ }
1287
+ }
1288
+ }
1289
+ /**
1290
+ * Parse TASKS.md into execution groups. Supports two formats:
1291
+ *
1292
+ * 1. Spec-kit format (preferred): `- [ ] T001 [P] [US1] description — \`file.ts\``
1293
+ * Tasks with [P] marker are parallelizable. Phases become groups.
1294
+ *
1295
+ * 2. Legacy format: `## Phase N` / `### Parallel Group` headings with `- [ ] **FND-001** ...`
1296
+ */
1297
+ parseTaskGroups(tasksContent) {
1298
+ // Detect format: if we find T001-style IDs, use spec-kit parser
1299
+ const hasSpecKitFormat = /^-\s+\[\s*\]\s+T\d{3}/m.test(tasksContent);
1300
+ if (hasSpecKitFormat) {
1301
+ return this.parseSpecKitTasks(tasksContent);
1302
+ }
1303
+ return this.parseLegacyTasks(tasksContent);
1304
+ }
1305
+ /**
1306
+ * Spec-kit format: phases are sequential gates, tasks with [P] run in parallel.
1307
+ * Groups adjacent [P] tasks within each phase into parallel batches.
1308
+ */
1309
+ parseSpecKitTasks(content) {
1310
+ const groups = [];
1311
+ let currentPhase = '';
1312
+ let parallelBatch = [];
1313
+ let sequentialBatch = [];
1314
+ const flushBatches = () => {
1315
+ if (parallelBatch.length > 0) {
1316
+ groups.push({ name: `${currentPhase} (parallel)`, taskIds: [...parallelBatch] });
1317
+ parallelBatch = [];
1318
+ }
1319
+ if (sequentialBatch.length > 0) {
1320
+ // Sequential tasks become individual groups of 1 (run one at a time)
1321
+ for (const id of sequentialBatch) {
1322
+ groups.push({ name: `${currentPhase} (sequential)`, taskIds: [id] });
1323
+ }
1324
+ sequentialBatch = [];
1325
+ }
1326
+ };
1327
+ for (const line of content.split('\n')) {
1328
+ // Detect phase headings
1329
+ const phaseMatch = line.match(/^#{2,3}\s+(?:Phase\s+\d+[:\s]*)?(.+)/i);
1330
+ if (phaseMatch) {
1331
+ flushBatches();
1332
+ currentPhase = phaseMatch[1].trim();
1333
+ continue;
1334
+ }
1335
+ // Match spec-kit task: - [ ] T001 [P] [US1] description
1336
+ const taskMatch = line.match(/^-\s+\[\s*\]\s+(T\d{3})\s*/);
1337
+ if (taskMatch) {
1338
+ const taskId = taskMatch[1];
1339
+ const isParallel = /\[P\]/.test(line);
1340
+ if (isParallel) {
1341
+ // Flush any pending sequential before starting parallel
1342
+ if (sequentialBatch.length > 0) {
1343
+ for (const id of sequentialBatch) {
1344
+ groups.push({ name: `${currentPhase} (sequential)`, taskIds: [id] });
1345
+ }
1346
+ sequentialBatch = [];
1347
+ }
1348
+ parallelBatch.push(taskId);
1349
+ }
1350
+ else {
1351
+ // Flush any pending parallel before starting sequential
1352
+ if (parallelBatch.length > 0) {
1353
+ groups.push({ name: `${currentPhase} (parallel)`, taskIds: [...parallelBatch] });
1354
+ parallelBatch = [];
1355
+ }
1356
+ sequentialBatch.push(taskId);
1357
+ }
1358
+ continue;
1359
+ }
1360
+ // Also match legacy FND-001 style within spec-kit files
1361
+ const legacyMatch = line.match(/^-\s+\[\s*\]\s+\**([A-Z]{2,4}-\d{3})\**/);
1362
+ if (legacyMatch) {
1363
+ const taskId = legacyMatch[1];
1364
+ const isParallel = /\[P\]/.test(line);
1365
+ if (isParallel) {
1366
+ parallelBatch.push(taskId);
1367
+ }
1368
+ else {
1369
+ sequentialBatch.push(taskId);
1370
+ }
1371
+ }
1372
+ }
1373
+ flushBatches();
1374
+ return groups;
1375
+ }
1376
+ /** Legacy format: ## Phase headings with ### Parallel Group sub-headings */
1377
+ parseLegacyTasks(content) {
1378
+ const groups = [];
1379
+ let currentGroup = null;
1380
+ for (const line of content.split('\n')) {
1381
+ const groupMatch = line.match(/^#{2,3}\s+(?:Phase\s+\d+[:\s]*|Parallel Group[:\s]*)?(.+)/i);
1382
+ if (groupMatch) {
1383
+ if (currentGroup && currentGroup.taskIds.length > 0) {
1384
+ groups.push(currentGroup);
1385
+ }
1386
+ currentGroup = { name: groupMatch[1].trim(), taskIds: [] };
1387
+ continue;
1388
+ }
1389
+ const taskMatch = line.match(/^-\s+\[\s*\]\s+\**([A-Z]{2,4}-\d{3})\**/);
1390
+ if (taskMatch && currentGroup) {
1391
+ currentGroup.taskIds.push(taskMatch[1]);
1392
+ }
1393
+ }
1394
+ if (currentGroup && currentGroup.taskIds.length > 0) {
1395
+ groups.push(currentGroup);
1396
+ }
1397
+ return groups;
1398
+ }
1399
+ buildPlaywrightContext() {
1400
+ const defaults = { testDir: 'e2e' };
1401
+ // Check .swarm/playwright.config.yaml
1402
+ const configPath = join(this.projectCwd, '.swarm', 'playwright.config.yaml');
1403
+ if (existsSync(configPath)) {
1404
+ try {
1405
+ const raw = readFileSync(configPath, 'utf-8');
1406
+ const parsed = parseYaml(raw);
1407
+ if (parsed) {
1408
+ return { ...defaults, ...parsed };
1409
+ }
1410
+ }
1411
+ catch {
1412
+ // Ignore invalid YAML
1413
+ }
1414
+ }
1415
+ // Check SwarmConfig
1416
+ if (this.config.playwright) {
1417
+ return { ...defaults, ...this.config.playwright };
1418
+ }
1419
+ return defaults;
1420
+ }
1421
+ // ═══════════════════════════════════════════════════════════════════
1422
+ // MayDay — autonomous end-to-end pipeline with fix-retest loop
1423
+ // ═══════════════════════════════════════════════════════════════════
1424
+ async runMayday(featureRequest, opts = {}) {
1425
+ if (this.state.getMayday()?.active) {
1426
+ throw new Error('MayDay pipeline is already running. Use --resume to continue or stop it first.');
1427
+ }
1428
+ const stack = opts.stack ?? this.config.stack;
1429
+ const maxIterations = opts.maxIterations ?? 5;
1430
+ // Apply model override for the entire mayday run
1431
+ if (opts.model) {
1432
+ this.config.model = opts.model;
1433
+ }
1434
+ // Validate fromStage if provided
1435
+ const fromStage = opts.fromStage;
1436
+ if (fromStage) {
1437
+ const validStages = ['analyze', 'architect', 'plan', 'build', 'test'];
1438
+ if (!validStages.includes(fromStage)) {
1439
+ throw new Error(`Invalid --from stage: "${fromStage}". Must be one of: ${validStages.join(', ')}`);
1440
+ }
1441
+ // Validate that required artifacts exist for skipped stages
1442
+ this.validateSkippedArtifacts(fromStage);
1443
+ }
1444
+ const mayday = {
1445
+ active: true,
1446
+ featureRequest,
1447
+ currentStage: fromStage ?? 'analyze',
1448
+ fixIteration: 0,
1449
+ maxFixIterations: maxIterations,
1450
+ lastTestOutput: null,
1451
+ lastTestPassed: null,
1452
+ failureCount: null,
1453
+ fixAgentIds: [],
1454
+ userMessages: [],
1455
+ startedAt: Date.now(),
1456
+ pausedAt: null,
1457
+ error: null,
1458
+ figmaUrl: opts.figmaUrl,
1459
+ maxFixBudgetUsd: opts.maxFixBudgetUsd !== undefined ? opts.maxFixBudgetUsd : 15,
1460
+ approvalRequired: opts.approvalRequired ?? false,
1461
+ pendingApproval: null,
1462
+ };
1463
+ this.state.setMayday(mayday);
1464
+ // Mark skipped stages
1465
+ if (fromStage) {
1466
+ const stages = ['analyze', 'architect', 'plan', 'build', 'test'];
1467
+ const fromIdx = stages.indexOf(fromStage);
1468
+ for (let i = 0; i < fromIdx; i++) {
1469
+ this.state.updateStage(stages[i], { status: 'skipped' });
1470
+ }
1471
+ console.log(chalk.dim(`Skipping stages before "${fromStage}"`));
1472
+ }
1473
+ console.log(chalk.bold(`\nSwarm Pipeline — building your feature`));
1474
+ console.log(chalk.dim(`Feature: ${featureRequest}`));
1475
+ console.log(chalk.dim(`Model: ${this.config.model} | Budget: ${this.config.maxBudgetUsd ? '$' + this.config.maxBudgetUsd : 'unlimited'} | Max fix iterations: ${maxIterations}\n`));
1476
+ const pipelineStart = Date.now();
1477
+ const headless = opts.headless ?? false;
1478
+ try {
1479
+ await this.executeMaydayPipeline(stack, opts.parallel, headless);
1480
+ this.state.archiveRun();
1481
+ this.attemptAutoCreatePR();
1482
+ this.webhooks.maydayComplete(this.config.projectName, this.state.getState()).catch(() => { });
1483
+ }
1484
+ catch (err) {
1485
+ const errMsg = err instanceof Error ? err.message : String(err);
1486
+ this.state.updateMayday({ active: false, error: errMsg });
1487
+ this.generateFailureReport();
1488
+ this.state.archiveRun();
1489
+ this.webhooks.maydayError(this.config.projectName, errMsg).catch(() => { });
1490
+ throw err;
1491
+ }
1492
+ // Print summary
1493
+ const totalElapsed = Date.now() - pipelineStart;
1494
+ const pipelineState = this.state.getState();
1495
+ const totalCost = pipelineState.totalCost.totalUsd;
1496
+ const elapsedMin = (totalElapsed / 60000).toFixed(1);
1497
+ console.log(chalk.bold(`\nPipeline complete`));
1498
+ console.log(chalk.dim(` Total time: ${elapsedMin}m | Total cost: $${totalCost.toFixed(2)}`));
1499
+ // Quality scores summary
1500
+ const scores = pipelineState.qualityScores ?? [];
1501
+ if (scores.length > 0) {
1502
+ const avgScore = Math.round(scores.reduce((sum, s) => sum + s.overall, 0) / scores.length);
1503
+ const color = avgScore >= 80 ? chalk.green : avgScore >= 50 ? chalk.yellow : chalk.red;
1504
+ console.log(` Quality: ${color(`${avgScore}/100`)}` + chalk.dim(` (${scores.map(s => `${s.artifact}: ${s.overall}`).join(', ')})`));
1505
+ }
1506
+ console.log(chalk.dim(` Run ${chalk.bold('hivemind status')} to see details or ${chalk.bold('hivemind dashboard')} to view in browser\n`));
1507
+ }
1508
+ async resumeMayday(opts = {}) {
1509
+ const mayday = this.state.getMayday();
1510
+ if (!mayday || !mayday.active) {
1511
+ throw new Error('No active MayDay session to resume.');
1512
+ }
1513
+ const stack = this.state.getState().stack;
1514
+ this.state.updateMayday({ pausedAt: null });
1515
+ console.log(chalk.bold(`\nSwarm Pipeline — resuming from ${mayday.currentStage}`));
1516
+ if (mayday.currentStage === 'fix-loop') {
1517
+ console.log(chalk.dim(`Fix iteration: ${mayday.fixIteration}/${mayday.maxFixIterations}`));
1518
+ }
1519
+ console.log('');
1520
+ try {
1521
+ await this.executeMaydayPipeline(stack, opts.parallel, opts.headless ?? false);
1522
+ this.state.archiveRun();
1523
+ this.attemptAutoCreatePR();
1524
+ this.webhooks.maydayComplete(this.config.projectName, this.state.getState()).catch(() => { });
1525
+ }
1526
+ catch (err) {
1527
+ const errMsg = err instanceof Error ? err.message : String(err);
1528
+ this.state.updateMayday({ active: false, error: errMsg });
1529
+ this.state.archiveRun();
1530
+ this.webhooks.maydayError(this.config.projectName, errMsg).catch(() => { });
1531
+ throw err;
1532
+ }
1533
+ }
1534
+ async executeMaydayPipeline(stack, parallel, headless = false) {
1535
+ const mayday = this.state.getMayday();
1536
+ const stageOpts = { stack, interactive: false, figmaUrl: mayday.figmaUrl, headless };
1537
+ // Create a feature branch for this mayday run
1538
+ this.createFeatureBranch(mayday.featureRequest);
1539
+ // Check for custom pipeline definition
1540
+ const swarmDir = join(this.projectCwd, '.swarm');
1541
+ const customDef = loadPipelineDefinition(swarmDir);
1542
+ if (customDef) {
1543
+ console.log(chalk.cyan(`[mayday] Using custom pipeline definition (${customDef.stages.length} stages)`));
1544
+ await this.runCustomPipeline(customDef, mayday.featureRequest, {
1545
+ stack,
1546
+ interactive: false,
1547
+ figmaUrl: mayday.figmaUrl,
1548
+ parallel,
1549
+ });
1550
+ // After custom pipeline, enter fix loop
1551
+ await this.maydayFixLoop(stack, parallel, headless);
1552
+ return;
1553
+ }
1554
+ // Pipeline stages in order — resume from currentStage
1555
+ const stages = ['analyze', 'architect', 'plan', 'build', 'test'];
1556
+ const startIdx = stages.indexOf(mayday.currentStage);
1557
+ const STAGE_LABELS = {
1558
+ analyze: 'Analyzing requirements',
1559
+ architect: 'Designing architecture',
1560
+ plan: 'Planning tasks',
1561
+ build: 'Building code',
1562
+ test: 'Running tests',
1563
+ };
1564
+ // Run pipeline stages (or resume from where we left off)
1565
+ if (startIdx >= 0) {
1566
+ for (let i = startIdx; i < stages.length; i++) {
1567
+ const stage = stages[i];
1568
+ // Check if mayday was stopped
1569
+ if (!this.state.getMayday()?.active) {
1570
+ console.log(chalk.yellow(`\nStopped by user.`));
1571
+ return;
1572
+ }
1573
+ // Skip already-completed stages (preserved across restarts)
1574
+ const existingStage = this.state.getState().stages[stage];
1575
+ if (existingStage.status === 'done' && existingStage.artifact) {
1576
+ console.log(chalk.dim(` [${i + 1}/5] ${STAGE_LABELS[stage] || stage}... (preserved)`));
1577
+ continue;
1578
+ }
1579
+ // Try to resume interrupted stage via session if available
1580
+ const canResume = existingStage.status === 'error'
1581
+ && existingStage.sessionId
1582
+ && existingStage.finishedAt
1583
+ && (Date.now() - existingStage.finishedAt) < 24 * 60 * 60 * 1000; // < 24h old
1584
+ // Consume any queued user messages and log them
1585
+ const userMsgs = this.state.consumeMaydayMessages();
1586
+ if (userMsgs.length > 0) {
1587
+ console.log(chalk.magenta(` User guidance: ${userMsgs.join(' | ')}`));
1588
+ }
1589
+ this.state.updateMayday({ currentStage: stage });
1590
+ const stageNum = i + 1;
1591
+ const stageLabel = STAGE_LABELS[stage] || stage;
1592
+ const costBefore = this.state.getState().totalCost.totalUsd;
1593
+ const stageStart = Date.now();
1594
+ if (canResume) {
1595
+ process.stdout.write(chalk.cyan(` [${stageNum}/5] ${stageLabel} (resuming session)...`));
1596
+ // Prepend context from prior stages to the resume prompt
1597
+ const resumeContext = this.state.getResumeContext(stage);
1598
+ const resumePrompt = resumeContext
1599
+ ? `${resumeContext}\nPlease continue where you left off.`
1600
+ : 'Please continue where you left off.';
1601
+ const resumed = await this.agentManager.resumeSession({
1602
+ sessionId: existingStage.sessionId,
1603
+ name: `${stage}-resume`,
1604
+ persona: stage === 'analyze' ? 'analyst' : stage === 'architect' ? 'architect' : stage === 'plan' ? 'lead' : stage === 'test' ? 'tester' : 'engineer',
1605
+ stack,
1606
+ prompt: resumePrompt,
1607
+ cwd: this.projectCwd,
1608
+ permissionMode: 'auto',
1609
+ });
1610
+ if (resumed && resumed.status === 'done') {
1611
+ // Resume succeeded — mark stage done
1612
+ const artifact = STAGE_ARTIFACT_MAP[stage];
1613
+ if (artifact && existsSync(join(this.projectCwd, artifact))) {
1614
+ await this.finishStage(stage, artifact);
1615
+ }
1616
+ else {
1617
+ this.state.updateStage(stage, { status: 'done', finishedAt: Date.now() });
1618
+ }
1619
+ const stageElapsed = Date.now() - stageStart;
1620
+ const stageCost = this.state.getState().totalCost.totalUsd - costBefore;
1621
+ this.state.updateStage(stage, { stageCost });
1622
+ console.log(chalk.dim(` (${(stageElapsed / 1000).toFixed(0)}s, $${stageCost.toFixed(2)})`));
1623
+ continue;
1624
+ }
1625
+ // Resume failed — fall through to fresh start
1626
+ console.log(chalk.yellow(` session expired, starting fresh...`));
1627
+ }
1628
+ process.stdout.write(chalk.cyan(` [${stageNum}/5] ${stageLabel}...`));
1629
+ // Prepend context summaries from completed prior stages to fresh-start prompts
1630
+ const priorContext = this.state.getResumeContext(stage);
1631
+ // Backward context flow: collect failure reasons from downstream stages
1632
+ const failureCtx = Array.from(this.stageFailureContext.entries())
1633
+ .map(([s, msg]) => `[${s}] ${msg}`)
1634
+ .join('\n');
1635
+ const backwardContext = failureCtx ? `\n\n## Prior Failure Context (avoid these issues):\n${failureCtx}` : '';
1636
+ await this.runStageWithRetry(stage, async () => {
1637
+ switch (stage) {
1638
+ case 'analyze':
1639
+ await this.runAnalyze(mayday.featureRequest, stageOpts);
1640
+ break;
1641
+ case 'architect':
1642
+ await this.runArchitect({ ...stageOpts, prompt: backwardContext || undefined });
1643
+ break;
1644
+ case 'plan': {
1645
+ const guidance = userMsgs.length > 0 ? userMsgs.join('\n') : undefined;
1646
+ const combined = [priorContext, guidance, backwardContext].filter(Boolean).join('\n');
1647
+ await this.runPlan({ ...stageOpts, prompt: combined || undefined });
1648
+ break;
1649
+ }
1650
+ case 'build':
1651
+ await this.runBuild({ stack, parallel: parallel ?? 3 });
1652
+ break;
1653
+ case 'test':
1654
+ await this.runTest({ stack, figmaUrl: mayday.figmaUrl });
1655
+ break;
1656
+ }
1657
+ });
1658
+ // Log stage completion with elapsed time and cost
1659
+ const stageElapsed = Date.now() - stageStart;
1660
+ const stageCost = this.state.getState().totalCost.totalUsd - costBefore;
1661
+ this.state.updateStage(stage, { stageCost });
1662
+ const elapsedStr = stageElapsed < 60000
1663
+ ? `${(stageElapsed / 1000).toFixed(0)}s`
1664
+ : `${(stageElapsed / 60000).toFixed(1)}m`;
1665
+ console.log(chalk.green(` done`) + chalk.dim(` (${elapsedStr}, $${stageCost.toFixed(2)})`));
1666
+ // Auto-commit after each stage completes
1667
+ this.autoCommitStage(stage, STAGE_ARTIFACT_MAP[stage]);
1668
+ // Stage transition pause — let user review and optionally send feedback
1669
+ if (!headless && i < stages.length - 1) {
1670
+ const nextStage = stages[i + 1];
1671
+ const feedback = await stageTransitionPause(stage, nextStage, STAGE_ARTIFACT_MAP[stage]);
1672
+ if (feedback) {
1673
+ // Inject user feedback as a message for the next stage
1674
+ this.state.pushMaydayMessage(feedback);
1675
+ console.log(chalk.magenta(` Feedback noted — will be included in ${nextStage} stage.`));
1676
+ }
1677
+ }
1678
+ // Approval gate: pause and wait for user approval before proceeding
1679
+ const currentMayday = this.state.getMayday();
1680
+ if (currentMayday?.approvalRequired && i < stages.length - 1) {
1681
+ this.state.updateMayday({
1682
+ pendingApproval: { stage, requestedAt: Date.now() },
1683
+ });
1684
+ console.log(chalk.yellow(`[mayday] Waiting for approval to proceed past ${stage}...`));
1685
+ // Poll until pendingApproval is cleared or mayday becomes inactive
1686
+ while (true) {
1687
+ await sleep(500);
1688
+ const m = this.state.getMayday();
1689
+ if (!m?.active) {
1690
+ console.log(chalk.yellow(`\n[mayday] Rejected or stopped during approval.`));
1691
+ return;
1692
+ }
1693
+ if (!m.pendingApproval) {
1694
+ console.log(chalk.green(`[mayday] Approval granted — continuing pipeline.`));
1695
+ break;
1696
+ }
1697
+ }
1698
+ }
1699
+ }
1700
+ }
1701
+ // After initial pipeline, enter the fix-retest loop
1702
+ await this.maydayFixLoop(stack, parallel, headless);
1703
+ }
1704
+ async maydayFixLoop(stack, _parallel, headless = false) {
1705
+ // Resolve test framework run command for this stack
1706
+ const frameworks = getTestFrameworks(stack);
1707
+ const testRunCmd = frameworks[0]?.runCmd ?? 'npx playwright test';
1708
+ // Evaluate initial test results
1709
+ let testResults = this.evaluateTestResults();
1710
+ const fixHistory = this.state.getMayday()?.fixHistory ?? [];
1711
+ this.state.updateMayday({
1712
+ currentStage: 'fix-loop',
1713
+ lastTestPassed: testResults.passed,
1714
+ lastTestOutput: testResults.output,
1715
+ failureCount: testResults.failureCount,
1716
+ fixHistory,
1717
+ });
1718
+ if (testResults.passed) {
1719
+ console.log(chalk.green.bold(`\n All tests passed on first run!`));
1720
+ this.state.updateMayday({ active: false, currentStage: 'complete' });
1721
+ this.recordSuccessMemory(0);
1722
+ return;
1723
+ }
1724
+ const mayday = this.state.getMayday();
1725
+ const maxIter = mayday.maxFixIterations;
1726
+ let previousFailedTests = testResults.failures.map(f => f.testName);
1727
+ let stuckCount = 0;
1728
+ for (let iteration = mayday.fixIteration + 1; iteration <= maxIter; iteration++) {
1729
+ // Check if stopped
1730
+ if (!this.state.getMayday()?.active) {
1731
+ console.log(chalk.yellow(`\n[mayday] Stopped by user.`));
1732
+ return;
1733
+ }
1734
+ // Check cumulative cost before each fix iteration
1735
+ const totalCost = this.state.getState().totalCost.totalUsd;
1736
+ const maxFixBudget = this.state.getMayday()?.maxFixBudgetUsd;
1737
+ if (maxFixBudget !== null && maxFixBudget !== undefined && totalCost >= maxFixBudget) {
1738
+ console.log(chalk.red.bold(`\n[mayday] Fix budget exhausted ($${totalCost.toFixed(2)} >= $${maxFixBudget}). Stopping.`));
1739
+ this.state.updateMayday({ active: false, error: `Fix budget limit reached: $${totalCost.toFixed(2)}` });
1740
+ this.generateFailureReport();
1741
+ return;
1742
+ }
1743
+ this.state.updateMayday({ fixIteration: iteration, currentStage: 'fix-loop' });
1744
+ console.log(chalk.red(`\n[mayday] Fix iteration ${iteration}/${maxIter} — ${testResults.failureCount} failure(s)`));
1745
+ // Interactive pause — let user guide the fix or skip
1746
+ if (!headless) {
1747
+ const fixPause = await fixLoopPause(iteration, maxIter, testResults.failureCount ?? 0);
1748
+ if (fixPause.action === 'skip') {
1749
+ console.log(chalk.yellow(`[mayday] Skipping remaining fix iterations.`));
1750
+ break;
1751
+ }
1752
+ if (fixPause.feedback) {
1753
+ this.state.pushMaydayMessage(fixPause.feedback);
1754
+ }
1755
+ }
1756
+ // Consume user messages for guidance
1757
+ const userMsgs = this.state.consumeMaydayMessages();
1758
+ // Group failures by file for targeted fixing (I1)
1759
+ const failureGroups = this.groupFailuresByFile(testResults.failures);
1760
+ // Determine approach via strategy escalation (I2 — stuck detection)
1761
+ let approach = 'standard';
1762
+ if (stuckCount >= 4) {
1763
+ approach = 'simplify';
1764
+ console.log(chalk.yellow(`[mayday] Stuck for ${stuckCount} iterations — escalating to SIMPLIFY (reduce scope to pass tests)`));
1765
+ }
1766
+ else if (stuckCount >= 3) {
1767
+ approach = 'rewrite';
1768
+ console.log(chalk.yellow(`[mayday] Stuck for ${stuckCount} iterations — escalating to REWRITE affected components`));
1769
+ }
1770
+ else if (stuckCount >= 2) {
1771
+ approach = 'broader-context';
1772
+ console.log(chalk.yellow(`[mayday] Stuck on same failures for ${stuckCount} iterations — trying broader context approach`));
1773
+ }
1774
+ // Spawn fix agents — one per failure group if multiple, or one for all
1775
+ const fixAgentIds = [];
1776
+ if (failureGroups.length > 1 && approach === 'standard') {
1777
+ console.log(chalk.cyan(`[mayday] Spawning ${failureGroups.length} targeted fix agents...`));
1778
+ const fixAgents = await Promise.all(failureGroups.map((group, idx) => this.agentManager.spawn({
1779
+ name: `fix-engineer-${iteration}-g${idx + 1}`,
1780
+ persona: 'engineer',
1781
+ stack,
1782
+ prompt: this.buildTargetedFixPrompt(group, fixHistory, userMsgs, testRunCmd),
1783
+ model: this.modelFor('engineer'),
1784
+ cwd: this.projectCwd,
1785
+ interactive: false,
1786
+ permissionMode: 'auto',
1787
+ appendSystemPrompt: this.getEngineerConventions(),
1788
+ })));
1789
+ for (const a of fixAgents)
1790
+ fixAgentIds.push(a.id);
1791
+ await Promise.allSettled(fixAgents.map(a => this.waitForAgentWithBudgetCheck(a.id)));
1792
+ const totalFixCost = fixAgents.reduce((sum, a) => sum + a.cost.totalUsd, 0);
1793
+ console.log(chalk.green(`[mayday] ${fixAgents.length} fix agents done. Cost: $${totalFixCost.toFixed(4)}`));
1794
+ }
1795
+ else {
1796
+ // Single fix agent for all failures
1797
+ const fixPrompt = this.buildFixPrompt(testResults, userMsgs, fixHistory, approach, testRunCmd);
1798
+ const fixAgent = await this.agentManager.spawn({
1799
+ name: `fix-engineer-${iteration}`,
1800
+ persona: 'engineer',
1801
+ stack,
1802
+ prompt: fixPrompt,
1803
+ model: this.modelFor('engineer'),
1804
+ cwd: this.projectCwd,
1805
+ interactive: false,
1806
+ permissionMode: 'auto',
1807
+ appendSystemPrompt: this.getEngineerConventions(),
1808
+ });
1809
+ fixAgentIds.push(fixAgent.id);
1810
+ await this.waitForAgentWithBudgetCheck(fixAgent.id);
1811
+ console.log(chalk.green(`[mayday] Fix engineer done. Cost: $${fixAgent.cost.totalUsd.toFixed(4)}`));
1812
+ }
1813
+ this.state.updateMayday({
1814
+ fixAgentIds: [...(this.state.getMayday()?.fixAgentIds ?? []), ...fixAgentIds],
1815
+ });
1816
+ // Re-run tests
1817
+ console.log(chalk.red(`[mayday] Re-running tests...`));
1818
+ this.state.updateStage('test', { status: 'pending' });
1819
+ await this.runTest({ stack });
1820
+ // Evaluate results
1821
+ const newTestResults = this.evaluateTestResults();
1822
+ const currentFailedTests = newTestResults.failures.map(f => f.testName);
1823
+ // I2: Regression detection
1824
+ const fixedTests = previousFailedTests.filter(t => !currentFailedTests.includes(t));
1825
+ const newFailures = currentFailedTests.filter(t => !previousFailedTests.includes(t));
1826
+ const sameFailures = currentFailedTests.filter(t => previousFailedTests.includes(t));
1827
+ if (newFailures.length > 0) {
1828
+ console.log(chalk.yellow(`[mayday] ⚠ Regressions detected: ${newFailures.length} new failure(s)`));
1829
+ }
1830
+ if (fixedTests.length > 0) {
1831
+ console.log(chalk.green(`[mayday] Fixed: ${fixedTests.length} test(s)`));
1832
+ }
1833
+ if (sameFailures.length > 0 && sameFailures.length === previousFailedTests.length) {
1834
+ stuckCount++;
1835
+ console.log(chalk.yellow(`[mayday] Same failures persisting (stuck count: ${stuckCount})`));
1836
+ }
1837
+ else {
1838
+ stuckCount = 0; // Reset if any progress
1839
+ }
1840
+ // I3: Record fix history
1841
+ const historyEntry = {
1842
+ iteration,
1843
+ failedTests: previousFailedTests,
1844
+ fixedTests,
1845
+ newFailures,
1846
+ approach,
1847
+ agentId: fixAgentIds[0],
1848
+ cost: this.state.getState().agents
1849
+ .filter(a => fixAgentIds.includes(a.id))
1850
+ .reduce((sum, a) => sum + a.cost.totalUsd, 0),
1851
+ timestamp: Date.now(),
1852
+ };
1853
+ fixHistory.push(historyEntry);
1854
+ testResults = newTestResults;
1855
+ previousFailedTests = currentFailedTests;
1856
+ this.state.updateMayday({
1857
+ lastTestPassed: testResults.passed,
1858
+ lastTestOutput: testResults.output,
1859
+ failureCount: testResults.failureCount,
1860
+ fixHistory,
1861
+ });
1862
+ if (testResults.passed) {
1863
+ console.log(chalk.green.bold(`\n All tests passed after ${iteration} fix iteration(s)!`));
1864
+ this.state.updateMayday({ active: false, currentStage: 'complete' });
1865
+ this.recordSuccessMemory(iteration);
1866
+ return;
1867
+ }
1868
+ // If stuck for 5+ iterations (all strategies exhausted), abort
1869
+ if (stuckCount >= 5) {
1870
+ console.log(chalk.red.bold(`\n[mayday] Stuck on same failures for ${stuckCount} iterations (all strategies exhausted). Aborting fix loop.`));
1871
+ const stuckError = `Stuck: same ${currentFailedTests.length} test(s) failing for ${stuckCount} iterations despite strategy escalation (standard → broader-context → rewrite → simplify).`;
1872
+ this.state.updateMayday({
1873
+ active: false,
1874
+ currentStage: 'complete',
1875
+ error: stuckError,
1876
+ });
1877
+ this.generateFailureReport();
1878
+ this.recordFailureMemory('fix-loop', stuckError, fixHistory);
1879
+ return;
1880
+ }
1881
+ }
1882
+ // Max iterations reached
1883
+ const maxIterError = `Max fix iterations reached. ${testResults.failureCount} test(s) still failing.`;
1884
+ console.log(chalk.yellow.bold(`\n[mayday] ${maxIterError}`));
1885
+ this.state.updateMayday({
1886
+ active: false,
1887
+ currentStage: 'complete',
1888
+ error: maxIterError,
1889
+ });
1890
+ this.generateFailureReport();
1891
+ this.recordFailureMemory('fix-loop', maxIterError, fixHistory);
1892
+ }
1893
+ /** Record a successful pipeline run to memory */
1894
+ recordSuccessMemory(fixIterations) {
1895
+ try {
1896
+ const mayday = this.state.getMayday();
1897
+ const pState = this.state.getState();
1898
+ const stages = Object.entries(pState.stages)
1899
+ .filter(([, s]) => s.status === 'done')
1900
+ .map(([name, s]) => ({
1901
+ name,
1902
+ cost: s.stageCost ?? 0,
1903
+ durationMs: (s.finishedAt ?? 0) - (s.startedAt ?? 0),
1904
+ }));
1905
+ recordPipelineSuccess(this.memoryStore, {
1906
+ featureRequest: mayday?.featureRequest ?? 'unknown',
1907
+ totalCost: pState.totalCost.totalUsd,
1908
+ durationMs: Date.now() - (mayday?.startedAt ?? Date.now()),
1909
+ fixIterations,
1910
+ stages,
1911
+ });
1912
+ }
1913
+ catch { /* non-critical */ }
1914
+ }
1915
+ /** Record a failed pipeline run to memory */
1916
+ recordFailureMemory(failedStage, error, fixHistory) {
1917
+ try {
1918
+ const mayday = this.state.getMayday();
1919
+ recordPipelineFailure(this.memoryStore, {
1920
+ featureRequest: mayday?.featureRequest ?? 'unknown',
1921
+ failedStage,
1922
+ error,
1923
+ fixHistory: fixHistory?.map(h => ({ approach: h.approach, failedTests: h.failedTests })),
1924
+ });
1925
+ }
1926
+ catch { /* non-critical */ }
1927
+ }
1928
+ /** Group test failures by source file for targeted fixing (I1) */
1929
+ groupFailuresByFile(failures) {
1930
+ if (failures.length === 0)
1931
+ return [];
1932
+ const groups = new Map();
1933
+ for (const f of failures) {
1934
+ const key = f.file || 'unknown';
1935
+ if (!groups.has(key))
1936
+ groups.set(key, []);
1937
+ groups.get(key).push(f);
1938
+ }
1939
+ return Array.from(groups.entries()).map(([file, failures]) => ({ file, failures }));
1940
+ }
1941
+ /** Build a targeted fix prompt for a specific failure group */
1942
+ buildTargetedFixPrompt(group, history, userMessages, testRunCmd) {
1943
+ const parts = [
1944
+ `You are a BUG FIX ENGINEER. Fix the ${group.failures.length} failing test(s) in ${group.file}.`,
1945
+ '',
1946
+ '## Failures:',
1947
+ ...group.failures.map(f => `- **${f.testName}**: ${f.error.slice(0, 300)}`),
1948
+ ];
1949
+ if (history.length > 0) {
1950
+ parts.push('', '## Fix History (what was already tried):', ...history.slice(-3).map(h => `- Iteration ${h.iteration} (${h.approach}): fixed ${h.fixedTests.length}, regressed ${h.newFailures.length}`));
1951
+ }
1952
+ if (userMessages.length > 0) {
1953
+ parts.push('', '## User Guidance:', ...userMessages.map(m => `- ${m}`));
1954
+ }
1955
+ parts.push('', '## Rules:', '- Read the failing test files to understand what is expected', '- Fix the APPLICATION code (not the tests, unless the test itself is clearly wrong)', '- Do NOT break passing tests', `- Run \`${testRunCmd ?? 'npx playwright test'}\` after fixing to verify`, '- Focus only on these specific failures. Do not refactor unrelated code.');
1956
+ return parts.join('\n');
1957
+ }
1958
+ buildFixPrompt(testResults, userMessages, history = [], approach = 'standard', testRunCmd) {
1959
+ const parts = [
1960
+ 'You are a BUG FIX ENGINEER. Tests are failing after a build. Fix the bugs.',
1961
+ ];
1962
+ if (approach === 'broader-context') {
1963
+ parts.push('', '⚠️ STRATEGY: BROADER CONTEXT — Previous standard fixes did not work.', 'Try a DIFFERENT approach: read more surrounding code, check imports/types carefully,', 'trace the full data flow, or consider that the test expectations may need updating.');
1964
+ }
1965
+ else if (approach === 'rewrite') {
1966
+ parts.push('', '⚠️ STRATEGY: REWRITE — Standard and broader-context approaches have BOTH failed.', 'Consider a COMPREHENSIVE REWRITE of the failing components:', '- Delete and rewrite the affected functions/modules from scratch', '- Re-examine the approach — maybe a different algorithm or pattern is needed', '- Check if test expectations match the requirements (tests may need updating too)', '- Do NOT try incremental patches — those have already failed repeatedly.');
1967
+ }
1968
+ else if (approach === 'simplify') {
1969
+ parts.push('', '⚠️ STRATEGY: SIMPLIFY — All previous approaches have failed. Last resort.', 'REDUCE SCOPE to get tests passing:', '- Implement the minimum viable version that satisfies the test assertions', '- Stub out complex features that are causing failures', '- Remove edge-case handling that introduces bugs', '- The goal is now: MAKE TESTS PASS, even with simplified functionality.', '- If a test is testing unimplemented behavior, mark the test as skipped with a TODO comment.');
1970
+ }
1971
+ // Use structured failures if available
1972
+ if (testResults.failures.length > 0) {
1973
+ parts.push('', '## Specific Failures:', ...testResults.failures.map(f => `- **${f.testName}** (${f.file}): ${f.error.slice(0, 300)}`));
1974
+ }
1975
+ parts.push('', '## Test Output (last run):', '```', testResults.output?.slice(-3000) ?? 'No test output captured.', '```', '', `Summary: ${testResults.summary}`);
1976
+ // I3: Include fix history so agent knows what was tried
1977
+ if (history.length > 0) {
1978
+ parts.push('', '## Fix History (what was already tried — do NOT repeat the same approach):', ...history.slice(-5).map(h => `- Iteration ${h.iteration} (${h.approach}): fixed=[${h.fixedTests.join(', ')}], new_failures=[${h.newFailures.join(', ')}]`));
1979
+ }
1980
+ if (userMessages.length > 0) {
1981
+ parts.push('', '## User Guidance:', ...userMessages.map((m) => `- ${m}`));
1982
+ }
1983
+ parts.push('', '## Rules:', '- Read the failing test files to understand what is expected', '- Read the application code to find the bug', '- Fix the APPLICATION code (not the tests, unless the test itself is clearly wrong)', '- Do NOT break passing tests', `- Run \`${testRunCmd ?? 'npx playwright test'}\` after fixing to verify`, '- If you cannot fix a bug, document why in a code comment', '- Focus only on the failures. Do not refactor unrelated code.');
1984
+ return parts.join('\n');
1985
+ }
1986
+ evaluateTestResults() {
1987
+ // 1. Try structured JSON results first
1988
+ const jsonPath = join(this.projectCwd, '.swarm', 'test-results.json');
1989
+ if (existsSync(jsonPath)) {
1990
+ try {
1991
+ const raw = readFileSync(jsonPath, 'utf-8');
1992
+ try {
1993
+ // Try standard JSON parse first
1994
+ const json = JSON.parse(raw);
1995
+ return this.parseStructuredTestResults(json);
1996
+ }
1997
+ catch {
1998
+ // Might be NDJSON (go test -json) — parse as string
1999
+ return this.parseGoTestJson(raw);
2000
+ }
2001
+ }
2002
+ catch {
2003
+ // File read failed — fall through to regex
2004
+ }
2005
+ }
2006
+ // Also check .swarm/test-results.txt for frameworks that don't produce JSON
2007
+ const txtPath = join(this.projectCwd, '.swarm', 'test-results.txt');
2008
+ if (existsSync(txtPath)) {
2009
+ try {
2010
+ const raw = readFileSync(txtPath, 'utf-8');
2011
+ return this.parseTestOutputRegex(raw);
2012
+ }
2013
+ catch { /* fall through */ }
2014
+ }
2015
+ // 2. Fall back to regex parsing of agent output
2016
+ const agents = this.state.getState().agents;
2017
+ const testRunner = [...agents]
2018
+ .reverse()
2019
+ .find((a) => a.name.startsWith('test-runner-'));
2020
+ if (!testRunner) {
2021
+ return { passed: false, failureCount: null, failures: [], summary: 'No test runner output found.', output: null };
2022
+ }
2023
+ return this.parseTestOutputRegex(testRunner.output);
2024
+ }
2025
+ /** Parse structured test results JSON — auto-detects format (Playwright, Vitest, go test, pytest) */
2026
+ parseStructuredTestResults(json) {
2027
+ // Detect format and dispatch
2028
+ if (typeof json === 'object' && json !== null) {
2029
+ const obj = json;
2030
+ // Playwright: has `suites` array at top level
2031
+ if (Array.isArray(obj.suites)) {
2032
+ return this.parsePlaywrightJson(obj);
2033
+ }
2034
+ // Vitest: has `testResults` array (Vitest JSON reporter)
2035
+ if (Array.isArray(obj.testResults)) {
2036
+ return this.parseVitestJson(obj);
2037
+ }
2038
+ // pytest-json-report: has `tests` array and `summary`
2039
+ if (Array.isArray(obj.tests) && obj.summary) {
2040
+ return this.parsePytestJson(obj);
2041
+ }
2042
+ }
2043
+ // go test -json: NDJSON lines (each line is an object with Action/Test/Output)
2044
+ if (typeof json === 'string') {
2045
+ return this.parseGoTestJson(json);
2046
+ }
2047
+ // If it's already parsed as an array of NDJSON objects
2048
+ if (Array.isArray(json)) {
2049
+ return this.parseGoTestJsonLines(json);
2050
+ }
2051
+ return { passed: false, failureCount: null, failures: [], summary: 'Unknown JSON test results format', output: null };
2052
+ }
2053
+ /** Parse Playwright JSON reporter output */
2054
+ parsePlaywrightJson(json) {
2055
+ const failures = [];
2056
+ let passedCount = 0;
2057
+ let failedCount = 0;
2058
+ const walkSpecs = (specs) => {
2059
+ for (const spec of specs) {
2060
+ const tests = spec.tests;
2061
+ if (!tests)
2062
+ continue;
2063
+ for (const test of tests) {
2064
+ const results = test.results;
2065
+ if (!results || results.length === 0)
2066
+ continue;
2067
+ const lastResult = results[results.length - 1];
2068
+ const status = lastResult.status;
2069
+ if (status === 'passed') {
2070
+ passedCount++;
2071
+ }
2072
+ else if (status === 'failed' || status === 'timedOut') {
2073
+ failedCount++;
2074
+ const errorMsg = lastResult.error
2075
+ ? lastResult.error.message ?? ''
2076
+ : '';
2077
+ failures.push({
2078
+ testName: `${spec.title ?? ''} > ${test.title ?? ''}`.trim(),
2079
+ file: spec.file ?? '',
2080
+ error: errorMsg.slice(0, 500),
2081
+ });
2082
+ }
2083
+ }
2084
+ }
2085
+ };
2086
+ const walkSuites = (suiteList) => {
2087
+ for (const suite of suiteList) {
2088
+ const specs = suite.specs;
2089
+ if (specs)
2090
+ walkSpecs(specs);
2091
+ const childSuites = suite.suites;
2092
+ if (childSuites)
2093
+ walkSuites(childSuites);
2094
+ }
2095
+ };
2096
+ walkSuites(json.suites);
2097
+ const passed = failedCount === 0 && passedCount > 0;
2098
+ const summary = `${passedCount} passed, ${failedCount} failed (Playwright JSON)`;
2099
+ const output = failures.length > 0
2100
+ ? failures.map(f => `FAIL: ${f.testName}\n File: ${f.file}\n Error: ${f.error}`).join('\n\n')
2101
+ : `All ${passedCount} tests passed.`;
2102
+ return { passed, failureCount: failedCount, failures, summary, output };
2103
+ }
2104
+ /** Parse Vitest JSON reporter output */
2105
+ parseVitestJson(json) {
2106
+ const failures = [];
2107
+ let passedCount = 0;
2108
+ let failedCount = 0;
2109
+ const testResults = json.testResults;
2110
+ if (!testResults) {
2111
+ return { passed: false, failureCount: null, failures: [], summary: 'Empty Vitest results', output: null };
2112
+ }
2113
+ for (const suite of testResults) {
2114
+ const assertionResults = suite.assertionResults;
2115
+ if (!assertionResults)
2116
+ continue;
2117
+ for (const test of assertionResults) {
2118
+ const status = test.status;
2119
+ if (status === 'passed') {
2120
+ passedCount++;
2121
+ }
2122
+ else if (status === 'failed') {
2123
+ failedCount++;
2124
+ const msgs = test.failureMessages;
2125
+ failures.push({
2126
+ testName: (test.fullName ?? test.title ?? 'unknown'),
2127
+ file: (suite.name ?? ''),
2128
+ error: (msgs?.[0] ?? '').slice(0, 500),
2129
+ });
2130
+ }
2131
+ }
2132
+ }
2133
+ const passed = failedCount === 0 && passedCount > 0;
2134
+ const summary = `${passedCount} passed, ${failedCount} failed (Vitest JSON)`;
2135
+ const output = failures.length > 0
2136
+ ? failures.map(f => `FAIL: ${f.testName}\n File: ${f.file}\n Error: ${f.error}`).join('\n\n')
2137
+ : `All ${passedCount} tests passed.`;
2138
+ return { passed, failureCount: failedCount, failures, summary, output };
2139
+ }
2140
+ /** Parse pytest-json-report output */
2141
+ parsePytestJson(json) {
2142
+ const failures = [];
2143
+ let passedCount = 0;
2144
+ let failedCount = 0;
2145
+ const tests = json.tests;
2146
+ if (!tests) {
2147
+ return { passed: false, failureCount: null, failures: [], summary: 'Empty pytest results', output: null };
2148
+ }
2149
+ for (const test of tests) {
2150
+ const outcome = test.outcome;
2151
+ if (outcome === 'passed') {
2152
+ passedCount++;
2153
+ }
2154
+ else if (outcome === 'failed') {
2155
+ failedCount++;
2156
+ const call = test.call;
2157
+ const longrepr = call?.longrepr ?? '';
2158
+ failures.push({
2159
+ testName: (test.nodeid ?? 'unknown'),
2160
+ file: (test.nodeid ?? '').split('::')[0] ?? '',
2161
+ error: longrepr.slice(0, 500),
2162
+ });
2163
+ }
2164
+ }
2165
+ const passed = failedCount === 0 && passedCount > 0;
2166
+ const summary = `${passedCount} passed, ${failedCount} failed (pytest JSON)`;
2167
+ const output = failures.length > 0
2168
+ ? failures.map(f => `FAIL: ${f.testName}\n File: ${f.file}\n Error: ${f.error}`).join('\n\n')
2169
+ : `All ${passedCount} tests passed.`;
2170
+ return { passed, failureCount: failedCount, failures, summary, output };
2171
+ }
2172
+ /** Parse go test -json NDJSON string */
2173
+ parseGoTestJson(raw) {
2174
+ const lines = [];
2175
+ for (const line of raw.split('\n')) {
2176
+ if (!line.trim())
2177
+ continue;
2178
+ try {
2179
+ lines.push(JSON.parse(line));
2180
+ }
2181
+ catch { /* skip non-JSON lines */ }
2182
+ }
2183
+ return this.parseGoTestJsonLines(lines);
2184
+ }
2185
+ /** Parse go test -json NDJSON lines */
2186
+ parseGoTestJsonLines(lines) {
2187
+ const failures = [];
2188
+ let passedCount = 0;
2189
+ let failedCount = 0;
2190
+ const failOutputs = new Map();
2191
+ for (const line of lines) {
2192
+ const action = line.Action;
2193
+ const testName = line.Test;
2194
+ const pkg = line.Package ?? '';
2195
+ if (!testName)
2196
+ continue; // Package-level events
2197
+ if (action === 'pass') {
2198
+ passedCount++;
2199
+ }
2200
+ else if (action === 'fail') {
2201
+ failedCount++;
2202
+ failures.push({
2203
+ testName: `${pkg}/${testName}`,
2204
+ file: pkg,
2205
+ error: (failOutputs.get(`${pkg}/${testName}`) ?? '').slice(0, 500),
2206
+ });
2207
+ }
2208
+ else if (action === 'output') {
2209
+ const key = `${pkg}/${testName}`;
2210
+ const prev = failOutputs.get(key) ?? '';
2211
+ failOutputs.set(key, prev + (line.Output ?? ''));
2212
+ }
2213
+ }
2214
+ const passed = failedCount === 0 && passedCount > 0;
2215
+ const summary = `${passedCount} passed, ${failedCount} failed (go test JSON)`;
2216
+ const output = failures.length > 0
2217
+ ? failures.map(f => `FAIL: ${f.testName}\n File: ${f.file}\n Error: ${f.error}`).join('\n\n')
2218
+ : `All ${passedCount} tests passed.`;
2219
+ return { passed, failureCount: failedCount, failures, summary, output };
2220
+ }
2221
+ /** Regex-based fallback for parsing test output */
2222
+ parseTestOutputRegex(output) {
2223
+ if (!output) {
2224
+ return { passed: false, failureCount: null, failures: [], summary: 'Empty test output.', output: null };
2225
+ }
2226
+ const passedMatch = output.match(/(\d+)\s+passed/);
2227
+ const failedMatch = output.match(/(\d+)\s+failed/);
2228
+ const passedCount = passedMatch ? parseInt(passedMatch[1], 10) : 0;
2229
+ const failedCount = failedMatch ? parseInt(failedMatch[1], 10) : 0;
2230
+ const hasGenericFail = /(?:FAIL|Error:|✗|AssertionError|expect\(.*\)\.to)/i.test(output);
2231
+ const passed = failedCount === 0 && !hasGenericFail && passedCount > 0;
2232
+ const summary = passedCount > 0 || failedCount > 0
2233
+ ? `${passedCount} passed, ${failedCount} failed`
2234
+ : hasGenericFail
2235
+ ? 'Test failures detected (non-Playwright output)'
2236
+ : 'Could not parse test results';
2237
+ const truncated = output.length > 5000 ? output.slice(-5000) : output;
2238
+ return { passed, failureCount: failedCount || (hasGenericFail ? -1 : 0), failures: [], summary, output: truncated };
2239
+ }
2240
+ chunk(arr, size) {
2241
+ const chunks = [];
2242
+ for (let i = 0; i < arr.length; i += size) {
2243
+ chunks.push(arr.slice(i, i + size));
2244
+ }
2245
+ return chunks;
2246
+ }
2247
+ /**
2248
+ * Estimate cost for a pipeline run based on model and number of stages.
2249
+ * Returns a { low, high } range in USD.
2250
+ */
2251
+ static estimateCost(stageCount, model) {
2252
+ const perStage = {
2253
+ opus: { low: 2, high: 4 },
2254
+ sonnet: { low: 0.5, high: 1.5 },
2255
+ haiku: { low: 0.1, high: 0.3 },
2256
+ };
2257
+ const rates = perStage[model] ?? { low: 1, high: 3 };
2258
+ return {
2259
+ low: Math.round(rates.low * stageCount * 100) / 100,
2260
+ high: Math.round(rates.high * stageCount * 100) / 100,
2261
+ };
2262
+ }
2263
+ }
2264
+ //# sourceMappingURL=pipeline.js.map