@ebrindley/build 0.0.0-stage → 1.42.1-rc28

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 (878) hide show
  1. package/.build-project/providers/catalog.json +13951 -0
  2. package/.build-project/providers/pricing.json +1978 -0
  3. package/.build-project/providers/registry.json +4103 -0
  4. package/.github/SECURITY.md +75 -0
  5. package/.nvmrc +1 -0
  6. package/CHANGELOG.md +661 -0
  7. package/CODE_OF_CONDUCT.md +44 -0
  8. package/CONTRIBUTING.md +24 -0
  9. package/INSTALL.md +623 -0
  10. package/LICENSE +21 -0
  11. package/README.md +118 -2
  12. package/THIRD_PARTY_NOTICES.txt +26 -0
  13. package/dist/ai-judge-UE5UOYOO.js +191 -0
  14. package/dist/allowlist-grounding-KPLN6NC5.js +16 -0
  15. package/dist/allowlist-utils-MZZKKXRD.js +31 -0
  16. package/dist/apply-completion-mode-EM37UZHB.js +110 -0
  17. package/dist/artifact-migrator-WPACNSEP.js +276 -0
  18. package/dist/ask-2OZ4JFQR.js +176 -0
  19. package/dist/auth-6UKBTJUQ.js +185 -0
  20. package/dist/auth-H5KJL6HL.js +165 -0
  21. package/dist/auth-LVJRJDMU.js +12 -0
  22. package/dist/auth-NHZOCNRR.js +94 -0
  23. package/dist/auth-PBXHWXGW.js +95 -0
  24. package/dist/auth-T7Y5DCAH.js +91 -0
  25. package/dist/auth-TVVKCHQW.js +13 -0
  26. package/dist/auth-UHB2UCSS.js +97 -0
  27. package/dist/auth-VACA3VUL.js +98 -0
  28. package/dist/auth-VVLC77S3.js +44 -0
  29. package/dist/auth-liveness-XO4AGDDD.js +53 -0
  30. package/dist/autoloop-cleanup-SI3VGAAR.js +106 -0
  31. package/dist/autoloop-evidence-gates-J5GG5YQB.js +39 -0
  32. package/dist/autoloop-gate-policy-SUNAEP6Q.js +169 -0
  33. package/dist/autoloop-helpers-VQGQNTT6.js +52 -0
  34. package/dist/autoloop-ledger-P6TIDP7C.js +96 -0
  35. package/dist/autoloop-ledger-subscriber-HFAGOZXH.js +161 -0
  36. package/dist/autoloop-run-defaults-GWGZBNT7.js +27 -0
  37. package/dist/autoloop-run-lock-GBWRM5A2.js +197 -0
  38. package/dist/autoloop-success-C32QVWOP.js +93 -0
  39. package/dist/autoloop-termination-summary-GI6IBQWD.js +58 -0
  40. package/dist/autoloop-trajectory-AMKSQISD.js +70 -0
  41. package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
  42. package/dist/automatic-branch-cleanup-R62X2C5O.js +49 -0
  43. package/dist/autonomous-loop-controller-2WL363LW.js +4386 -0
  44. package/dist/backend-YUTFDZXZ.js +30 -0
  45. package/dist/background-executor-Z3ODTMD7.js +260 -0
  46. package/dist/backlog-75QPEAMW.js +403 -0
  47. package/dist/backlog-execution-intent-GWUV5TUK.js +19 -0
  48. package/dist/backup-active-store-XHWLN34M.js +16 -0
  49. package/dist/backup-machine-stores-UI4LTJOY.js +251 -0
  50. package/dist/backup-telemetry-db-Z4ICYCNW.js +26 -0
  51. package/dist/branch-archive-manager-FMSQ7YJF.js +18 -0
  52. package/dist/branch-cleanup-manager-ST6FJGND.js +35 -0
  53. package/dist/build-gate-7NKWPY62.js +68 -0
  54. package/dist/build-root-S7DINODY.js +18 -0
  55. package/dist/change-summary-R6CB6VXJ.js +50 -0
  56. package/dist/chunk-24MNKKOM.js +173 -0
  57. package/dist/chunk-2H6VZFIO.js +235 -0
  58. package/dist/chunk-2JIX3EKV.js +39 -0
  59. package/dist/chunk-2KO7HVZM.js +106 -0
  60. package/dist/chunk-2L36U25O.js +100 -0
  61. package/dist/chunk-2NMNFUQA.js +475 -0
  62. package/dist/chunk-2OUAEH67.js +83 -0
  63. package/dist/chunk-2VLJUXJU.js +115 -0
  64. package/dist/chunk-2VUEDIUM.js +77 -0
  65. package/dist/chunk-2Y2IIHPI.js +430 -0
  66. package/dist/chunk-2YRG3LHZ.js +220 -0
  67. package/dist/chunk-2Z5Q3SUY.js +65 -0
  68. package/dist/chunk-32IYXIE5.js +873 -0
  69. package/dist/chunk-33IGPPUI.js +292 -0
  70. package/dist/chunk-352PCJLK.js +283 -0
  71. package/dist/chunk-35J4NE6S.js +226 -0
  72. package/dist/chunk-3C7WBIVK.js +1016 -0
  73. package/dist/chunk-3H3EY6U7.js +1225 -0
  74. package/dist/chunk-3H4MWYIC.js +198 -0
  75. package/dist/chunk-3HHWQR3N.js +2479 -0
  76. package/dist/chunk-3JKNRYEE.js +14 -0
  77. package/dist/chunk-3LECCYZY.js +128 -0
  78. package/dist/chunk-3LOY6YZI.js +852 -0
  79. package/dist/chunk-3M57DADF.js +44 -0
  80. package/dist/chunk-3NOTLVLG.js +3803 -0
  81. package/dist/chunk-3OPP2OZB.js +1480 -0
  82. package/dist/chunk-3P5IN5E7.js +9 -0
  83. package/dist/chunk-3PL22IYZ.js +20 -0
  84. package/dist/chunk-3REDAJUJ.js +14 -0
  85. package/dist/chunk-3S2R4XJI.js +203 -0
  86. package/dist/chunk-3UBZI5DW.js +61 -0
  87. package/dist/chunk-3VV5OBGG.js +308 -0
  88. package/dist/chunk-3WY5TU2O.js +823 -0
  89. package/dist/chunk-43OKHTW4.js +82 -0
  90. package/dist/chunk-45KUJBDI.js +112 -0
  91. package/dist/chunk-47X5LM7B.js +224 -0
  92. package/dist/chunk-4B6FQJ77.js +268 -0
  93. package/dist/chunk-4BTDUT2S.js +42 -0
  94. package/dist/chunk-4DJL2VXA.js +24 -0
  95. package/dist/chunk-4EBSXZ73.js +48 -0
  96. package/dist/chunk-4QKP6I4C.js +294 -0
  97. package/dist/chunk-4U6XK2DG.js +300 -0
  98. package/dist/chunk-4UMRUFBD.js +132 -0
  99. package/dist/chunk-4US4SYJ4.js +569 -0
  100. package/dist/chunk-4ZPLWEZO.js +154 -0
  101. package/dist/chunk-56WPYDJH.js +624 -0
  102. package/dist/chunk-57WRCJJW.js +50 -0
  103. package/dist/chunk-5AW4RMNG.js +24 -0
  104. package/dist/chunk-5EQN56OZ.js +155 -0
  105. package/dist/chunk-5F43TUIN.js +38 -0
  106. package/dist/chunk-5FKHIKTS.js +115 -0
  107. package/dist/chunk-5HDP7XZG.js +91 -0
  108. package/dist/chunk-5KQETTNS.js +152 -0
  109. package/dist/chunk-5PDVN2KO.js +498 -0
  110. package/dist/chunk-5UBIFVRD.js +509 -0
  111. package/dist/chunk-5YDE3Q62.js +46 -0
  112. package/dist/chunk-626AJVMU.js +302 -0
  113. package/dist/chunk-633XZ7UF.js +3583 -0
  114. package/dist/chunk-63DHNHGA.js +236 -0
  115. package/dist/chunk-666656AR.js +568 -0
  116. package/dist/chunk-67YRJPJO.js +19 -0
  117. package/dist/chunk-6CAU7VBA.js +20 -0
  118. package/dist/chunk-6CB3KJRY.js +21 -0
  119. package/dist/chunk-6DBMALXL.js +29 -0
  120. package/dist/chunk-6E25C4IA.js +269 -0
  121. package/dist/chunk-6FIBNIQU.js +73 -0
  122. package/dist/chunk-6G57FYBV.js +189 -0
  123. package/dist/chunk-6KZKRTG3.js +4885 -0
  124. package/dist/chunk-6LAH2UV2.js +525 -0
  125. package/dist/chunk-6PZY2TTU.js +921 -0
  126. package/dist/chunk-6T6NHXK4.js +230 -0
  127. package/dist/chunk-6TBEX4R4.js +432 -0
  128. package/dist/chunk-6TRD7TEE.js +911 -0
  129. package/dist/chunk-6XCP2RAI.js +47 -0
  130. package/dist/chunk-726G6WDL.js +351 -0
  131. package/dist/chunk-75EMT7BF.js +616 -0
  132. package/dist/chunk-762ACDZJ.js +4696 -0
  133. package/dist/chunk-77I6G4CO.js +1 -0
  134. package/dist/chunk-7DJ6SQH3.js +510 -0
  135. package/dist/chunk-7HPCIOMA.js +1383 -0
  136. package/dist/chunk-7HYBSRPZ.js +17503 -0
  137. package/dist/chunk-7KBEOPVB.js +73 -0
  138. package/dist/chunk-7LHJDJB3.js +2222 -0
  139. package/dist/chunk-7NVJMH52.js +306 -0
  140. package/dist/chunk-7RMARDVH.js +33 -0
  141. package/dist/chunk-7SEWU4QG.js +36 -0
  142. package/dist/chunk-7TUTZTI2.js +911 -0
  143. package/dist/chunk-7VBDFPUM.js +340 -0
  144. package/dist/chunk-7VRJHTGZ.js +6406 -0
  145. package/dist/chunk-7YAOUUKK.js +493 -0
  146. package/dist/chunk-AAAODV76.js +860 -0
  147. package/dist/chunk-AB4HUJVQ.js +339 -0
  148. package/dist/chunk-AEQWV746.js +611 -0
  149. package/dist/chunk-AHU3FKPA.js +11639 -0
  150. package/dist/chunk-AISTV5XU.js +226 -0
  151. package/dist/chunk-AJBTKGJZ.js +48 -0
  152. package/dist/chunk-AJJNW7XT.js +248 -0
  153. package/dist/chunk-AJN5Y5YZ.js +160 -0
  154. package/dist/chunk-ALDTFNZ3.js +150 -0
  155. package/dist/chunk-ARARVIZK.js +281 -0
  156. package/dist/chunk-ATBM6AW5.js +53 -0
  157. package/dist/chunk-ATKEKM3E.js +206 -0
  158. package/dist/chunk-AUJLW6JZ.js +92 -0
  159. package/dist/chunk-AY3MHEFM.js +24 -0
  160. package/dist/chunk-B7PYTV72.js +236 -0
  161. package/dist/chunk-B7YRSULE.js +1423 -0
  162. package/dist/chunk-BAWJNMWF.js +339 -0
  163. package/dist/chunk-BBPIILO2.js +88 -0
  164. package/dist/chunk-BD3PGW6A.js +957 -0
  165. package/dist/chunk-BDPYFAB4.js +44 -0
  166. package/dist/chunk-BIUV23HH.js +729 -0
  167. package/dist/chunk-BQ3VAGXH.js +243 -0
  168. package/dist/chunk-BQJOGAWT.js +31 -0
  169. package/dist/chunk-BT7EG7CO.js +68 -0
  170. package/dist/chunk-BV3XQQLU.js +27 -0
  171. package/dist/chunk-BY6ODHNZ.js +78 -0
  172. package/dist/chunk-BZRLSZNV.js +79 -0
  173. package/dist/chunk-C3DH3NCY.js +2773 -0
  174. package/dist/chunk-C3KCMAJP.js +756 -0
  175. package/dist/chunk-C7AGUJ5K.js +267 -0
  176. package/dist/chunk-CBSWKQCX.js +342 -0
  177. package/dist/chunk-CE5OZBYY.js +21 -0
  178. package/dist/chunk-CFCVYHSW.js +373 -0
  179. package/dist/chunk-CHA2HDE5.js +126 -0
  180. package/dist/chunk-CIHWPGS5.js +146 -0
  181. package/dist/chunk-CJBSZX3F.js +127 -0
  182. package/dist/chunk-CLA6QIJM.js +43 -0
  183. package/dist/chunk-CPSVQR2I.js +365 -0
  184. package/dist/chunk-CRPRVNQ4.js +254 -0
  185. package/dist/chunk-CSIE4Y7C.js +1093 -0
  186. package/dist/chunk-CSOPSYDY.js +142 -0
  187. package/dist/chunk-CSXLLEGB.js +205 -0
  188. package/dist/chunk-CTDLERWU.js +293 -0
  189. package/dist/chunk-CULBXMSK.js +110 -0
  190. package/dist/chunk-D2M2Y5ID.js +21 -0
  191. package/dist/chunk-D37WY7SH.js +1060 -0
  192. package/dist/chunk-D4VJFCGZ.js +89 -0
  193. package/dist/chunk-D6WTZMZB.js +48 -0
  194. package/dist/chunk-DBHOWDUH.js +976 -0
  195. package/dist/chunk-DCACX7BV.js +19364 -0
  196. package/dist/chunk-DFHX4XYA.js +128 -0
  197. package/dist/chunk-DIKBRRKE.js +176 -0
  198. package/dist/chunk-DJG5RIOR.js +9 -0
  199. package/dist/chunk-DNIOIISW.js +616 -0
  200. package/dist/chunk-DOA5G7IA.js +315 -0
  201. package/dist/chunk-DP7FKP6W.js +855 -0
  202. package/dist/chunk-DS2JZ677.js +79 -0
  203. package/dist/chunk-DSRLVYBS.js +82 -0
  204. package/dist/chunk-EAEVKNJA.js +209 -0
  205. package/dist/chunk-EEUESN3T.js +575 -0
  206. package/dist/chunk-EJ4LUU5Y.js +73 -0
  207. package/dist/chunk-EJIWTPSZ.js +1063 -0
  208. package/dist/chunk-EJVCDTAN.js +116 -0
  209. package/dist/chunk-EKLUBDBQ.js +627 -0
  210. package/dist/chunk-EN62WLFD.js +849 -0
  211. package/dist/chunk-ENYRCL43.js +26 -0
  212. package/dist/chunk-EP22ZMFO.js +273 -0
  213. package/dist/chunk-EP4SVAVI.js +87 -0
  214. package/dist/chunk-EPAMVGI3.js +1562 -0
  215. package/dist/chunk-EPRMH6HO.js +24 -0
  216. package/dist/chunk-ES6NWJ36.js +4057 -0
  217. package/dist/chunk-ETA2K4KI.js +30 -0
  218. package/dist/chunk-ETAD4NKD.js +31 -0
  219. package/dist/chunk-EUWIIDQ7.js +93 -0
  220. package/dist/chunk-EVE3OD2W.js +141 -0
  221. package/dist/chunk-EXA26FFC.js +459 -0
  222. package/dist/chunk-EXSPAA35.js +244 -0
  223. package/dist/chunk-F55YABKV.js +258 -0
  224. package/dist/chunk-F5DNGU5W.js +27 -0
  225. package/dist/chunk-F622MJYV.js +103 -0
  226. package/dist/chunk-F6NIQD74.js +5111 -0
  227. package/dist/chunk-FAWJTC34.js +43 -0
  228. package/dist/chunk-FAZBRHGG.js +47 -0
  229. package/dist/chunk-FIEUSGNJ.js +39 -0
  230. package/dist/chunk-FKCG7K6H.js +1146 -0
  231. package/dist/chunk-FOVVXV45.js +229 -0
  232. package/dist/chunk-FRBPKBFE.js +30 -0
  233. package/dist/chunk-FSA5BPFI.js +268 -0
  234. package/dist/chunk-FSJ5TTZ3.js +29 -0
  235. package/dist/chunk-FT7SGLXT.js +193 -0
  236. package/dist/chunk-FUUVZZUB.js +62 -0
  237. package/dist/chunk-FYMRVUNZ.js +1697 -0
  238. package/dist/chunk-FYTAA5FG.js +162 -0
  239. package/dist/chunk-G3VB2UCZ.js +3892 -0
  240. package/dist/chunk-G6OIMVXH.js +405 -0
  241. package/dist/chunk-G7PQCXVH.js +228 -0
  242. package/dist/chunk-GAB6UW52.js +1087 -0
  243. package/dist/chunk-GAFJEYLK.js +29 -0
  244. package/dist/chunk-GC4CRMTS.js +642 -0
  245. package/dist/chunk-GFHSIWHL.js +5297 -0
  246. package/dist/chunk-GI5M67YA.js +735 -0
  247. package/dist/chunk-GIJWHUWF.js +191 -0
  248. package/dist/chunk-GO6DRECI.js +1 -0
  249. package/dist/chunk-GOFVBTMN.js +314 -0
  250. package/dist/chunk-GQIP72ZJ.js +13 -0
  251. package/dist/chunk-GQXLXRFA.js +28 -0
  252. package/dist/chunk-GT6HFKJB.js +48 -0
  253. package/dist/chunk-GVJGSSKM.js +174 -0
  254. package/dist/chunk-GWHGTG37.js +36 -0
  255. package/dist/chunk-GZAJHLKO.js +18 -0
  256. package/dist/chunk-GZK62I3N.js +314 -0
  257. package/dist/chunk-H3HGMISG.js +483 -0
  258. package/dist/chunk-H3ISZJMJ.js +403 -0
  259. package/dist/chunk-H4HXVWEC.js +9 -0
  260. package/dist/chunk-H6MJGKAW.js +120 -0
  261. package/dist/chunk-HBTTGVR5.js +353 -0
  262. package/dist/chunk-HBZ322WZ.js +35 -0
  263. package/dist/chunk-HEP3ILP4.js +34 -0
  264. package/dist/chunk-HGFXODKO.js +106 -0
  265. package/dist/chunk-HHWDTB6S.js +59 -0
  266. package/dist/chunk-HKO45NLZ.js +286 -0
  267. package/dist/chunk-HLVTEA6A.js +89 -0
  268. package/dist/chunk-HS3SY6HN.js +160 -0
  269. package/dist/chunk-HT5OCGKN.js +899 -0
  270. package/dist/chunk-HXIIEMGF.js +78 -0
  271. package/dist/chunk-I56TH7VM.js +46 -0
  272. package/dist/chunk-I6FN3R5D.js +352 -0
  273. package/dist/chunk-I7SCBHJN.js +655 -0
  274. package/dist/chunk-IJM4GKAT.js +529 -0
  275. package/dist/chunk-IMEYU36R.js +344 -0
  276. package/dist/chunk-INJ3SS6A.js +936 -0
  277. package/dist/chunk-IUKOPCCE.js +441 -0
  278. package/dist/chunk-IVEPZQJO.js +33 -0
  279. package/dist/chunk-IVZEONFC.js +43 -0
  280. package/dist/chunk-IWBHRSG7.js +936 -0
  281. package/dist/chunk-IYY3HQAD.js +484 -0
  282. package/dist/chunk-J2ICMYDA.js +48 -0
  283. package/dist/chunk-J2QCFLUN.js +505 -0
  284. package/dist/chunk-J5HM6V5P.js +1039 -0
  285. package/dist/chunk-J6NZX6QH.js +398 -0
  286. package/dist/chunk-JBBB2Y47.js +432 -0
  287. package/dist/chunk-JDCZQZU7.js +42 -0
  288. package/dist/chunk-JEDNXGSS.js +169 -0
  289. package/dist/chunk-JHP7OT6K.js +89 -0
  290. package/dist/chunk-JLS65NUQ.js +14 -0
  291. package/dist/chunk-JNMWYMC6.js +38 -0
  292. package/dist/chunk-JNSTWNNW.js +25 -0
  293. package/dist/chunk-JOSK6GEU.js +9564 -0
  294. package/dist/chunk-JQUMSIGC.js +100 -0
  295. package/dist/chunk-JVHGMURQ.js +1106 -0
  296. package/dist/chunk-JYXS47FQ.js +951 -0
  297. package/dist/chunk-K2ZMW2Z5.js +2147 -0
  298. package/dist/chunk-K7FAFORM.js +14 -0
  299. package/dist/chunk-KBJXX3ON.js +333 -0
  300. package/dist/chunk-KHFKVQWY.js +486 -0
  301. package/dist/chunk-KHMEI3DQ.js +173 -0
  302. package/dist/chunk-KHW3UD7B.js +69 -0
  303. package/dist/chunk-KKQKWAMF.js +365 -0
  304. package/dist/chunk-KOKXJV2U.js +455 -0
  305. package/dist/chunk-KQ4NGOJI.js +14 -0
  306. package/dist/chunk-KQYQOXMS.js +342 -0
  307. package/dist/chunk-KTMW65UX.js +14 -0
  308. package/dist/chunk-KUEWV2JH.js +593 -0
  309. package/dist/chunk-KXYDV3HP.js +1523 -0
  310. package/dist/chunk-KZ4JMGZ5.js +96 -0
  311. package/dist/chunk-KZWGBUIO.js +2659 -0
  312. package/dist/chunk-L2CBG5IX.js +1892 -0
  313. package/dist/chunk-L2S235CU.js +314 -0
  314. package/dist/chunk-L32WD57F.js +98 -0
  315. package/dist/chunk-LDJCKAJP.js +123 -0
  316. package/dist/chunk-LDWNBEN7.js +541 -0
  317. package/dist/chunk-LJVH57G2.js +241 -0
  318. package/dist/chunk-LPQ7NZEN.js +209 -0
  319. package/dist/chunk-LRGH3CCV.js +148 -0
  320. package/dist/chunk-LULOGMMM.js +1133 -0
  321. package/dist/chunk-LWGKN2GY.js +8284 -0
  322. package/dist/chunk-LX4F24ZH.js +157 -0
  323. package/dist/chunk-M4BUEXXF.js +839 -0
  324. package/dist/chunk-M6UHOC4I.js +553 -0
  325. package/dist/chunk-MCBNWZNO.js +657 -0
  326. package/dist/chunk-MEMSY6VY.js +223 -0
  327. package/dist/chunk-MHLEZQQI.js +145 -0
  328. package/dist/chunk-MJ6WNSIX.js +192 -0
  329. package/dist/chunk-MKASIKWZ.js +442 -0
  330. package/dist/chunk-MNOVBYFU.js +60 -0
  331. package/dist/chunk-MO2BYAAF.js +2896 -0
  332. package/dist/chunk-MP6P7Y2A.js +180 -0
  333. package/dist/chunk-MQ4TFJAG.js +3365 -0
  334. package/dist/chunk-MT2WAIR3.js +131 -0
  335. package/dist/chunk-MTU7VTNN.js +1122 -0
  336. package/dist/chunk-MULMXX4V.js +150 -0
  337. package/dist/chunk-MW2DWWVA.js +147 -0
  338. package/dist/chunk-MY3ZPUN4.js +151 -0
  339. package/dist/chunk-MYLH2S4G.js +64 -0
  340. package/dist/chunk-MZ5WYKNA.js +28 -0
  341. package/dist/chunk-N3Q2JNPV.js +249 -0
  342. package/dist/chunk-N5DX4JAK.js +12 -0
  343. package/dist/chunk-NAINGKHF.js +35 -0
  344. package/dist/chunk-NEQUQHOX.js +223 -0
  345. package/dist/chunk-NHNPEZS4.js +1408 -0
  346. package/dist/chunk-NHTIXJCR.js +17 -0
  347. package/dist/chunk-NKLPMBNV.js +1154 -0
  348. package/dist/chunk-NMVZ5YES.js +199 -0
  349. package/dist/chunk-NRFRY3GE.js +256 -0
  350. package/dist/chunk-NTD6CSI6.js +1266 -0
  351. package/dist/chunk-NUO7B3SH.js +318 -0
  352. package/dist/chunk-NVOHIJKG.js +1319 -0
  353. package/dist/chunk-NXH3RX7O.js +523 -0
  354. package/dist/chunk-NXZ3DURF.js +257 -0
  355. package/dist/chunk-O26L6F7F.js +209 -0
  356. package/dist/chunk-O5RRIUEP.js +124 -0
  357. package/dist/chunk-O5X6BKAO.js +10 -0
  358. package/dist/chunk-O6ZQUVTR.js +31 -0
  359. package/dist/chunk-O7FE75QZ.js +306 -0
  360. package/dist/chunk-OC7Z6AJQ.js +81 -0
  361. package/dist/chunk-OH7IEZ63.js +307 -0
  362. package/dist/chunk-OI734GRS.js +115 -0
  363. package/dist/chunk-OJBH62DK.js +684 -0
  364. package/dist/chunk-OKJLVSTJ.js +247 -0
  365. package/dist/chunk-OMX5VL43.js +79 -0
  366. package/dist/chunk-ONHAMBN6.js +12 -0
  367. package/dist/chunk-ONYPLIVE.js +753 -0
  368. package/dist/chunk-OSK7QWPD.js +637 -0
  369. package/dist/chunk-OTXLMJKP.js +438 -0
  370. package/dist/chunk-OV5PKQMA.js +213 -0
  371. package/dist/chunk-OXK5HA4B.js +31 -0
  372. package/dist/chunk-OYDWFQA4.js +272 -0
  373. package/dist/chunk-OZAUKWHB.js +22 -0
  374. package/dist/chunk-P5P2EKGH.js +94 -0
  375. package/dist/chunk-P6SSWAKY.js +29 -0
  376. package/dist/chunk-P7CQGPLS.js +30 -0
  377. package/dist/chunk-P7RVCHQV.js +197 -0
  378. package/dist/chunk-PEBWDYIW.js +726 -0
  379. package/dist/chunk-PFCXFO7A.js +9193 -0
  380. package/dist/chunk-PFLDWMX6.js +168 -0
  381. package/dist/chunk-PHZGLQSK.js +15883 -0
  382. package/dist/chunk-PNFTPNSR.js +20 -0
  383. package/dist/chunk-PPHXX4W7.js +175 -0
  384. package/dist/chunk-PPZESPJT.js +262 -0
  385. package/dist/chunk-PRDHI2L5.js +296 -0
  386. package/dist/chunk-PSTKWHHN.js +32 -0
  387. package/dist/chunk-PV53ODOT.js +555 -0
  388. package/dist/chunk-PVJBTI7P.js +24 -0
  389. package/dist/chunk-PXKERBKX.js +100 -0
  390. package/dist/chunk-PZ5AY32C.js +10 -0
  391. package/dist/chunk-Q3DHEZA5.js +85 -0
  392. package/dist/chunk-Q5RZDVQK.js +204 -0
  393. package/dist/chunk-Q64IR2Z2.js +4933 -0
  394. package/dist/chunk-Q6Z5UGPL.js +398 -0
  395. package/dist/chunk-QAMAV4DT.js +627 -0
  396. package/dist/chunk-QBWF4444.js +112 -0
  397. package/dist/chunk-QDKYT4CI.js +22805 -0
  398. package/dist/chunk-QFLBRWCJ.js +126 -0
  399. package/dist/chunk-QILAQACE.js +107 -0
  400. package/dist/chunk-QJ6OEH4K.js +3683 -0
  401. package/dist/chunk-QK3NLYTV.js +320 -0
  402. package/dist/chunk-QOFLUS24.js +535 -0
  403. package/dist/chunk-QOIASNEJ.js +98 -0
  404. package/dist/chunk-QP4UTSOV.js +3587 -0
  405. package/dist/chunk-QP5ST7RI.js +904 -0
  406. package/dist/chunk-QS5AGRZN.js +167 -0
  407. package/dist/chunk-QTAOU53N.js +107 -0
  408. package/dist/chunk-QTCLJHLZ.js +351 -0
  409. package/dist/chunk-QTOLTG4L.js +119 -0
  410. package/dist/chunk-QWDV4OSL.js +142 -0
  411. package/dist/chunk-QWMQ74KD.js +508 -0
  412. package/dist/chunk-QX6LOMMH.js +158 -0
  413. package/dist/chunk-QXBKIHY7.js +238 -0
  414. package/dist/chunk-QXBKTJT6.js +270 -0
  415. package/dist/chunk-QYQOVOLR.js +196 -0
  416. package/dist/chunk-R2CKS6WT.js +73 -0
  417. package/dist/chunk-R2TIYG7U.js +68 -0
  418. package/dist/chunk-R2W3LESH.js +465 -0
  419. package/dist/chunk-R5EXHMUN.js +100 -0
  420. package/dist/chunk-RBBTBCHX.js +289 -0
  421. package/dist/chunk-RBKLOUXA.js +121 -0
  422. package/dist/chunk-RCP2Y6DQ.js +86 -0
  423. package/dist/chunk-RCYEJSD5.js +260 -0
  424. package/dist/chunk-RFDVSI4N.js +134 -0
  425. package/dist/chunk-RGJU63FV.js +1717 -0
  426. package/dist/chunk-RI4EX2QE.js +18 -0
  427. package/dist/chunk-RIMARPCF.js +152 -0
  428. package/dist/chunk-RJ6RSNT6.js +42 -0
  429. package/dist/chunk-RNI2GUIF.js +131 -0
  430. package/dist/chunk-RPHUWJ3K.js +264 -0
  431. package/dist/chunk-RPIMAMV7.js +361 -0
  432. package/dist/chunk-RQFX6YRN.js +49 -0
  433. package/dist/chunk-RRC5BL7B.js +285 -0
  434. package/dist/chunk-RSYTSBOO.js +79 -0
  435. package/dist/chunk-RWQQIKIC.js +35 -0
  436. package/dist/chunk-RZUBOZH2.js +373 -0
  437. package/dist/chunk-S2E632FQ.js +380 -0
  438. package/dist/chunk-S2VQCZO4.js +22 -0
  439. package/dist/chunk-S2XIMQJQ.js +681 -0
  440. package/dist/chunk-S47ISY27.js +183 -0
  441. package/dist/chunk-S6I4KISU.js +142 -0
  442. package/dist/chunk-S6TAFQI5.js +2403 -0
  443. package/dist/chunk-SBBNT5ZJ.js +27 -0
  444. package/dist/chunk-SDJV2DTQ.js +491 -0
  445. package/dist/chunk-SDPIFNLD.js +33 -0
  446. package/dist/chunk-SF6YK3JI.js +835 -0
  447. package/dist/chunk-SK3ZFONO.js +976 -0
  448. package/dist/chunk-SKTFRFQ7.js +1775 -0
  449. package/dist/chunk-SNGM452K.js +2002 -0
  450. package/dist/chunk-STW7OFOA.js +581 -0
  451. package/dist/chunk-SU6UFUQY.js +1351 -0
  452. package/dist/chunk-SVJM3LJA.js +27 -0
  453. package/dist/chunk-SVUNZOPG.js +129 -0
  454. package/dist/chunk-T5XLUYV3.js +34 -0
  455. package/dist/chunk-T7E5JS6N.js +57 -0
  456. package/dist/chunk-TA3N5MMO.js +450 -0
  457. package/dist/chunk-TAJPCXSB.js +13 -0
  458. package/dist/chunk-TDEMDRN2.js +1639 -0
  459. package/dist/chunk-TDSEX5CF.js +24 -0
  460. package/dist/chunk-TGCVL74Y.js +28 -0
  461. package/dist/chunk-TGXXYDL4.js +1012 -0
  462. package/dist/chunk-TIKTYUJO.js +296 -0
  463. package/dist/chunk-TIZAJD4M.js +6029 -0
  464. package/dist/chunk-TLSEZZCG.js +5549 -0
  465. package/dist/chunk-TMBDRHAX.js +19 -0
  466. package/dist/chunk-TRURQUBP.js +1217 -0
  467. package/dist/chunk-TSQ256N5.js +152 -0
  468. package/dist/chunk-TUQMMFEQ.js +2244 -0
  469. package/dist/chunk-U424MJLC.js +81 -0
  470. package/dist/chunk-U4DRS6ZW.js +11 -0
  471. package/dist/chunk-U5KS22IN.js +204 -0
  472. package/dist/chunk-UAPHFJPN.js +673 -0
  473. package/dist/chunk-UB7LQITD.js +1040 -0
  474. package/dist/chunk-UBVRHGAZ.js +237 -0
  475. package/dist/chunk-UDNNAQN5.js +98 -0
  476. package/dist/chunk-UJP3XM54.js +342 -0
  477. package/dist/chunk-UJYDH2MW.js +305 -0
  478. package/dist/chunk-UKMPASIC.js +399 -0
  479. package/dist/chunk-UKUL3EHK.js +763 -0
  480. package/dist/chunk-UPHU64NB.js +1327 -0
  481. package/dist/chunk-URMSZXAD.js +43 -0
  482. package/dist/chunk-US3RT6CY.js +525 -0
  483. package/dist/chunk-UTDEOPKV.js +30 -0
  484. package/dist/chunk-UUUDKV7N.js +13 -0
  485. package/dist/chunk-UVKWU3SM.js +39 -0
  486. package/dist/chunk-V2TVPO3X.js +521 -0
  487. package/dist/chunk-V3KLVK7B.js +398 -0
  488. package/dist/chunk-V3YFFHDR.js +17 -0
  489. package/dist/chunk-VAG6GUBR.js +1162 -0
  490. package/dist/chunk-VBENAHDA.js +1814 -0
  491. package/dist/chunk-VBNCKCCI.js +16 -0
  492. package/dist/chunk-VCGSWN5M.js +1300 -0
  493. package/dist/chunk-VE2O2I7M.js +115 -0
  494. package/dist/chunk-VIFHYTI5.js +31 -0
  495. package/dist/chunk-VJER3FUS.js +5080 -0
  496. package/dist/chunk-VLMDM56Q.js +268 -0
  497. package/dist/chunk-VMFZEVVX.js +99 -0
  498. package/dist/chunk-VNDPQVUZ.js +1853 -0
  499. package/dist/chunk-VSPJRLGA.js +4783 -0
  500. package/dist/chunk-VSWC5ZXI.js +103 -0
  501. package/dist/chunk-VYI4DSZP.js +2017 -0
  502. package/dist/chunk-W4BAIQ6L.js +1212 -0
  503. package/dist/chunk-W7VQAL2K.js +2203 -0
  504. package/dist/chunk-WEI3ZDZH.js +33 -0
  505. package/dist/chunk-WEIUS5NT.js +26 -0
  506. package/dist/chunk-WEW7UFWE.js +546 -0
  507. package/dist/chunk-WGQAXLNA.js +384 -0
  508. package/dist/chunk-WJAL4JL3.js +206 -0
  509. package/dist/chunk-WKFOJ5ZG.js +141 -0
  510. package/dist/chunk-WMWQEFLN.js +2177 -0
  511. package/dist/chunk-WNECKKO2.js +154 -0
  512. package/dist/chunk-WPKLYOAM.js +195 -0
  513. package/dist/chunk-WSH37YQN.js +243 -0
  514. package/dist/chunk-WV3KQDPE.js +84 -0
  515. package/dist/chunk-WZFC4V4U.js +389 -0
  516. package/dist/chunk-XCOIBNET.js +13 -0
  517. package/dist/chunk-XDM65LBX.js +53 -0
  518. package/dist/chunk-XGU6PBO5.js +645 -0
  519. package/dist/chunk-XHOI4V62.js +43 -0
  520. package/dist/chunk-XIBVFMLQ.js +6858 -0
  521. package/dist/chunk-XJ2E7YEJ.js +117 -0
  522. package/dist/chunk-XON4ZCYX.js +69 -0
  523. package/dist/chunk-XQPEWJPI.js +127 -0
  524. package/dist/chunk-XSJTIFUY.js +31 -0
  525. package/dist/chunk-XT7OVWM3.js +64 -0
  526. package/dist/chunk-XTXYIISX.js +1104 -0
  527. package/dist/chunk-XV7NZL4B.js +57 -0
  528. package/dist/chunk-XWIZ6B5S.js +109 -0
  529. package/dist/chunk-XWLIKOBX.js +240 -0
  530. package/dist/chunk-XZOXEIN4.js +156 -0
  531. package/dist/chunk-XZRS7I7W.js +3673 -0
  532. package/dist/chunk-Y4ZDDHB6.js +44 -0
  533. package/dist/chunk-Y733RB7T.js +194 -0
  534. package/dist/chunk-YBHUB4XR.js +14931 -0
  535. package/dist/chunk-YKC75XK4.js +147 -0
  536. package/dist/chunk-YKMXFVK7.js +27 -0
  537. package/dist/chunk-YO5BAJ55.js +225 -0
  538. package/dist/chunk-YPDCLXNZ.js +568 -0
  539. package/dist/chunk-YSQPGVFK.js +116 -0
  540. package/dist/chunk-YTVVTIWJ.js +716 -0
  541. package/dist/chunk-YVCMDVBB.js +698 -0
  542. package/dist/chunk-YYTOA6DP.js +114 -0
  543. package/dist/chunk-Z5WGNULV.js +88 -0
  544. package/dist/chunk-Z6NGVVXG.js +66 -0
  545. package/dist/chunk-Z7CDQNH3.js +20 -0
  546. package/dist/chunk-ZH4G3FU5.js +187 -0
  547. package/dist/chunk-ZJB6SQPA.js +257 -0
  548. package/dist/chunk-ZL6E4HMY.js +42 -0
  549. package/dist/chunk-ZMLOC3DW.js +62 -0
  550. package/dist/chunk-ZORW64QH.js +101 -0
  551. package/dist/chunk-ZVVD2XGV.js +1369 -0
  552. package/dist/cli-utils-ER33ODC5.js +31 -0
  553. package/dist/cli-validation-6CHPTANV.js +146 -0
  554. package/dist/commit-utils-3STAY7C7.js +49 -0
  555. package/dist/compatibility-notice-IHNXR2JR.js +21 -0
  556. package/dist/compete-config-resolver-V3GDNJMM.js +125 -0
  557. package/dist/compete-results-XUM3PJHS.js +318 -0
  558. package/dist/competition-outcome-tracker-NWSMVUPG.js +62 -0
  559. package/dist/config-4HHE4OIK.js +238 -0
  560. package/dist/config-D6YAZASH.js +10 -0
  561. package/dist/config-MIPRDYYV.js +46 -0
  562. package/dist/config-audit-GA5DCUAI.js +371 -0
  563. package/dist/config-explain-4LMO556H.js +71 -0
  564. package/dist/config-loader-RGP634PC.js +51 -0
  565. package/dist/config-manager-J2UTO635.js +114 -0
  566. package/dist/config-merger-TVUJBZTS.js +14 -0
  567. package/dist/config-validator-H4ZVIE56.js +122 -0
  568. package/dist/cost-HSIV5U2Y.js +85 -0
  569. package/dist/data-migration-state-OAUL2WPQ.js +35 -0
  570. package/dist/db-migrator-NUOW5NKR.js +34 -0
  571. package/dist/default-paths-UILB62BN.js +49 -0
  572. package/dist/discovery-V6GYUJUC.js +53 -0
  573. package/dist/doctor-BBSMCDWE.js +271 -0
  574. package/dist/doctor-context-blocks-LPY75VZA.js +231 -0
  575. package/dist/doctor-instructions-import-JXHDDOE5.js +70 -0
  576. package/dist/doctor-purpose-6YJOXPBE.js +30 -0
  577. package/dist/domain-analyzer-S35ABUNA.js +48 -0
  578. package/dist/ensure-initialized-Q6PEU55O.js +78 -0
  579. package/dist/entry.js +922 -0
  580. package/dist/error-parser-core-MZFMFANN.js +70 -0
  581. package/dist/evaluation-context-GQBYDAEV.js +63 -0
  582. package/dist/evidence-plan-resolver-5FUAP3SA.js +111 -0
  583. package/dist/execution-data-writer-YCG5UH37.js +72 -0
  584. package/dist/execution-policy-applier-PWZM5LNA.js +63 -0
  585. package/dist/execution-preflight-DASJWWQF.js +223 -0
  586. package/dist/execution-roles-KNSNNB2D.js +112 -0
  587. package/dist/exit-code-error-QIY5DOKF.js +18 -0
  588. package/dist/external-temp-cleanup-CAW7LQ6R.js +360 -0
  589. package/dist/factory-YCYCB7AE.js +209 -0
  590. package/dist/file-utils-ZQYNBQBW.js +45 -0
  591. package/dist/flywheel-allowlist-revert-H22HA6JE.js +21 -0
  592. package/dist/flywheel-autoloop-executor-PDOASJEZ.js +316 -0
  593. package/dist/flywheel-autoloop-unverified-TOKYFCDC.js +36 -0
  594. package/dist/flywheel-competition-executor-YJUMMEJO.js +320 -0
  595. package/dist/flywheel-executor-JWU43KAY.js +437 -0
  596. package/dist/flywheel-git-isolation-FQ2TRWMB.js +56 -0
  597. package/dist/flywheel-manifest-HDPNMWWF.js +114 -0
  598. package/dist/flywheel-model-defaults-UHAKTZT3.js +110 -0
  599. package/dist/flywheel-preflight-III2C7TH.js +163 -0
  600. package/dist/flywheel-resume-2O45MS7Y.js +31 -0
  601. package/dist/flywheel-resume-execution-lease-P2BK257J.js +59 -0
  602. package/dist/flywheel-safety-AVAVFXIJ.js +19 -0
  603. package/dist/flywheel-scope-decision-ALJUUNGG.js +14 -0
  604. package/dist/get-telemetry-logger-MPDVO2JA.js +155 -0
  605. package/dist/git-message-style-KCXGMTDT.js +104 -0
  606. package/dist/guidance-profile-config-G3K5YQOO.js +113 -0
  607. package/dist/guidance-profile-renderer-TX4TNDFL.js +23 -0
  608. package/dist/identity-XLQJ4YJF.js +25 -0
  609. package/dist/index-query-6NUY4FKX.js +37 -0
  610. package/dist/index.js +85588 -0
  611. package/dist/indexer-DL54CM3K.js +44 -0
  612. package/dist/init-OUZ72344.js +245 -0
  613. package/dist/instructions-import-offer-NSYXSITC.js +363 -0
  614. package/dist/instructions-run-NBII7W36.js +114 -0
  615. package/dist/interrupted-work-E4TLKJLE.js +43 -0
  616. package/dist/invoke-provider-auth-NEWEIHCI.js +38 -0
  617. package/dist/judge-scoring.json +64 -0
  618. package/dist/lab-mode-YXQ2Y3NG.js +44 -0
  619. package/dist/lab-utils-5QAMDJR5.js +29 -0
  620. package/dist/linux-host-class-YDWQX65O.js +31 -0
  621. package/dist/llm-judge-executor-OJSNCTFJ.js +179 -0
  622. package/dist/local-ci-config-VZ4WWDT5.js +48 -0
  623. package/dist/local-executor-DCMDDRX2.js +280 -0
  624. package/dist/logger-5KNKS3O4.js +154 -0
  625. package/dist/matrix-prompt-builder-4ZBOGZSB.js +527 -0
  626. package/dist/matrix-result-parser-EZJM4YXP.js +14 -0
  627. package/dist/merge-batch-RTEEMVDB.js +313 -0
  628. package/dist/merge-batch-members-VIWGVRHP.js +24 -0
  629. package/dist/merge-queue-TIIUSOY6.js +126 -0
  630. package/dist/merge-queue-trust-DUNZ4J65.js +21 -0
  631. package/dist/metadata-UKSMWM6J.js +29 -0
  632. package/dist/model-catalog-readiness-R5KJBP5M.js +202 -0
  633. package/dist/model-invocations-db-XTUPPQ34.js +70 -0
  634. package/dist/model-resolver-JFZ2MHOJ.js +39 -0
  635. package/dist/model-resolver-SCQ57KAQ.js +217 -0
  636. package/dist/model-roles-D3OR4NAH.js +110 -0
  637. package/dist/monitor-F4SIU5G2.js +96 -0
  638. package/dist/nested-worktrees-2WUWQ75N.js +23 -0
  639. package/dist/next-S3IRLVER.js +96 -0
  640. package/dist/next-UKPFRXOO.js +148 -0
  641. package/dist/opportunistic-TL4S26VX.js +86 -0
  642. package/dist/optimization-config-resolver-NAEIYU6A.js +148 -0
  643. package/dist/package-HJR4QJT4.js +10 -0
  644. package/dist/parse-git-status-7PIYFPS2.js +21 -0
  645. package/dist/path-security-5WJSY56G.js +41 -0
  646. package/dist/paths-JUI7C4KU.js +45 -0
  647. package/dist/plan-backlog-list-fast-SSTQL5XP.js +89 -0
  648. package/dist/plan-backlog-show-fast-PFRAQ3JB.js +114 -0
  649. package/dist/plan-sprint-list-fast-HD2KBZSY.js +110 -0
  650. package/dist/plan-sprint-status-fast-YFYH7WQF.js +116 -0
  651. package/dist/plan-task-status-fast-JU5PNLUU.js +114 -0
  652. package/dist/plan-to-flywheel-PY5MSY2H.js +529 -0
  653. package/dist/preflight-PJHGTJNZ.js +12 -0
  654. package/dist/process-registry-B4ZUCN7T.js +28 -0
  655. package/dist/process-scanner-TU3QHWVB.js +23 -0
  656. package/dist/processes-6FHVGT72.js +28 -0
  657. package/dist/processes-json-fast-YWTMUWXB.js +72 -0
  658. package/dist/profile-HWTC56SG.js +209 -0
  659. package/dist/project-runtime-paths-2UQNG5Y4.js +41 -0
  660. package/dist/protected-branches-N36LDBFY.js +71 -0
  661. package/dist/provider-aware-resource-manager-YTFAHNGJ.js +40 -0
  662. package/dist/provider-matrix-2KO2V5OM.js +214 -0
  663. package/dist/provider-matrix-renderer-MEC3PER7.js +139 -0
  664. package/dist/provider-output-forwarding-2AHL7YMA.js +26 -0
  665. package/dist/provider-registry-WCZ65QHC.js +136 -0
  666. package/dist/provider-temp-cleanup-IUNPJCZF.js +14 -0
  667. package/dist/prune-engine-EOQVWEP6.js +448 -0
  668. package/dist/publication-turn-S42RZTQF.js +44 -0
  669. package/dist/quality-gate-JQMVCKOO.js +72 -0
  670. package/dist/quickstart-3O6FRCFB.js +105 -0
  671. package/dist/readiness-GW6ABRHB.js +139 -0
  672. package/dist/readiness-KFZCXDTT.js +215 -0
  673. package/dist/registry-7XG376SY.js +217 -0
  674. package/dist/repair-helpers-TTV5FEF4.js +117 -0
  675. package/dist/repo-root-YG6MQZDW.js +28 -0
  676. package/dist/resolution-engine-FFEK7HQ2.js +187 -0
  677. package/dist/resolve-provider-cli-ZEXVZKET.js +44 -0
  678. package/dist/restore-telemetry-db-E7BR5PO7.js +459 -0
  679. package/dist/routing-events-AW6SRZ6G.js +13 -0
  680. package/dist/routing-history-store-FMDDQL6W.js +39 -0
  681. package/dist/routing-planner-C6OHO3EY.js +221 -0
  682. package/dist/runner-KHDNQ5KU.js +1194 -0
  683. package/dist/runner-PIS2BW7Y.js +1801 -0
  684. package/dist/safety-E3JJVSFL.js +67 -0
  685. package/dist/safety-stanza-HE6GXP4G.js +49 -0
  686. package/dist/sandbox-7LBZC5PE.js +72 -0
  687. package/dist/sandbox-ASWCDYZL.js +80 -0
  688. package/dist/schema-extensions.sql +234 -0
  689. package/dist/security-JI22TBEB.js +17 -0
  690. package/dist/setup-bb-plugin-FQXLA76Y.js +531 -0
  691. package/dist/setup-claude-code-NF4ZSHVE.js +197 -0
  692. package/dist/setup-temp-cleanup-2APZ4HMA.js +29 -0
  693. package/dist/shared-utils-34HJZ6JX.js +40 -0
  694. package/dist/simple-artifact-goal-MQLDDMMX.js +29 -0
  695. package/dist/sprint-N7JFTEGL.js +403 -0
  696. package/dist/sprint-bridge-KJXIBNRJ.js +117 -0
  697. package/dist/sprint-completion-dependents-34SR7JVL.js +43 -0
  698. package/dist/sprint-execution-service-A6JPUBWY.js +376 -0
  699. package/dist/sprint-manager-MU2ZUWN2.js +140 -0
  700. package/dist/sqlite-timeout-config-U2CT4LKU.js +13 -0
  701. package/dist/sqlite-wrapper-FSGJ2QEW.js +19 -0
  702. package/dist/stale-cleanup-orchestrator-4X6324VF.js +71 -0
  703. package/dist/storage-access-I3V5LER7.js +31 -0
  704. package/dist/store-XVG64ZR2.js +25 -0
  705. package/dist/task-classifier-MN2DYE5R.js +558 -0
  706. package/dist/task-list-fast-7AT6LTTA.js +112 -0
  707. package/dist/task-manager-LHPBBRRE.js +136 -0
  708. package/dist/task-report-OLPAWBTK.js +64 -0
  709. package/dist/task-type-detector-6RG3YNZF.js +27 -0
  710. package/dist/task-type-resolver-EQHOUIHT.js +64 -0
  711. package/dist/telemetry-5VYSK772.js +212 -0
  712. package/dist/telemetry-health-tracker-4GFW2EMP.js +12 -0
  713. package/dist/telemetry-path-resolver-H3NFHHQY.js +34 -0
  714. package/dist/telemetry-reliability-URJDTNPZ.js +76 -0
  715. package/dist/transports-NREZQTJK.js +106 -0
  716. package/dist/unified-cost-tracker-5MTBJ2CL.js +59 -0
  717. package/dist/unified-state-cleanup-GGAXLHXF.js +292 -0
  718. package/dist/universal-cost-calculator-JTFEJVWK.js +70 -0
  719. package/dist/user-agents-QLIGPLP2.js +30 -0
  720. package/dist/variant-execution-I4ZZUAWY.js +319 -0
  721. package/dist/variant-opt-parser-JHCXKY2K.js +251 -0
  722. package/dist/variant-request-input-PL37N3AN.js +11 -0
  723. package/dist/variant-state-cleanup-KHMXRBZN.js +287 -0
  724. package/dist/variant-worker-COB5YTJX.js +4517 -0
  725. package/dist/variant-worker.js +26 -0
  726. package/dist/variants-XGDSXYG3.js +267 -0
  727. package/dist/verification-command-YSPU4VBH.js +66 -0
  728. package/dist/verify-command-timeout-DQUYH33Z.js +11 -0
  729. package/dist/verify-commands-LOHMOFKC.js +65 -0
  730. package/dist/warning-JSKSFKBT.js +13 -0
  731. package/dist/work-edge-K65NHVWY.js +50 -0
  732. package/dist/work-item-writer-KY5ZWZWW.js +30 -0
  733. package/dist/worker-JNXXEIT6.js +1504 -0
  734. package/dist/worker.js +21 -0
  735. package/dist/workspace-tree-identity-JO5PSKTW.js +52 -0
  736. package/dist/worktree-environment-WTVABAQW.js +64 -0
  737. package/dist/worktree-metrics-VZRQI26T.js +66 -0
  738. package/dist/writeback-TQFQBJNU.js +50 -0
  739. package/docs/CLI_REFERENCE.md +8287 -0
  740. package/docs/PROVIDER_SETUP.md +1737 -0
  741. package/docs/README.md +96 -0
  742. package/docs/TROUBLESHOOTING.md +2209 -0
  743. package/docs/getting-started/QUICK_START.md +260 -0
  744. package/docs/getting-started/README.md +198 -0
  745. package/docs/getting-started/SETUP.md +25 -0
  746. package/docs/reference/AGENT_CONTEXT.md +84 -0
  747. package/docs/reference/DELIVERY_ONBOARDING.md +198 -0
  748. package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
  749. package/docs/reference/SECURITY.md +291 -0
  750. package/docs/reference/TELEMETRY_PRIVACY.md +259 -0
  751. package/integrations/bb-plugin-build/README.md +391 -0
  752. package/integrations/bb-plugin-build/app.css +431 -0
  753. package/integrations/bb-plugin-build/app.tsx +6210 -0
  754. package/integrations/bb-plugin-build/package.json +45 -0
  755. package/integrations/bb-plugin-build/server.ts +488 -0
  756. package/integrations/bb-plugin-build/src/adapter.ts +3672 -0
  757. package/integrations/bb-plugin-build/src/backlog-authoring-model.ts +150 -0
  758. package/integrations/bb-plugin-build/src/backlog-authoring-schema.ts +458 -0
  759. package/integrations/bb-plugin-build/src/backlog-authoring-service.ts +467 -0
  760. package/integrations/bb-plugin-build/src/backlog-authoring-view.ts +1025 -0
  761. package/integrations/bb-plugin-build/src/command-evidence-schema.ts +13 -0
  762. package/integrations/bb-plugin-build/src/competition-defaults-schema.ts +122 -0
  763. package/integrations/bb-plugin-build/src/competition-defaults-service.ts +105 -0
  764. package/integrations/bb-plugin-build/src/competition-defaults-view.ts +138 -0
  765. package/integrations/bb-plugin-build/src/contract.ts +718 -0
  766. package/integrations/bb-plugin-build/src/execution-activity-view.ts +98 -0
  767. package/integrations/bb-plugin-build/src/finalize-model.ts +205 -0
  768. package/integrations/bb-plugin-build/src/finalize-schema.ts +150 -0
  769. package/integrations/bb-plugin-build/src/finalize-service.ts +330 -0
  770. package/integrations/bb-plugin-build/src/finalize-view.ts +159 -0
  771. package/integrations/bb-plugin-build/src/judge-operations-readback.ts +303 -0
  772. package/integrations/bb-plugin-build/src/judge-operations-schema.ts +33 -0
  773. package/integrations/bb-plugin-build/src/judge-operations-view.ts +459 -0
  774. package/integrations/bb-plugin-build/src/model.ts +1318 -0
  775. package/integrations/bb-plugin-build/src/monitor-service.ts +233 -0
  776. package/integrations/bb-plugin-build/src/panel-read-ux.ts +97 -0
  777. package/integrations/bb-plugin-build/src/patch-preview-schema.ts +166 -0
  778. package/integrations/bb-plugin-build/src/patch-preview-service.ts +480 -0
  779. package/integrations/bb-plugin-build/src/patch-preview-view.ts +109 -0
  780. package/integrations/bb-plugin-build/src/planning-readback.ts +228 -0
  781. package/integrations/bb-plugin-build/src/planning-service.ts +647 -0
  782. package/integrations/bb-plugin-build/src/planning-sync-evidence.ts +37 -0
  783. package/integrations/bb-plugin-build/src/planning-workspace-readback.ts +33 -0
  784. package/integrations/bb-plugin-build/src/planning-workspace-schema.ts +131 -0
  785. package/integrations/bb-plugin-build/src/planning-workspace.ts +333 -0
  786. package/integrations/bb-plugin-build/src/poll-handoff.ts +136 -0
  787. package/integrations/bb-plugin-build/src/project-target-server.ts +21 -0
  788. package/integrations/bb-plugin-build/src/project-target.ts +45 -0
  789. package/integrations/bb-plugin-build/src/reference-index.ts +218 -0
  790. package/integrations/bb-plugin-build/src/repository-identity.ts +17 -0
  791. package/integrations/bb-plugin-build/src/repository-schema.ts +30 -0
  792. package/integrations/bb-plugin-build/src/result-explorer-view.ts +597 -0
  793. package/integrations/bb-plugin-build/src/result-readback-model.ts +163 -0
  794. package/integrations/bb-plugin-build/src/result-readback-schema.ts +319 -0
  795. package/integrations/bb-plugin-build/src/result-readback-service.ts +333 -0
  796. package/integrations/bb-plugin-build/src/run-page-view.ts +332 -0
  797. package/integrations/bb-plugin-build/src/setup-config-schema.ts +293 -0
  798. package/integrations/bb-plugin-build/src/setup-config-service.ts +620 -0
  799. package/integrations/bb-plugin-build/src/setup-config-view.ts +388 -0
  800. package/integrations/bb-plugin-build/src/sprint-close-model.ts +150 -0
  801. package/integrations/bb-plugin-build/src/sprint-close-schema.ts +128 -0
  802. package/integrations/bb-plugin-build/src/sprint-close-service.ts +293 -0
  803. package/integrations/bb-plugin-build/src/sprint-close-view.ts +116 -0
  804. package/integrations/bb-plugin-build/src/sprint-composition-model.ts +709 -0
  805. package/integrations/bb-plugin-build/src/sprint-composition-readback.ts +231 -0
  806. package/integrations/bb-plugin-build/src/sprint-composition-schema.ts +597 -0
  807. package/integrations/bb-plugin-build/src/sprint-composition-service.ts +773 -0
  808. package/integrations/bb-plugin-build/src/sprint-composition-view.ts +1221 -0
  809. package/integrations/bb-plugin-build/src/sprint-execution-schema.ts +188 -0
  810. package/integrations/bb-plugin-build/src/sprint-execution-service.ts +289 -0
  811. package/integrations/bb-plugin-build/src/sprint-execution-view.ts +54 -0
  812. package/integrations/bb-plugin-build/src/task-authoring-model.ts +563 -0
  813. package/integrations/bb-plugin-build/src/task-authoring-schema.ts +629 -0
  814. package/integrations/bb-plugin-build/src/task-authoring-service.ts +842 -0
  815. package/integrations/bb-plugin-build/src/task-authoring-view.ts +1227 -0
  816. package/integrations/bb-plugin-build/src/task-catalog-schema.ts +46 -0
  817. package/integrations/bb-plugin-build/src/task-catalog-service.ts +44 -0
  818. package/integrations/bb-plugin-build/src/task-detail-readback.ts +56 -0
  819. package/integrations/bb-plugin-build/src/task-detail-schema.ts +171 -0
  820. package/integrations/bb-plugin-build/src/task-navigation.ts +446 -0
  821. package/integrations/bb-plugin-build/src/workbench-route.ts +74 -0
  822. package/package.json +306 -4
  823. package/schemas/README.md +80 -0
  824. package/schemas/build.config.schema.json +2110 -0
  825. package/schemas/config-v1.schema.json +136 -0
  826. package/schemas/execution-config.schema.json +47 -0
  827. package/schemas/judge-scoring.schema.json +257 -0
  828. package/schemas/provider-config.schema.json +179 -0
  829. package/schemas/telemetry-config.schema.json +79 -0
  830. package/schemas/user-preferences.schema.json +141 -0
  831. package/scripts/assert-node-runtime.mjs +146 -0
  832. package/scripts/preflight-native.mjs +49 -0
  833. package/scripts/setup-git-hooks.mjs +39 -0
  834. package/scripts/sync.sh +2871 -0
  835. package/scripts/write-node-launcher.sh +138 -0
  836. package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
  837. package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
  838. package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
  839. package/templates/.build-project/README.md +41 -0
  840. package/templates/.build-project/agents/README.md +50 -0
  841. package/templates/.build-project/agents/architect.md +10 -0
  842. package/templates/.build-project/agents/code-reviewer.md +10 -0
  843. package/templates/.build-project/agents/debugger.md +10 -0
  844. package/templates/.build-project/agents/documenter.md +10 -0
  845. package/templates/.build-project/agents/test-automator.md +10 -0
  846. package/templates/.build-project/config/build.config.jsonc +64 -0
  847. package/templates/.build-project/config/project-context.template.json +6 -0
  848. package/templates/.build-project/config/task-type-aliases.presets/kanban.yaml +14 -0
  849. package/templates/.build-project/config/task-type-aliases.presets/scrum.yaml +17 -0
  850. package/templates/.build-project/config/task-type-aliases.presets/xp.yaml +12 -0
  851. package/templates/.build-project/config/task-type-aliases.yaml +29 -0
  852. package/templates/.build-project/gitignore.template +70 -0
  853. package/templates/.build-project/task-types/analysis.yaml +38 -0
  854. package/templates/.build-project/task-types/architecture.yaml +35 -0
  855. package/templates/.build-project/task-types/doc.yaml +34 -0
  856. package/templates/.build-project/task-types/feature.yaml +22 -0
  857. package/templates/.build-project/task-types/general.yaml +4 -0
  858. package/templates/.build-project/task-types/security.yaml +38 -0
  859. package/templates/AGENTS.template.md +85 -0
  860. package/templates/CLAUDE.template.md +1 -0
  861. package/templates/GEMINI.template.md +1 -0
  862. package/templates/builtin-workflows/code-review.yaml +73 -0
  863. package/templates/builtin-workflows/skills/code-review/SKILL.md +61 -0
  864. package/templates/builtin-workflows/skills/execution-contract-analysis/SKILL.md +24 -0
  865. package/templates/builtin-workflows/skills/execution-contract-code/SKILL.md +19 -0
  866. package/templates/builtin-workflows/skills/execution-contract-structured/SKILL.md +17 -0
  867. package/templates/ci/incident-intake.mjs +787 -0
  868. package/templates/ci/incidents.yml.template +103 -0
  869. package/templates/ci/node.yml.template +17 -0
  870. package/templates/ci/python.yml.template +17 -0
  871. package/templates/guard-kit/GUARD_SETUP.md.template +258 -0
  872. package/templates/guard-kit/build-guard.yml.template +189 -0
  873. package/templates/guard-kit/check.mjs.template +932 -0
  874. package/templates/guard-kit/config.json.template +7 -0
  875. package/templates/guard-kit/policy.json.template +1 -0
  876. package/templates/profiles/README.md +57 -0
  877. package/templates/profiles/frontier-claude.json +23 -0
  878. package/templates/profiles/frontier-codex.json +23 -0
@@ -0,0 +1,4057 @@
1
+ import {
2
+ getWorktreeManager
3
+ } from "./chunk-57WRCJJW.js";
4
+ import {
5
+ isPathSafeIdentifier,
6
+ parseArtifactRef
7
+ } from "./chunk-SK3ZFONO.js";
8
+ import {
9
+ decideDistRebuild,
10
+ resolveDistOwnership,
11
+ writeDistBuildStamp
12
+ } from "./chunk-WKFOJ5ZG.js";
13
+ import {
14
+ normalizeProviderForEstimator
15
+ } from "./chunk-VJER3FUS.js";
16
+ import {
17
+ findInstalledDependencyDrift
18
+ } from "./chunk-BIUV23HH.js";
19
+ import {
20
+ isProviderFailureKeptRecord
21
+ } from "./chunk-UJYDH2MW.js";
22
+ import {
23
+ validateCompetitionJudgeConfig
24
+ } from "./chunk-EJVCDTAN.js";
25
+ import {
26
+ extractCreateAnnotatedFilePaths,
27
+ parseFileDirectiveLines
28
+ } from "./chunk-ETA2K4KI.js";
29
+ import {
30
+ getProviderHealth
31
+ } from "./chunk-CRPRVNQ4.js";
32
+ import {
33
+ isValidCheckPath,
34
+ validateCompletionContracts
35
+ } from "./chunk-MCBNWZNO.js";
36
+ import {
37
+ getBucketPathByKind
38
+ } from "./chunk-V3KLVK7B.js";
39
+ import {
40
+ applyFlywheelModelDefaults
41
+ } from "./chunk-DSRLVYBS.js";
42
+ import {
43
+ WORKTREE_RECORD_RECOVERY_REASON,
44
+ getProjectRoot,
45
+ getWorktreeRecordsByStatus,
46
+ listManifests,
47
+ reconcileAndSaveManifest,
48
+ updateWorktreeRecordStatus
49
+ } from "./chunk-6TRD7TEE.js";
50
+ import {
51
+ signalCoordinator
52
+ } from "./chunk-GC4CRMTS.js";
53
+ import {
54
+ resolveDefaultProvider
55
+ } from "./chunk-C3DH3NCY.js";
56
+ import {
57
+ getProviderReadiness
58
+ } from "./chunk-XTXYIISX.js";
59
+ import {
60
+ pruneGitWorktreeEntries
61
+ } from "./chunk-33IGPPUI.js";
62
+ import {
63
+ WorktreeCleanupManager
64
+ } from "./chunk-UPHU64NB.js";
65
+ import {
66
+ filterWorktreeRunSentinelStatusOutput,
67
+ isActiveRun,
68
+ withCleanupLock
69
+ } from "./chunk-2NMNFUQA.js";
70
+ import {
71
+ tryRemoveWorktreeNestedAware
72
+ } from "./chunk-UJP3XM54.js";
73
+ import {
74
+ ModelMetadataResolver,
75
+ findOpencodeFallbackContextWindow,
76
+ getOpencodeCatalogEntry,
77
+ isExactCompatibilityMatch,
78
+ resolveOpencodeContextWindow
79
+ } from "./chunk-YVCMDVBB.js";
80
+ import {
81
+ assertSafePathComponent
82
+ } from "./chunk-S2VQCZO4.js";
83
+ import {
84
+ stripBuildContextBlocks
85
+ } from "./chunk-KUEWV2JH.js";
86
+ import {
87
+ classifyBranchRole,
88
+ isProtectedPipelineBranch
89
+ } from "./chunk-XGU6PBO5.js";
90
+ import {
91
+ buildBranchKindPrefix,
92
+ buildVariantBranchPrefixes,
93
+ isBuildOwnedBranch,
94
+ runChildBranchTargets,
95
+ sanitizeRefPart
96
+ } from "./chunk-F55YABKV.js";
97
+ import {
98
+ summarizeRepoPaths
99
+ } from "./chunk-RBBTBCHX.js";
100
+ import {
101
+ checkVerifyCommandSafety
102
+ } from "./chunk-AHU3FKPA.js";
103
+ import {
104
+ isDualContainedInManagedRoots,
105
+ safeRmDir
106
+ } from "./chunk-ALDTFNZ3.js";
107
+ import {
108
+ parsePorcelainLinePaths
109
+ } from "./chunk-5KQETTNS.js";
110
+ import {
111
+ splitClaudeModelContextSuffix
112
+ } from "./chunk-H3HGMISG.js";
113
+ import {
114
+ execGit
115
+ } from "./chunk-MHLEZQQI.js";
116
+ import {
117
+ ProviderRegistry
118
+ } from "./chunk-QJ6OEH4K.js";
119
+ import {
120
+ isJsonOutputRequested,
121
+ isQuietOutputRequested,
122
+ isVerboseOutputRequested
123
+ } from "./chunk-XWIZ6B5S.js";
124
+ import {
125
+ GIT_COMMAND_TIMEOUT_MS
126
+ } from "./chunk-I56TH7VM.js";
127
+ import {
128
+ WORKTREE_HYGIENE_MAX_MANIFESTS_PER_PASS,
129
+ WORKTREE_HYGIENE_MAX_RECOVERY_RECORDS_PER_PASS,
130
+ WORKTREE_HYGIENE_MAX_RETAINED_TOTAL_BYTES,
131
+ WORKTREE_HYGIENE_MAX_RETENTION_COLLATION,
132
+ WORKTREE_HYGIENE_MAX_RETENTION_PRUNE_OPS,
133
+ getBuildFlywheelManifestsRoot,
134
+ getBuildRepoWorktreePoolRoot,
135
+ getWorktreeBaseDir
136
+ } from "./chunk-Z5WGNULV.js";
137
+
138
+ // src/automation/flywheel-preflight.ts
139
+ import { appendFileSync, existsSync as existsSync2, readFileSync } from "fs";
140
+ import * as fs3 from "fs/promises";
141
+ import os2 from "os";
142
+ import * as path4 from "path";
143
+ import { fileURLToPath } from "url";
144
+ import chalk from "chalk";
145
+ import { execa as execa2 } from "execa";
146
+
147
+ // src/orchestrator/token-estimator.ts
148
+ var UTILIZATION_WARN_PERCENT = 80;
149
+ var UTILIZATION_SPLIT_PERCENT = 85;
150
+ var CONTEXT_WINDOWS = {
151
+ // Provider marker for normalization; Claude context windows return early
152
+ // through ProviderRegistry so model metadata stays in providers.yaml.
153
+ claude: {},
154
+ // Provider marker for normalization; Codex context windows return early
155
+ // through ProviderRegistry so model metadata stays in providers.yaml.
156
+ codex: {},
157
+ // Compatibility-only estimator bucket for OpenAI-family model names.
158
+ // This is not a runnable Build provider; Codex owns OpenAI-backed CLI execution.
159
+ openai: {},
160
+ // Provider marker for normalization; Gemini context windows return early
161
+ // through ProviderRegistry so model metadata stays in providers.yaml.
162
+ gemini: {},
163
+ // Provider marker for normalization; Cursor context windows resolve through
164
+ // ProviderRegistry first and keep conservative fallbacks for legacy aliases.
165
+ cursor: {},
166
+ // Provider marker for normalization; OpenCode context windows return early
167
+ // through the provider catalog so runtime and estimator share one fallback table.
168
+ opencode: {},
169
+ // Provider marker for normalization; Copilot context windows return early
170
+ // through ProviderRegistry, with legacy fallbacks for removed aliases.
171
+ copilot: {},
172
+ // Provider marker for normalization; Grok context windows resolve through
173
+ // ModelMetadataResolver (config override, harness window, model maximum).
174
+ // Grok is not an OpenAI-tokenizer provider, so it uses the generic heuristic.
175
+ grok: {}
176
+ };
177
+ function lookupProviderEntry(table, provider) {
178
+ return Object.hasOwn(table, provider) ? table[provider] : void 0;
179
+ }
180
+ function isKnownEstimatorProviderInput(provider) {
181
+ const normalizedProvider = (provider || "").toLowerCase().trim();
182
+ if (!normalizedProvider) return false;
183
+ if (normalizedProvider in CONTEXT_WINDOWS) return true;
184
+ return ESTIMATOR_PROVIDER_ALIASES.has(normalizedProvider);
185
+ }
186
+ var DEFAULT_CONTEXT_WINDOW = 128e3;
187
+ var GPT_FAMILY = "gpt";
188
+ var CLAUDE_FAMILY = "claude";
189
+ var ESTIMATOR_PROVIDER_ALIASES = /* @__PURE__ */ new Set(["anthropic", "google", "github", "opencode"]);
190
+ var OPENAI_TOKENIZER_PROVIDERS = /* @__PURE__ */ new Set([
191
+ "codex",
192
+ "openai",
193
+ "copilot"
194
+ ]);
195
+ var CLAUDE_TOKENIZER_PROVIDERS = /* @__PURE__ */ new Set(["claude"]);
196
+ var GPT_4_TIKTOKEN_MODEL = [GPT_FAMILY, "4"].join("-");
197
+ var GPT_4_TURBO_MODEL = [GPT_FAMILY, "4", "turbo"].join("-");
198
+ var GPT_4_TURBO_PREVIEW_TIKTOKEN_MODEL = [GPT_FAMILY, "4", "turbo", "preview"].join("-");
199
+ var GPT_4O_TIKTOKEN_MODEL = `${GPT_FAMILY}-4o`;
200
+ var GPT_4O_MINI_TIKTOKEN_MODEL = `${GPT_FAMILY}-4o-mini`;
201
+ var GPT_3_5_TURBO_TIKTOKEN_MODEL = `${GPT_FAMILY}-3.5-turbo`;
202
+ var GPT_3_5_TIKTOKEN_ALIAS = `${GPT_FAMILY}-3.5`;
203
+ var GPT_5_TIKTOKEN_ALIAS = [GPT_FAMILY, "5"].join("-");
204
+ var GPT_5_CODEX_MINI_TIKTOKEN_ALIAS = [GPT_FAMILY, "5", "codex", "mini"].join("-");
205
+ var CLAUDE_OPUS_4_1_COMPAT_MODEL = [CLAUDE_FAMILY, "opus", "4", "1"].join("-");
206
+ var CLAUDE_3_OPUS_COMPAT_MODEL = [CLAUDE_FAMILY, "3", "opus"].join("-");
207
+ var CLAUDE_3_SONNET_COMPAT_MODEL = [CLAUDE_FAMILY, "3", "sonnet"].join("-");
208
+ var CLAUDE_3_HAIKU_COMPAT_MODEL = [CLAUDE_FAMILY, "3", "haiku"].join("-");
209
+ var CLAUDE_LEGACY_CONTEXT_WINDOWS = {
210
+ [CLAUDE_OPUS_4_1_COMPAT_MODEL]: 2e5,
211
+ "sonnet-4": 2e5,
212
+ "sonnet-3-5": 2e5,
213
+ "sonnet-3": 2e5,
214
+ [CLAUDE_3_OPUS_COMPAT_MODEL]: 2e5,
215
+ [CLAUDE_3_SONNET_COMPAT_MODEL]: 2e5,
216
+ [CLAUDE_3_HAIKU_COMPAT_MODEL]: 2e5
217
+ };
218
+ var CODEX_LEGACY_CONTEXT_WINDOWS = {
219
+ codex: 8e3
220
+ };
221
+ var COPILOT_LEGACY_CONTEXT_WINDOWS = {
222
+ [GPT_4_TIKTOKEN_MODEL]: 128e3,
223
+ [GPT_3_5_TURBO_TIKTOKEN_MODEL]: 16e3
224
+ };
225
+ function normalizeModelKey(model) {
226
+ return model.trim().toLowerCase();
227
+ }
228
+ function findFallbackContextWindowMatch(model, windows) {
229
+ const normalizedModel = normalizeModelKey(model);
230
+ if (normalizedModel === "") {
231
+ return void 0;
232
+ }
233
+ const exactMatch = windows[normalizedModel];
234
+ if (exactMatch !== void 0) {
235
+ return { contextWindow: exactMatch, exact: true };
236
+ }
237
+ for (const [knownModel, contextWindow] of Object.entries(windows)) {
238
+ if (normalizedModel.includes(knownModel) || knownModel.includes(normalizedModel)) {
239
+ return { contextWindow, exact: false };
240
+ }
241
+ }
242
+ return void 0;
243
+ }
244
+ var DEFAULT_CHARS_PER_TOKEN = 4;
245
+ var HEURISTIC_CHARS_PER_TOKEN = {
246
+ // Gemini uses similar tokenization to GPT-4 (~4 chars per token)
247
+ gemini: 4
248
+ };
249
+ var MODEL_CONFIDENCE_RESOLVERS = {
250
+ openai: (_provider, model) => getOpenAiCompatibilityConfidence(model) ?? 0.3,
251
+ opencode: (_provider, model) => getOpencodeCatalogConfidence(model) ?? 0.3,
252
+ cursor: (_provider, model) => normalizeModelKey(model) === "auto" ? 0.8 : void 0,
253
+ claude: (provider, model) => {
254
+ const legacyConfidence = getLegacyContextConfidence(model, CLAUDE_LEGACY_CONTEXT_WINDOWS);
255
+ if (legacyConfidence === 1) {
256
+ return legacyConfidence;
257
+ }
258
+ const registryConfidence = getProviderRegistryModelConfidence(provider, model);
259
+ if (registryConfidence !== void 0) {
260
+ return registryConfidence;
261
+ }
262
+ return legacyConfidence;
263
+ },
264
+ codex: (_provider, model) => CODEX_LEGACY_CONTEXT_WINDOWS[normalizeModelKey(model)] !== void 0 ? 0.9 : void 0,
265
+ copilot: (provider, model) => {
266
+ const exactLegacyConfidence = COPILOT_LEGACY_CONTEXT_WINDOWS[normalizeModelKey(model)] !== void 0 ? 0.9 : void 0;
267
+ if (exactLegacyConfidence !== void 0) {
268
+ return exactLegacyConfidence;
269
+ }
270
+ const registryConfidence = getProviderRegistryModelConfidence(provider, model);
271
+ if (registryConfidence !== void 0) {
272
+ return registryConfidence;
273
+ }
274
+ return getLegacyContextConfidence(model, COPILOT_LEGACY_CONTEXT_WINDOWS, 0.9) ?? 0.3;
275
+ }
276
+ };
277
+ var CONTEXT_WINDOW_RESOLVERS = {
278
+ opencode: (model) => resolveOpencodeContextWindow(model, DEFAULT_CONTEXT_WINDOW),
279
+ claude: (model) => {
280
+ const baseModel = splitClaudeModelContextSuffix(model).base;
281
+ return CLAUDE_LEGACY_CONTEXT_WINDOWS[normalizeModelKey(baseModel)];
282
+ },
283
+ codex: (model) => CODEX_LEGACY_CONTEXT_WINDOWS[normalizeModelKey(model)],
284
+ copilot: (model) => COPILOT_LEGACY_CONTEXT_WINDOWS[normalizeModelKey(model)]
285
+ };
286
+ var PROVIDER_OVERHEAD_PROFILES = {
287
+ claude: {
288
+ systemPromptTokens: 1500,
289
+ outputReservePercent: 0.3
290
+ // 30% for Claude's verbose outputs
291
+ },
292
+ codex: {
293
+ systemPromptTokens: 800,
294
+ outputReservePercent: 0.25
295
+ },
296
+ openai: {
297
+ systemPromptTokens: 800,
298
+ outputReservePercent: 0.25
299
+ },
300
+ gemini: {
301
+ systemPromptTokens: 1e3,
302
+ outputReservePercent: 0.25
303
+ },
304
+ cursor: {
305
+ systemPromptTokens: 1e3,
306
+ outputReservePercent: 0.25
307
+ },
308
+ opencode: {
309
+ systemPromptTokens: 1e3,
310
+ outputReservePercent: 0.25
311
+ },
312
+ copilot: {
313
+ systemPromptTokens: 800,
314
+ outputReservePercent: 0.25
315
+ },
316
+ grok: {
317
+ systemPromptTokens: 1e3,
318
+ outputReservePercent: 0.25
319
+ }
320
+ };
321
+ var GUESSED_MODEL_MATCH_CONFIDENCE = 0.4;
322
+ function isGuessedModelMatch(resolved, substringMatched) {
323
+ if (!resolved) return false;
324
+ if (resolved.matchType === "fuzzy") return true;
325
+ return resolved.matchType === "fallback" && substringMatched;
326
+ }
327
+ function getProviderRegistryModelConfidence(provider, model) {
328
+ const resolved = ModelMetadataResolver.resolveModel(provider, model, { allowFuzzy: true });
329
+ if (!resolved) return void 0;
330
+ return resolved.matchType === "fuzzy" ? GUESSED_MODEL_MATCH_CONFIDENCE : 1;
331
+ }
332
+ function getLegacyContextConfidence(model, windows, exactConfidence = 1) {
333
+ const match = findFallbackContextWindowMatch(model, windows);
334
+ if (!match) {
335
+ return void 0;
336
+ }
337
+ return match.exact ? exactConfidence : GUESSED_MODEL_MATCH_CONFIDENCE;
338
+ }
339
+ function getOpenAiCompatibilityConfidence(model) {
340
+ const resolved = ModelMetadataResolver.resolveModel("openai", model, {
341
+ allowFallback: true,
342
+ allowFuzzy: true
343
+ });
344
+ if (!resolved) return void 0;
345
+ return isGuessedModelMatch(resolved, isExactCompatibilityMatch("openai", model) === false) ? GUESSED_MODEL_MATCH_CONFIDENCE : 1;
346
+ }
347
+ function getOpencodeCatalogConfidence(model) {
348
+ const catalogEntry = getOpencodeCatalogEntry(model);
349
+ if (catalogEntry.source === "registry") {
350
+ return 1;
351
+ }
352
+ const fallbackMatch = findOpencodeFallbackContextWindow(model);
353
+ if (fallbackMatch) {
354
+ return fallbackMatch.exact ? 0.9 : GUESSED_MODEL_MATCH_CONFIDENCE;
355
+ }
356
+ return void 0;
357
+ }
358
+ var DEFAULT_THRESHOLD_PERCENT = 0.65;
359
+ var TokenEstimator = class {
360
+ // tiktoken is a declared dependency; assume available at runtime.
361
+ // estimateOpenAITokens / estimateClaudeTokens each contain their own try/catch
362
+ // that falls back to chars/4 or chars/3.5 if the dynamic import fails.
363
+ tiketokenAvailable = true;
364
+ encodingCache = /* @__PURE__ */ new Map();
365
+ /**
366
+ * Analyze task to determine if splitting is needed
367
+ *
368
+ * @param prompt - Task description
369
+ * @param provider - Target provider ID (e.g., 'claude', 'openai', or a config-defined provider)
370
+ * @param model - Target model (e.g., 'claude-sonnet-4-5', 'gpt-4')
371
+ * @returns Decomposition decision with token analysis
372
+ */
373
+ async analyzeTask(prompt, provider, model) {
374
+ const normalizedProvider = normalizeProviderForEstimator(provider, model);
375
+ const inputProviderKnown = isKnownEstimatorProviderInput(provider);
376
+ const promptForEstimation = stripBuildInjectedContext(prompt);
377
+ const estimatedTokens = await this.estimateTokens(
378
+ promptForEstimation,
379
+ normalizedProvider,
380
+ model
381
+ );
382
+ const contextWindow = this.getContextWindow(normalizedProvider, model);
383
+ const thresholdTokens = this.getRecommendedThreshold(normalizedProvider, model);
384
+ const overhead = this.getContextOverhead(normalizedProvider, model);
385
+ const utilizationPercent = estimatedTokens / contextWindow * 100;
386
+ const adjustedUtilizationPercent = (estimatedTokens + overhead.total) / contextWindow * 100;
387
+ const estimationConfidence = inputProviderKnown ? this.getModelConfidence(normalizedProvider, model) : 0.5;
388
+ const validationMetadata = this.computeValidationMetadata(
389
+ promptForEstimation,
390
+ utilizationPercent
391
+ );
392
+ if (utilizationPercent < 60) {
393
+ return {
394
+ shouldSplit: false,
395
+ reason: "within-threshold",
396
+ estimatedTokens,
397
+ contextWindow,
398
+ utilizationPercent,
399
+ thresholdTokens,
400
+ adjustedUtilizationPercent,
401
+ overhead,
402
+ estimationConfidence,
403
+ validationMetadata
404
+ };
405
+ }
406
+ const strategy = this.selectSplitStrategy(
407
+ promptForEstimation,
408
+ estimatedTokens,
409
+ thresholdTokens
410
+ );
411
+ return {
412
+ shouldSplit: true,
413
+ reason: "exceeds-threshold",
414
+ estimatedTokens,
415
+ contextWindow,
416
+ utilizationPercent,
417
+ thresholdTokens,
418
+ strategy,
419
+ adjustedUtilizationPercent,
420
+ overhead,
421
+ estimationConfidence,
422
+ validationMetadata
423
+ };
424
+ }
425
+ /**
426
+ * Compute validation metadata for autoloop gate selection
427
+ *
428
+ * Provides hints about quality gate policy based on task characteristics:
429
+ * - Simple tasks with low token utilization → deterministic gates only (fast CI)
430
+ * - Complex tasks or high utilization → all gates (comprehensive verification)
431
+ *
432
+ * @param prompt - Task description
433
+ * @param utilizationPercent - Context window utilization percentage
434
+ * @returns Validation metadata with policy recommendation
435
+ */
436
+ computeValidationMetadata(prompt, utilizationPercent) {
437
+ const rationale = [];
438
+ if (utilizationPercent < 40) {
439
+ rationale.push("Low complexity task (< 40% context usage)");
440
+ rationale.push("Recommend deterministic gates only for fast CI velocity");
441
+ return {
442
+ recommendedGatePolicy: "deterministic",
443
+ rationale
444
+ };
445
+ }
446
+ const complexitySignals = [
447
+ "refactor",
448
+ "architecture",
449
+ "breaking change",
450
+ "migration",
451
+ "security"
452
+ ];
453
+ const isComplex = complexitySignals.some(
454
+ (signal) => prompt.toLowerCase().includes(signal.toLowerCase())
455
+ );
456
+ if (isComplex || utilizationPercent >= 40) {
457
+ rationale.push(
458
+ isComplex ? "Complex task detected (refactor/architecture/migration)" : "High complexity task (\u2265 40% context usage)"
459
+ );
460
+ rationale.push("Recommend all gates for comprehensive verification");
461
+ return {
462
+ recommendedGatePolicy: "all",
463
+ rationale
464
+ };
465
+ }
466
+ rationale.push("Default to deterministic gates for CI velocity");
467
+ return {
468
+ recommendedGatePolicy: "deterministic",
469
+ rationale
470
+ };
471
+ }
472
+ /**
473
+ * Estimate token count for text
474
+ *
475
+ * @param text - Text to analyze
476
+ * @param provider - Target provider
477
+ * @param model - Target model
478
+ * @returns Estimated token count
479
+ */
480
+ async estimateTokens(text, provider, model) {
481
+ if (OPENAI_TOKENIZER_PROVIDERS.has(provider)) {
482
+ return this.estimateOpenAITokens(text, model);
483
+ }
484
+ if (CLAUDE_TOKENIZER_PROVIDERS.has(provider)) {
485
+ return this.estimateClaudeTokens(text, model);
486
+ }
487
+ const charsPerToken = lookupProviderEntry(HEURISTIC_CHARS_PER_TOKEN, provider) ?? DEFAULT_CHARS_PER_TOKEN;
488
+ return Math.ceil(text.length / charsPerToken);
489
+ }
490
+ /**
491
+ * Estimate tokens using OpenAI's tiktoken
492
+ * Falls back to character-based approximation if tiktoken unavailable
493
+ */
494
+ async estimateOpenAITokens(text, model) {
495
+ if (!this.tiketokenAvailable) {
496
+ return Math.ceil(text.length / 4);
497
+ }
498
+ try {
499
+ const { encoding_for_model } = await import("tiktoken");
500
+ const modelMap = {
501
+ [GPT_4_TIKTOKEN_MODEL]: GPT_4_TIKTOKEN_MODEL,
502
+ [GPT_4_TURBO_MODEL]: GPT_4_TURBO_PREVIEW_TIKTOKEN_MODEL,
503
+ [GPT_4O_TIKTOKEN_MODEL]: GPT_4O_TIKTOKEN_MODEL,
504
+ [GPT_4O_MINI_TIKTOKEN_MODEL]: GPT_4O_MINI_TIKTOKEN_MODEL,
505
+ [GPT_3_5_TURBO_TIKTOKEN_MODEL]: GPT_3_5_TURBO_TIKTOKEN_MODEL,
506
+ [GPT_3_5_TIKTOKEN_ALIAS]: GPT_3_5_TURBO_TIKTOKEN_MODEL,
507
+ [GPT_5_CODEX_MINI_TIKTOKEN_ALIAS]: GPT_4_TIKTOKEN_MODEL,
508
+ // Use GPT-4 tokenizer for Codex
509
+ [GPT_5_TIKTOKEN_ALIAS]: GPT_4_TIKTOKEN_MODEL,
510
+ codex: GPT_4_TIKTOKEN_MODEL
511
+ };
512
+ const tiketokenModel = modelMap[model] || GPT_4_TIKTOKEN_MODEL;
513
+ const cacheKey = tiketokenModel;
514
+ let encoding = this.encodingCache.get(cacheKey);
515
+ if (!encoding) {
516
+ encoding = encoding_for_model(
517
+ tiketokenModel
518
+ );
519
+ this.encodingCache.set(cacheKey, encoding);
520
+ }
521
+ const tokens = encoding.encode(text);
522
+ return tokens.length;
523
+ } catch {
524
+ return Math.ceil(text.length / 4);
525
+ }
526
+ }
527
+ /**
528
+ * Estimate tokens for Claude models using tiktoken cl100k_base.
529
+ * Falls back to chars/3.5 if tiktoken is unavailable.
530
+ */
531
+ async estimateClaudeTokens(text, _model) {
532
+ if (!this.tiketokenAvailable) {
533
+ return Math.ceil(text.length / 3.5);
534
+ }
535
+ try {
536
+ const { get_encoding } = await import("tiktoken");
537
+ const cacheKey = "cl100k_base";
538
+ let encoding = this.encodingCache.get(cacheKey);
539
+ if (!encoding) {
540
+ encoding = get_encoding("cl100k_base");
541
+ this.encodingCache.set(cacheKey, encoding);
542
+ }
543
+ const tokens = encoding.encode(text);
544
+ return tokens.length;
545
+ } catch {
546
+ return Math.ceil(text.length / 3.5);
547
+ }
548
+ }
549
+ /**
550
+ * Get confidence score for a provider and model
551
+ *
552
+ * @param provider - Provider ID
553
+ * @param model - Model identifier
554
+ * @returns Confidence score (0.0-1.0)
555
+ */
556
+ getModelConfidence(provider, model) {
557
+ const resolverConfidence = lookupProviderEntry(MODEL_CONFIDENCE_RESOLVERS, provider)?.(
558
+ provider,
559
+ model
560
+ );
561
+ if (resolverConfidence !== void 0) {
562
+ return resolverConfidence;
563
+ }
564
+ return getProviderRegistryModelConfidence(provider, model) ?? 0.3;
565
+ }
566
+ /**
567
+ * Get context window size for provider and model
568
+ *
569
+ * @param provider - Provider ID
570
+ * @param model - Model identifier
571
+ * @returns Context window in tokens
572
+ */
573
+ getContextWindow(provider, model) {
574
+ const resolvedWindow = lookupProviderEntry(CONTEXT_WINDOW_RESOLVERS, provider)?.(model);
575
+ if (resolvedWindow !== void 0) {
576
+ return resolvedWindow;
577
+ }
578
+ try {
579
+ return ModelMetadataResolver.getContextWindow(provider, model, {
580
+ allowFallback: true,
581
+ allowFuzzy: true
582
+ }) ?? DEFAULT_CONTEXT_WINDOW;
583
+ } catch {
584
+ return DEFAULT_CONTEXT_WINDOW;
585
+ }
586
+ }
587
+ /**
588
+ * Get recommended threshold in tokens (65% of context window)
589
+ *
590
+ * @param provider - Provider ID
591
+ * @param model - Model identifier
592
+ * @returns Recommended threshold in tokens
593
+ */
594
+ getRecommendedThreshold(provider, model) {
595
+ const contextWindow = this.getContextWindow(provider, model);
596
+ return Math.floor(contextWindow * DEFAULT_THRESHOLD_PERCENT);
597
+ }
598
+ /**
599
+ * Calculate context overhead for a provider and model
600
+ *
601
+ * Conservative overhead estimates:
602
+ * - Claude: 1500 tokens system, 30% output reserve
603
+ * - Codex/OpenAI: 800 tokens system, 25% output reserve
604
+ * - Gemini: 1000 tokens system, 25% output reserve
605
+ * - Other providers: 1000 tokens system, 25% output reserve
606
+ * - Always add 5% safety margin
607
+ *
608
+ * @param provider - Provider ID
609
+ * @param model - Model identifier
610
+ * @returns Token overhead breakdown
611
+ */
612
+ getContextOverhead(provider, model) {
613
+ const contextWindow = this.getContextWindow(provider, model);
614
+ const profile = lookupProviderEntry(PROVIDER_OVERHEAD_PROFILES, provider) || {
615
+ systemPromptTokens: 1e3,
616
+ outputReservePercent: 0.25
617
+ };
618
+ const systemPrompt = profile.systemPromptTokens;
619
+ const outputReservePercent = profile.outputReservePercent;
620
+ const outputReserve = Math.floor(contextWindow * outputReservePercent);
621
+ const safetyMargin = Math.floor(contextWindow * 0.05);
622
+ const total = systemPrompt + outputReserve + safetyMargin;
623
+ return {
624
+ systemPrompt,
625
+ outputReserve,
626
+ safetyMargin,
627
+ total
628
+ };
629
+ }
630
+ /**
631
+ * Select best split strategy based on task content and token count
632
+ *
633
+ * @param prompt - Task description
634
+ * @param estimatedTokens - Estimated token count
635
+ * @param threshold - Recommended threshold
636
+ * @returns Recommended split strategy
637
+ */
638
+ selectSplitStrategy(prompt, estimatedTokens, threshold) {
639
+ const sampleText = prompt.length > 5e3 ? prompt.slice(0, 5e3) : prompt;
640
+ const fileMatches = sampleText.match(
641
+ /([a-zA-Z0-9_\-/]+\.(?:ts|js|tsx|jsx|py|java|go|rs|cpp|c|h))\b/g
642
+ );
643
+ if (fileMatches && fileMatches.length > 1) {
644
+ return "file-based";
645
+ }
646
+ const firstLine = sampleText.split("\n")[0] || "";
647
+ const colonPattern = /^(.+?):\s*(.+)$/;
648
+ const colonMatch = firstLine.match(colonPattern);
649
+ if (colonMatch) {
650
+ const featureList = colonMatch[2] || "";
651
+ const features = featureList.split(/\s*,\s*|\s+and\s+/i);
652
+ if (features.length > 1) {
653
+ return "feature-based";
654
+ }
655
+ }
656
+ if (estimatedTokens > threshold * 2) {
657
+ return "sequential";
658
+ }
659
+ return "semantic";
660
+ }
661
+ /**
662
+ * Free cached encodings
663
+ */
664
+ cleanup() {
665
+ for (const encoding of this.encodingCache.values()) {
666
+ try {
667
+ encoding.free?.();
668
+ } catch {
669
+ }
670
+ }
671
+ this.encodingCache.clear();
672
+ }
673
+ };
674
+ function stripBuildInjectedContext(prompt) {
675
+ if (!prompt) return "";
676
+ let text = stripBuildContextBlocks(String(prompt));
677
+ const toolPolicySplit = text.split(/\n---\n\n/);
678
+ if (toolPolicySplit.length > 1 && /##\s*Tool Usage Policy/i.test(toolPolicySplit[0] ?? "")) {
679
+ text = toolPolicySplit.slice(1).join("\n---\n\n");
680
+ }
681
+ text = text.replace(/(^|\n)\s*FILE TARGETS:\s*\n(?:\s*[-•]\s+[^\n]+\n?)+/gi, "\n");
682
+ return text;
683
+ }
684
+
685
+ // src/utils/sync-stdout-io.ts
686
+ import { execFileSync } from "child_process";
687
+ import { accessSync, constants as fsConstants, fstatSync, statSync, writeSync } from "fs";
688
+ var MAX_CONSECUTIVE_ZERO_BYTE_WRITES = 1e3;
689
+ var TRUSTED_LSOF_PATHS = ["/usr/sbin/lsof", "/usr/bin/lsof"];
690
+ function recordSyncWriteNoProgress(functionName, attempts) {
691
+ const nextAttempts = attempts + 1;
692
+ if (nextAttempts > MAX_CONSECUTIVE_ZERO_BYTE_WRITES) {
693
+ throw new Error(`${functionName} made no progress (repeated 0-byte writes or EAGAIN)`);
694
+ }
695
+ return nextAttempts;
696
+ }
697
+ function writeAllSyncToFd(fd, text, functionName = "writeAllSyncToFd") {
698
+ const buffer = Buffer.from(text, "utf8");
699
+ let offset = 0;
700
+ let noProgressAttempts = 0;
701
+ while (offset < buffer.length) {
702
+ try {
703
+ const written = writeSync(fd, buffer, offset, buffer.length - offset);
704
+ offset += written;
705
+ if (written === 0) {
706
+ noProgressAttempts = recordSyncWriteNoProgress(functionName, noProgressAttempts);
707
+ } else {
708
+ noProgressAttempts = 0;
709
+ }
710
+ } catch (error) {
711
+ if (error.code === "EAGAIN") {
712
+ noProgressAttempts = recordSyncWriteNoProgress(functionName, noProgressAttempts);
713
+ continue;
714
+ }
715
+ throw error;
716
+ }
717
+ }
718
+ }
719
+ function resolveTrustedLsofPath() {
720
+ for (const candidate of TRUSTED_LSOF_PATHS) {
721
+ try {
722
+ if (!statSync(candidate).isFile()) continue;
723
+ accessSync(candidate, fsConstants.X_OK);
724
+ return candidate;
725
+ } catch {
726
+ }
727
+ }
728
+ return void 0;
729
+ }
730
+ function resolveStdoutRegularFilePath() {
731
+ try {
732
+ const stat4 = fstatSync(1);
733
+ if (!stat4.isFile()) return void 0;
734
+ } catch {
735
+ return void 0;
736
+ }
737
+ const lsofPath = resolveTrustedLsofPath();
738
+ if (!lsofPath) return void 0;
739
+ try {
740
+ const output = execFileSync(lsofPath, ["-p", String(process.pid), "-a", "-d", "1", "-Fn"], {
741
+ encoding: "utf8",
742
+ stdio: ["ignore", "pipe", "ignore"],
743
+ timeout: 1e3
744
+ });
745
+ const rawPath = output.split("\n").find((line) => line.startsWith("n/"))?.slice(1).replace(/ \(deleted\)$/, "");
746
+ if (!rawPath) return void 0;
747
+ const pathStat = statSync(rawPath);
748
+ return pathStat.isFile() ? rawPath : void 0;
749
+ } catch {
750
+ return void 0;
751
+ }
752
+ }
753
+
754
+ // src/automation/dist-entry-integrity.ts
755
+ import * as fs from "fs/promises";
756
+ import * as path from "path";
757
+ function collectReferencedDistChunks(entrySource) {
758
+ const matches = entrySource.matchAll(
759
+ /(?:from\s+|import\()\s*['"]\.\/([^'"]+\.js(?:\.map)?)['"]/g
760
+ );
761
+ const chunks = /* @__PURE__ */ new Set();
762
+ for (const match of matches) {
763
+ const chunk = match[1];
764
+ if (!chunk || chunk === "entry.js") continue;
765
+ chunks.add(chunk);
766
+ }
767
+ return [...chunks];
768
+ }
769
+ async function readDistEntryIntegrity(projectRoot) {
770
+ let entrySource;
771
+ try {
772
+ entrySource = await fs.readFile(path.join(projectRoot, "dist", "entry.js"), "utf8");
773
+ } catch (error) {
774
+ return { readable: false, reason: error instanceof Error ? error.message : String(error) };
775
+ }
776
+ const missingChunks = [];
777
+ for (const relativeChunk of collectReferencedDistChunks(entrySource)) {
778
+ try {
779
+ await fs.stat(path.join(projectRoot, "dist", relativeChunk));
780
+ } catch {
781
+ missingChunks.push(relativeChunk);
782
+ }
783
+ }
784
+ return { readable: true, missingChunks };
785
+ }
786
+
787
+ // src/automation/flywheel-run-cleanup.ts
788
+ import * as fs2 from "fs";
789
+ import { stat as stat2 } from "fs/promises";
790
+ import * as path3 from "path";
791
+ import { execa } from "execa";
792
+
793
+ // src/core/extensions/paths.ts
794
+ import { mkdirSync } from "fs";
795
+ import os from "os";
796
+ import * as path2 from "path";
797
+ function getDefaultExtensionsRootDir() {
798
+ return path2.join(os.homedir(), ".build", "extensions");
799
+ }
800
+ function getExtensionsRootDir() {
801
+ return process.env.BUILD_EXTENSIONS_DIR?.trim() || getDefaultExtensionsRootDir();
802
+ }
803
+ function getExtensionDirs(extensionId) {
804
+ const safeId = assertSafePathComponent(extensionId, "extensionId");
805
+ const rootDir = path2.join(getExtensionsRootDir(), safeId);
806
+ return {
807
+ id: safeId,
808
+ rootDir,
809
+ packageDir: path2.join(rootDir, "package"),
810
+ configDir: path2.join(rootDir, "config"),
811
+ dataDir: path2.join(rootDir, "data"),
812
+ cacheDir: path2.join(rootDir, "cache"),
813
+ logsDir: path2.join(rootDir, "logs"),
814
+ stateDir: path2.join(rootDir, "state")
815
+ };
816
+ }
817
+ function getBuildRepoWorktreePoolRoot2() {
818
+ return getBuildRepoWorktreePoolRoot();
819
+ }
820
+ function getBuildFlywheelWorkflowsRoot(projectRoot) {
821
+ return getBuildFlywheelManifestsRoot(projectRoot);
822
+ }
823
+
824
+ // src/automation/flywheel-run-cleanup.ts
825
+ async function runAsyncWithOptionalTimeout(fn, timeoutMs, label) {
826
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
827
+ return fn();
828
+ }
829
+ let timeoutId;
830
+ const timeoutPromise = new Promise((_, reject) => {
831
+ timeoutId = setTimeout(() => {
832
+ reject(new Error(`${label} timed out after ${timeoutMs}ms`));
833
+ }, timeoutMs);
834
+ });
835
+ try {
836
+ return await Promise.race([fn(), timeoutPromise]);
837
+ } finally {
838
+ if (timeoutId !== void 0) {
839
+ clearTimeout(timeoutId);
840
+ }
841
+ }
842
+ }
843
+ function resolvePrimaryWorktreePoolRoot() {
844
+ return path3.resolve(getBuildRepoWorktreePoolRoot2());
845
+ }
846
+ function resolveWorktreePoolRootViaFlywheelLayout() {
847
+ return path3.resolve(path3.join(getBuildFlywheelWorkflowsRoot(), "..", "worktrees"));
848
+ }
849
+ function getWorktreePoolPathRootsForHygiene() {
850
+ const primary = resolvePrimaryWorktreePoolRoot();
851
+ const viaFlywheel = resolveWorktreePoolRootViaFlywheelLayout();
852
+ return primary === viaFlywheel ? [primary] : [primary, viaFlywheel];
853
+ }
854
+ function isPathUnderWorktreePoolRoots(candidatePath, roots) {
855
+ const abs = path3.resolve(candidatePath.trim());
856
+ return roots.some((root) => abs === root || abs.startsWith(`${root}${path3.sep}`));
857
+ }
858
+ function getWorktreeCleanupProtectedPaths(extraPaths = []) {
859
+ return Array.from(
860
+ new Set(
861
+ [
862
+ getProjectRoot(),
863
+ getWorktreeBaseDir(),
864
+ ...getWorktreePoolPathRootsForHygiene(),
865
+ ...extraPaths
866
+ ].map((protectedPath) => path3.resolve(protectedPath))
867
+ )
868
+ );
869
+ }
870
+ function getWorktreeCleanupManagedRoots(extraRoots = []) {
871
+ return Array.from(
872
+ new Set(
873
+ [getWorktreeBaseDir(), ...getWorktreePoolPathRootsForHygiene(), ...extraRoots].map(
874
+ (managedRoot) => path3.resolve(managedRoot)
875
+ )
876
+ )
877
+ );
878
+ }
879
+ async function removeWorktreeDirectoryWithGuard(worktreePath, protectedPaths, managedRoots) {
880
+ const result = await safeRmDir(worktreePath, {
881
+ protectedPaths: Array.from(protectedPaths),
882
+ managedRoots: Array.from(managedRoots)
883
+ });
884
+ if (!result.deleted) {
885
+ throw new Error(result.reason ?? `Refusing to delete unsafe worktree path ${worktreePath}`);
886
+ }
887
+ }
888
+ async function roughDirectoryFootprintBytes(worktreePath) {
889
+ try {
890
+ const st = await stat2(worktreePath);
891
+ const blocks = typeof st.blocks === "number" && st.blocks > 0 ? st.blocks * 512 : 0;
892
+ return st.size + blocks;
893
+ } catch {
894
+ return 0;
895
+ }
896
+ }
897
+ function normalizeGitErrorText(error) {
898
+ const message = error instanceof Error ? error.message : String(error);
899
+ const stderr = error?.stderr ?? "";
900
+ return `${message}
901
+ ${stderr}`;
902
+ }
903
+ function isWorktreeMetadataGone(error, worktreePath) {
904
+ const text = normalizeGitErrorText(error);
905
+ return !fs2.existsSync(worktreePath) || /cwd option is invalid/i.test(text) || /no such file or directory/i.test(text) || /not a git repository/i.test(text) || /this operation must be run in a work tree/i.test(text) || /not a working tree/i.test(text) || /bad default revision/i.test(text) || /unknown revision or path/i.test(text) || /ambiguous argument.*HEAD/i.test(text);
906
+ }
907
+ function isNotAGitRepositoryError(error) {
908
+ const text = normalizeGitErrorText(error);
909
+ return /not a git repository/i.test(text) || /not a git repo/i.test(text) || /this operation must be run in a work tree/i.test(text);
910
+ }
911
+ function isBenignGitBranchDeleteError(error) {
912
+ const text = normalizeGitErrorText(error);
913
+ return /error: branch ['"][^'"]+['"] not found/i.test(text) || /couldn't find local branch/i.test(text) || /has already been deleted/i.test(text);
914
+ }
915
+ function getCleanupWorktreeManager(gitCwd) {
916
+ return getWorktreeManager({
917
+ baseDir: getWorktreeBaseDir(),
918
+ gitCwd,
919
+ enablePooling: false
920
+ });
921
+ }
922
+ function isManagedCleanupWorktreePath(worktreePath, gitCwd) {
923
+ const repoPools = [
924
+ path3.resolve(getWorktreeBaseDir(gitCwd)),
925
+ path3.resolve(gitCwd, ".build-project", "tmp", "worktrees")
926
+ ];
927
+ return isDualContainedInManagedRoots(
928
+ worktreePath.trim(),
929
+ getWorktreeCleanupManagedRoots(repoPools)
930
+ );
931
+ }
932
+ async function safeUpdateWorktreeRecordStatus(workflowId, worktreePath, status, retentionReason, errors) {
933
+ try {
934
+ await updateWorktreeRecordStatus(workflowId, worktreePath, status, retentionReason);
935
+ return true;
936
+ } catch (error) {
937
+ errors.push({
938
+ path: worktreePath,
939
+ error: error instanceof Error ? error.message : String(error)
940
+ });
941
+ return false;
942
+ }
943
+ }
944
+ async function listGitWorktrees(repoCwd = getProjectRoot()) {
945
+ let stdout;
946
+ try {
947
+ ({ stdout } = await execa("git", ["worktree", "list", "--porcelain"], {
948
+ cwd: repoCwd,
949
+ stdio: "pipe",
950
+ timeout: 1e4
951
+ }));
952
+ } catch (error) {
953
+ if (isNotAGitRepositoryError(error)) {
954
+ return [];
955
+ }
956
+ throw error;
957
+ }
958
+ const entries = [];
959
+ let current;
960
+ for (const rawLine of stdout.split("\n")) {
961
+ const line = rawLine.trimEnd();
962
+ if (line.startsWith("worktree ")) {
963
+ if (current) entries.push(current);
964
+ current = { path: line.slice("worktree ".length).trim() };
965
+ continue;
966
+ }
967
+ if (line.startsWith("branch ") && current) {
968
+ const ref = line.slice("branch ".length).trim();
969
+ const prefix = "refs/heads/";
970
+ if (ref.startsWith(prefix)) {
971
+ current.branch = ref.slice(prefix.length);
972
+ }
973
+ }
974
+ }
975
+ if (current) entries.push(current);
976
+ return entries;
977
+ }
978
+ async function listLocalBranches(repoCwd = getProjectRoot()) {
979
+ let stdout;
980
+ try {
981
+ ({ stdout } = await execa("git", ["branch", "--format=%(refname:short)"], {
982
+ cwd: repoCwd,
983
+ stdio: "pipe",
984
+ timeout: 1e4
985
+ }));
986
+ } catch (error) {
987
+ if (isNotAGitRepositoryError(error)) {
988
+ return [];
989
+ }
990
+ throw error;
991
+ }
992
+ return stdout.split("\n").map((b) => b.trim()).filter(Boolean);
993
+ }
994
+ function buildRelatedBranchPrefixes(taskId) {
995
+ const safeTaskId = sanitizeRefPart(taskId, { fallback: "task", maxLen: 80 });
996
+ return [
997
+ ...buildVariantBranchPrefixes(taskId),
998
+ `${buildBranchKindPrefix("lab")}${safeTaskId}-`,
999
+ `${buildBranchKindPrefix("stage")}${safeTaskId}-`,
1000
+ `build/substage/${safeTaskId}-`,
1001
+ `build/wt-${safeTaskId}-`
1002
+ ];
1003
+ }
1004
+ function matchesCleanupBranchPrefix(branch, prefix) {
1005
+ if (!branch.startsWith(prefix)) {
1006
+ return false;
1007
+ }
1008
+ if (prefix.endsWith("/") || prefix.endsWith("-")) {
1009
+ return true;
1010
+ }
1011
+ const next = branch.charAt(prefix.length);
1012
+ return next === "/" || next === "-";
1013
+ }
1014
+ async function cleanupWorkflowRelatedBranchesAndWorktrees(options) {
1015
+ const relatedTaskIds = (options.relatedTaskIds ?? []).map((t) => t.trim()).filter(Boolean);
1016
+ const relatedSafeTaskIds = relatedTaskIds.map(
1017
+ (taskId) => sanitizeRefPart(taskId, { fallback: "task", maxLen: 80 })
1018
+ );
1019
+ const trackedBranches = (options.trackedBranches ?? []).map((b) => b.trim()).filter(Boolean);
1020
+ const trackedWorktrees = (options.trackedWorktrees ?? []).map((w) => w.trim()).filter(Boolean);
1021
+ const enablePooledWorktreePathRecovery = options.enablePooledWorktreePathRecovery ?? true;
1022
+ const childTargets = runChildBranchTargets(options.integrationBranch);
1023
+ const repoCwd = getProjectRoot();
1024
+ const [branches, worktrees] = await Promise.all([
1025
+ listLocalBranches(repoCwd),
1026
+ listGitWorktrees(repoCwd)
1027
+ ]);
1028
+ const prefixes = [
1029
+ ...childTargets.prefixes,
1030
+ ...relatedTaskIds.flatMap((taskId) => buildRelatedBranchPrefixes(taskId))
1031
+ ];
1032
+ const branchesToDelete = Array.from(
1033
+ new Set(
1034
+ branches.filter(
1035
+ (branch) => trackedBranches.includes(branch) || childTargets.exact.includes(branch) || prefixes.some((prefix) => matchesCleanupBranchPrefix(branch, prefix))
1036
+ )
1037
+ )
1038
+ );
1039
+ const worktreesToRemove = Array.from(
1040
+ new Set(
1041
+ [
1042
+ ...trackedWorktrees,
1043
+ ...worktrees.filter((w) => {
1044
+ if (trackedWorktrees.includes(w.path)) return true;
1045
+ if (w.branch && branchesToDelete.includes(w.branch)) return true;
1046
+ if (!enablePooledWorktreePathRecovery) return false;
1047
+ const base = path3.basename(w.path);
1048
+ return relatedSafeTaskIds.some((taskId) => base.startsWith(`${taskId}-`));
1049
+ }).map((w) => w.path)
1050
+ ].filter(Boolean)
1051
+ )
1052
+ );
1053
+ if (options.dryRun) {
1054
+ return {
1055
+ branchesToDelete,
1056
+ worktreesToRemove,
1057
+ skippedWorktreesMissingOnDisk: [],
1058
+ skippedBranchesAlreadyAbsent: [],
1059
+ cleanupWarnings: [],
1060
+ cleanupErrors: []
1061
+ };
1062
+ }
1063
+ const skippedWorktreesMissingOnDisk = worktreesToRemove.filter((p) => !fs2.existsSync(p));
1064
+ const skippedWorktreesMissing = new Set(skippedWorktreesMissingOnDisk);
1065
+ const skippedBranchesAlreadyAbsent = [];
1066
+ const cleanupWarnings = [];
1067
+ const cleanupErrors = [];
1068
+ const activeWorktreeBranches = /* @__PURE__ */ new Set();
1069
+ const branchByWorktreePath = new Map(
1070
+ worktrees.filter(
1071
+ (worktree) => Boolean(worktree.branch)
1072
+ ).map((worktree) => [path3.resolve(worktree.path), worktree.branch])
1073
+ );
1074
+ for (const worktreePath of worktreesToRemove) {
1075
+ if (!fs2.existsSync(worktreePath)) {
1076
+ skippedWorktreesMissing.add(worktreePath);
1077
+ continue;
1078
+ }
1079
+ if (await isActiveRun(worktreePath)) {
1080
+ const resolvedWorktreePath = path3.resolve(worktreePath);
1081
+ const attachedBranch = branchByWorktreePath.get(resolvedWorktreePath);
1082
+ if (attachedBranch) {
1083
+ activeWorktreeBranches.add(attachedBranch);
1084
+ }
1085
+ cleanupWarnings.push({
1086
+ path: worktreePath,
1087
+ message: "Preserved active worktree; its owning run is still active"
1088
+ });
1089
+ continue;
1090
+ }
1091
+ if (!isManagedCleanupWorktreePath(worktreePath, repoCwd)) {
1092
+ cleanupWarnings.push({
1093
+ path: worktreePath,
1094
+ message: "Refusing to clean worktree path outside canonical Build worktree pools"
1095
+ });
1096
+ continue;
1097
+ }
1098
+ try {
1099
+ const worktreeManager = getCleanupWorktreeManager(repoCwd);
1100
+ await worktreeManager.cleanup(worktreePath, { preserve: false, gitCwd: repoCwd });
1101
+ } catch (error) {
1102
+ if (!fs2.existsSync(worktreePath)) {
1103
+ skippedWorktreesMissing.add(worktreePath);
1104
+ continue;
1105
+ }
1106
+ cleanupWarnings.push({
1107
+ path: worktreePath,
1108
+ message: normalizeGitErrorText(error)
1109
+ });
1110
+ }
1111
+ }
1112
+ const branchesLive = await listLocalBranches(repoCwd);
1113
+ const liveBranchSet = new Set(branchesLive);
1114
+ for (const branch of branchesToDelete) {
1115
+ if (activeWorktreeBranches.has(branch)) {
1116
+ cleanupWarnings.push({
1117
+ path: branch,
1118
+ message: "Preserved branch attached to an active worktree"
1119
+ });
1120
+ continue;
1121
+ }
1122
+ if (!liveBranchSet.has(branch)) {
1123
+ skippedBranchesAlreadyAbsent.push(branch);
1124
+ continue;
1125
+ }
1126
+ const branchRole = classifyBranchRole(branch);
1127
+ if (!isBuildOwnedBranch(branch) || isProtectedPipelineBranch(branch) || branchRole !== "generated-artifact") {
1128
+ cleanupWarnings.push({
1129
+ path: branch,
1130
+ message: `Refusing to delete non-generated or protected branch ${branch}`
1131
+ });
1132
+ continue;
1133
+ }
1134
+ try {
1135
+ await execa("git", ["branch", "-D", branch], {
1136
+ cwd: repoCwd,
1137
+ stdio: "pipe",
1138
+ timeout: 1e4
1139
+ });
1140
+ } catch (error) {
1141
+ if (isBenignGitBranchDeleteError(error)) {
1142
+ skippedBranchesAlreadyAbsent.push(branch);
1143
+ } else {
1144
+ cleanupErrors.push({
1145
+ path: branch,
1146
+ error: normalizeGitErrorText(error)
1147
+ });
1148
+ }
1149
+ }
1150
+ }
1151
+ if (worktreesToRemove.length > 0) {
1152
+ await pruneGitWorktreeEntries({ cwd: repoCwd, timeoutMs: 1e4, paths: worktreesToRemove });
1153
+ }
1154
+ return {
1155
+ branchesToDelete,
1156
+ worktreesToRemove,
1157
+ skippedWorktreesMissingOnDisk: Array.from(skippedWorktreesMissing),
1158
+ skippedBranchesAlreadyAbsent,
1159
+ cleanupWarnings,
1160
+ cleanupErrors
1161
+ };
1162
+ }
1163
+ async function recoverPendingWorktrees(options = {}) {
1164
+ const {
1165
+ dryRun = false,
1166
+ verbose = false,
1167
+ includeCleanupFailed = true,
1168
+ maxManifests = WORKTREE_HYGIENE_MAX_MANIFESTS_PER_PASS,
1169
+ maxRecords = WORKTREE_HYGIENE_MAX_RECOVERY_RECORDS_PER_PASS,
1170
+ restrictToBuildWorktreePool = false
1171
+ } = options;
1172
+ const manifestCap = Math.max(1, maxManifests);
1173
+ const recordCap = Math.max(1, maxRecords);
1174
+ const result = {
1175
+ recovered: [],
1176
+ skippedActive: [],
1177
+ skippedDirty: [],
1178
+ alreadyGone: [],
1179
+ errors: []
1180
+ };
1181
+ const protectedPaths = getWorktreeCleanupProtectedPaths();
1182
+ const managedRoots = getWorktreeCleanupManagedRoots();
1183
+ const runRecovery = async () => {
1184
+ let manifests;
1185
+ try {
1186
+ manifests = await listManifests();
1187
+ } catch (error) {
1188
+ result.errors.push({
1189
+ path: "<manifests>",
1190
+ error: error instanceof Error ? error.message : String(error)
1191
+ });
1192
+ return result;
1193
+ }
1194
+ const manifestsSlice = manifests.slice(0, manifestCap);
1195
+ if (manifestsSlice.length < manifests.length) {
1196
+ result.hygieneCapReached = true;
1197
+ }
1198
+ const poolRoots = getWorktreePoolPathRootsForHygiene();
1199
+ let recordsExamined = 0;
1200
+ const seenRecoveryKeys = /* @__PURE__ */ new Set();
1201
+ for (let mi = 0; mi < manifestsSlice.length; mi++) {
1202
+ const manifest = manifestsSlice[mi];
1203
+ const reconciledManifest = dryRun ? manifest : await reconcileAndSaveManifest(manifest.workflowId).catch(() => manifest);
1204
+ const statuses = ["pending"];
1205
+ if (includeCleanupFailed) {
1206
+ statuses.push("cleanup_failed");
1207
+ }
1208
+ const pending = statuses.flatMap(
1209
+ (status) => getWorktreeRecordsByStatus(reconciledManifest, status)
1210
+ );
1211
+ for (const record of pending) {
1212
+ const recoveryKey = `${reconciledManifest.workflowId}::${record.worktreePath}`;
1213
+ if (seenRecoveryKeys.has(recoveryKey)) {
1214
+ continue;
1215
+ }
1216
+ seenRecoveryKeys.add(recoveryKey);
1217
+ if (recordsExamined >= recordCap) {
1218
+ result.hygieneCapReached = true;
1219
+ break;
1220
+ }
1221
+ recordsExamined += 1;
1222
+ const worktreePath = record.worktreePath;
1223
+ try {
1224
+ if (restrictToBuildWorktreePool && !isPathUnderWorktreePoolRoots(worktreePath, poolRoots)) {
1225
+ if (verbose) {
1226
+ console.log(`[recover] Skipping worktree outside pool roots: ${worktreePath}`);
1227
+ }
1228
+ continue;
1229
+ }
1230
+ const exists = fs2.existsSync(worktreePath);
1231
+ if (!exists) {
1232
+ if (verbose) {
1233
+ console.log(`[recover] Worktree already gone: ${worktreePath}`);
1234
+ }
1235
+ if (!dryRun) {
1236
+ const ok = await safeUpdateWorktreeRecordStatus(
1237
+ reconciledManifest.workflowId,
1238
+ worktreePath,
1239
+ "cleaned",
1240
+ WORKTREE_RECORD_RECOVERY_REASON.PATH_ALREADY_ABSENT,
1241
+ result.errors
1242
+ );
1243
+ if (!ok) {
1244
+ continue;
1245
+ }
1246
+ }
1247
+ result.alreadyGone.push(worktreePath);
1248
+ continue;
1249
+ }
1250
+ if (await isActiveRun(worktreePath)) {
1251
+ if (verbose) {
1252
+ console.log(`[recover] Preserving active worktree: ${worktreePath}`);
1253
+ }
1254
+ result.skippedActive.push(worktreePath);
1255
+ continue;
1256
+ }
1257
+ let dirty = false;
1258
+ try {
1259
+ const { stdout } = await execa("git", ["status", "--porcelain"], {
1260
+ cwd: worktreePath,
1261
+ stdio: "pipe",
1262
+ timeout: GIT_COMMAND_TIMEOUT_MS
1263
+ });
1264
+ dirty = filterWorktreeRunSentinelStatusOutput(stdout).trim().length > 0;
1265
+ } catch (error) {
1266
+ const text = normalizeGitErrorText(error);
1267
+ const disappearedDuringStatus = isWorktreeMetadataGone(error, worktreePath);
1268
+ if (disappearedDuringStatus) {
1269
+ if (verbose) {
1270
+ console.log(
1271
+ `[recover] Worktree path missing or not a git repository, marking cleaned: ${worktreePath}`
1272
+ );
1273
+ }
1274
+ if (!dryRun) {
1275
+ const ok = await safeUpdateWorktreeRecordStatus(
1276
+ reconciledManifest.workflowId,
1277
+ worktreePath,
1278
+ "cleaned",
1279
+ WORKTREE_RECORD_RECOVERY_REASON.PATH_INVALID_DURING_STATUS_PROBE,
1280
+ result.errors
1281
+ );
1282
+ if (!ok) {
1283
+ continue;
1284
+ }
1285
+ }
1286
+ result.alreadyGone.push(worktreePath);
1287
+ } else {
1288
+ result.errors.push({
1289
+ path: worktreePath,
1290
+ error: text
1291
+ });
1292
+ }
1293
+ continue;
1294
+ }
1295
+ if (dirty) {
1296
+ if (verbose) {
1297
+ console.log(`[recover] Skipping dirty worktree: ${worktreePath}`);
1298
+ }
1299
+ result.skippedDirty.push(worktreePath);
1300
+ continue;
1301
+ }
1302
+ if (verbose) {
1303
+ console.log(`[recover] Recovering clean worktree: ${worktreePath}`);
1304
+ }
1305
+ if (!dryRun) {
1306
+ const gitRemoval = await tryRemoveWorktreeNestedAware(worktreePath, {
1307
+ force: true,
1308
+ repoCwd: getProjectRoot()
1309
+ });
1310
+ if (gitRemoval.refusal) {
1311
+ result.errors.push({ path: worktreePath, error: gitRemoval.refusal });
1312
+ continue;
1313
+ }
1314
+ if (fs2.existsSync(worktreePath)) {
1315
+ try {
1316
+ await removeWorktreeDirectoryWithGuard(worktreePath, protectedPaths, managedRoots);
1317
+ } catch (rmErr) {
1318
+ result.errors.push({
1319
+ path: worktreePath,
1320
+ error: normalizeGitErrorText(rmErr)
1321
+ });
1322
+ continue;
1323
+ }
1324
+ }
1325
+ if (fs2.existsSync(worktreePath)) {
1326
+ result.errors.push({
1327
+ path: worktreePath,
1328
+ error: "Recovery could not remove worktree directory; leaving manifest unchanged"
1329
+ });
1330
+ continue;
1331
+ }
1332
+ const updated = await safeUpdateWorktreeRecordStatus(
1333
+ reconciledManifest.workflowId,
1334
+ worktreePath,
1335
+ "cleaned",
1336
+ null,
1337
+ result.errors
1338
+ );
1339
+ if (!updated) {
1340
+ continue;
1341
+ }
1342
+ }
1343
+ result.recovered.push(worktreePath);
1344
+ } catch (error) {
1345
+ result.errors.push({
1346
+ path: worktreePath,
1347
+ error: error instanceof Error ? error.message : String(error)
1348
+ });
1349
+ }
1350
+ }
1351
+ if (recordsExamined >= recordCap) {
1352
+ if (mi < manifestsSlice.length - 1) {
1353
+ result.hygieneCapReached = true;
1354
+ }
1355
+ break;
1356
+ }
1357
+ }
1358
+ if (!dryRun && (result.recovered.length > 0 || result.alreadyGone.length > 0)) {
1359
+ await pruneGitWorktreeEntries({
1360
+ cwd: getProjectRoot(),
1361
+ paths: [...result.recovered, ...result.alreadyGone]
1362
+ });
1363
+ }
1364
+ return result;
1365
+ };
1366
+ if (dryRun) {
1367
+ return runRecovery();
1368
+ }
1369
+ return withCleanupLock(getWorktreeBaseDir(), runRecovery);
1370
+ }
1371
+ async function enforceRetentionBounds(options = {}) {
1372
+ const {
1373
+ maxAgeMs = 7 * 24 * 60 * 60 * 1e3,
1374
+ maxCount = 20,
1375
+ maxManifests = WORKTREE_HYGIENE_MAX_MANIFESTS_PER_PASS,
1376
+ maxGitStatusProbes = WORKTREE_HYGIENE_MAX_RETENTION_PRUNE_OPS,
1377
+ dryRun = false,
1378
+ verbose = false
1379
+ } = options;
1380
+ const diskCapResolved = options.maxRetainedTotalDiskBytes !== void 0 ? options.maxRetainedTotalDiskBytes : WORKTREE_HYGIENE_MAX_RETAINED_TOTAL_BYTES;
1381
+ const manifestCap = Math.max(1, maxManifests);
1382
+ const probeCap = Math.max(1, maxGitStatusProbes);
1383
+ const result = {
1384
+ pruned: [],
1385
+ preserved: [],
1386
+ errors: []
1387
+ };
1388
+ const protectedPaths = getWorktreeCleanupProtectedPaths();
1389
+ const managedRoots = getWorktreeCleanupManagedRoots();
1390
+ const runRetention = async () => {
1391
+ let manifests;
1392
+ try {
1393
+ manifests = await listManifests();
1394
+ } catch (error) {
1395
+ result.errors.push({
1396
+ path: "<manifests>",
1397
+ error: error instanceof Error ? error.message : String(error)
1398
+ });
1399
+ return result;
1400
+ }
1401
+ const manifestsSlice = manifests.slice(0, manifestCap);
1402
+ if (manifestsSlice.length < manifests.length) {
1403
+ result.hygieneCapReached = true;
1404
+ }
1405
+ const retainedEntries = [];
1406
+ const retainedByWorkflowPath = /* @__PURE__ */ new Map();
1407
+ for (const manifest of manifestsSlice) {
1408
+ const retained = getWorktreeRecordsByStatus(manifest, "retained");
1409
+ for (const record of retained) {
1410
+ if (isProviderFailureKeptRecord(record)) continue;
1411
+ const key = `${manifest.workflowId}::${record.worktreePath}`;
1412
+ const next = {
1413
+ workflowId: manifest.workflowId,
1414
+ worktreePath: record.worktreePath,
1415
+ updatedAt: record.updatedAt
1416
+ };
1417
+ const existing = retainedByWorkflowPath.get(key);
1418
+ if (!existing || new Date(next.updatedAt).getTime() > new Date(existing.updatedAt).getTime()) {
1419
+ retainedByWorkflowPath.set(key, next);
1420
+ }
1421
+ }
1422
+ }
1423
+ retainedEntries.push(...retainedByWorkflowPath.values());
1424
+ retainedEntries.sort(
1425
+ (a, b) => new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime()
1426
+ );
1427
+ const diskCap = diskCapResolved > 0 ? diskCapResolved : 0;
1428
+ const footprintByPath = /* @__PURE__ */ new Map();
1429
+ let aggregateRetainedBytes = 0;
1430
+ let diskFootprintTruncated = false;
1431
+ if (diskCap > 0) {
1432
+ const maxStat = WORKTREE_HYGIENE_MAX_RETENTION_COLLATION;
1433
+ for (const [si, entry] of retainedEntries.entries()) {
1434
+ if (si >= maxStat) {
1435
+ diskFootprintTruncated = true;
1436
+ result.hygieneCapReached = true;
1437
+ break;
1438
+ }
1439
+ const p = entry.worktreePath;
1440
+ if (!fs2.existsSync(p)) {
1441
+ continue;
1442
+ }
1443
+ const bytes = await roughDirectoryFootprintBytes(p);
1444
+ footprintByPath.set(p, bytes);
1445
+ aggregateRetainedBytes += bytes;
1446
+ }
1447
+ }
1448
+ const now = Date.now();
1449
+ let remainingCountExcess = Math.max(0, retainedEntries.length - maxCount);
1450
+ let gitStatusProbes = 0;
1451
+ const consumeFootprintForPath = (wp) => {
1452
+ if (diskCap <= 0 || diskFootprintTruncated) {
1453
+ return;
1454
+ }
1455
+ const used = footprintByPath.get(wp);
1456
+ if (used !== void 0) {
1457
+ aggregateRetainedBytes -= used;
1458
+ footprintByPath.delete(wp);
1459
+ }
1460
+ };
1461
+ for (let i = 0; i < retainedEntries.length; i++) {
1462
+ const entry = retainedEntries[i];
1463
+ if (!entry) continue;
1464
+ const worktreePath = entry.worktreePath;
1465
+ const ageMs = now - new Date(entry.updatedAt).getTime();
1466
+ const isOverAge = ageMs > maxAgeMs;
1467
+ const isOverCount = remainingCountExcess > 0;
1468
+ const isOverDisk = diskCap > 0 && !diskFootprintTruncated && aggregateRetainedBytes > diskCap;
1469
+ if (!isOverAge && !isOverCount && !isOverDisk) {
1470
+ result.preserved.push(worktreePath);
1471
+ continue;
1472
+ }
1473
+ if (await isActiveRun(worktreePath)) {
1474
+ if (verbose) {
1475
+ console.log(`[retention] Preserving active retained worktree: ${worktreePath}`);
1476
+ }
1477
+ result.preserved.push(worktreePath);
1478
+ continue;
1479
+ }
1480
+ let dirty = false;
1481
+ if (fs2.existsSync(worktreePath)) {
1482
+ if (gitStatusProbes >= probeCap) {
1483
+ result.hygieneCapReached = true;
1484
+ for (const retainedEntry of retainedEntries.slice(i)) {
1485
+ result.preserved.push(retainedEntry.worktreePath);
1486
+ }
1487
+ break;
1488
+ }
1489
+ gitStatusProbes += 1;
1490
+ try {
1491
+ const { stdout } = await execa("git", ["status", "--porcelain"], {
1492
+ cwd: worktreePath,
1493
+ stdio: "pipe",
1494
+ timeout: GIT_COMMAND_TIMEOUT_MS
1495
+ });
1496
+ dirty = filterWorktreeRunSentinelStatusOutput(stdout).trim().length > 0;
1497
+ } catch (error) {
1498
+ const disappearedDuringStatus = isWorktreeMetadataGone(error, worktreePath);
1499
+ if (disappearedDuringStatus) {
1500
+ if (verbose) {
1501
+ console.log(
1502
+ `[retention] Worktree path missing or not a git repository, marking cleaned: ${worktreePath}`
1503
+ );
1504
+ }
1505
+ if (!dryRun) {
1506
+ try {
1507
+ await updateWorktreeRecordStatus(entry.workflowId, worktreePath, "cleaned", null);
1508
+ } catch (updateError) {
1509
+ result.errors.push({
1510
+ path: worktreePath,
1511
+ error: updateError instanceof Error ? updateError.message : String(updateError)
1512
+ });
1513
+ continue;
1514
+ }
1515
+ }
1516
+ if (remainingCountExcess > 0) {
1517
+ remainingCountExcess--;
1518
+ }
1519
+ consumeFootprintForPath(worktreePath);
1520
+ result.pruned.push(worktreePath);
1521
+ continue;
1522
+ }
1523
+ dirty = true;
1524
+ }
1525
+ }
1526
+ if (dirty) {
1527
+ if (verbose) {
1528
+ console.log(`[retention] Preserving dirty retained worktree: ${worktreePath}`);
1529
+ }
1530
+ result.preserved.push(worktreePath);
1531
+ continue;
1532
+ }
1533
+ if (verbose) {
1534
+ console.log(`[retention] Pruning retained worktree: ${worktreePath}`);
1535
+ }
1536
+ if (!dryRun) {
1537
+ try {
1538
+ if (fs2.existsSync(worktreePath)) {
1539
+ const gitRemoval = await tryRemoveWorktreeNestedAware(worktreePath, {
1540
+ force: true,
1541
+ repoCwd: getProjectRoot()
1542
+ });
1543
+ if (gitRemoval.refusal) {
1544
+ result.errors.push({ path: worktreePath, error: gitRemoval.refusal });
1545
+ continue;
1546
+ }
1547
+ if (!gitRemoval.removed) {
1548
+ await removeWorktreeDirectoryWithGuard(worktreePath, protectedPaths, managedRoots);
1549
+ }
1550
+ }
1551
+ await updateWorktreeRecordStatus(entry.workflowId, worktreePath, "cleaned", null);
1552
+ } catch (error) {
1553
+ result.errors.push({
1554
+ path: worktreePath,
1555
+ error: error instanceof Error ? error.message : String(error)
1556
+ });
1557
+ continue;
1558
+ }
1559
+ }
1560
+ if (remainingCountExcess > 0) {
1561
+ remainingCountExcess--;
1562
+ }
1563
+ consumeFootprintForPath(worktreePath);
1564
+ result.pruned.push(worktreePath);
1565
+ }
1566
+ if (!dryRun && result.pruned.length > 0) {
1567
+ try {
1568
+ await pruneGitWorktreeEntries({ cwd: getProjectRoot(), paths: result.pruned });
1569
+ } catch {
1570
+ }
1571
+ }
1572
+ return result;
1573
+ };
1574
+ if (dryRun) {
1575
+ return runRetention();
1576
+ }
1577
+ return withCleanupLock(getWorktreeBaseDir(), runRetention);
1578
+ }
1579
+ async function runFlywheelWorktreeHygiene(options) {
1580
+ const recoverResult = await recoverPendingWorktrees({
1581
+ dryRun: options.dryRun,
1582
+ verbose: options.verbose,
1583
+ maxManifests: options.maxManifests,
1584
+ maxRecords: options.maxRecoveryRecords,
1585
+ restrictToBuildWorktreePool: options.restrictRecoveryToBuildWorktreePool
1586
+ });
1587
+ const retentionResult = await enforceRetentionBounds({
1588
+ dryRun: options.dryRun,
1589
+ verbose: options.verbose,
1590
+ maxAgeMs: options.maxAgeMs,
1591
+ maxCount: options.maxCount,
1592
+ maxRetainedTotalDiskBytes: options.maxRetainedTotalDiskBytes,
1593
+ maxManifests: options.maxManifests,
1594
+ maxGitStatusProbes: options.maxGitStatusProbes
1595
+ });
1596
+ return {
1597
+ recoverPendingWorktrees: recoverResult,
1598
+ enforceRetentionBounds: retentionResult
1599
+ };
1600
+ }
1601
+ async function getFlywheelCleanupDryRunWorktreeSummary() {
1602
+ const { recoverPendingWorktrees: recoverPendingWorktrees2, enforceRetentionBounds: enforceRetentionBounds2 } = await runFlywheelWorktreeHygiene({
1603
+ dryRun: true,
1604
+ verbose: false
1605
+ });
1606
+ return {
1607
+ recoverPendingWorktrees: recoverPendingWorktrees2,
1608
+ enforceRetentionBounds: enforceRetentionBounds2,
1609
+ retained: enforceRetentionBounds2.preserved.length,
1610
+ pruned: enforceRetentionBounds2.pruned.length,
1611
+ skipped: recoverPendingWorktrees2.skippedActive.length + recoverPendingWorktrees2.skippedDirty.length
1612
+ };
1613
+ }
1614
+
1615
+ // src/automation/flywheel-retry-policies.ts
1616
+ var VALID_ON_FAILURE_POLICIES = [
1617
+ "abort",
1618
+ "retry-with-feedback",
1619
+ "warn",
1620
+ "skip"
1621
+ ];
1622
+ function isValidOnFailurePolicy(value) {
1623
+ return typeof value === "string" && VALID_ON_FAILURE_POLICIES.includes(value);
1624
+ }
1625
+ var DEFAULT_RETRY_CONFIG = {
1626
+ onFailure: "abort",
1627
+ maxRetries: 2,
1628
+ feedbackTemplate: void 0,
1629
+ // Use built-in template
1630
+ backoffMultiplier: 2,
1631
+ initialDelaySeconds: 5,
1632
+ maxDelaySeconds: 60
1633
+ };
1634
+ var DEFAULT_FEEDBACK_TEMPLATE = `
1635
+ ## Previous Attempt Failed
1636
+
1637
+ **Feedback attempt:** {{attempt}} of {{maxAttempts}}
1638
+ **Error:** {{error}}
1639
+ {{#if validation}}
1640
+ **Validation Failure:** {{validation}}
1641
+ {{/if}}
1642
+
1643
+ Please address the issue and try again. Focus on:
1644
+ 1. Understanding why the previous attempt failed
1645
+ 2. Fixing the specific issue mentioned above
1646
+ 3. Ensuring your output passes all validation checks
1647
+
1648
+ ---
1649
+
1650
+ ## Original Task
1651
+
1652
+ {{originalPrompt}}
1653
+ `;
1654
+ var FEEDBACK_RETRYABLE_TAXONOMY_OVERRIDES = [
1655
+ "VALIDATION_FAILED",
1656
+ "SEMANTIC_CHECK_FAILED",
1657
+ "ALLOWLIST_VIOLATION",
1658
+ "ARTIFACT_INVALID"
1659
+ ];
1660
+ var FEEDBACK_RETRYABLE_ERRORS = new Set(
1661
+ FEEDBACK_RETRYABLE_TAXONOMY_OVERRIDES
1662
+ );
1663
+ var NON_RETRYABLE_ERRORS = /* @__PURE__ */ new Set([
1664
+ "CONFIG_INVALID",
1665
+ "STAGE_NOT_FOUND",
1666
+ "REPO_DIRTY",
1667
+ "GIT_CONFLICT",
1668
+ "MODEL_NOT_FOUND"
1669
+ ]);
1670
+ function resolveRetryAuthority(config = {}) {
1671
+ const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
1672
+ return {
1673
+ ...fullConfig,
1674
+ // maxRetries is additional feedback retries after the first eligible attempt.
1675
+ maxAttempts: fullConfig.maxRetries + 1
1676
+ };
1677
+ }
1678
+ function getFeedbackRetryEligibility(error) {
1679
+ if (NON_RETRYABLE_ERRORS.has(error.code)) {
1680
+ return {
1681
+ retryable: false,
1682
+ reason: `Error code ${error.code} is blocked from feedback retry`,
1683
+ overridesTaxonomy: false
1684
+ };
1685
+ }
1686
+ if (error.retryable) {
1687
+ return {
1688
+ retryable: true,
1689
+ reason: `Error code ${error.code} is retryable by taxonomy`,
1690
+ overridesTaxonomy: false
1691
+ };
1692
+ }
1693
+ if (FEEDBACK_RETRYABLE_ERRORS.has(error.code)) {
1694
+ return {
1695
+ retryable: true,
1696
+ reason: `Error code ${error.code} is eligible for corrective retry under retry-with-feedback despite taxonomy retryable:false`,
1697
+ overridesTaxonomy: true
1698
+ };
1699
+ }
1700
+ return {
1701
+ retryable: false,
1702
+ reason: `Error code ${error.code} is not retryable by taxonomy or feedback policy`,
1703
+ overridesTaxonomy: false
1704
+ };
1705
+ }
1706
+ function isResolvedRetryAuthority(value) {
1707
+ return typeof value === "object";
1708
+ }
1709
+ function shouldRetry(authorityOrPolicy, error, attempt, maxAttempts) {
1710
+ const authority = isResolvedRetryAuthority(authorityOrPolicy) ? authorityOrPolicy : {
1711
+ ...DEFAULT_RETRY_CONFIG,
1712
+ onFailure: authorityOrPolicy,
1713
+ maxRetries: typeof maxAttempts === "number" ? maxAttempts - 1 : DEFAULT_RETRY_CONFIG.maxRetries,
1714
+ maxAttempts: typeof maxAttempts === "number" ? maxAttempts : DEFAULT_RETRY_CONFIG.maxRetries + 1
1715
+ };
1716
+ if (authority.onFailure !== "retry-with-feedback") {
1717
+ return false;
1718
+ }
1719
+ if (attempt >= authority.maxAttempts) {
1720
+ return false;
1721
+ }
1722
+ return getFeedbackRetryEligibility(error).retryable;
1723
+ }
1724
+ function buildRetryPrompt(context, template) {
1725
+ const feedbackTemplate = template || DEFAULT_FEEDBACK_TEMPLATE;
1726
+ let validationMessage = "";
1727
+ if (context.lastValidationResult) {
1728
+ validationMessage = `[${context.lastValidationResult.type}] ${context.lastValidationResult.message}`;
1729
+ } else if (context.allValidationResults && context.allValidationResults.length > 0) {
1730
+ const failed = context.allValidationResults.filter((r) => !r.passed);
1731
+ if (failed.length > 0) {
1732
+ validationMessage = failed.map((r) => `[${r.type}] ${r.message}`).join("\n");
1733
+ }
1734
+ }
1735
+ let feedback = feedbackTemplate.replace(/\{\{error\}\}/g, context.lastError.message).replace(/\{\{errorCode\}\}/g, context.lastError.code).replace(/\{\{attempt\}\}/g, String(context.attempt)).replace(/\{\{maxAttempts\}\}/g, String(context.maxAttempts)).replace(/\{\{originalPrompt\}\}/g, context.originalPrompt);
1736
+ if (validationMessage) {
1737
+ feedback = feedback.replace(/\{\{#if validation\}\}([\s\S]*?)\{\{\/if\}\}/g, "$1");
1738
+ feedback = feedback.replace(/\{\{validation\}\}/g, validationMessage);
1739
+ } else {
1740
+ feedback = feedback.replace(/\{\{#if validation\}\}[\s\S]*?\{\{\/if\}\}/g, "");
1741
+ }
1742
+ return feedback.trim();
1743
+ }
1744
+ function calculateRetryDelay(attempt, config) {
1745
+ const initial = config?.initialDelaySeconds ?? DEFAULT_RETRY_CONFIG.initialDelaySeconds;
1746
+ const multiplier = config?.backoffMultiplier ?? DEFAULT_RETRY_CONFIG.backoffMultiplier;
1747
+ const max = config?.maxDelaySeconds ?? DEFAULT_RETRY_CONFIG.maxDelaySeconds;
1748
+ const delay = initial * multiplier ** (attempt - 1);
1749
+ const jitter = delay * 0.2 * (Math.random() * 2 - 1);
1750
+ return Math.min(delay + jitter, max);
1751
+ }
1752
+ function makeRetryDecision(context, config = {}) {
1753
+ const authority = resolveRetryAuthority(config);
1754
+ const authoritativeContext = { ...context, maxAttempts: authority.maxAttempts };
1755
+ const feedbackEligibility = getFeedbackRetryEligibility(context.lastError);
1756
+ const policy = isValidOnFailurePolicy(authority.onFailure) ? authority.onFailure : "abort";
1757
+ const willRetry = policy === "retry-with-feedback" && shouldRetry(authority, context.lastError, context.attempt);
1758
+ if (!willRetry) {
1759
+ let action;
1760
+ let reason;
1761
+ if (policy === "warn") {
1762
+ action = "warn-continue";
1763
+ reason = "Policy is 'warn': record warning and continue workflow (validation remains failed)";
1764
+ } else if (policy === "skip") {
1765
+ action = "skip-continue";
1766
+ reason = "Policy is 'skip': skip remaining stage work and continue workflow";
1767
+ } else if (policy === "retry-with-feedback") {
1768
+ action = "abort";
1769
+ if (context.attempt >= authority.maxAttempts) {
1770
+ reason = `Exceeded maximum retries (${authority.maxRetries})`;
1771
+ } else {
1772
+ reason = feedbackEligibility.reason;
1773
+ }
1774
+ } else if (!isValidOnFailurePolicy(authority.onFailure)) {
1775
+ action = "abort";
1776
+ reason = `Policy '${String(authority.onFailure)}' is not a valid onFailure value; treating as abort`;
1777
+ } else {
1778
+ action = "abort";
1779
+ reason = "Policy is 'abort': stop workflow on failure";
1780
+ }
1781
+ return {
1782
+ shouldRetry: false,
1783
+ action,
1784
+ reason,
1785
+ retryEligibility: feedbackEligibility,
1786
+ delaySeconds: 0
1787
+ };
1788
+ }
1789
+ const feedbackInjected = buildRetryPrompt(authoritativeContext, authority.feedbackTemplate);
1790
+ const modifiedPrompt = feedbackInjected;
1791
+ const delaySeconds = calculateRetryDelay(context.attempt, authority);
1792
+ return {
1793
+ shouldRetry: true,
1794
+ action: "retry",
1795
+ reason: `Retrying after ${context.lastError.code} (feedback attempt ${context.attempt + 1} of ${authority.maxAttempts}): ${feedbackEligibility.reason}`,
1796
+ retryEligibility: feedbackEligibility,
1797
+ delaySeconds,
1798
+ modifiedPrompt,
1799
+ feedbackInjected
1800
+ };
1801
+ }
1802
+
1803
+ // src/automation/flywheel-validation.ts
1804
+ function getValidProviderIds() {
1805
+ try {
1806
+ return { ids: ProviderRegistry.getAllProviderIds() };
1807
+ } catch (error) {
1808
+ return {
1809
+ error: error instanceof Error ? error.message : String(error)
1810
+ };
1811
+ }
1812
+ }
1813
+ var VALID_OUTPUT_TYPES = ["diff", "files", "text", "testResults", "json"];
1814
+ var VALIDATION_NO_SIGNALS_WARNING_DETAILS = "has validation enabled but no outputs or checks defined";
1815
+ var VALID_CHECK_TYPES = ["file-exists", "file-contains", "file-extension"];
1816
+ var VALID_PROVIDER_EXECUTION_TRANSPORTS = [
1817
+ "auto",
1818
+ "headless",
1819
+ "tmux-exec",
1820
+ "interactive"
1821
+ ];
1822
+ function isValidProviderExecutionTransport(value) {
1823
+ return VALID_PROVIDER_EXECUTION_TRANSPORTS.includes(
1824
+ value
1825
+ );
1826
+ }
1827
+ function appendSeededPlanCompletionContractErrors(parentStageId, plan, errors, pathPrefix = "") {
1828
+ const subStages = plan?.subStages;
1829
+ if (!subStages || subStages.length === 0) {
1830
+ return;
1831
+ }
1832
+ for (const sub of subStages) {
1833
+ const subId = typeof sub.id === "string" && sub.id.length > 0 ? sub.id : "(anonymous)";
1834
+ const nestedPath = pathPrefix ? `${pathPrefix}/${subId}` : subId;
1835
+ if (sub.validation?.completionContract !== void 0) {
1836
+ const contractValidation = validateCompletionContracts(sub.validation.completionContract);
1837
+ for (const contractError of contractValidation.errors) {
1838
+ errors.push(
1839
+ `Stage '${parentStageId}' seeded substage '${nestedPath}' ${contractError.field}: ${contractError.message}`
1840
+ );
1841
+ }
1842
+ }
1843
+ const nestedPlan = sub.seededPlan;
1844
+ appendSeededPlanCompletionContractErrors(parentStageId, nestedPlan, errors, nestedPath);
1845
+ }
1846
+ }
1847
+ function validateVerifyCommandStrings(commands, label, errors, options) {
1848
+ if (!Array.isArray(commands) || options.requireNonEmpty && commands.length === 0) {
1849
+ errors.push(`${label} must be a non-empty string[]`);
1850
+ return false;
1851
+ }
1852
+ for (const [idx, cmd] of commands.entries()) {
1853
+ if (typeof cmd !== "string" || cmd.trim().length === 0) {
1854
+ errors.push(`${label}[${idx}] must be a non-empty string`);
1855
+ continue;
1856
+ }
1857
+ const safety = checkVerifyCommandSafety(cmd);
1858
+ if (!safety.safe) {
1859
+ errors.push(`${label}[${idx}] is unsafe: ${safety.error}`);
1860
+ }
1861
+ }
1862
+ return true;
1863
+ }
1864
+ function validateGeneratedSubStageVerifyCommands(subStages) {
1865
+ const errors = [];
1866
+ for (const subStage of subStages) {
1867
+ const label = `Sub-stage '${subStage.id}' verify.commands`;
1868
+ if (subStage.type === "verify") {
1869
+ validateVerifyCommandStrings(subStage.verify?.commands, label, errors, {
1870
+ requireNonEmpty: true
1871
+ });
1872
+ } else if (subStage.verify?.commands !== void 0) {
1873
+ validateVerifyCommandStrings(subStage.verify.commands, label, errors, {
1874
+ requireNonEmpty: false
1875
+ });
1876
+ }
1877
+ }
1878
+ return errors;
1879
+ }
1880
+ function validateExplicitCheck(check, stageId) {
1881
+ const errors = [];
1882
+ if (!VALID_CHECK_TYPES.includes(check.type)) {
1883
+ errors.push(`Stage '${stageId}' has unknown check type: ${check.type}`);
1884
+ }
1885
+ if ("path" in check && check.path) {
1886
+ const pathResult = isValidCheckPath(check.path);
1887
+ if (!pathResult.valid) {
1888
+ errors.push(`Stage '${stageId}' check path invalid: ${pathResult.error}`);
1889
+ }
1890
+ } else if ("path" in check && !check.path) {
1891
+ errors.push(`Stage '${stageId}' check of type '${check.type}' missing required 'path' field`);
1892
+ }
1893
+ if (check.type === "file-contains") {
1894
+ const containsCheck = check;
1895
+ if (!containsCheck.content || containsCheck.content.length === 0) {
1896
+ errors.push(`Stage '${stageId}' file-contains check missing 'content' field`);
1897
+ }
1898
+ }
1899
+ if (errors.length > 0) {
1900
+ return { valid: false, error: errors.join("; ") };
1901
+ }
1902
+ return { valid: true };
1903
+ }
1904
+ function validateStageConfig(stage) {
1905
+ const errors = [];
1906
+ const warnings = [];
1907
+ if (!stage.id || typeof stage.id !== "string") {
1908
+ errors.push("Stage must have a valid id");
1909
+ } else if (!isPathSafeIdentifier(stage.id)) {
1910
+ errors.push(
1911
+ `Stage id '${stage.id}' contains invalid characters. Use only alphanumeric, underscore, and hyphen.`
1912
+ );
1913
+ }
1914
+ if (stage.type !== void 0 && stage.type !== "standard" && stage.type !== "competition" && stage.type !== "verify" && stage.type !== "refinement" && stage.type !== "autoloop" && stage.type !== "orchestrator") {
1915
+ errors.push(
1916
+ `Stage '${stage.id}' has invalid type '${String(stage.type)}'. Valid types: standard, competition, verify, refinement, autoloop, orchestrator`
1917
+ );
1918
+ }
1919
+ if (stage.type === "verify") {
1920
+ const cmds = stage.verify?.commands;
1921
+ validateVerifyCommandStrings(cmds, `Stage '${stage.id}' verify.commands`, errors, {
1922
+ requireNonEmpty: true
1923
+ });
1924
+ if (stage.provider) {
1925
+ warnings.push(
1926
+ `Stage '${stage.id}' is type 'verify' but specifies provider '${stage.provider}'. Provider will be ignored (verification stages run locally).`
1927
+ );
1928
+ }
1929
+ if (stage.cacheable === true) {
1930
+ warnings.push(
1931
+ `Stage '${stage.id}' sets deprecated cacheable: true; verify stage caching is disabled and commands will run`
1932
+ );
1933
+ }
1934
+ } else if (stage.cacheable === true) {
1935
+ errors.push(
1936
+ `Stage '${stage.id}' sets cacheable: true, but deterministic stage caching is only supported for type 'verify' stages`
1937
+ );
1938
+ } else if (stage.verify?.commands !== void 0) {
1939
+ validateVerifyCommandStrings(
1940
+ stage.verify.commands,
1941
+ `Stage '${stage.id}' verify.commands`,
1942
+ errors,
1943
+ {
1944
+ requireNonEmpty: false
1945
+ }
1946
+ );
1947
+ }
1948
+ if (stage.competition?.candidateGate) {
1949
+ const gate = stage.competition.candidateGate;
1950
+ const stageId = stage.id || "unknown";
1951
+ if (!Array.isArray(gate.commands) || gate.commands.length === 0) {
1952
+ errors.push(
1953
+ `Stage '${stageId}' competition.candidateGate.commands must be a non-empty string[]`
1954
+ );
1955
+ } else {
1956
+ for (const [idx, cmd] of gate.commands.entries()) {
1957
+ if (typeof cmd !== "string" || cmd.trim().length === 0) {
1958
+ errors.push(
1959
+ `Stage '${stageId}' competition.candidateGate.commands[${idx}] must be a non-empty string`
1960
+ );
1961
+ continue;
1962
+ }
1963
+ const safety = checkVerifyCommandSafety(cmd);
1964
+ if (!safety.safe) {
1965
+ errors.push(
1966
+ `Stage '${stageId}' competition.candidateGate.commands[${idx}] is unsafe: ${safety.error}`
1967
+ );
1968
+ }
1969
+ }
1970
+ }
1971
+ if (gate.coverage) {
1972
+ const coverage = gate.coverage;
1973
+ if (!Array.isArray(coverage.targets) || coverage.targets.length === 0) {
1974
+ errors.push(
1975
+ `Stage '${stageId}' competition.candidateGate.coverage.targets must be string[]`
1976
+ );
1977
+ } else {
1978
+ for (const [idx, target] of coverage.targets.entries()) {
1979
+ if (typeof target !== "string" || target.trim().length === 0) {
1980
+ errors.push(
1981
+ `Stage '${stageId}' competition.candidateGate.coverage.targets[${idx}] must be a non-empty string`
1982
+ );
1983
+ continue;
1984
+ }
1985
+ const pathResult = isValidCheckPath(target);
1986
+ if (!pathResult.valid) {
1987
+ errors.push(
1988
+ `Stage '${stageId}' competition.candidateGate.coverage.targets[${idx}] invalid: ${pathResult.error}`
1989
+ );
1990
+ }
1991
+ }
1992
+ }
1993
+ if (coverage.summaryPath !== void 0) {
1994
+ if (typeof coverage.summaryPath !== "string" || coverage.summaryPath.trim().length === 0) {
1995
+ errors.push(
1996
+ `Stage '${stageId}' competition.candidateGate.coverage.summaryPath must be a non-empty string when provided`
1997
+ );
1998
+ } else {
1999
+ const pathResult = isValidCheckPath(coverage.summaryPath);
2000
+ if (!pathResult.valid) {
2001
+ errors.push(
2002
+ `Stage '${stageId}' competition.candidateGate.coverage.summaryPath invalid: ${pathResult.error}`
2003
+ );
2004
+ }
2005
+ }
2006
+ }
2007
+ if (coverage.threshold !== void 0) {
2008
+ if (typeof coverage.threshold !== "number" || !Number.isFinite(coverage.threshold)) {
2009
+ errors.push(
2010
+ `Stage '${stageId}' competition.candidateGate.coverage.threshold must be a finite number when provided`
2011
+ );
2012
+ } else if (coverage.threshold < 0 || coverage.threshold > 100) {
2013
+ errors.push(
2014
+ `Stage '${stageId}' competition.candidateGate.coverage.threshold must be between 0 and 100`
2015
+ );
2016
+ }
2017
+ }
2018
+ }
2019
+ }
2020
+ if (stage.competition?.judgeConfig) {
2021
+ const judgeValidation = validateCompetitionJudgeConfig(
2022
+ stage.id || "unknown",
2023
+ stage.competition.judgeConfig
2024
+ );
2025
+ if (!judgeValidation.valid && judgeValidation.error) {
2026
+ errors.push(judgeValidation.error);
2027
+ }
2028
+ }
2029
+ if (stage.type === "refinement") {
2030
+ if (!stage.refinement) {
2031
+ warnings.push(
2032
+ `Stage '${stage.id}' is type 'refinement' but has no refinement config; it will run as a normal stage without post-competition inputs/routing.`
2033
+ );
2034
+ }
2035
+ if (stage.competition) {
2036
+ warnings.push(
2037
+ `Stage '${stage.id}' is type 'refinement' but also specifies competition config; competition config will be ignored.`
2038
+ );
2039
+ }
2040
+ if (stage.verify?.commands?.length) {
2041
+ warnings.push(
2042
+ `Stage '${stage.id}' is type 'refinement' but also specifies verify.commands; verify commands will be ignored.`
2043
+ );
2044
+ }
2045
+ }
2046
+ if (stage.type === "orchestrator") {
2047
+ if (!stage.orchestrator) {
2048
+ warnings.push(
2049
+ `Stage '${stage.id}' is type 'orchestrator' but has no orchestrator config; using defaults.`
2050
+ );
2051
+ } else {
2052
+ const cfg = stage.orchestrator;
2053
+ if (cfg.orchestrationProvider !== void 0) {
2054
+ try {
2055
+ ProviderRegistry.getProviderConfig(cfg.orchestrationProvider);
2056
+ } catch (error) {
2057
+ errors.push(
2058
+ `Stage '${stage.id}' orchestrator.orchestrationProvider is invalid: ${error instanceof Error ? error.message : String(error)}`
2059
+ );
2060
+ }
2061
+ }
2062
+ if (cfg.orchestrationProvider !== void 0 && cfg.orchestrationModel !== void 0) {
2063
+ try {
2064
+ ProviderRegistry.resolveModel(cfg.orchestrationProvider, cfg.orchestrationModel);
2065
+ } catch (error) {
2066
+ errors.push(
2067
+ `Stage '${stage.id}' orchestrator.orchestrationModel is invalid for provider '${cfg.orchestrationProvider}': ${error instanceof Error ? error.message : String(error)}`
2068
+ );
2069
+ }
2070
+ }
2071
+ if (cfg.maxSubStages !== void 0) {
2072
+ if (!Number.isInteger(cfg.maxSubStages) || cfg.maxSubStages < 1) {
2073
+ errors.push(
2074
+ `Stage '${stage.id}' orchestrator.maxSubStages must be a positive integer (got ${cfg.maxSubStages})`
2075
+ );
2076
+ } else if (cfg.maxSubStages > 20) {
2077
+ warnings.push(
2078
+ `Stage '${stage.id}' orchestrator.maxSubStages is very high (${cfg.maxSubStages}). Consider limiting to \u226410 for cost control.`
2079
+ );
2080
+ }
2081
+ }
2082
+ if (cfg.budgetPerSubStage !== void 0) {
2083
+ if (!Number.isFinite(cfg.budgetPerSubStage) || cfg.budgetPerSubStage < 0) {
2084
+ errors.push(
2085
+ `Stage '${stage.id}' orchestrator.budgetPerSubStage must be non-negative (got ${cfg.budgetPerSubStage})`
2086
+ );
2087
+ }
2088
+ }
2089
+ if (cfg.totalBudget !== void 0) {
2090
+ if (!Number.isFinite(cfg.totalBudget) || cfg.totalBudget < 0) {
2091
+ errors.push(
2092
+ `Stage '${stage.id}' orchestrator.totalBudget must be non-negative (got ${cfg.totalBudget})`
2093
+ );
2094
+ }
2095
+ }
2096
+ if (cfg.maxReplanningAttempts !== void 0) {
2097
+ if (!Number.isInteger(cfg.maxReplanningAttempts) || cfg.maxReplanningAttempts < 0) {
2098
+ errors.push(
2099
+ `Stage '${stage.id}' orchestrator.maxReplanningAttempts must be non-negative integer (got ${cfg.maxReplanningAttempts})`
2100
+ );
2101
+ } else if (cfg.maxReplanningAttempts > 1) {
2102
+ errors.push(
2103
+ `Stage '${stage.id}' orchestrator.maxReplanningAttempts cannot exceed 1 until multi-pass replanning is supported (got ${cfg.maxReplanningAttempts})`
2104
+ );
2105
+ }
2106
+ }
2107
+ if (cfg.retryBudgetPerSubStage !== void 0) {
2108
+ if (!Number.isInteger(cfg.retryBudgetPerSubStage) || cfg.retryBudgetPerSubStage < 0) {
2109
+ errors.push(
2110
+ `Stage '${stage.id}' orchestrator.retryBudgetPerSubStage must be non-negative integer (got ${cfg.retryBudgetPerSubStage})`
2111
+ );
2112
+ }
2113
+ }
2114
+ if (cfg.maxRecursionDepth !== void 0) {
2115
+ if (!Number.isInteger(cfg.maxRecursionDepth) || cfg.maxRecursionDepth < 0) {
2116
+ errors.push(
2117
+ `Stage '${stage.id}' orchestrator.maxRecursionDepth must be non-negative integer (got ${cfg.maxRecursionDepth})`
2118
+ );
2119
+ } else if (cfg.maxRecursionDepth > 3) {
2120
+ warnings.push(
2121
+ `Stage '${stage.id}' orchestrator.maxRecursionDepth is high (${cfg.maxRecursionDepth}). Deep recursion may be expensive.`
2122
+ );
2123
+ }
2124
+ }
2125
+ }
2126
+ if (stage.competition) {
2127
+ warnings.push(
2128
+ `Stage '${stage.id}' is type 'orchestrator' but also specifies competition config; competition config will be ignored.`
2129
+ );
2130
+ }
2131
+ if (stage.autoloop) {
2132
+ warnings.push(
2133
+ `Stage '${stage.id}' is type 'orchestrator' but also specifies autoloop config; autoloop config will be ignored.`
2134
+ );
2135
+ }
2136
+ if (stage.verify?.commands?.length) {
2137
+ warnings.push(
2138
+ `Stage '${stage.id}' is type 'orchestrator' but also specifies verify.commands; verify commands will be ignored.`
2139
+ );
2140
+ }
2141
+ if (stage.refinement) {
2142
+ warnings.push(
2143
+ `Stage '${stage.id}' is type 'orchestrator' but also specifies refinement config; refinement config will be ignored.`
2144
+ );
2145
+ }
2146
+ }
2147
+ if (stage.type === "autoloop") {
2148
+ if (!stage.autoloop) {
2149
+ warnings.push(
2150
+ `Stage '${stage.id}' is type 'autoloop' but has no autoloop config; using defaults (maxIterations: 3, timeboxMs: 1800000).`
2151
+ );
2152
+ } else {
2153
+ const cfg = stage.autoloop;
2154
+ if (cfg.maxIterations !== void 0) {
2155
+ if (!Number.isInteger(cfg.maxIterations) || cfg.maxIterations < 1) {
2156
+ errors.push(
2157
+ `Stage '${stage.id}' autoloop.maxIterations must be a positive integer (got ${cfg.maxIterations})`
2158
+ );
2159
+ } else if (cfg.maxIterations > 20) {
2160
+ warnings.push(
2161
+ `Stage '${stage.id}' autoloop.maxIterations is very high (${cfg.maxIterations}). Consider limiting to \u226410 for cost control.`
2162
+ );
2163
+ }
2164
+ }
2165
+ if (cfg.timeboxMs !== void 0) {
2166
+ if (!Number.isFinite(cfg.timeboxMs) || cfg.timeboxMs < 1e3) {
2167
+ errors.push(
2168
+ `Stage '${stage.id}' autoloop.timeboxMs must be at least 1000ms (got ${cfg.timeboxMs})`
2169
+ );
2170
+ } else if (cfg.timeboxMs > 36e5) {
2171
+ warnings.push(
2172
+ `Stage '${stage.id}' autoloop.timeboxMs is very high (${cfg.timeboxMs}ms = ${(cfg.timeboxMs / 6e4).toFixed(1)} minutes). Consider limiting to \u226430 minutes.`
2173
+ );
2174
+ }
2175
+ }
2176
+ if (cfg.minProgress !== void 0) {
2177
+ if (!Number.isFinite(cfg.minProgress) || cfg.minProgress < 0 || cfg.minProgress > 1) {
2178
+ errors.push(
2179
+ `Stage '${stage.id}' autoloop.minProgress must be between 0 and 1 (got ${cfg.minProgress})`
2180
+ );
2181
+ }
2182
+ }
2183
+ }
2184
+ if (stage.competition) {
2185
+ warnings.push(
2186
+ `Stage '${stage.id}' is type 'autoloop' but also specifies competition config; competition config will be ignored.`
2187
+ );
2188
+ }
2189
+ if (stage.verify?.commands?.length) {
2190
+ const gatesEnabled = stage.autoloop?.stopOnQualityGate === true || Array.isArray(stage.autoloop?.qualityGateCommands) && stage.autoloop?.qualityGateCommands.length > 0;
2191
+ if (!gatesEnabled) {
2192
+ warnings.push(
2193
+ `Stage '${stage.id}' is type 'autoloop' but also specifies verify.commands; verify commands will be ignored unless autoloop.stopOnQualityGate=true (or autoloop.qualityGateCommands is set).`
2194
+ );
2195
+ }
2196
+ }
2197
+ if (stage.refinement) {
2198
+ warnings.push(
2199
+ `Stage '${stage.id}' is type 'autoloop' but also specifies refinement config; refinement config will be ignored.`
2200
+ );
2201
+ }
2202
+ }
2203
+ if (stage.outputs) {
2204
+ for (const [outputName, outputType] of Object.entries(stage.outputs)) {
2205
+ if (!isPathSafeIdentifier(outputName)) {
2206
+ errors.push(
2207
+ `Stage '${stage.id}' output name '${outputName}' contains invalid characters. Use only alphanumeric, underscore, and hyphen.`
2208
+ );
2209
+ }
2210
+ if (!VALID_OUTPUT_TYPES.includes(outputType)) {
2211
+ errors.push(
2212
+ `Stage '${stage.id}' output '${outputName}' has invalid type '${outputType}'. Valid types: ${VALID_OUTPUT_TYPES.join(", ")}`
2213
+ );
2214
+ }
2215
+ }
2216
+ }
2217
+ if (stage.provider !== void 0) {
2218
+ const providerIds = getValidProviderIds();
2219
+ if (!providerIds.ids) {
2220
+ errors.push(
2221
+ `Stage '${stage.id}' provider '${stage.provider}' could not be validated because the provider registry is unavailable: ${providerIds.error}`
2222
+ );
2223
+ } else if (!providerIds.ids.includes(stage.provider)) {
2224
+ errors.push(
2225
+ `Stage '${stage.id}' has invalid provider '${stage.provider}'. Valid providers: ${providerIds.ids.join(", ")}`
2226
+ );
2227
+ }
2228
+ }
2229
+ if (stage.model !== void 0 && stage.provider === void 0) {
2230
+ warnings.push(
2231
+ `Stage '${stage.id}' specifies model '${stage.model}' without provider. Model may be ignored during routing. Consider setting provider explicitly.`
2232
+ );
2233
+ }
2234
+ if (stage.providerExecutionTransport !== void 0 && !isValidProviderExecutionTransport(stage.providerExecutionTransport)) {
2235
+ errors.push(
2236
+ `Stage '${stage.id}' has invalid providerExecutionTransport '${String(
2237
+ stage.providerExecutionTransport
2238
+ )}'. Valid values: ${VALID_PROVIDER_EXECUTION_TRANSPORTS.join(", ")}`
2239
+ );
2240
+ }
2241
+ if (stage.competition?.variants) {
2242
+ for (const [variantIdx, variant] of stage.competition.variants.entries()) {
2243
+ if (variant.providerExecutionTransport !== void 0 && !isValidProviderExecutionTransport(variant.providerExecutionTransport)) {
2244
+ errors.push(
2245
+ `Stage '${stage.id}' competition.variants[${variantIdx}] has invalid providerExecutionTransport '${String(
2246
+ variant.providerExecutionTransport
2247
+ )}'. Valid values: ${VALID_PROVIDER_EXECUTION_TRANSPORTS.join(", ")}`
2248
+ );
2249
+ }
2250
+ }
2251
+ }
2252
+ if (stage.modelRole !== void 0 && stage.provider === void 0) {
2253
+ warnings.push(
2254
+ `Stage '${stage.id}' specifies modelRole '${stage.modelRole}' without provider. modelRole requires provider to be set for registry role lookup.`
2255
+ );
2256
+ }
2257
+ if (stage.validation?.enabled && stage.outputs) {
2258
+ const outputCount = Object.keys(stage.outputs).length;
2259
+ if (outputCount > 1 && !stage.validation.target) {
2260
+ errors.push(
2261
+ `Stage '${stage.id}' has ${outputCount} outputs but validation.target not specified. Must specify which output to validate: [${Object.keys(stage.outputs).join(", ")}]`
2262
+ );
2263
+ }
2264
+ if (stage.validation.target) {
2265
+ if (!Object.hasOwn(stage.outputs, stage.validation.target)) {
2266
+ errors.push(
2267
+ `Stage '${stage.id}' validation target '${stage.validation.target}' not found in outputs: [${Object.keys(stage.outputs).join(", ")}]`
2268
+ );
2269
+ }
2270
+ }
2271
+ }
2272
+ if (stage.validation?.threshold !== void 0) {
2273
+ if (stage.validation.threshold < 0 || stage.validation.threshold > 100) {
2274
+ errors.push(
2275
+ `Stage '${stage.id}' validation threshold ${stage.validation.threshold} must be between 0 and 100`
2276
+ );
2277
+ }
2278
+ }
2279
+ if (stage.validation?.maxRetries !== void 0) {
2280
+ if (stage.validation.maxRetries < 0 || stage.validation.maxRetries > 10) {
2281
+ errors.push(
2282
+ `Stage '${stage.id}' validation maxRetries ${stage.validation.maxRetries} must be between 0 and 10`
2283
+ );
2284
+ }
2285
+ }
2286
+ if (stage.validation?.onFailure !== void 0) {
2287
+ if (!isValidOnFailurePolicy(stage.validation.onFailure)) {
2288
+ errors.push(
2289
+ `Stage '${stage.id}' validation.onFailure '${String(stage.validation.onFailure)}' is invalid. Valid values: ${VALID_ON_FAILURE_POLICIES.join(", ")}`
2290
+ );
2291
+ }
2292
+ if (stage.validation.onFailure === "retry-with-feedback" && stage.type !== void 0 && stage.type !== "standard" && stage.type !== "refinement") {
2293
+ errors.push(
2294
+ `Stage '${stage.id}' validation.onFailure 'retry-with-feedback' is only supported for standard and refinement stages`
2295
+ );
2296
+ }
2297
+ }
2298
+ if (stage.validation?.checks && stage.validation.checks.length > 0) {
2299
+ for (const check of stage.validation.checks) {
2300
+ const checkResult = validateExplicitCheck(check, stage.id);
2301
+ if (!checkResult.valid && checkResult.error) {
2302
+ errors.push(checkResult.error);
2303
+ }
2304
+ }
2305
+ }
2306
+ if (stage.validation?.requiredFiles && stage.validation.requiredFiles.length > 0) {
2307
+ for (const requiredFile of stage.validation.requiredFiles) {
2308
+ const pathResult = isValidCheckPath(requiredFile);
2309
+ if (!pathResult.valid) {
2310
+ errors.push(`Stage '${stage.id}' requiredFiles path invalid: ${pathResult.error}`);
2311
+ continue;
2312
+ }
2313
+ if (requiredFile.endsWith("/")) {
2314
+ errors.push(
2315
+ `Stage '${stage.id}' requiredFiles must use exact file paths, not directories: ${requiredFile}`
2316
+ );
2317
+ }
2318
+ }
2319
+ }
2320
+ if (stage.validation?.completionContract !== void 0) {
2321
+ const contractValidation = validateCompletionContracts(stage.validation.completionContract);
2322
+ for (const contractError of contractValidation.errors) {
2323
+ errors.push(`Stage '${stage.id}' ${contractError.field}: ${contractError.message}`);
2324
+ }
2325
+ }
2326
+ appendSeededPlanCompletionContractErrors(stage.id, stage.seededPlan, errors);
2327
+ if (stage.allowlist && stage.allowlist.length > 0) {
2328
+ for (const allowlistPath of stage.allowlist) {
2329
+ const pathResult = isValidCheckPath(allowlistPath);
2330
+ if (!pathResult.valid) {
2331
+ errors.push(`Stage '${stage.id}' allowlist path invalid: ${pathResult.error}`);
2332
+ }
2333
+ }
2334
+ }
2335
+ if (stage.type !== "verify" && stage.capabilities?.executionIntent === "analysis" && (!stage.outputs || Object.keys(stage.outputs).length === 0)) {
2336
+ errors.push(
2337
+ `Stage '${stage.id}' is analysis-intent but has no outputs configured. Add outputs (recommended: outputs: { report: 'text' }).`
2338
+ );
2339
+ }
2340
+ if (stage.allowlist && stage.allowlist.length > 0 && stage.outputs) {
2341
+ const outputTypes = Object.values(stage.outputs);
2342
+ const hasCodeOutput = outputTypes.some((t) => t === "diff" || t === "files");
2343
+ const hasTextOutput = outputTypes.some(
2344
+ (t) => t === "text" || t === "json" || t === "testResults"
2345
+ );
2346
+ if (hasTextOutput && !hasCodeOutput) {
2347
+ if (stage.capabilities?.executionIntent === "analysis") {
2348
+ warnings.push(
2349
+ `Stage '${stage.id}' sets executionIntent: analysis with a non-empty allowlist. Providers run read-only; allowlisted edits will be denied unless intent or outputs change.`
2350
+ );
2351
+ } else if (stage.capabilities?.executionIntent !== "edit") {
2352
+ warnings.push(
2353
+ `Stage '${stage.id}' declares an allowlist but only text-type outputs. Execution runs with edit intent because the allowlist signals file modifications. Add a code-type output (e.g. outputs: { patch: diff }) or set capabilities.executionIntent: edit.`
2354
+ );
2355
+ }
2356
+ }
2357
+ }
2358
+ if (stage.allowlistMode === "strict") {
2359
+ if (!stage.allowlist || stage.allowlist.length === 0) {
2360
+ warnings.push(
2361
+ `Stage '${stage.id}' uses allowlistMode: strict with no allowlist entries. Any file changes will fail the stage.`
2362
+ );
2363
+ } else if (!stage.allowlist.some((p) => p.endsWith("/"))) {
2364
+ warnings.push(
2365
+ `Stage '${stage.id}' uses allowlistMode: strict with file-level allowlist entries only. This commonly fails on ripple edits (e.g. async propagation). Consider directory prefixes or allowlistMode: warn.`
2366
+ );
2367
+ }
2368
+ }
2369
+ if (stage.validation?.enabled && stage.type !== "verify" && !stage.outputs && !stage.validation?.checks?.length && !stage.validation?.semanticChecks?.length && !stage.verify?.commands?.length) {
2370
+ warnings.push(
2371
+ `Stage '${stage.id}' ${VALIDATION_NO_SIGNALS_WARNING_DETAILS}. This is informational only and does not cause stage failure. Consider adding validation.checks or outputs for meaningful validation signals.`
2372
+ );
2373
+ }
2374
+ if (stage.gates && stage.gates.length > 0) {
2375
+ for (const [idx, gate] of stage.gates.entries()) {
2376
+ const validGateTypes = ["biome", "test", "build", "custom"];
2377
+ if (!validGateTypes.includes(gate.type)) {
2378
+ errors.push(
2379
+ `Stage '${stage.id}' gates[${idx}].type is invalid: '${gate.type}'. Valid types: ${validGateTypes.join(", ")}`
2380
+ );
2381
+ }
2382
+ const validGateModes = ["block", "warn"];
2383
+ if (!validGateModes.includes(gate.mode)) {
2384
+ errors.push(
2385
+ `Stage '${stage.id}' gates[${idx}].mode is invalid: '${gate.mode}'. Valid modes: ${validGateModes.join(", ")}`
2386
+ );
2387
+ }
2388
+ if (gate.files && gate.files.length > 0) {
2389
+ for (const [fileIdx, filePath] of gate.files.entries()) {
2390
+ const pathResult = isValidCheckPath(filePath);
2391
+ if (!pathResult.valid) {
2392
+ errors.push(
2393
+ `Stage '${stage.id}' gates[${idx}].files[${fileIdx}] invalid: ${pathResult.error}`
2394
+ );
2395
+ }
2396
+ }
2397
+ }
2398
+ if (gate.type === "custom" && (!gate.command || gate.command.trim().length === 0)) {
2399
+ errors.push(
2400
+ `Stage '${stage.id}' gates[${idx}] type 'custom' requires non-empty 'command' field`
2401
+ );
2402
+ }
2403
+ if (gate.maxDiagnostics !== void 0) {
2404
+ if (gate.type !== "biome") {
2405
+ warnings.push(
2406
+ `Stage '${stage.id}' gates[${idx}].maxDiagnostics is set but gate type is '${gate.type}' (only applies to 'biome')`
2407
+ );
2408
+ } else if (!Number.isInteger(gate.maxDiagnostics) || gate.maxDiagnostics < 1 || gate.maxDiagnostics > 1e3) {
2409
+ errors.push(
2410
+ `Stage '${stage.id}' gates[${idx}].maxDiagnostics must be an integer between 1 and 1000 (got ${gate.maxDiagnostics})`
2411
+ );
2412
+ }
2413
+ }
2414
+ }
2415
+ }
2416
+ if (errors.length > 0) {
2417
+ return {
2418
+ valid: false,
2419
+ error: errors.join("; "),
2420
+ warnings: warnings.length > 0 ? warnings : void 0
2421
+ };
2422
+ }
2423
+ return {
2424
+ valid: true,
2425
+ warnings: warnings.length > 0 ? warnings : void 0
2426
+ };
2427
+ }
2428
+ function isValidationNoSignalsWarning(warning, stageId) {
2429
+ return warning.startsWith(`Stage '${stageId}' ${VALIDATION_NO_SIGNALS_WARNING_DETAILS}.`);
2430
+ }
2431
+ function validateWorkflowConfig(workflow) {
2432
+ const errors = [];
2433
+ const warnings = [];
2434
+ const validationNoSignalsStageIds = [];
2435
+ if (!workflow.stages || workflow.stages.length === 0) {
2436
+ return {
2437
+ valid: false,
2438
+ error: "Workflow must have at least one stage"
2439
+ };
2440
+ }
2441
+ const stageIds = new Set(workflow.stages.map((s) => s.id));
2442
+ const seenIds = /* @__PURE__ */ new Set();
2443
+ for (const stage of workflow.stages) {
2444
+ if (seenIds.has(stage.id)) {
2445
+ errors.push(`Duplicate stage id: '${stage.id}'`);
2446
+ }
2447
+ seenIds.add(stage.id);
2448
+ }
2449
+ for (const stage of workflow.stages) {
2450
+ const stageResult = validateStageConfig(stage);
2451
+ if (!stageResult.valid && stageResult.error) {
2452
+ errors.push(stageResult.error);
2453
+ }
2454
+ if (stageResult.warnings) {
2455
+ for (const warning of stageResult.warnings) {
2456
+ if (isValidationNoSignalsWarning(warning, stage.id)) {
2457
+ validationNoSignalsStageIds.push(stage.id);
2458
+ } else {
2459
+ warnings.push(warning);
2460
+ }
2461
+ }
2462
+ }
2463
+ if (stage.deps) {
2464
+ for (const dep of stage.deps) {
2465
+ if (!stageIds.has(dep)) {
2466
+ errors.push(
2467
+ `Stage '${stage.id}' has dependency '${dep}' which does not exist. Available stages: [${Array.from(stageIds).join(", ")}]`
2468
+ );
2469
+ }
2470
+ }
2471
+ }
2472
+ }
2473
+ const cycleError = detectCircularDependencies(workflow.stages);
2474
+ if (cycleError) {
2475
+ errors.push(cycleError);
2476
+ }
2477
+ errors.push(...validateArtifactReferences(workflow));
2478
+ if (workflow.strict?.denylist && workflow.strict.denylist.length > 0) {
2479
+ for (const denylistPath of workflow.strict.denylist) {
2480
+ const pathResult = isValidCheckPath(denylistPath);
2481
+ if (!pathResult.valid) {
2482
+ errors.push(`Workflow strict.denylist path invalid: ${pathResult.error}`);
2483
+ }
2484
+ }
2485
+ }
2486
+ if (workflow.strict?.denylistMode) {
2487
+ const mode = workflow.strict.denylistMode;
2488
+ if (mode !== "off" && mode !== "warn" && mode !== "strict") {
2489
+ errors.push(
2490
+ `Workflow strict.denylistMode invalid: '${String(mode)}' (expected off|warn|strict)`
2491
+ );
2492
+ }
2493
+ }
2494
+ if (validationNoSignalsStageIds.length > 0) {
2495
+ warnings.push(
2496
+ `Workflow has validation enabled but no outputs or checks defined for stages: ${validationNoSignalsStageIds.join(
2497
+ ", "
2498
+ )}. This is informational only; add validation.checks or outputs for meaningful validation signals.`
2499
+ );
2500
+ }
2501
+ if (errors.length > 0) {
2502
+ return {
2503
+ valid: false,
2504
+ error: errors.join("; "),
2505
+ warnings: warnings.length > 0 ? warnings : void 0
2506
+ };
2507
+ }
2508
+ return {
2509
+ valid: true,
2510
+ warnings: warnings.length > 0 ? warnings : void 0
2511
+ };
2512
+ }
2513
+ function validateArtifactReferences(workflow) {
2514
+ const errors = [];
2515
+ const stageOutputs = /* @__PURE__ */ new Map();
2516
+ const stageMap = /* @__PURE__ */ new Map();
2517
+ for (const stage of workflow.stages ?? []) {
2518
+ stageMap.set(stage.id, stage);
2519
+ stageOutputs.set(stage.id, new Set(Object.keys(stage.outputs ?? {})));
2520
+ }
2521
+ const dependencyClosureCache = /* @__PURE__ */ new Map();
2522
+ const getDependencyClosure = (stageId) => {
2523
+ const cached = dependencyClosureCache.get(stageId);
2524
+ if (cached) return cached;
2525
+ const closure = /* @__PURE__ */ new Set();
2526
+ const visit = (depId) => {
2527
+ if (closure.has(depId)) return;
2528
+ closure.add(depId);
2529
+ const depStage = stageMap.get(depId);
2530
+ for (const transitiveDep of depStage?.deps ?? []) {
2531
+ visit(transitiveDep);
2532
+ }
2533
+ };
2534
+ const stage = stageMap.get(stageId);
2535
+ for (const dep of stage?.deps ?? []) {
2536
+ visit(dep);
2537
+ }
2538
+ dependencyClosureCache.set(stageId, closure);
2539
+ return closure;
2540
+ };
2541
+ for (const stage of workflow.stages ?? []) {
2542
+ for (const ref of stage.inputs ?? []) {
2543
+ try {
2544
+ const { stageId, outputName } = parseArtifactRef(ref);
2545
+ const outputs = stageOutputs.get(stageId);
2546
+ if (!outputs) {
2547
+ errors.push(
2548
+ `Stage '${stage.id}' references artifact '${ref}' from non-existent stage '${stageId}'`
2549
+ );
2550
+ continue;
2551
+ }
2552
+ if (!outputs.has(outputName)) {
2553
+ const available = outputs.size > 0 ? Array.from(outputs).join(", ") : "none";
2554
+ errors.push(
2555
+ `Stage '${stage.id}' references output '${outputName}' which doesn't exist in stage '${stageId}'. Available: [${available}]`
2556
+ );
2557
+ continue;
2558
+ }
2559
+ if (!getDependencyClosure(stage.id).has(stageId)) {
2560
+ errors.push(
2561
+ `Stage '${stage.id}' references artifact '${ref}' but does not depend on producer stage '${stageId}'`
2562
+ );
2563
+ }
2564
+ } catch (err) {
2565
+ errors.push(
2566
+ `Stage '${stage.id}' has invalid artifact reference '${ref}': ${err instanceof Error ? err.message : String(err)}`
2567
+ );
2568
+ }
2569
+ }
2570
+ }
2571
+ return errors;
2572
+ }
2573
+ function detectCircularDependencies(stages) {
2574
+ const stageMap = new Map(stages.map((s) => [s.id, s]));
2575
+ const visiting = /* @__PURE__ */ new Set();
2576
+ const visited = /* @__PURE__ */ new Set();
2577
+ function dfs(stageId, path5) {
2578
+ if (visited.has(stageId)) {
2579
+ return null;
2580
+ }
2581
+ if (visiting.has(stageId)) {
2582
+ const cycleStart = path5.indexOf(stageId);
2583
+ const cycle = [...path5.slice(cycleStart), stageId].join(" \u2192 ");
2584
+ return `Circular dependency detected: ${cycle}`;
2585
+ }
2586
+ visiting.add(stageId);
2587
+ path5.push(stageId);
2588
+ const stage = stageMap.get(stageId);
2589
+ if (stage?.deps) {
2590
+ for (const dep of stage.deps) {
2591
+ const result = dfs(dep, [...path5]);
2592
+ if (result) {
2593
+ return result;
2594
+ }
2595
+ }
2596
+ }
2597
+ visiting.delete(stageId);
2598
+ visited.add(stageId);
2599
+ return null;
2600
+ }
2601
+ for (const stage of stages) {
2602
+ const result = dfs(stage.id, []);
2603
+ if (result) {
2604
+ return result;
2605
+ }
2606
+ }
2607
+ return null;
2608
+ }
2609
+
2610
+ // src/automation/installed-dependency-drift.ts
2611
+ function formatInstalledDependencyDriftWarning(drift, projectRoot) {
2612
+ const [first] = drift;
2613
+ if (!first) return null;
2614
+ const more = drift.length > 1 ? ` and ${drift.length - 1} more` : "";
2615
+ return `Warning: installed packages do not match package-lock.json (${first.name} ${first.installed}, locked ${first.locked}${more}). Run \`npm ci\` in ${projectRoot} to install them.`;
2616
+ }
2617
+
2618
+ // src/automation/flywheel-preflight.ts
2619
+ var PACKAGE_ROOT_ENV = "BUILD_PACKAGE_ROOT";
2620
+ function isCodexProviderInsideCodexSandbox(providerId) {
2621
+ return providerId === "codex" && process.env.CODEX_SANDBOX !== void 0;
2622
+ }
2623
+ async function checkRepoClean(cwd, mode, suppressExpectedSetupWarning = false) {
2624
+ const start = Date.now();
2625
+ const check = "repo-clean";
2626
+ const description = "Repository working directory is clean";
2627
+ try {
2628
+ const { stdout } = await execa2("git", ["status", "--porcelain", "--untracked-files=all"], {
2629
+ cwd,
2630
+ stdio: "pipe",
2631
+ timeout: 1e4
2632
+ });
2633
+ const lines = filterWorktreeRunSentinelStatusOutput(stdout).trimEnd().split("\n").filter((line) => line.length > 0);
2634
+ if (lines.length === 0) {
2635
+ return {
2636
+ check,
2637
+ description,
2638
+ passed: true,
2639
+ durationMs: Date.now() - start
2640
+ };
2641
+ }
2642
+ const changedPaths = lines.flatMap(parsePorcelainLinePaths);
2643
+ const pathSummary = summarizeRepoPaths(changedPaths);
2644
+ const conflictStatuses = /* @__PURE__ */ new Set(["DD", "AU", "UD", "UA", "DU", "AA", "UU"]);
2645
+ const hasConflictStatuses = lines.some((line) => conflictStatuses.has(line.slice(0, 2)));
2646
+ const runtimeArtifactOnly = pathSummary.categories.length > 0 && pathSummary.categories.every(
2647
+ (category) => category === "runtime-artifact" || category === "generated-artifact"
2648
+ );
2649
+ const expectedSetupDirt = pathSummary.systemWriteOnly;
2650
+ const unstaged = lines.filter((l) => l.startsWith(" M") || l.startsWith("??"));
2651
+ const staged = lines.filter((l) => l.startsWith("M ") || l.startsWith("A "));
2652
+ let message = `Repository has ${lines.length} uncommitted change(s):
2653
+ `;
2654
+ if (staged.length > 0) {
2655
+ message += ` Staged: ${staged.length} file(s)
2656
+ `;
2657
+ }
2658
+ if (unstaged.length > 0) {
2659
+ message += ` Unstaged/Untracked: ${unstaged.length} file(s)
2660
+ `;
2661
+ }
2662
+ if (hasConflictStatuses) {
2663
+ message += " Scope: merge/conflict state detected\n";
2664
+ } else if (pathSummary.systemWriteOnly) {
2665
+ message += " Scope: managed/runtime metadata only (task/, sprint/, backlog/, .build-project/codex-home/, .build-project/plans/, .build-project/planning/)\n";
2666
+ } else if (pathSummary.harmlessOnly) {
2667
+ message += " Scope: generated/runtime artifacts only\n";
2668
+ } else {
2669
+ message += " Scope: includes repository content changes\n";
2670
+ }
2671
+ message += `Run 'git status' to see details.`;
2672
+ const managedSetupOnly = pathSummary.systemWriteOnly || pathSummary.harmlessOnly;
2673
+ const metadata = {
2674
+ changedPaths,
2675
+ systemWriteOnly: pathSummary.systemWriteOnly,
2676
+ harmlessOnly: pathSummary.harmlessOnly,
2677
+ categories: pathSummary.categories,
2678
+ hasConflictStatuses,
2679
+ runtimeArtifactOnly,
2680
+ expectedSetupDirt
2681
+ };
2682
+ if (hasConflictStatuses || mode === "fail" && !runtimeArtifactOnly && !managedSetupOnly) {
2683
+ message += hasConflictStatuses ? " Resolve conflicts before running Flywheel." : " Commit or stash changes before running Flywheel (or run without strict cleanliness).";
2684
+ return {
2685
+ check,
2686
+ description,
2687
+ passed: false,
2688
+ error: message,
2689
+ metadata,
2690
+ durationMs: Date.now() - start
2691
+ };
2692
+ }
2693
+ if (suppressExpectedSetupWarning && managedSetupOnly) {
2694
+ return {
2695
+ check,
2696
+ description,
2697
+ passed: true,
2698
+ metadata,
2699
+ durationMs: Date.now() - start
2700
+ };
2701
+ }
2702
+ return {
2703
+ check,
2704
+ description,
2705
+ passed: true,
2706
+ warning: message,
2707
+ metadata,
2708
+ durationMs: Date.now() - start
2709
+ };
2710
+ } catch (err) {
2711
+ return {
2712
+ check,
2713
+ description,
2714
+ passed: false,
2715
+ error: `Failed to check git status: ${err instanceof Error ? err.message : String(err)}`,
2716
+ durationMs: Date.now() - start
2717
+ };
2718
+ }
2719
+ }
2720
+ async function checkWorktreeSanity(baseDir) {
2721
+ const start = Date.now();
2722
+ const check = "worktree-sanity";
2723
+ const description = "Worktree base directory is accessible and writable";
2724
+ try {
2725
+ await fs3.mkdir(baseDir, { recursive: true });
2726
+ const testFile = path4.join(baseDir, ".preflight-test");
2727
+ await fs3.writeFile(testFile, "test");
2728
+ await fs3.unlink(testFile);
2729
+ return {
2730
+ check,
2731
+ description,
2732
+ passed: true,
2733
+ durationMs: Date.now() - start
2734
+ };
2735
+ } catch (err) {
2736
+ return {
2737
+ check,
2738
+ description,
2739
+ passed: false,
2740
+ error: `Worktree base directory ${baseDir} is not writable: ${err instanceof Error ? err.message : String(err)}`,
2741
+ durationMs: Date.now() - start
2742
+ };
2743
+ }
2744
+ }
2745
+ async function registeredWorktreeCount(repoRoot) {
2746
+ try {
2747
+ const result = await execGit(["worktree", "list", "--porcelain"], { cwd: repoRoot });
2748
+ if (result.exitCode !== 0) return void 0;
2749
+ return result.stdout.split("\n").filter((line) => line.startsWith("worktree ")).length;
2750
+ } catch {
2751
+ return void 0;
2752
+ }
2753
+ }
2754
+ async function checkWorktreeHygiene(baseDir, repoRoot, hygieneConfig) {
2755
+ const start = Date.now();
2756
+ const check = "worktree-hygiene";
2757
+ const description = "Worktree count and age are within acceptable limits";
2758
+ const warnThreshold = hygieneConfig?.warnThreshold ?? 50;
2759
+ const blockThreshold = hygieneConfig?.blockThreshold ?? 100;
2760
+ const registeredWarnThreshold = hygieneConfig?.registeredWarnThreshold ?? warnThreshold;
2761
+ const staleAgeDays = hygieneConfig?.staleAgeDays ?? 7;
2762
+ const autoCleanup = hygieneConfig?.autoCleanup ?? false;
2763
+ const autoCleanupDryRun = hygieneConfig?.autoCleanupDryRun ?? false;
2764
+ try {
2765
+ const entries = await fs3.readdir(baseDir, { withFileTypes: true });
2766
+ const worktrees = entries.filter((e) => e.isDirectory());
2767
+ const totalCount = worktrees.length;
2768
+ const registeredCount = await registeredWorktreeCount(repoRoot);
2769
+ const staleAgeMs = staleAgeDays * 24 * 60 * 60 * 1e3;
2770
+ const now = Date.now();
2771
+ let staleCount = 0;
2772
+ for (const entry of worktrees) {
2773
+ try {
2774
+ const wtPath = path4.join(baseDir, entry.name);
2775
+ const stats = await fs3.stat(wtPath);
2776
+ const age = now - stats.mtimeMs;
2777
+ if (age > staleAgeMs) {
2778
+ staleCount++;
2779
+ }
2780
+ } catch {
2781
+ }
2782
+ }
2783
+ let currentCount = totalCount;
2784
+ if (autoCleanup && staleCount > 0) {
2785
+ try {
2786
+ const cleanupManager = new WorktreeCleanupManager(baseDir);
2787
+ const report = await cleanupManager.cleanup({
2788
+ maxAgeMs: staleAgeMs,
2789
+ dryRun: autoCleanupDryRun
2790
+ });
2791
+ if (!autoCleanupDryRun && report.deleted > 0) {
2792
+ console.log(`Cleaned ${report.deleted} stale worktree(s) (older than ${staleAgeDays}d)`);
2793
+ const updatedEntries = await fs3.readdir(baseDir, { withFileTypes: true });
2794
+ const updatedWorktrees = updatedEntries.filter((e) => e.isDirectory());
2795
+ currentCount = updatedWorktrees.length;
2796
+ staleCount = 0;
2797
+ } else if (autoCleanupDryRun && report.deleted > 0) {
2798
+ console.log(
2799
+ `Would clean ${report.deleted} stale worktree(s) (older than ${staleAgeDays}d)`
2800
+ );
2801
+ }
2802
+ } catch (cleanupErr) {
2803
+ console.warn(
2804
+ `Auto-cleanup failed: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
2805
+ );
2806
+ }
2807
+ }
2808
+ const metadata = {
2809
+ baseDirectoryCount: currentCount,
2810
+ ...registeredCount === void 0 ? {} : { registeredWorktreeCount: registeredCount }
2811
+ };
2812
+ if (currentCount >= blockThreshold) {
2813
+ return {
2814
+ check,
2815
+ description,
2816
+ passed: false,
2817
+ error: `Worktree count (${currentCount}) exceeds block threshold (${blockThreshold}). Run build cleanup doctor or build cleanup worktrees --show-preserved to review preserved categories (dirty, uninspectable, active-run, lab/manual-only, nested-child, external, missing path, too-recent, branch-attached/protected/current) before any cleanup.`,
2818
+ metadata,
2819
+ durationMs: Date.now() - start
2820
+ };
2821
+ }
2822
+ const warnings = [];
2823
+ if (currentCount >= warnThreshold) {
2824
+ warnings.push(
2825
+ `Worktree count (${currentCount}) exceeds warning threshold (${warnThreshold}). Consider cleaning up old worktrees.`
2826
+ );
2827
+ }
2828
+ if (registeredCount !== void 0 && registeredCount >= registeredWarnThreshold) {
2829
+ warnings.push(
2830
+ `Worktrees registered to this repository (${registeredCount}, across all linked roots) exceed advisory threshold (${registeredWarnThreshold}). Review with build cleanup worktrees --show-preserved --verbose.`
2831
+ );
2832
+ }
2833
+ if (!autoCleanup && staleCount > 0) {
2834
+ warnings.push(
2835
+ `Found ${staleCount} stale worktree(s) (older than ${staleAgeDays}d). Consider enabling --auto-cleanup.`
2836
+ );
2837
+ }
2838
+ if (warnings.length > 0) {
2839
+ return {
2840
+ check,
2841
+ description,
2842
+ passed: true,
2843
+ warning: warnings.join(" "),
2844
+ metadata,
2845
+ durationMs: Date.now() - start
2846
+ };
2847
+ }
2848
+ return {
2849
+ check,
2850
+ description,
2851
+ passed: true,
2852
+ metadata,
2853
+ durationMs: Date.now() - start
2854
+ };
2855
+ } catch (err) {
2856
+ return {
2857
+ check,
2858
+ description,
2859
+ passed: false,
2860
+ error: `Worktree hygiene check failed: ${err instanceof Error ? err.message : String(err)}`,
2861
+ durationMs: Date.now() - start
2862
+ };
2863
+ }
2864
+ }
2865
+ async function checkWorktreeRecovery(hygieneConfig) {
2866
+ const start = Date.now();
2867
+ const check = "worktree-recovery";
2868
+ const description = "Recover orphaned worktrees from prior runs";
2869
+ try {
2870
+ const maxAgeMs = (hygieneConfig?.staleAgeDays ?? 7) * 24 * 60 * 60 * 1e3;
2871
+ const { recoverPendingWorktrees: recovery, enforceRetentionBounds: retention } = await runFlywheelWorktreeHygiene({
2872
+ dryRun: hygieneConfig?.recoveryDryRun ?? true,
2873
+ verbose: false,
2874
+ maxAgeMs,
2875
+ maxCount: 20
2876
+ });
2877
+ const totalRecovered = recovery.recovered.length + recovery.alreadyGone.length;
2878
+ const totalPruned = retention.pruned.length;
2879
+ const totalErrors = recovery.errors.length + retention.errors.length;
2880
+ const metadata = {
2881
+ dryRun: hygieneConfig?.recoveryDryRun ?? true,
2882
+ recovered: recovery.recovered,
2883
+ recoveredCount: recovery.recovered.length,
2884
+ alreadyGone: recovery.alreadyGone,
2885
+ alreadyGoneCount: recovery.alreadyGone.length,
2886
+ skippedActive: recovery.skippedActive,
2887
+ skippedActiveCount: recovery.skippedActive.length,
2888
+ skippedDirty: recovery.skippedDirty,
2889
+ skippedDirtyCount: recovery.skippedDirty.length,
2890
+ pruned: retention.pruned,
2891
+ prunedCount: retention.pruned.length,
2892
+ errors: [...recovery.errors, ...retention.errors],
2893
+ errorCount: totalErrors,
2894
+ totalRecovered,
2895
+ totalPruned
2896
+ };
2897
+ let warning;
2898
+ if (totalRecovered > 0 || totalPruned > 0 || totalErrors > 0 || recovery.skippedActive.length > 0 || recovery.skippedDirty.length > 0) {
2899
+ const parts = [];
2900
+ if (recovery.recovered.length > 0) {
2901
+ parts.push(`recovered ${recovery.recovered.length} orphaned worktree(s)`);
2902
+ }
2903
+ if (recovery.alreadyGone.length > 0) {
2904
+ parts.push(`reconciled ${recovery.alreadyGone.length} already-removed record(s)`);
2905
+ }
2906
+ if (totalPruned > 0) {
2907
+ parts.push(`pruned ${totalPruned} over-retained worktree(s)`);
2908
+ }
2909
+ if (recovery.skippedActive.length > 0) {
2910
+ parts.push(`preserved ${recovery.skippedActive.length} active worktree(s)`);
2911
+ }
2912
+ if (recovery.skippedDirty.length > 0) {
2913
+ parts.push(`skipped ${recovery.skippedDirty.length} dirty worktree(s)`);
2914
+ }
2915
+ if (totalErrors > 0) {
2916
+ const sampleError = recovery.errors[0]?.error ?? retention.errors[0]?.error ?? "see logs for details";
2917
+ const trimmedSample = sampleError.length > 200 ? `${sampleError.slice(0, 197)}...` : sampleError;
2918
+ parts.push(`encountered ${totalErrors} error(s) during cleanup (first: ${trimmedSample})`);
2919
+ }
2920
+ warning = `Worktree recovery: ${parts.join("; ")}`;
2921
+ }
2922
+ return {
2923
+ check,
2924
+ description,
2925
+ passed: true,
2926
+ warning,
2927
+ metadata,
2928
+ durationMs: Date.now() - start
2929
+ };
2930
+ } catch (err) {
2931
+ return {
2932
+ check,
2933
+ description,
2934
+ passed: true,
2935
+ // Don't block execution on recovery failure
2936
+ warning: `Worktree recovery failed: ${err instanceof Error ? err.message : String(err)}`,
2937
+ durationMs: Date.now() - start
2938
+ };
2939
+ }
2940
+ }
2941
+ async function checkProviderAuth(workflow, cwd, authProbeEnv) {
2942
+ const start = Date.now();
2943
+ const check = "provider-auth";
2944
+ const description = "All providers are authenticated and available";
2945
+ try {
2946
+ const providers = /* @__PURE__ */ new Set();
2947
+ const implicitDefaultProviderStages = [];
2948
+ const defaultProvider = resolveDefaultProvider(void 0, cwd).value;
2949
+ for (const stage of workflow.stages) {
2950
+ const hasCompetitionVariants = Array.isArray(stage.competition?.variants) && stage.competition.variants.length > 0;
2951
+ if (stage.provider) {
2952
+ providers.add(stage.provider);
2953
+ } else if (stage.type !== "verify" && !hasCompetitionVariants) {
2954
+ providers.add(defaultProvider);
2955
+ implicitDefaultProviderStages.push(stage.id);
2956
+ }
2957
+ if (stage.orchestrator?.orchestrationProvider) {
2958
+ providers.add(stage.orchestrator.orchestrationProvider);
2959
+ }
2960
+ if (stage.competition?.variants) {
2961
+ for (const variant of stage.competition.variants) {
2962
+ providers.add(variant.provider);
2963
+ }
2964
+ }
2965
+ }
2966
+ if (providers.size === 0) {
2967
+ return {
2968
+ check,
2969
+ description,
2970
+ passed: true,
2971
+ metadata: {
2972
+ info: "No provider-backed stages found"
2973
+ },
2974
+ durationMs: Date.now() - start
2975
+ };
2976
+ }
2977
+ const errors = [];
2978
+ const warnings = [];
2979
+ for (const providerId of providers) {
2980
+ try {
2981
+ const isEnabled = ProviderRegistry.isProviderEnabled(providerId);
2982
+ if (!isEnabled) {
2983
+ errors.push(`Provider '${providerId}' is not enabled`);
2984
+ continue;
2985
+ }
2986
+ if (isCodexProviderInsideCodexSandbox(providerId)) {
2987
+ errors.push(
2988
+ "Provider 'codex' cannot run from inside Codex's outer sandbox; run Codex-provider stages from a normal terminal or choose a different provider."
2989
+ );
2990
+ continue;
2991
+ }
2992
+ const providerConfig = ProviderRegistry.getProviderConfig(providerId);
2993
+ if (providerConfig?.experimental) {
2994
+ warnings.push(
2995
+ `Provider '${providerId}' is experimental; startup behavior may be less stable.`
2996
+ );
2997
+ }
2998
+ const health = getProviderHealth(providerId);
2999
+ if (health.lastFailurePhase && health.consecutiveFailures > 0) {
3000
+ warnings.push(
3001
+ `Provider '${providerId}' has recorded ${health.lastFailurePhase} failure history (health ${health.score.toFixed(2)}); watch for recurrence.`
3002
+ );
3003
+ }
3004
+ for (const stage of workflow.stages) {
3005
+ const pinnedModels = [];
3006
+ if (stage.provider === providerId) {
3007
+ pinnedModels.push({ provider: providerId, model: stage.model, stageId: stage.id });
3008
+ }
3009
+ if (stage.orchestrator?.orchestrationProvider === providerId) {
3010
+ pinnedModels.push({
3011
+ provider: providerId,
3012
+ model: stage.orchestrator.orchestrationModel,
3013
+ stageId: stage.id
3014
+ });
3015
+ }
3016
+ if (stage.competition?.variants) {
3017
+ for (const variant of stage.competition.variants) {
3018
+ if (variant.provider === providerId) {
3019
+ pinnedModels.push({
3020
+ provider: providerId,
3021
+ model: variant.model,
3022
+ stageId: stage.id
3023
+ });
3024
+ }
3025
+ }
3026
+ }
3027
+ for (const pinned of pinnedModels) {
3028
+ try {
3029
+ ProviderRegistry.resolveModel(pinned.provider, pinned.model);
3030
+ } catch (err) {
3031
+ errors.push(
3032
+ `Stage '${pinned.stageId}' pins invalid model for provider '${pinned.provider}': ` + (err instanceof Error ? err.message : String(err))
3033
+ );
3034
+ }
3035
+ }
3036
+ }
3037
+ const readiness = await getProviderReadiness(providerId, {
3038
+ mode: "execute",
3039
+ authProbeMode: "passive",
3040
+ ...authProbeEnv ? { authProbeEnv } : {}
3041
+ });
3042
+ if (readiness.known === false || readiness.configured === false) {
3043
+ errors.push(
3044
+ `Provider '${providerId}' is unavailable: ${readiness.reason ?? "provider is not configured for CLI execution"}`
3045
+ );
3046
+ } else if (!readiness.installed) {
3047
+ errors.push(
3048
+ `Provider '${providerId}' is unavailable: ${readiness.reason ?? "CLI is not installed"}`
3049
+ );
3050
+ } else if (!readiness.cliHealthy) {
3051
+ errors.push(
3052
+ `Provider '${providerId}' is unavailable: ${readiness.reason ?? "CLI health check failed"}`
3053
+ );
3054
+ } else if (readiness.sandboxCapable === false) {
3055
+ errors.push(
3056
+ `Provider '${providerId}' is unavailable: ${readiness.sandboxReason ?? readiness.reason ?? "sandbox capability check failed"}`
3057
+ );
3058
+ } else if (readiness.authenticated === "no") {
3059
+ errors.push(
3060
+ `Provider '${providerId}' has its CLI but authentication is missing.` + (readiness.authCommand ? ` ${readiness.authCommand}` : "")
3061
+ );
3062
+ } else if (readiness.authenticated === "unknown") {
3063
+ warnings.push(
3064
+ `Provider '${providerId}' auth status is unreported by passive preflight; runtime will defer to the provider CLI.`
3065
+ );
3066
+ }
3067
+ } catch (err) {
3068
+ warnings.push(
3069
+ `Could not verify provider '${providerId}': ${err instanceof Error ? err.message : String(err)}`
3070
+ );
3071
+ }
3072
+ }
3073
+ if (errors.length > 0) {
3074
+ return {
3075
+ check,
3076
+ description,
3077
+ passed: false,
3078
+ error: errors.join("\n"),
3079
+ durationMs: Date.now() - start
3080
+ };
3081
+ }
3082
+ return {
3083
+ check,
3084
+ description,
3085
+ passed: true,
3086
+ warning: warnings.length > 0 ? warnings.join("\n") : void 0,
3087
+ metadata: implicitDefaultProviderStages.length > 0 ? { implicitDefaultProvider: defaultProvider, implicitDefaultProviderStages } : void 0,
3088
+ durationMs: Date.now() - start
3089
+ };
3090
+ } catch (err) {
3091
+ return {
3092
+ check,
3093
+ description,
3094
+ passed: false,
3095
+ error: `Provider auth check failed: ${err instanceof Error ? err.message : String(err)}`,
3096
+ durationMs: Date.now() - start
3097
+ };
3098
+ }
3099
+ }
3100
+ async function checkArtifactRefs(workflow) {
3101
+ const start = Date.now();
3102
+ const check = "artifact-refs";
3103
+ const description = "All artifact references are valid (DAG validation)";
3104
+ try {
3105
+ const errors = [];
3106
+ const stageOutputs = /* @__PURE__ */ new Map();
3107
+ for (const stage of workflow.stages) {
3108
+ const outputs = /* @__PURE__ */ new Set();
3109
+ if (stage.outputs) {
3110
+ for (const outputName of Object.keys(stage.outputs)) {
3111
+ outputs.add(outputName);
3112
+ }
3113
+ }
3114
+ stageOutputs.set(stage.id, outputs);
3115
+ }
3116
+ const stageOrder = /* @__PURE__ */ new Map();
3117
+ for (let i = 0; i < workflow.stages.length; i++) {
3118
+ const stage = workflow.stages[i];
3119
+ if (stage) {
3120
+ stageOrder.set(stage.id, i);
3121
+ }
3122
+ }
3123
+ const stageMap = new Map(workflow.stages.map((stage) => [stage.id, stage]));
3124
+ const closureCache = /* @__PURE__ */ new Map();
3125
+ const getDependencyClosure = (stageId) => {
3126
+ const cached = closureCache.get(stageId);
3127
+ if (cached) return cached;
3128
+ const closure = /* @__PURE__ */ new Set();
3129
+ const visit = (depId) => {
3130
+ if (closure.has(depId)) return;
3131
+ closure.add(depId);
3132
+ for (const transitiveDep of stageMap.get(depId)?.deps ?? []) {
3133
+ visit(transitiveDep);
3134
+ }
3135
+ };
3136
+ for (const dep of stageMap.get(stageId)?.deps ?? []) {
3137
+ visit(dep);
3138
+ }
3139
+ closureCache.set(stageId, closure);
3140
+ return closure;
3141
+ };
3142
+ for (const stage of workflow.stages) {
3143
+ if (!stage.inputs || stage.inputs.length === 0) continue;
3144
+ const stageIndex = stageOrder.get(stage.id) ?? 0;
3145
+ for (const ref of stage.inputs) {
3146
+ try {
3147
+ const { stageId, outputName } = parseArtifactRef(ref);
3148
+ if (!stageOutputs.has(stageId)) {
3149
+ errors.push(
3150
+ `Stage '${stage.id}' references artifact '${ref}' from non-existent stage '${stageId}'`
3151
+ );
3152
+ continue;
3153
+ }
3154
+ const outputs = stageOutputs.get(stageId);
3155
+ if (!outputs) {
3156
+ errors.push(
3157
+ `Stage '${stage.id}' references artifact '${ref}' from stage '${stageId}', but that stage has no recorded outputs`
3158
+ );
3159
+ continue;
3160
+ }
3161
+ if (!outputs.has(outputName)) {
3162
+ const available = outputs.size > 0 ? Array.from(outputs).join(", ") : "none";
3163
+ errors.push(
3164
+ `Stage '${stage.id}' references output '${outputName}' which doesn't exist in stage '${stageId}'. Available: [${available}]`
3165
+ );
3166
+ continue;
3167
+ }
3168
+ const sourceIndex = stageOrder.get(stageId) ?? 0;
3169
+ if (sourceIndex >= stageIndex) {
3170
+ errors.push(
3171
+ `Stage '${stage.id}' has forward reference to '${ref}' (source stage '${stageId}' must come before this stage)`
3172
+ );
3173
+ continue;
3174
+ }
3175
+ if (!getDependencyClosure(stage.id).has(stageId)) {
3176
+ errors.push(
3177
+ `Stage '${stage.id}' references artifact '${ref}' but does not depend on producer stage '${stageId}'`
3178
+ );
3179
+ }
3180
+ } catch (err) {
3181
+ errors.push(
3182
+ `Stage '${stage.id}' has invalid artifact reference '${ref}': ${err instanceof Error ? err.message : String(err)}`
3183
+ );
3184
+ }
3185
+ }
3186
+ }
3187
+ if (errors.length > 0) {
3188
+ return {
3189
+ check,
3190
+ description,
3191
+ passed: false,
3192
+ error: errors.join("\n"),
3193
+ durationMs: Date.now() - start
3194
+ };
3195
+ }
3196
+ return {
3197
+ check,
3198
+ description,
3199
+ passed: true,
3200
+ durationMs: Date.now() - start
3201
+ };
3202
+ } catch (err) {
3203
+ return {
3204
+ check,
3205
+ description,
3206
+ passed: false,
3207
+ error: `Artifact reference validation failed: ${err instanceof Error ? err.message : String(err)}`,
3208
+ durationMs: Date.now() - start
3209
+ };
3210
+ }
3211
+ }
3212
+ async function checkConfig(workflow) {
3213
+ const start = Date.now();
3214
+ const check = "config-validation";
3215
+ const description = "Workflow configuration is valid";
3216
+ try {
3217
+ const result = validateWorkflowConfig(workflow);
3218
+ if (!result.valid) {
3219
+ return {
3220
+ check,
3221
+ description,
3222
+ passed: false,
3223
+ error: result.error || "Configuration validation failed",
3224
+ durationMs: Date.now() - start
3225
+ };
3226
+ }
3227
+ return {
3228
+ check,
3229
+ description,
3230
+ passed: true,
3231
+ warning: result.warnings?.join("\n"),
3232
+ durationMs: Date.now() - start
3233
+ };
3234
+ } catch (err) {
3235
+ return {
3236
+ check,
3237
+ description,
3238
+ passed: false,
3239
+ error: `Configuration validation error: ${err instanceof Error ? err.message : String(err)}`,
3240
+ durationMs: Date.now() - start
3241
+ };
3242
+ }
3243
+ }
3244
+ async function checkContextUtilization(workflow, cwd) {
3245
+ const start = Date.now();
3246
+ const check = "context-utilization";
3247
+ const description = "Prompt context fits within context window limits";
3248
+ try {
3249
+ const estimator = new TokenEstimator();
3250
+ const warnings = [];
3251
+ const WARN_THRESHOLD = 0.65;
3252
+ const defaultProvider = resolveDefaultProvider(void 0, cwd).value;
3253
+ for (const stage of workflow.stages) {
3254
+ if (!stage.prompt) continue;
3255
+ const provider = stage.provider || defaultProvider;
3256
+ const model = stage.model || applyFlywheelModelDefaults(stage, provider);
3257
+ if (!model) continue;
3258
+ const decision = await estimator.analyzeTask(stage.prompt, provider, model);
3259
+ if (decision.utilizationPercent / 100 >= WARN_THRESHOLD) {
3260
+ const utilizationPct = decision.utilizationPercent.toFixed(1);
3261
+ const adjustedPct = decision.adjustedUtilizationPercent.toFixed(1);
3262
+ warnings.push(
3263
+ `Stage '${stage.id}': Prompt context utilization is ${utilizationPct}% (${adjustedPct}% with overhead) of ${decision.contextWindow.toLocaleString()} token context window. Estimated: ${decision.estimatedTokens.toLocaleString()} tokens. Consider splitting task or reducing context to improve reliability.`
3264
+ );
3265
+ }
3266
+ if (stage.competition?.variants && stage.prompt) {
3267
+ for (const variant of stage.competition.variants) {
3268
+ const variantProvider = variant.provider || provider;
3269
+ const variantModel = variant.model || applyFlywheelModelDefaults(stage, variantProvider);
3270
+ if (!variantModel) continue;
3271
+ if (variantProvider === provider && variantModel === model) continue;
3272
+ const variantDecision = await estimator.analyzeTask(
3273
+ stage.prompt,
3274
+ variantProvider,
3275
+ variantModel
3276
+ );
3277
+ if (variantDecision.utilizationPercent / 100 >= WARN_THRESHOLD) {
3278
+ const utilizationPct = variantDecision.utilizationPercent.toFixed(1);
3279
+ const adjustedPct = variantDecision.adjustedUtilizationPercent.toFixed(1);
3280
+ warnings.push(
3281
+ `Stage '${stage.id}' variant '${[variant.provider, variant.model].filter(Boolean).join(":")}': Prompt context utilization is ${utilizationPct}% (${adjustedPct}% with overhead) of ${variantDecision.contextWindow.toLocaleString()} token context window. Estimated: ${variantDecision.estimatedTokens.toLocaleString()} tokens. Consider splitting task or reducing context to improve reliability.`
3282
+ );
3283
+ }
3284
+ }
3285
+ }
3286
+ }
3287
+ estimator.cleanup();
3288
+ return {
3289
+ check,
3290
+ description,
3291
+ passed: true,
3292
+ warning: warnings.length > 0 ? warnings.join("\n") : void 0,
3293
+ durationMs: Date.now() - start
3294
+ };
3295
+ } catch (err) {
3296
+ return {
3297
+ check,
3298
+ description,
3299
+ passed: true,
3300
+ warning: `Context utilization check failed: ${err instanceof Error ? err.message : String(err)}`,
3301
+ durationMs: Date.now() - start
3302
+ };
3303
+ }
3304
+ }
3305
+ function getStringProp(obj, key) {
3306
+ if (typeof obj === "object" && obj !== null) {
3307
+ const val = obj[key];
3308
+ return typeof val === "string" ? val : void 0;
3309
+ }
3310
+ return void 0;
3311
+ }
3312
+ function extractFileRefs(prompt) {
3313
+ return parseFileDirectiveLines(prompt).map((line) => line.path);
3314
+ }
3315
+ function extractCreateAnnotatedFileRefs(prompt) {
3316
+ return new Set(extractCreateAnnotatedFilePaths(prompt));
3317
+ }
3318
+ async function checkTaskSpecFreshness(workflow, cwd) {
3319
+ const start = Date.now();
3320
+ const check = "task-spec-freshness";
3321
+ const description = "Task spec files exist and git refs are valid";
3322
+ const errors = [];
3323
+ const warnings = [];
3324
+ try {
3325
+ const displayPath = (filePath) => path4.relative(cwd, filePath).replace(/\\/g, "/");
3326
+ for (const stage of workflow.stages) {
3327
+ const stageUnknown = stage;
3328
+ const taskId = getStringProp(stageUnknown, "taskId");
3329
+ if (taskId) {
3330
+ const taskBucketPath = getBucketPathByKind("task", cwd);
3331
+ const taskFilePath = path4.join(taskBucketPath, `${taskId}.md`);
3332
+ try {
3333
+ await fs3.access(taskFilePath);
3334
+ } catch {
3335
+ errors.push(`Stage '${stage.id}': task file not found: ${displayPath(taskFilePath)}`);
3336
+ }
3337
+ }
3338
+ const backlogItemId = getStringProp(stageUnknown, "backlogItemId");
3339
+ if (backlogItemId) {
3340
+ const backlogBucketPath = getBucketPathByKind("backlog", cwd);
3341
+ const backlogFilePath = path4.join(backlogBucketPath, `${backlogItemId}.md`);
3342
+ try {
3343
+ await fs3.access(backlogFilePath);
3344
+ } catch {
3345
+ errors.push(
3346
+ `Stage '${stage.id}': backlog file not found: ${displayPath(backlogFilePath)}`
3347
+ );
3348
+ }
3349
+ }
3350
+ if (stage.prompt) {
3351
+ const fileRefs = extractFileRefs(stage.prompt);
3352
+ const createAnnotatedRefs = extractCreateAnnotatedFileRefs(stage.prompt);
3353
+ for (const fileRef of fileRefs) {
3354
+ const filePath = path4.isAbsolute(fileRef) ? fileRef : path4.join(cwd, fileRef);
3355
+ try {
3356
+ await fs3.access(filePath);
3357
+ } catch {
3358
+ if (!createAnnotatedRefs.has(fileRef)) {
3359
+ errors.push(`Stage '${stage.id}': FILE reference not found on disk: ${fileRef}`);
3360
+ }
3361
+ }
3362
+ }
3363
+ }
3364
+ const baseRef = getStringProp(stageUnknown, "baseRef");
3365
+ if (baseRef) {
3366
+ try {
3367
+ await execa2("git", ["rev-parse", "--verify", baseRef], {
3368
+ cwd,
3369
+ stdio: "pipe",
3370
+ timeout: 1e4
3371
+ });
3372
+ } catch {
3373
+ errors.push(`Stage '${stage.id}': baseRef '${baseRef}' does not exist in git repository`);
3374
+ }
3375
+ }
3376
+ const baseSha = getStringProp(stageUnknown, "baseSha");
3377
+ if (baseSha) {
3378
+ try {
3379
+ await execa2("git", ["rev-parse", "--verify", baseSha], {
3380
+ cwd,
3381
+ stdio: "pipe",
3382
+ timeout: 1e4
3383
+ });
3384
+ try {
3385
+ await execa2("git", ["merge-base", "--is-ancestor", baseSha, "HEAD"], {
3386
+ cwd,
3387
+ stdio: "pipe",
3388
+ timeout: 1e4
3389
+ });
3390
+ } catch {
3391
+ warnings.push(
3392
+ `Stage '${stage.id}': baseSha '${baseSha}' exists but is not an ancestor of HEAD \u2014 may reference a rebased or force-pushed commit`
3393
+ );
3394
+ }
3395
+ } catch {
3396
+ errors.push(
3397
+ `Stage '${stage.id}': baseSha '${baseSha}' does not exist in git repository (SHA unreachable \u2014 commit may have been pruned or the ref was never pushed)`
3398
+ );
3399
+ }
3400
+ }
3401
+ }
3402
+ if (errors.length > 0) {
3403
+ return {
3404
+ check,
3405
+ description,
3406
+ passed: false,
3407
+ error: errors.join("\n"),
3408
+ warning: warnings.length > 0 ? warnings.join("\n") : void 0,
3409
+ durationMs: Date.now() - start
3410
+ };
3411
+ }
3412
+ return {
3413
+ check,
3414
+ description,
3415
+ passed: true,
3416
+ warning: warnings.length > 0 ? warnings.join("\n") : void 0,
3417
+ durationMs: Date.now() - start
3418
+ };
3419
+ } catch (err) {
3420
+ return {
3421
+ check,
3422
+ description,
3423
+ passed: false,
3424
+ error: `Task spec freshness check failed: ${err instanceof Error ? err.message : String(err)}`,
3425
+ durationMs: Date.now() - start
3426
+ };
3427
+ }
3428
+ }
3429
+ async function collectBuildInputFiles(dir) {
3430
+ let entries;
3431
+ try {
3432
+ entries = await fs3.readdir(dir, { withFileTypes: true });
3433
+ } catch (error) {
3434
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
3435
+ return [];
3436
+ }
3437
+ throw error;
3438
+ }
3439
+ const files = [];
3440
+ for (const entry of entries) {
3441
+ const fullPath = path4.join(dir, entry.name);
3442
+ if (entry.isDirectory()) {
3443
+ if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") {
3444
+ continue;
3445
+ }
3446
+ files.push(...await collectBuildInputFiles(fullPath));
3447
+ continue;
3448
+ }
3449
+ if (!entry.isFile()) continue;
3450
+ if (!entry.name.endsWith(".ts") && !entry.name.endsWith(".tsx")) continue;
3451
+ if (entry.name.endsWith(".test.ts") || entry.name.endsWith(".spec.ts")) continue;
3452
+ files.push(fullPath);
3453
+ }
3454
+ return files;
3455
+ }
3456
+ async function collectGeneratedProviderFiles(dir) {
3457
+ let entries;
3458
+ try {
3459
+ entries = await fs3.readdir(dir, { withFileTypes: true });
3460
+ } catch (error) {
3461
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
3462
+ return [];
3463
+ }
3464
+ throw error;
3465
+ }
3466
+ const files = [];
3467
+ for (const entry of entries) {
3468
+ const fullPath = path4.join(dir, entry.name);
3469
+ if (entry.isDirectory()) {
3470
+ files.push(...await collectGeneratedProviderFiles(fullPath));
3471
+ continue;
3472
+ }
3473
+ if (!entry.isFile()) continue;
3474
+ if (entry.name.endsWith(".lock")) continue;
3475
+ if (!entry.name.endsWith(".json")) continue;
3476
+ files.push(fullPath);
3477
+ }
3478
+ return files;
3479
+ }
3480
+ var BUILD_INPUT_PATHS = [
3481
+ "src",
3482
+ "package.json",
3483
+ "tsconfig.json",
3484
+ "tsup.config.ts",
3485
+ ".build-project/config/providers.yaml",
3486
+ "providers"
3487
+ ];
3488
+ function parseLineOutput(stdout) {
3489
+ return stdout.split("\n").map((line) => line.trim()).filter(Boolean);
3490
+ }
3491
+ async function getDirtyBuildInputPaths(projectRoot) {
3492
+ try {
3493
+ const tracked = await execa2(
3494
+ "git",
3495
+ ["diff", "--name-only", "HEAD", "--", ...BUILD_INPUT_PATHS],
3496
+ {
3497
+ cwd: projectRoot,
3498
+ stdio: "pipe",
3499
+ reject: false
3500
+ }
3501
+ );
3502
+ const untracked = await execa2(
3503
+ "git",
3504
+ ["ls-files", "--others", "--exclude-standard", "--", ...BUILD_INPUT_PATHS],
3505
+ {
3506
+ cwd: projectRoot,
3507
+ stdio: "pipe",
3508
+ reject: false
3509
+ }
3510
+ );
3511
+ if (tracked.exitCode !== 0 || untracked.exitCode !== 0) {
3512
+ return null;
3513
+ }
3514
+ return new Set(
3515
+ [...parseLineOutput(tracked.stdout), ...parseLineOutput(untracked.stdout)].map(
3516
+ (relativePath) => path4.join(projectRoot, relativePath)
3517
+ )
3518
+ );
3519
+ } catch {
3520
+ return null;
3521
+ }
3522
+ }
3523
+ async function hasSourceBuildInputs(projectRoot) {
3524
+ try {
3525
+ await fs3.stat(path4.join(projectRoot, "src"));
3526
+ await fs3.stat(path4.join(projectRoot, "tsup.config.ts"));
3527
+ return true;
3528
+ } catch (error) {
3529
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
3530
+ return false;
3531
+ }
3532
+ throw error;
3533
+ }
3534
+ }
3535
+ async function getLatestCommittedBuildInput(projectRoot) {
3536
+ try {
3537
+ const latestCommit = await execa2(
3538
+ "git",
3539
+ ["log", "-1", "--format=%ct%x00%H", "--", ...BUILD_INPUT_PATHS],
3540
+ {
3541
+ cwd: projectRoot,
3542
+ stdio: "pipe",
3543
+ reject: false
3544
+ }
3545
+ );
3546
+ if (latestCommit.exitCode !== 0 || !latestCommit.stdout) {
3547
+ return null;
3548
+ }
3549
+ const [timestampRaw, commitSha] = latestCommit.stdout.split("\0");
3550
+ const committedAtMs = Number.parseInt(timestampRaw ?? "", 10) * 1e3;
3551
+ if (!Number.isFinite(committedAtMs) || !commitSha) {
3552
+ return null;
3553
+ }
3554
+ const latestPaths = await execa2(
3555
+ "git",
3556
+ ["diff-tree", "--no-commit-id", "--name-only", "-r", commitSha, "--", ...BUILD_INPUT_PATHS],
3557
+ {
3558
+ cwd: projectRoot,
3559
+ stdio: "pipe",
3560
+ reject: false
3561
+ }
3562
+ );
3563
+ const latestPath = latestPaths.exitCode === 0 ? parseLineOutput(latestPaths.stdout)[0] : void 0;
3564
+ return {
3565
+ path: latestPath,
3566
+ committedAtMs
3567
+ };
3568
+ } catch {
3569
+ return null;
3570
+ }
3571
+ }
3572
+ function resolveBuildPackageRoot() {
3573
+ const configuredRoot = process.env[PACKAGE_ROOT_ENV]?.trim();
3574
+ if (configuredRoot) return path4.resolve(configuredRoot);
3575
+ let current = path4.dirname(fileURLToPath(import.meta.url));
3576
+ let distOnlyPackageRoot;
3577
+ while (true) {
3578
+ if (path4.basename(current) === "dist" && existsSync2(path4.join(current, "entry.js"))) {
3579
+ distOnlyPackageRoot ??= path4.dirname(current);
3580
+ }
3581
+ const packageJsonPath = path4.join(current, "package.json");
3582
+ if (existsSync2(packageJsonPath)) {
3583
+ try {
3584
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
3585
+ if (packageJson.name === "@ebrindley/build") {
3586
+ return current;
3587
+ }
3588
+ } catch {
3589
+ }
3590
+ }
3591
+ const parent = path4.dirname(current);
3592
+ if (parent === current) {
3593
+ return distOnlyPackageRoot ?? getProjectRoot();
3594
+ }
3595
+ current = parent;
3596
+ }
3597
+ }
3598
+ async function checkBuildFreshness(projectRoot = resolveBuildPackageRoot()) {
3599
+ const start = Date.now();
3600
+ const check = "build-freshness";
3601
+ const description = "Built CLI artifacts are up-to-date";
3602
+ const distEntry = path4.join(projectRoot, "dist", "entry.js");
3603
+ try {
3604
+ let distStat;
3605
+ try {
3606
+ distStat = await fs3.stat(distEntry);
3607
+ } catch {
3608
+ return {
3609
+ check,
3610
+ description,
3611
+ passed: false,
3612
+ error: "dist/entry.js not found. Run `npm run build` before executing the pipeline.",
3613
+ durationMs: Date.now() - start
3614
+ };
3615
+ }
3616
+ const distMtimeMs = distStat.mtimeMs;
3617
+ const entryIntegrity = await readDistEntryIntegrity(projectRoot);
3618
+ if (!entryIntegrity.readable) {
3619
+ return {
3620
+ check,
3621
+ description,
3622
+ passed: false,
3623
+ error: `Build is unusable: dist/entry.js could not be read (${entryIntegrity.reason}). Run \`npm run build\` before executing.`,
3624
+ durationMs: Date.now() - start
3625
+ };
3626
+ }
3627
+ if (entryIntegrity.missingChunks.length > 0) {
3628
+ return {
3629
+ check,
3630
+ description,
3631
+ passed: false,
3632
+ error: `Build is inconsistent: dist/entry.js references missing dist/${entryIntegrity.missingChunks[0]}. Run \`npm run build\` before executing.`,
3633
+ durationMs: Date.now() - start
3634
+ };
3635
+ }
3636
+ const sourceBuildInputsPresent = await hasSourceBuildInputs(projectRoot);
3637
+ if (!sourceBuildInputsPresent) {
3638
+ return {
3639
+ check,
3640
+ description,
3641
+ passed: true,
3642
+ metadata: { packageMode: true },
3643
+ durationMs: Date.now() - start
3644
+ };
3645
+ }
3646
+ const buildInputs = [
3647
+ ...await collectBuildInputFiles(path4.join(projectRoot, "src")),
3648
+ ...await collectGeneratedProviderFiles(path4.join(projectRoot, "providers")),
3649
+ ...BUILD_INPUT_PATHS.slice(1).filter((relativePath) => relativePath !== "providers").map((relativePath) => path4.join(projectRoot, relativePath))
3650
+ ];
3651
+ const dirtyBuildInputPaths = await getDirtyBuildInputPaths(projectRoot);
3652
+ const latestCommittedBuildInput = dirtyBuildInputPaths ? await getLatestCommittedBuildInput(projectRoot) : null;
3653
+ let newestInputPath;
3654
+ let newestInputMtimeMs = Number.NEGATIVE_INFINITY;
3655
+ for (const buildInput of buildInputs) {
3656
+ try {
3657
+ const inputStat = await fs3.stat(buildInput);
3658
+ const isDirty = dirtyBuildInputPaths?.has(buildInput) ?? true;
3659
+ if (isDirty && inputStat.mtimeMs > distMtimeMs) {
3660
+ return {
3661
+ check,
3662
+ description,
3663
+ passed: false,
3664
+ error: `Build is stale: ${path4.relative(projectRoot, buildInput)} is newer than dist/entry.js. Run \`npm run build\` before executing.`,
3665
+ durationMs: Date.now() - start
3666
+ };
3667
+ }
3668
+ if (inputStat.mtimeMs > newestInputMtimeMs) {
3669
+ newestInputMtimeMs = inputStat.mtimeMs;
3670
+ newestInputPath = buildInput;
3671
+ }
3672
+ } catch (error) {
3673
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
3674
+ continue;
3675
+ }
3676
+ throw error;
3677
+ }
3678
+ }
3679
+ if (latestCommittedBuildInput && latestCommittedBuildInput.committedAtMs > distMtimeMs) {
3680
+ const rel = latestCommittedBuildInput.path ? latestCommittedBuildInput.path : BUILD_INPUT_PATHS.join(", ");
3681
+ return {
3682
+ check,
3683
+ description,
3684
+ passed: false,
3685
+ error: `Build is stale: ${rel} is newer than dist/entry.js. Run \`npm run build\` before executing.`,
3686
+ durationMs: Date.now() - start
3687
+ };
3688
+ }
3689
+ if (!dirtyBuildInputPaths && newestInputPath && newestInputMtimeMs > distMtimeMs) {
3690
+ const rel = path4.relative(projectRoot, newestInputPath);
3691
+ return {
3692
+ check,
3693
+ description,
3694
+ passed: false,
3695
+ error: `Build is stale: ${rel} is newer than dist/entry.js. Run \`npm run build\` before executing.`,
3696
+ durationMs: Date.now() - start
3697
+ };
3698
+ }
3699
+ return {
3700
+ check,
3701
+ description,
3702
+ passed: true,
3703
+ durationMs: Date.now() - start
3704
+ };
3705
+ } catch (err) {
3706
+ return {
3707
+ check,
3708
+ description,
3709
+ passed: false,
3710
+ error: `Build freshness check failed: ${err instanceof Error ? err.message : String(err)}`,
3711
+ durationMs: Date.now() - start
3712
+ };
3713
+ }
3714
+ }
3715
+ async function describeInstalledDependencyDrift() {
3716
+ const projectRoot = resolveBuildPackageRoot();
3717
+ const drift = await findInstalledDependencyDrift(projectRoot);
3718
+ return formatInstalledDependencyDriftWarning(drift, projectRoot);
3719
+ }
3720
+ async function ensureFreshBuild(options) {
3721
+ if (process.env.BUILD_CLI_REEXEC_AFTER_BUILD === "1") {
3722
+ return { fresh: true, rebuilt: false };
3723
+ }
3724
+ const initialResult = await checkBuildFreshness();
3725
+ if (initialResult.passed) {
3726
+ return { fresh: true, rebuilt: false };
3727
+ }
3728
+ const originalError = initialResult.error ?? "Build is stale or missing.";
3729
+ const quiet = options?.quiet ?? isQuietOutputRequested();
3730
+ const projectRoot = resolveBuildPackageRoot();
3731
+ const entryExists = existsSync2(path4.join(projectRoot, "dist", "entry.js"));
3732
+ const entryIntegrity = entryExists ? await readDistEntryIntegrity(projectRoot) : null;
3733
+ const ownership = await resolveDistOwnership(projectRoot);
3734
+ const decision = decideDistRebuild(ownership, {
3735
+ artifactExists: entryExists,
3736
+ artifactRelativePath: "dist/entry.js",
3737
+ missingBundledFiles: entryIntegrity?.readable ? entryIntegrity.missingChunks : [],
3738
+ unusableReason: entryIntegrity && !entryIntegrity.readable ? `it could not be read (${entryIntegrity.reason})` : void 0
3739
+ });
3740
+ if (decision.action === "fail") {
3741
+ throw new Error(decision.message);
3742
+ }
3743
+ if (decision.action === "proceed") {
3744
+ if (options?.requireFresh) {
3745
+ throw new Error(originalError);
3746
+ }
3747
+ if (!quiet && isVerboseOutputRequested()) {
3748
+ console.log(decision.diagnostic);
3749
+ }
3750
+ return { fresh: true, rebuilt: false };
3751
+ }
3752
+ if (!quiet) {
3753
+ console.log(chalk.yellow("Build is stale. Auto-rebuilding..."));
3754
+ }
3755
+ const buildResult = await execa2("npm", ["run", "build"], {
3756
+ cwd: projectRoot,
3757
+ stdio: quiet ? ["ignore", "pipe", "pipe"] : "inherit",
3758
+ reject: false
3759
+ });
3760
+ if (buildResult.exitCode !== 0) {
3761
+ const output = [buildResult.stderr, buildResult.stdout].map((value) => value?.trim()).filter(Boolean).join("\n").slice(0, 4e3);
3762
+ throw new Error(
3763
+ `npm run build failed with exit code ${buildResult.exitCode}${output ? `:
3764
+ ${output}` : ""}`
3765
+ );
3766
+ }
3767
+ await writeDistBuildStamp(projectRoot, { ownership });
3768
+ const verifyResult = await checkBuildFreshness(projectRoot);
3769
+ if (verifyResult.passed) {
3770
+ return { fresh: true, rebuilt: true };
3771
+ }
3772
+ throw new Error(`${originalError} Auto-rebuild did not resolve the issue.`);
3773
+ }
3774
+ var CLI_REEXEC_ENV = "BUILD_CLI_REEXEC_AFTER_BUILD";
3775
+ var JSONL_CAPTURE_FILE_ENV = "BUILD_JSONL_CAPTURE_FILE";
3776
+ var REEXEC_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
3777
+ function signalToExitCode(signal) {
3778
+ switch (signal) {
3779
+ case "SIGINT":
3780
+ return 130;
3781
+ case "SIGTERM":
3782
+ return 143;
3783
+ case "SIGHUP":
3784
+ return 129;
3785
+ default:
3786
+ return signal ? 1 : null;
3787
+ }
3788
+ }
3789
+ function resolveCliReexecEntry() {
3790
+ const packageRoot = resolveBuildPackageRoot();
3791
+ const fallbackEntry = path4.join(packageRoot, "dist", "entry.js");
3792
+ const invokedEntry = process.argv[1] ? path4.resolve(process.argv[1]) : fallbackEntry;
3793
+ return invokedEntry.endsWith(".js") || invokedEntry.endsWith(".mjs") ? invokedEntry : fallbackEntry;
3794
+ }
3795
+ function writeJsonStdoutText(text, stdoutFilePath) {
3796
+ if (!text) return;
3797
+ if (stdoutFilePath) {
3798
+ appendFileSync(stdoutFilePath, text, "utf8");
3799
+ return;
3800
+ }
3801
+ writeAllSyncToFd(1, text);
3802
+ }
3803
+ function argvRequestsJsonOutput(argv) {
3804
+ return argv.includes("--json") || argv.includes("-j") || argv.includes("--output=json");
3805
+ }
3806
+ async function reexecCurrentCliCommand(cwd) {
3807
+ const entryPath = resolveCliReexecEntry();
3808
+ const captureJsonStdout = isJsonOutputRequested() || argvRequestsJsonOutput(process.argv);
3809
+ const jsonCaptureDir = captureJsonStdout ? await fs3.mkdtemp(path4.join(os2.tmpdir(), "build-jsonl-reexec-")) : void 0;
3810
+ const jsonCaptureFile = jsonCaptureDir ? path4.join(jsonCaptureDir, "stdout.jsonl") : void 0;
3811
+ const stdoutFilePath = captureJsonStdout ? resolveStdoutRegularFilePath() : void 0;
3812
+ signalCoordinator.suspendProcessSignalHandlers();
3813
+ let child = null;
3814
+ let pendingSignal = null;
3815
+ const forwardSignal = (signal) => {
3816
+ if (!child) {
3817
+ pendingSignal = signal;
3818
+ return;
3819
+ }
3820
+ if (signal === "SIGINT" && process.stdin.isTTY) {
3821
+ return;
3822
+ }
3823
+ if (process.platform !== "win32" && typeof child.pid === "number") {
3824
+ try {
3825
+ process.kill(child.pid, signal);
3826
+ return;
3827
+ } catch {
3828
+ }
3829
+ }
3830
+ child.kill?.(signal);
3831
+ };
3832
+ const forwardSigint = () => forwardSignal("SIGINT");
3833
+ const forwardSigterm = () => forwardSignal("SIGTERM");
3834
+ const forwardSighup = () => forwardSignal("SIGHUP");
3835
+ const forwarders = {
3836
+ SIGINT: forwardSigint,
3837
+ SIGTERM: forwardSigterm,
3838
+ SIGHUP: forwardSighup
3839
+ };
3840
+ for (const signal of REEXEC_SIGNALS) {
3841
+ process.on(signal, forwarders[signal]);
3842
+ }
3843
+ const exitCode = await (async () => {
3844
+ try {
3845
+ child = execa2(process.execPath, [entryPath, ...process.argv.slice(2)], {
3846
+ cwd: cwd ?? getProjectRoot(),
3847
+ stdio: captureJsonStdout ? ["inherit", "pipe", "inherit"] : "inherit",
3848
+ reject: false,
3849
+ env: {
3850
+ ...process.env,
3851
+ [CLI_REEXEC_ENV]: "1",
3852
+ ...jsonCaptureFile ? { [JSONL_CAPTURE_FILE_ENV]: jsonCaptureFile } : {}
3853
+ }
3854
+ });
3855
+ if (pendingSignal) {
3856
+ forwardSignal(pendingSignal);
3857
+ }
3858
+ const result = await child;
3859
+ if (jsonCaptureFile) {
3860
+ const captured = await fs3.readFile(jsonCaptureFile, "utf8").catch(() => "");
3861
+ if (captured) {
3862
+ writeJsonStdoutText(captured, stdoutFilePath);
3863
+ } else if (typeof result.stdout === "string" && result.stdout) {
3864
+ writeJsonStdoutText(result.stdout, stdoutFilePath);
3865
+ }
3866
+ }
3867
+ return result.exitCode ?? signalToExitCode(result.signal) ?? 1;
3868
+ } finally {
3869
+ if (jsonCaptureDir) {
3870
+ await fs3.rm(jsonCaptureDir, { recursive: true, force: true }).catch(() => void 0);
3871
+ }
3872
+ for (const signal of REEXEC_SIGNALS) {
3873
+ process.removeListener(signal, forwarders[signal]);
3874
+ }
3875
+ signalCoordinator.resumeProcessSignalHandlers();
3876
+ }
3877
+ })();
3878
+ process.exit(exitCode);
3879
+ }
3880
+ async function runWorkflowPreflight(workflow, options = {}) {
3881
+ const startTime = Date.now();
3882
+ const cwd = options.cwd || process.cwd();
3883
+ const worktreeRoot = options.worktreeHygiene?.baseDir ?? getWorktreeBaseDir();
3884
+ const skip = options.skip || {};
3885
+ const checks = [];
3886
+ const errors = [];
3887
+ const warnings = [];
3888
+ const repoCleanMode = options.repoCleanMode ?? "warn";
3889
+ const suppressExpectedSetupRepoDirtyWarning = options.suppressExpectedSetupRepoDirtyWarning ?? false;
3890
+ const runCheck = async (name, skipFlag, fn) => {
3891
+ if (skipFlag) {
3892
+ if (options.verbose) {
3893
+ console.log(` [SKIP] ${name}`);
3894
+ }
3895
+ return;
3896
+ }
3897
+ if (options.verbose) {
3898
+ process.stdout.write(` [....] ${name}`);
3899
+ }
3900
+ const result = await fn();
3901
+ checks.push(result);
3902
+ if (!result.passed && result.error) {
3903
+ errors.push(result.error);
3904
+ }
3905
+ if (result.warning) {
3906
+ warnings.push(result.warning);
3907
+ }
3908
+ if (options.verbose) {
3909
+ const status = result.passed ? result.warning ? "WARN" : "PASS" : "FAIL";
3910
+ process.stdout.write(`\r [${status}] ${name} (${result.durationMs}ms)
3911
+ `);
3912
+ }
3913
+ };
3914
+ if (options.verbose) {
3915
+ console.log("\nRunning preflight checks...\n");
3916
+ }
3917
+ await runCheck(
3918
+ "Build freshness",
3919
+ skip.buildFreshness || process.env[CLI_REEXEC_ENV] === "1",
3920
+ () => checkBuildFreshness()
3921
+ );
3922
+ await runCheck("Configuration validation", skip.configValidation, () => checkConfig(workflow));
3923
+ await runCheck(
3924
+ "Task spec freshness",
3925
+ skip.taskSpecFreshness,
3926
+ () => checkTaskSpecFreshness(workflow, cwd)
3927
+ );
3928
+ await runCheck(
3929
+ "Repository cleanliness",
3930
+ skip.repoClean,
3931
+ () => checkRepoClean(cwd, repoCleanMode, suppressExpectedSetupRepoDirtyWarning)
3932
+ );
3933
+ await runCheck("Worktree sanity", skip.worktreeSanity, () => checkWorktreeSanity(worktreeRoot));
3934
+ await runCheck(
3935
+ "Worktree hygiene",
3936
+ skip.worktreeHygiene,
3937
+ () => checkWorktreeHygiene(worktreeRoot, cwd, { ...options.worktreeHygiene, baseDir: worktreeRoot })
3938
+ );
3939
+ await runCheck(
3940
+ "Worktree recovery",
3941
+ skip.worktreeRecovery,
3942
+ () => checkWorktreeRecovery({ ...options.worktreeHygiene, baseDir: worktreeRoot })
3943
+ );
3944
+ await runCheck(
3945
+ "Provider authentication",
3946
+ skip.providerAuth,
3947
+ () => checkProviderAuth(workflow, cwd, options.authProbeEnv)
3948
+ );
3949
+ await runCheck("Artifact references", skip.artifactRefs, () => checkArtifactRefs(workflow));
3950
+ await runCheck(
3951
+ "Context window utilization",
3952
+ skip.contextUtilization,
3953
+ () => checkContextUtilization(workflow, cwd)
3954
+ );
3955
+ const totalDurationMs = Date.now() - startTime;
3956
+ const passed = errors.length === 0;
3957
+ const passedCount = checks.filter((c) => c.passed).length;
3958
+ const failedCount = checks.filter((c) => !c.passed).length;
3959
+ const warnCount = checks.filter((c) => c.warning).length;
3960
+ let summary = `Preflight: ${passedCount}/${checks.length} checks passed`;
3961
+ if (failedCount > 0) {
3962
+ summary += `, ${failedCount} failed`;
3963
+ }
3964
+ if (warnCount > 0) {
3965
+ summary += `, ${warnCount} warnings`;
3966
+ }
3967
+ summary += ` (${totalDurationMs}ms)`;
3968
+ if (options.verbose) {
3969
+ console.log(`
3970
+ ${passed ? "Preflight PASSED" : "Preflight FAILED"}: ${summary}
3971
+ `);
3972
+ if (errors.length > 0) {
3973
+ console.log("Errors:");
3974
+ for (const error of errors) {
3975
+ console.log(` - ${error}`);
3976
+ }
3977
+ }
3978
+ if (warnings.length > 0) {
3979
+ console.log("Warnings:");
3980
+ for (const warning of warnings) {
3981
+ console.log(` - ${warning}`);
3982
+ }
3983
+ }
3984
+ }
3985
+ return {
3986
+ passed,
3987
+ checks,
3988
+ errors,
3989
+ warnings,
3990
+ totalDurationMs,
3991
+ summary
3992
+ };
3993
+ }
3994
+ async function runPlanPreflight(cwd, options) {
3995
+ let buildResult;
3996
+ try {
3997
+ buildResult = await ensureFreshBuild({ requireFresh: true });
3998
+ } catch (error) {
3999
+ return { passed: false, error: error instanceof Error ? error.message : String(error) };
4000
+ }
4001
+ if (options?.reexecOnRebuild === true && buildResult.rebuilt && process.env[CLI_REEXEC_ENV] !== "1") {
4002
+ await reexecCurrentCliCommand(cwd);
4003
+ }
4004
+ const workingDir = cwd ?? process.cwd();
4005
+ const repoResult = await checkRepoClean(workingDir, "warn");
4006
+ if (!repoResult.passed) {
4007
+ const meta = repoResult.metadata;
4008
+ return {
4009
+ passed: false,
4010
+ error: meta?.hasConflictStatuses ? `${repoResult.error?.split("\n")[0] ?? "Repository has uncommitted changes"}
4011
+ Resolve conflicts before running planned build.` : repoResult.error
4012
+ };
4013
+ }
4014
+ if (repoResult.warning) {
4015
+ const meta = repoResult.metadata;
4016
+ if (meta?.hasConflictStatuses) {
4017
+ return {
4018
+ passed: false,
4019
+ error: `${repoResult.warning.split("\n")[0]}
4020
+ Resolve conflicts before running planned build.`
4021
+ };
4022
+ }
4023
+ return {
4024
+ passed: true,
4025
+ warning: repoResult.warning
4026
+ };
4027
+ }
4028
+ return { passed: true };
4029
+ }
4030
+
4031
+ export {
4032
+ UTILIZATION_WARN_PERCENT,
4033
+ UTILIZATION_SPLIT_PERCENT,
4034
+ TokenEstimator,
4035
+ recordSyncWriteNoProgress,
4036
+ writeAllSyncToFd,
4037
+ resolveStdoutRegularFilePath,
4038
+ getExtensionsRootDir,
4039
+ getExtensionDirs,
4040
+ runAsyncWithOptionalTimeout,
4041
+ cleanupWorkflowRelatedBranchesAndWorktrees,
4042
+ recoverPendingWorktrees,
4043
+ enforceRetentionBounds,
4044
+ getFlywheelCleanupDryRunWorktreeSummary,
4045
+ makeRetryDecision,
4046
+ validateGeneratedSubStageVerifyCommands,
4047
+ validateWorkflowConfig,
4048
+ BUILD_INPUT_PATHS,
4049
+ checkBuildFreshness,
4050
+ describeInstalledDependencyDrift,
4051
+ ensureFreshBuild,
4052
+ writeJsonStdoutText,
4053
+ reexecCurrentCliCommand,
4054
+ runWorkflowPreflight,
4055
+ runPlanPreflight
4056
+ };
4057
+ //# sourceMappingURL=chunk-ES6NWJ36.js.map