@dylanpiercey/work 0.2.17 → 0.2.18

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 (517) hide show
  1. package/README.md +13 -0
  2. package/bin/cli/README.md +34 -0
  3. package/bin/cli/_progress.mjs +68 -0
  4. package/bin/cli/_repo.mjs +32 -0
  5. package/bin/cli/_target.mjs +49 -0
  6. package/bin/cli/agent.mjs +153 -0
  7. package/bin/cli/branches.mjs +79 -0
  8. package/bin/cli/config.mjs +175 -0
  9. package/bin/cli/host.mjs +142 -0
  10. package/bin/cli/lsp.mjs +117 -0
  11. package/bin/cli/notify.mjs +53 -0
  12. package/bin/cli/open.mjs +37 -0
  13. package/bin/cli/repo.mjs +98 -0
  14. package/bin/cli/session.mjs +167 -0
  15. package/bin/cli/skill.mjs +99 -0
  16. package/bin/cli/stack.mjs +51 -0
  17. package/bin/cli/status.mjs +86 -0
  18. package/bin/cli/topic.mjs +585 -0
  19. package/bin/cli/workspace.mjs +184 -0
  20. package/bin/work.mjs +402 -80
  21. package/dist/_BAJGOEa1.js +168 -0
  22. package/dist/{_Bj-WwfnW.js → _BlLEM8du.js} +76 -46
  23. package/dist/_t9VsALwo.js +2 -0
  24. package/dist/index.mjs +3550 -2616
  25. package/dist/public/assets/{_CXVkXsw1.js → _10dZ1R0m.js} +1 -1
  26. package/dist/public/assets/_10dZ1R0m.js.br +0 -0
  27. package/dist/public/assets/_1EPD0XJG.js +1 -0
  28. package/dist/public/assets/_1jA3fFvQ.js +1 -0
  29. package/dist/public/assets/{_DZanHI_X.js → _4Sj6a8Ya.js} +1 -1
  30. package/dist/public/assets/_4Sj6a8Ya.js.br +0 -0
  31. package/dist/public/assets/{_Quh6tl1u.js → _5tN48iuJ.js} +1 -1
  32. package/dist/public/assets/_5tN48iuJ.js.br +0 -0
  33. package/dist/public/assets/{_CYQ4srZb.js → _8_DzkMgB.js} +1 -1
  34. package/dist/public/assets/_8_DzkMgB.js.br +0 -0
  35. package/dist/public/assets/{_CxgPm_xy.js → _9JG-F3GR.js} +1 -1
  36. package/dist/public/assets/_9JG-F3GR.js.br +0 -0
  37. package/dist/public/assets/_9KUM3Nch.js +2 -0
  38. package/dist/public/assets/_9KUM3Nch.js.br +0 -0
  39. package/dist/public/assets/{_BoEcMQmX.js → _B-H6ABVZ.js} +1 -1
  40. package/dist/public/assets/_B-H6ABVZ.js.br +0 -0
  41. package/dist/public/assets/{_D-0pUYXq.js → _B-iOtMg6.js} +1 -1
  42. package/dist/public/assets/_B-iOtMg6.js.br +0 -0
  43. package/dist/public/assets/{_CJgqLdsx.js → _B0FhzjB2.js} +1 -1
  44. package/dist/public/assets/_B0FhzjB2.js.br +0 -0
  45. package/dist/public/assets/{_C1-Nli75.js → _B26gPL08.js} +1 -1
  46. package/dist/public/assets/_B26gPL08.js.br +0 -0
  47. package/dist/public/assets/{_BA-MNdjL.js → _B2DHvTZR.js} +1 -1
  48. package/dist/public/assets/{_vX9d6g5Y.js → _B3Z1kRpD.js} +1 -1
  49. package/dist/public/assets/_B3Z1kRpD.js.br +0 -0
  50. package/dist/public/assets/{_BfQeZPXW.js → _B3sH8Usv.js} +1 -1
  51. package/dist/public/assets/_B3sH8Usv.js.br +0 -0
  52. package/dist/public/assets/_B42hLH2K.js +1 -0
  53. package/dist/public/assets/{_CY_Z-kV5.js → _B4hn3NNW.js} +1 -1
  54. package/dist/public/assets/_B4hn3NNW.js.br +0 -0
  55. package/dist/public/assets/{_DK-MOyi_.js → _B5NleRC6.js} +1 -1
  56. package/dist/public/assets/_B5NleRC6.js.br +0 -0
  57. package/dist/public/assets/_B5fRGxPQ.js +1 -0
  58. package/dist/public/assets/_B5fRGxPQ.js.br +0 -0
  59. package/dist/public/assets/_B65yR5Ej.js +1 -0
  60. package/dist/public/assets/_B6Sh9WEH.js +1 -0
  61. package/dist/public/assets/{_C7bHTlG1.js → _B6VtVybz.js} +1 -1
  62. package/dist/public/assets/_B6VtVybz.js.br +0 -0
  63. package/dist/public/assets/_B7rJ4n9b.js +1 -0
  64. package/dist/public/assets/_B8M-AD29.js +1 -0
  65. package/dist/public/assets/{_BQEpu56f.js → _B8joFpf1.js} +1 -1
  66. package/dist/public/assets/_B8joFpf1.js.br +0 -0
  67. package/dist/public/assets/_B9nA-Uh1.js +1 -0
  68. package/dist/public/assets/_B9nA-Uh1.js.br +0 -0
  69. package/dist/public/assets/_BA6X_Aj6.js +1 -0
  70. package/dist/public/assets/_BBFJV2Bx.js +1 -0
  71. package/dist/public/assets/_BCW5GFMW.js +1 -0
  72. package/dist/public/assets/{_TynjiShF.js → _BD1SQU0V.js} +1 -1
  73. package/dist/public/assets/_BD1SQU0V.js.br +0 -0
  74. package/dist/public/assets/_BEpd9Y1R.js +1 -0
  75. package/dist/public/assets/_BEy7R4W_.js +1 -0
  76. package/dist/public/assets/_BFGJOHQb.js +1 -0
  77. package/dist/public/assets/{_BiY8chB-.js → _BI45WgW9.js} +1 -1
  78. package/dist/public/assets/_BI45WgW9.js.br +0 -0
  79. package/dist/public/assets/_BIKvQKWe.js +1 -0
  80. package/dist/public/assets/_BIPnL2aI.js +1 -0
  81. package/dist/public/assets/{_Dj5RDA5P.js → _BIUaA4Nl.js} +1 -1
  82. package/dist/public/assets/_BIUaA4Nl.js.br +0 -0
  83. package/dist/public/assets/{_Ch-UiovX.js → _BJpBnuvv.js} +1 -1
  84. package/dist/public/assets/_BJpBnuvv.js.br +0 -0
  85. package/dist/public/assets/{_0KruTMn9.js → _BLeLpnYL.js} +1 -1
  86. package/dist/public/assets/_BLeLpnYL.js.br +0 -0
  87. package/dist/public/assets/_BMb5ANTv.js +1 -0
  88. package/dist/public/assets/{_ChPy2IX4.js → _BSRsPQTe.js} +1 -1
  89. package/dist/public/assets/_BSRsPQTe.js.br +0 -0
  90. package/dist/public/assets/_BVuerXin.js +6 -0
  91. package/dist/public/assets/_BVuerXin.js.br +0 -0
  92. package/dist/public/assets/_BWV8aSlZ.js +1 -0
  93. package/dist/public/assets/{_Q5s6o5H4.js → _BXHggM8M.js} +1 -1
  94. package/dist/public/assets/_BXHggM8M.js.br +0 -0
  95. package/dist/public/assets/{_D8uBvHXU.js → _BXPUZfKX.js} +1 -1
  96. package/dist/public/assets/_BXPUZfKX.js.br +0 -0
  97. package/dist/public/assets/_BYebhygn.js +1 -0
  98. package/dist/public/assets/_BZgCxd-U.js +1 -0
  99. package/dist/public/assets/_BZgCxd-U.js.br +0 -0
  100. package/dist/public/assets/_B_W51WxU.js +1 -0
  101. package/dist/public/assets/_B_jFFNyT.js +1 -0
  102. package/dist/public/assets/_Ba9OQzPV.js +1 -0
  103. package/dist/public/assets/_Ba9OQzPV.js.br +0 -0
  104. package/dist/public/assets/_BayiY7GU.js +1 -0
  105. package/dist/public/assets/_BcDMeS82.js +1 -0
  106. package/dist/public/assets/_BdD1sZp_.js +1 -0
  107. package/dist/public/assets/_Bg_WwlL2.js +1 -0
  108. package/dist/public/assets/_BhHzv4g0.js +1 -0
  109. package/dist/public/assets/_BjEinMEe.js +1 -0
  110. package/dist/public/assets/_BjEinMEe.js.br +0 -0
  111. package/dist/public/assets/{_DPiemRX1.js → _BkGAUdMd.js} +1 -1
  112. package/dist/public/assets/_BkGAUdMd.js.br +0 -0
  113. package/dist/public/assets/{_FwFz43m8.js → _BkHLXMjq.js} +1 -1
  114. package/dist/public/assets/_BkHLXMjq.js.br +0 -0
  115. package/dist/public/assets/_BmSWsZvV.js +1 -0
  116. package/dist/public/assets/{_D_GeNCls.js → _Bn6y2_ex.js} +1 -1
  117. package/dist/public/assets/_Bn6y2_ex.js.br +0 -0
  118. package/dist/public/assets/_Bp2gqUaf.js +1 -0
  119. package/dist/public/assets/_Bs6oYKkD.js +1 -0
  120. package/dist/public/assets/{_CYhJ6ae-.js → _Bs_MPTBZ.js} +1 -1
  121. package/dist/public/assets/_Bs_MPTBZ.js.br +0 -0
  122. package/dist/public/assets/_Bt13j1ev.js +1 -0
  123. package/dist/public/assets/{_D-pUpBL1.js → _Bt4cxzaP.js} +1 -1
  124. package/dist/public/assets/_BwlnYt-t.js +1 -0
  125. package/dist/public/assets/_Bx9aUXTG.js +1 -0
  126. package/dist/public/assets/{_DUdEYxV0.js → _BxKDE6Ct.js} +1 -1
  127. package/dist/public/assets/_BxKDE6Ct.js.br +0 -0
  128. package/dist/public/assets/_BymFDqKj.js +1 -0
  129. package/dist/public/assets/{_i4iteLF8.js → _C-z8Ntse.js} +1 -1
  130. package/dist/public/assets/_C-z8Ntse.js.br +0 -0
  131. package/dist/public/assets/_C0XUbqWe.js +1 -0
  132. package/dist/public/assets/_C1upDwBv.js +1 -0
  133. package/dist/public/assets/_C3f4huqn.js +1 -0
  134. package/dist/public/assets/{_BHyWcuOR.js → _C46o3wQ8.js} +1 -1
  135. package/dist/public/assets/_C46o3wQ8.js.br +0 -0
  136. package/dist/public/assets/_C4_3qKLX.js +1 -0
  137. package/dist/public/assets/_C7tG4xwv.js +1 -0
  138. package/dist/public/assets/_C8LciIOa.js +1 -0
  139. package/dist/public/assets/{_zcAaK79d.js → _C9k00otG.js} +1 -1
  140. package/dist/public/assets/_C9k00otG.js.br +0 -0
  141. package/dist/public/assets/_CBG92pZO.js +1 -0
  142. package/dist/public/assets/_CBfI2lSr.js +1 -0
  143. package/dist/public/assets/{_BPNSknLT.js → _CFKwHn62.js} +1 -1
  144. package/dist/public/assets/_CFKwHn62.js.br +0 -0
  145. package/dist/public/assets/_CHDvRNgn.js +1 -0
  146. package/dist/public/assets/{_DVLNzEkW.js → _CIDjLk1A.js} +1 -1
  147. package/dist/public/assets/_CIDjLk1A.js.br +0 -0
  148. package/dist/public/assets/{_3fvr5bU1.js → _CLV83hY7.js} +1 -1
  149. package/dist/public/assets/_CLV83hY7.js.br +0 -0
  150. package/dist/public/assets/_CO8ZosDU.js +1 -0
  151. package/dist/public/assets/{_D4CkNo0r.js → _CPqT91JX.js} +1 -1
  152. package/dist/public/assets/_CPqT91JX.js.br +0 -0
  153. package/dist/public/assets/_CRZ03hrO.js +1 -0
  154. package/dist/public/assets/_CUKvJRmi.js +1 -0
  155. package/dist/public/assets/_CU_FKEsy.js +1 -0
  156. package/dist/public/assets/{_Do6b-nfX.js → _CUxBk_RE.js} +1 -1
  157. package/dist/public/assets/_CUxBk_RE.js.br +0 -0
  158. package/dist/public/assets/_CW_vyZfo.js +1 -0
  159. package/dist/public/assets/{_BOBYB-cj.js → _CX5VOdNU.js} +1 -1
  160. package/dist/public/assets/_CX5VOdNU.js.br +0 -0
  161. package/dist/public/assets/{_C5c3o9N3.js → _CYcgwayF.js} +1 -1
  162. package/dist/public/assets/_CYcgwayF.js.br +0 -0
  163. package/dist/public/assets/{_BXcMBVCY.js → _CZsqsL_m.js} +1 -1
  164. package/dist/public/assets/_CZsqsL_m.js.br +0 -0
  165. package/dist/public/assets/_C_a8QggH.js +1 -0
  166. package/dist/public/assets/_C_ymFHNL.js +1 -0
  167. package/dist/public/assets/_CaU4irFc.js +1 -0
  168. package/dist/public/assets/_Cb3fSi2R.js +1 -0
  169. package/dist/public/assets/{_DOKecL0e.js → _CcINcTUt.js} +1 -1
  170. package/dist/public/assets/_CcINcTUt.js.br +0 -0
  171. package/dist/public/assets/{_BTh4v5Tr.js → _CcflXOTj.js} +1 -1
  172. package/dist/public/assets/_CcflXOTj.js.br +0 -0
  173. package/dist/public/assets/{_DbLQjG4R.js → _Cfts4mFJ.js} +1 -1
  174. package/dist/public/assets/_Cfts4mFJ.js.br +0 -0
  175. package/dist/public/assets/{_v3HPnNBs.js → _ClHeQc6g.js} +1 -1
  176. package/dist/public/assets/_ClHeQc6g.js.br +0 -0
  177. package/dist/public/assets/_CnSlVcEh.js +1 -0
  178. package/dist/public/assets/_Co71-HWR.js +1 -0
  179. package/dist/public/assets/_CpEeHKyF.js +1 -0
  180. package/dist/public/assets/{_CNUs2Gqk.js → _CpVeIwQ4.js} +1 -1
  181. package/dist/public/assets/_CpVeIwQ4.js.br +0 -0
  182. package/dist/public/assets/{_Cc_GUPR4.js → _Cq4DpL3R.js} +1 -1
  183. package/dist/public/assets/_Cq4DpL3R.js.br +0 -0
  184. package/dist/public/assets/_CvxsvMFa.js +1 -0
  185. package/dist/public/assets/{_DorQTaAa.js → _Cwdn40Q2.js} +1 -1
  186. package/dist/public/assets/_Cwdn40Q2.js.br +0 -0
  187. package/dist/public/assets/_CzN0wC_5.js +16 -0
  188. package/dist/public/assets/_CzN0wC_5.js.br +0 -0
  189. package/dist/public/assets/_D3ieGLoq.js +1 -0
  190. package/dist/public/assets/_D4wF26fk.js +1 -0
  191. package/dist/public/assets/_D9a9nY1Q.js +1 -0
  192. package/dist/public/assets/{_CIWiehYG.js → _DB7_Yora.js} +1 -1
  193. package/dist/public/assets/_DB7_Yora.js.br +0 -0
  194. package/dist/public/assets/{_6klEiBhY.js → _DDWIDZyP.js} +1 -1
  195. package/dist/public/assets/_DDWIDZyP.js.br +0 -0
  196. package/dist/public/assets/{_Cwtn28hL.js → _DG3L8-RO.js} +1 -1
  197. package/dist/public/assets/_DG3L8-RO.js.br +0 -0
  198. package/dist/public/assets/{_BxS3xvmE.js → _DGRgLaG-.js} +1 -1
  199. package/dist/public/assets/_DGRgLaG-.js.br +0 -0
  200. package/dist/public/assets/{_pEUCjhqm.js → _DG_QOtsx.js} +1 -1
  201. package/dist/public/assets/_DG_QOtsx.js.br +0 -0
  202. package/dist/public/assets/{_BwSXxzqy.js → _DHYIZWev.js} +11 -11
  203. package/dist/public/assets/_DHYIZWev.js.br +0 -0
  204. package/dist/public/assets/{_BoTzWlXR.js → _DJwY_zBh.js} +1 -1
  205. package/dist/public/assets/_DJwY_zBh.js.br +0 -0
  206. package/dist/public/assets/{_C9jR1P3s.js → _DKlB-yMq.js} +1 -1
  207. package/dist/public/assets/_DKlB-yMq.js.br +0 -0
  208. package/dist/public/assets/{_DYqx8g78.js → _DMCVOg7x.js} +1 -1
  209. package/dist/public/assets/_DMCVOg7x.js.br +0 -0
  210. package/dist/public/assets/_DMXnrdYy.js +1 -0
  211. package/dist/public/assets/_DMXnrdYy.js.br +0 -0
  212. package/dist/public/assets/{_Di78jTI-.js → _DNrRZhO7.js} +1 -1
  213. package/dist/public/assets/_DNrRZhO7.js.br +0 -0
  214. package/dist/public/assets/_DPdb5l-Z.js +1 -0
  215. package/dist/public/assets/{_Depr5Jps.js → _DRpsUHP5.js} +1 -1
  216. package/dist/public/assets/_DRpsUHP5.js.br +0 -0
  217. package/dist/public/assets/{_BHgwN0LC.js → _DUH5o_iN.js} +1 -1
  218. package/dist/public/assets/_DUH5o_iN.js.br +0 -0
  219. package/dist/public/assets/{_-ycNE92Z.js → _DUmYv8Kf.js} +1 -1
  220. package/dist/public/assets/_DUmYv8Kf.js.br +0 -0
  221. package/dist/public/assets/{_CPbClwhX.js → _DXvsvUmJ.js} +1 -1
  222. package/dist/public/assets/_DXvsvUmJ.js.br +0 -0
  223. package/dist/public/assets/{_DqGOYRpi.js → _DYxQeog7.js} +1 -1
  224. package/dist/public/assets/_DYxQeog7.js.br +0 -0
  225. package/dist/public/assets/_DZIDdpeW.js +1 -0
  226. package/dist/public/assets/{_CCONYsJT.js → _DZqoQxTm.js} +1 -1
  227. package/dist/public/assets/_DZqoQxTm.js.br +0 -0
  228. package/dist/public/assets/_D_4RJnk0.js +2 -0
  229. package/dist/public/assets/_D_4RJnk0.js.br +0 -0
  230. package/dist/public/assets/_DclJXqwW.js +1 -0
  231. package/dist/public/assets/_Debmjz1c.js +1 -0
  232. package/dist/public/assets/_DeiCZCO_.js +1 -0
  233. package/dist/public/assets/{_yp48NYOW.js → _Df-N1G_n.js} +1 -1
  234. package/dist/public/assets/_Df-N1G_n.js.br +0 -0
  235. package/dist/public/assets/_Df_3rEok.js +86 -0
  236. package/dist/public/assets/_Df_3rEok.js.br +0 -0
  237. package/dist/public/assets/{_DO_4_u2Q.js → _Dh9434oc.js} +1 -1
  238. package/dist/public/assets/{_Dhn3A3KJ.js → _DjYscMjF.js} +1 -1
  239. package/dist/public/assets/_DjYscMjF.js.br +0 -0
  240. package/dist/public/assets/{_cyorvuL1.js → _Dku55Ypt.js} +1 -1
  241. package/dist/public/assets/_Dku55Ypt.js.br +0 -0
  242. package/dist/public/assets/_Dm8TVkBX.js +1 -0
  243. package/dist/public/assets/_DnUBW0Qq.js +1 -0
  244. package/dist/public/assets/_DoOPrRIy.js +1 -0
  245. package/dist/public/assets/_DqJvxlYV.js +1 -0
  246. package/dist/public/assets/_Drle66GA.js +1 -0
  247. package/dist/public/assets/_DtrvrAfl.js +1 -0
  248. package/dist/public/assets/{_B5JxE4Sx.js → _DwTaEsY5.js} +1 -1
  249. package/dist/public/assets/_DwTaEsY5.js.br +0 -0
  250. package/dist/public/assets/_Dypj9l-z.js +1 -0
  251. package/dist/public/assets/_DyvwMfCp.js +1 -0
  252. package/dist/public/assets/{_OpbuiQ7h.js → _GK8r-gEu.js} +1 -1
  253. package/dist/public/assets/_GK8r-gEu.js.br +0 -0
  254. package/dist/public/assets/{_BqWlTTxo.js → _HpxvfbM2.js} +1 -1
  255. package/dist/public/assets/_HpxvfbM2.js.br +0 -0
  256. package/dist/public/assets/{_CIrZGxEf.js → _RudCGuBM.js} +1 -1
  257. package/dist/public/assets/_RudCGuBM.js.br +0 -0
  258. package/dist/public/assets/_UbfI3k4y.js +1 -0
  259. package/dist/public/assets/{_A-jpjc8j.js → _WOU8TCkO.js} +1 -1
  260. package/dist/public/assets/_WOU8TCkO.js.br +0 -0
  261. package/dist/public/assets/{_C-lB_p3U.js → _XWw1j8vS.js} +1 -1
  262. package/dist/public/assets/_XWw1j8vS.js.br +0 -0
  263. package/dist/public/assets/{_BCB8LQS3.js → _YDyNvOET.js} +1 -1
  264. package/dist/public/assets/_YDyNvOET.js.br +0 -0
  265. package/dist/public/assets/{_DzlXwaiG.js → _f8D86HXZ.js} +1 -1
  266. package/dist/public/assets/_f8D86HXZ.js.br +0 -0
  267. package/dist/public/assets/{_BUW_V5kN.js → _hbXd8Ogo.js} +1 -1
  268. package/dist/public/assets/_hbXd8Ogo.js.br +0 -0
  269. package/dist/public/assets/{_CrlNjDih.js → _i6YkSowH.js} +1 -1
  270. package/dist/public/assets/_i6YkSowH.js.br +0 -0
  271. package/dist/public/assets/_k1ciacDf.js +1 -0
  272. package/dist/public/assets/_k6nXIP-i.js +1 -0
  273. package/dist/public/assets/_keX09TNx.js +1 -0
  274. package/dist/public/assets/{_Dwe1TUjY.js → _l9xpLTP_.js} +1 -1
  275. package/dist/public/assets/_l9xpLTP_.js.br +0 -0
  276. package/dist/public/assets/_n9Rb26YX.js +1 -0
  277. package/dist/public/assets/{_4_sKOYsy.js → _o8mJsMHw.js} +1 -1
  278. package/dist/public/assets/_o8mJsMHw.js.br +0 -0
  279. package/dist/public/assets/{_Ca2DTddL.js → _oVsjszGq.js} +1 -1
  280. package/dist/public/assets/_oVsjszGq.js.br +0 -0
  281. package/dist/public/assets/_pM8W420Q.js +1 -0
  282. package/dist/public/assets/{_C449plW0.js → _rv9KtZHz.js} +1 -1
  283. package/dist/public/assets/_rv9KtZHz.js.br +0 -0
  284. package/dist/public/assets/{_B19mS3yM.js → _sSKEak2x.js} +1 -1
  285. package/dist/public/assets/_sSKEak2x.js.br +0 -0
  286. package/dist/public/assets/_tJgkkxOD.js +1 -0
  287. package/dist/public/assets/_tJgkkxOD.js.br +0 -0
  288. package/dist/public/assets/_uP3LGOtV.js +1 -0
  289. package/dist/public/assets/{_BT7DMail.js → _vG7bXn-C.js} +1 -1
  290. package/dist/public/assets/_vG7bXn-C.js.br +0 -0
  291. package/dist/public/assets/{_BTWfPOi-.js → _vHqiFYsh.js} +1 -1
  292. package/dist/public/assets/_vHqiFYsh.js.br +0 -0
  293. package/dist/public/assets/_vXJ7nP0d.js +1 -0
  294. package/dist/public/assets/{_Dks8YY2K.js → _w-MAJku6.js} +1 -1
  295. package/dist/public/assets/_wvhU__5f.js +1 -0
  296. package/dist/public/assets/_xFMpRp_e.js +1 -0
  297. package/dist/public/assets/_yXimF5vw.js +1 -0
  298. package/dist/public/assets/files-view-BennlUmP.js +2 -0
  299. package/dist/public/assets/manage-workspace-CaESLJdc.js +2 -0
  300. package/dist/public/assets/new-BNH4m99d.js +1 -0
  301. package/dist/public/assets/new-workspace-CwiZjHNn.js +2 -0
  302. package/dist/public/assets/review-view-BdbMyVt7.js +2 -0
  303. package/dist/public/assets/settings-BNH4m99d.js +1 -0
  304. package/dist/public/assets/settings-page-CU3aC70Q.js +2 -0
  305. package/dist/public/assets/styles-ctr-Sxsj.css +1 -0
  306. package/dist/public/assets/styles-ctr-Sxsj.css.br +0 -0
  307. package/dist/public/assets/w-BNH4m99d.js +1 -0
  308. package/dist/public/assets/w._-BNH4m99d.js +1 -0
  309. package/dist/public/assets/w._-CLYxMMR4.js +1 -0
  310. package/dist/public/assets/w._-_w8eUYMy.js +1 -0
  311. package/dist/public/sw.js +11 -4
  312. package/dist/public/sw.js.br +0 -0
  313. package/lib/ports/url.d.mts +20 -0
  314. package/lib/ports/url.mjs +80 -0
  315. package/package.json +1 -1
  316. package/dist/_CLoLdV87.js +0 -2
  317. package/dist/_DPpY3UqO.js +0 -167
  318. package/dist/public/assets/_-ycNE92Z.js.br +0 -0
  319. package/dist/public/assets/_0KruTMn9.js.br +0 -0
  320. package/dist/public/assets/_0NH5yJMx.js +0 -1
  321. package/dist/public/assets/_1CAp-NbR.js +0 -1
  322. package/dist/public/assets/_3fvr5bU1.js.br +0 -0
  323. package/dist/public/assets/_4Dkmi0BI.js +0 -1
  324. package/dist/public/assets/_4_sKOYsy.js.br +0 -0
  325. package/dist/public/assets/_6klEiBhY.js.br +0 -0
  326. package/dist/public/assets/_7_64OpLh.js +0 -1
  327. package/dist/public/assets/_7p-zxUs0.js +0 -1
  328. package/dist/public/assets/_8RAv3gFP.js +0 -1
  329. package/dist/public/assets/_9PDxaMA9.js +0 -1
  330. package/dist/public/assets/_9PDxaMA9.js.br +0 -0
  331. package/dist/public/assets/_9mbDrbe7.js +0 -1
  332. package/dist/public/assets/_A-jpjc8j.js.br +0 -0
  333. package/dist/public/assets/_B0JwTeu6.js +0 -1
  334. package/dist/public/assets/_B19mS3yM.js.br +0 -0
  335. package/dist/public/assets/_B1Sfrnda.js +0 -1
  336. package/dist/public/assets/_B3i0wS_I.js +0 -1
  337. package/dist/public/assets/_B5JxE4Sx.js.br +0 -0
  338. package/dist/public/assets/_B9RLcC8I.js +0 -1
  339. package/dist/public/assets/_BCB8LQS3.js.br +0 -0
  340. package/dist/public/assets/_BE7UYyYb.js +0 -1
  341. package/dist/public/assets/_BHgwN0LC.js.br +0 -0
  342. package/dist/public/assets/_BHyWcuOR.js.br +0 -0
  343. package/dist/public/assets/_BIpUDgQH.js +0 -16
  344. package/dist/public/assets/_BIpUDgQH.js.br +0 -0
  345. package/dist/public/assets/_BMlgaRlM.js +0 -1
  346. package/dist/public/assets/_BOBYB-cj.js.br +0 -0
  347. package/dist/public/assets/_BPNSknLT.js.br +0 -0
  348. package/dist/public/assets/_BQEpu56f.js.br +0 -0
  349. package/dist/public/assets/_BT7DMail.js.br +0 -0
  350. package/dist/public/assets/_BTJHQNIz.js +0 -1
  351. package/dist/public/assets/_BTWfPOi-.js.br +0 -0
  352. package/dist/public/assets/_BTh4v5Tr.js.br +0 -0
  353. package/dist/public/assets/_BUW_V5kN.js.br +0 -0
  354. package/dist/public/assets/_BXcMBVCY.js.br +0 -0
  355. package/dist/public/assets/_B_MtJKnj.js +0 -1
  356. package/dist/public/assets/_Bay3D1io.js +0 -1
  357. package/dist/public/assets/_BfQeZPXW.js.br +0 -0
  358. package/dist/public/assets/_BgKjJWz_.js +0 -1
  359. package/dist/public/assets/_BiY8chB-.js.br +0 -0
  360. package/dist/public/assets/_Bk5THQZb.js +0 -1
  361. package/dist/public/assets/_BmeQRnXS.js +0 -1
  362. package/dist/public/assets/_BoEcMQmX.js.br +0 -0
  363. package/dist/public/assets/_BoTzWlXR.js.br +0 -0
  364. package/dist/public/assets/_BqWlTTxo.js.br +0 -0
  365. package/dist/public/assets/_BuMTHEPk.js +0 -1
  366. package/dist/public/assets/_BuMTHEPk.js.br +0 -0
  367. package/dist/public/assets/_BubhcEjq.js +0 -1
  368. package/dist/public/assets/_BwSXxzqy.js.br +0 -0
  369. package/dist/public/assets/_BxS3xvmE.js.br +0 -0
  370. package/dist/public/assets/_ByvA6zJz.js +0 -1
  371. package/dist/public/assets/_Bz2Sejcy.js +0 -1
  372. package/dist/public/assets/_C-lB_p3U.js.br +0 -0
  373. package/dist/public/assets/_C1-Nli75.js.br +0 -0
  374. package/dist/public/assets/_C3mw4T_r.js +0 -1
  375. package/dist/public/assets/_C449plW0.js.br +0 -0
  376. package/dist/public/assets/_C5c3o9N3.js.br +0 -0
  377. package/dist/public/assets/_C7bHTlG1.js.br +0 -0
  378. package/dist/public/assets/_C8mt6nRC.js +0 -1
  379. package/dist/public/assets/_C9jR1P3s.js.br +0 -0
  380. package/dist/public/assets/_CCONYsJT.js.br +0 -0
  381. package/dist/public/assets/_CCZAROAW.js +0 -1
  382. package/dist/public/assets/_CCZAROAW.js.br +0 -0
  383. package/dist/public/assets/_CG3s9pE5.js +0 -1
  384. package/dist/public/assets/_CIWiehYG.js.br +0 -0
  385. package/dist/public/assets/_CIrZGxEf.js.br +0 -0
  386. package/dist/public/assets/_CIurl9jC.js +0 -1
  387. package/dist/public/assets/_CJgqLdsx.js.br +0 -0
  388. package/dist/public/assets/_CMbVHIgP.js +0 -1
  389. package/dist/public/assets/_CNUs2Gqk.js.br +0 -0
  390. package/dist/public/assets/_CPbClwhX.js.br +0 -0
  391. package/dist/public/assets/_CQUlxerJ.js +0 -1
  392. package/dist/public/assets/_CTDYlYHi.js +0 -1
  393. package/dist/public/assets/_CXVkXsw1.js.br +0 -0
  394. package/dist/public/assets/_CY5EBxZ6.js +0 -1
  395. package/dist/public/assets/_CYQ4srZb.js.br +0 -0
  396. package/dist/public/assets/_CY_Z-kV5.js.br +0 -0
  397. package/dist/public/assets/_CYhJ6ae-.js.br +0 -0
  398. package/dist/public/assets/_CZbVK94d.js +0 -1
  399. package/dist/public/assets/_C_A97h_2.js +0 -1
  400. package/dist/public/assets/_C_A97h_2.js.br +0 -0
  401. package/dist/public/assets/_C_Y5Dwzy.js +0 -1
  402. package/dist/public/assets/_Ca2DTddL.js.br +0 -0
  403. package/dist/public/assets/_Cc2nu3_5.js +0 -1
  404. package/dist/public/assets/_Cc_GUPR4.js.br +0 -0
  405. package/dist/public/assets/_CgFUs0bE.js +0 -1
  406. package/dist/public/assets/_Ch-UiovX.js.br +0 -0
  407. package/dist/public/assets/_ChDp0-FJ.js +0 -1
  408. package/dist/public/assets/_ChPy2IX4.js.br +0 -0
  409. package/dist/public/assets/_Chsgo2e1.js +0 -1
  410. package/dist/public/assets/_CjECwrWB.js +0 -1
  411. package/dist/public/assets/_CmW74dH7.js +0 -1
  412. package/dist/public/assets/_CmhAUlqu.js +0 -1
  413. package/dist/public/assets/_CrlNjDih.js.br +0 -0
  414. package/dist/public/assets/_CvzTEaQq.js +0 -1
  415. package/dist/public/assets/_Cw-dmwPJ.js +0 -86
  416. package/dist/public/assets/_Cw-dmwPJ.js.br +0 -0
  417. package/dist/public/assets/_Cwtn28hL.js.br +0 -0
  418. package/dist/public/assets/_CxHuLHdz.js +0 -1
  419. package/dist/public/assets/_CxgPm_xy.js.br +0 -0
  420. package/dist/public/assets/_D-0pUYXq.js.br +0 -0
  421. package/dist/public/assets/_D-rV-z_T.js +0 -1
  422. package/dist/public/assets/_D4CkNo0r.js.br +0 -0
  423. package/dist/public/assets/_D4fP_viq.js +0 -1
  424. package/dist/public/assets/_D570Sd6p.js +0 -1
  425. package/dist/public/assets/_D6Gudpw8.js +0 -1
  426. package/dist/public/assets/_D8ZTP1ck.js +0 -1
  427. package/dist/public/assets/_D8uBvHXU.js.br +0 -0
  428. package/dist/public/assets/_D8xGEGlm.js +0 -1
  429. package/dist/public/assets/_D9GZHKow.js +0 -1
  430. package/dist/public/assets/_DE0eZjad.js +0 -1
  431. package/dist/public/assets/_DElIEG9n.js +0 -1
  432. package/dist/public/assets/_DF0n9xzn.js +0 -2
  433. package/dist/public/assets/_DF0n9xzn.js.br +0 -0
  434. package/dist/public/assets/_DFX_tXcw.js +0 -1
  435. package/dist/public/assets/_DJ2gfhU_.js +0 -1
  436. package/dist/public/assets/_DK-MOyi_.js.br +0 -0
  437. package/dist/public/assets/_DOKecL0e.js.br +0 -0
  438. package/dist/public/assets/_DPXXIOLX.js +0 -1
  439. package/dist/public/assets/_DPb3t58B.js +0 -1
  440. package/dist/public/assets/_DPiemRX1.js.br +0 -0
  441. package/dist/public/assets/_DPpbgqBC.js +0 -1
  442. package/dist/public/assets/_DRVSLjxA.js +0 -1
  443. package/dist/public/assets/_DTHWnu6M.js +0 -1
  444. package/dist/public/assets/_DUdEYxV0.js.br +0 -0
  445. package/dist/public/assets/_DVLNzEkW.js.br +0 -0
  446. package/dist/public/assets/_DWpx2Uof.js +0 -1
  447. package/dist/public/assets/_DXYEUAmC.js +0 -1
  448. package/dist/public/assets/_DYTVambU.js +0 -1
  449. package/dist/public/assets/_DYqx8g78.js.br +0 -0
  450. package/dist/public/assets/_DZanHI_X.js.br +0 -0
  451. package/dist/public/assets/_D_GeNCls.js.br +0 -0
  452. package/dist/public/assets/_DbLQjG4R.js.br +0 -0
  453. package/dist/public/assets/_DbmusecK.js +0 -1
  454. package/dist/public/assets/_DbmusecK.js.br +0 -0
  455. package/dist/public/assets/_DemN-D5U.js +0 -1
  456. package/dist/public/assets/_Depr5Jps.js.br +0 -0
  457. package/dist/public/assets/_DhJfHI4f.js +0 -1
  458. package/dist/public/assets/_Dhn3A3KJ.js.br +0 -0
  459. package/dist/public/assets/_Di78jTI-.js.br +0 -0
  460. package/dist/public/assets/_DiBTtPlu.js +0 -1
  461. package/dist/public/assets/_Dj5RDA5P.js.br +0 -0
  462. package/dist/public/assets/_DkjKARSS.js +0 -1
  463. package/dist/public/assets/_Do6b-nfX.js.br +0 -0
  464. package/dist/public/assets/_DorQTaAa.js.br +0 -0
  465. package/dist/public/assets/_DpxVmwni.js +0 -1
  466. package/dist/public/assets/_DqGOYRpi.js.br +0 -0
  467. package/dist/public/assets/_DsbxQ1i8.js +0 -1
  468. package/dist/public/assets/_Dt_P7K1V.js +0 -6
  469. package/dist/public/assets/_Dt_P7K1V.js.br +0 -0
  470. package/dist/public/assets/_Dwe1TUjY.js.br +0 -0
  471. package/dist/public/assets/_DyROQKS8.js +0 -1
  472. package/dist/public/assets/_DzEUPsq0.js +0 -1
  473. package/dist/public/assets/_DzlXwaiG.js.br +0 -0
  474. package/dist/public/assets/_FwFz43m8.js.br +0 -0
  475. package/dist/public/assets/_OpbuiQ7h.js.br +0 -0
  476. package/dist/public/assets/_Q5s6o5H4.js.br +0 -0
  477. package/dist/public/assets/_Quh6tl1u.js.br +0 -0
  478. package/dist/public/assets/_Rh99sjDe.js +0 -1
  479. package/dist/public/assets/_SEE1XtID.js +0 -1
  480. package/dist/public/assets/_SZjr1kid.js +0 -1
  481. package/dist/public/assets/_T7rMqM5P.js +0 -1
  482. package/dist/public/assets/_TynjiShF.js.br +0 -0
  483. package/dist/public/assets/_UENAEFcY.js +0 -1
  484. package/dist/public/assets/_UwTOb1gP.js +0 -1
  485. package/dist/public/assets/_WrxtT1Ok.js +0 -1
  486. package/dist/public/assets/_WrxtT1Ok.js.br +0 -0
  487. package/dist/public/assets/_bcvRRCkB.js +0 -1
  488. package/dist/public/assets/_cyorvuL1.js.br +0 -0
  489. package/dist/public/assets/_gi--JOq8.js +0 -1
  490. package/dist/public/assets/_i4iteLF8.js.br +0 -0
  491. package/dist/public/assets/_i7vMugBN.js +0 -1
  492. package/dist/public/assets/_j7jPRUEQ.js +0 -2
  493. package/dist/public/assets/_j7jPRUEQ.js.br +0 -0
  494. package/dist/public/assets/_pEUCjhqm.js.br +0 -0
  495. package/dist/public/assets/_qkJuNTl-.js +0 -1
  496. package/dist/public/assets/_uuZJDLIV.js +0 -1
  497. package/dist/public/assets/_v3HPnNBs.js.br +0 -0
  498. package/dist/public/assets/_vX9d6g5Y.js.br +0 -0
  499. package/dist/public/assets/_vhysM7gk.js +0 -1
  500. package/dist/public/assets/_vjwPQHk_.js +0 -1
  501. package/dist/public/assets/_yp48NYOW.js.br +0 -0
  502. package/dist/public/assets/_zcAaK79d.js.br +0 -0
  503. package/dist/public/assets/_zwPrEKxN.js +0 -1
  504. package/dist/public/assets/_zwPrEKxN.js.br +0 -0
  505. package/dist/public/assets/files-view-CzRAql9-.js +0 -2
  506. package/dist/public/assets/manage-workspace-C-c1R4pP.js +0 -2
  507. package/dist/public/assets/new-DWfNwssk.js +0 -1
  508. package/dist/public/assets/new-workspace-B8XAfkIH.js +0 -2
  509. package/dist/public/assets/review-view-DivHZNBf.js +0 -2
  510. package/dist/public/assets/settings-DWfNwssk.js +0 -1
  511. package/dist/public/assets/settings-page-BN9Hj1ui.js +0 -2
  512. package/dist/public/assets/styles-NmwxQh5L.css +0 -1
  513. package/dist/public/assets/styles-NmwxQh5L.css.br +0 -0
  514. package/dist/public/assets/w-DWfNwssk.js +0 -1
  515. package/dist/public/assets/w._-C9dj1aZ0.js +0 -1
  516. package/dist/public/assets/w._-D7_D4HlM.js +0 -1
  517. package/dist/public/assets/w._-DWfNwssk.js +0 -1
@@ -0,0 +1,585 @@
1
+ // `work topic`: a workspace's topic branches (`<ws-branch>-<topic>`, each
2
+ // in its own worktree at `<ws>/.worktrees/<topic>/<repo>`), made, listed and
3
+ // put away as the generated workspace AGENTS.md describes, so an agent
4
+ // never hand-types the naming, the path or the after-merge chores — the
5
+ // places a slip loses work. Plain git in the workspace's own checkouts,
6
+ // plus the server's workspace rows (bases) and status (PRs).
7
+ import { spawnSync } from "node:child_process";
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+
11
+ /** A topic's name: a word, or `<group>.<topic>` (folded into one review
12
+ * chip). No `/`: it names a directory and a review section too. */
13
+ const NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
14
+
15
+ /** Lockfile → the command that installs it, first match wins. */
16
+ const INSTALLERS = [
17
+ ["pnpm-lock.yaml", "pnpm install --frozen-lockfile"],
18
+ ["bun.lock", "bun install --frozen-lockfile"],
19
+ ["bun.lockb", "bun install --frozen-lockfile"],
20
+ ["yarn.lock", "yarn install --immutable"],
21
+ ["package-lock.json", "npm ci"],
22
+ ["uv.lock", "uv sync --frozen"],
23
+ ["poetry.lock", "poetry install"],
24
+ ["Gemfile.lock", "bundle install"],
25
+ ];
26
+
27
+ function git(cwd, args, { allowFail = false } = {}) {
28
+ const r = spawnSync("git", args, {
29
+ cwd,
30
+ encoding: "utf8",
31
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
32
+ });
33
+ if (r.status !== 0 && !allowFail) {
34
+ const why = (r.stderr || r.stdout || "").trim() || `exit ${r.status}`;
35
+ throw new Error(`git ${args.join(" ")}: ${why}`);
36
+ }
37
+ return { ok: r.status === 0, out: (r.stdout || "").trim(), err: r.stderr };
38
+ }
39
+
40
+ const has = (cwd, ref) =>
41
+ git(cwd, ["rev-parse", "--verify", "--quiet", ref], { allowFail: true }).ok;
42
+
43
+ const oid = (cwd, ref) =>
44
+ git(cwd, ["rev-parse", "--verify", "--quiet", ref], { allowFail: true })
45
+ .out || null;
46
+
47
+ /** `a` is reachable from `b` (both must exist). */
48
+ const contains = (cwd, b, a) =>
49
+ git(cwd, ["merge-base", "--is-ancestor", a, b], { allowFail: true }).ok;
50
+
51
+ /** Is `ref` this workspace's, given the other workspaces' branches? Its
52
+ * branch or a `-topic` of it, unless another workspace's branch extends
53
+ * this one's and the ref is that one's (server `ownsBranchOf`). */
54
+ function owns(ref, branch, foreign) {
55
+ const of = (r, p) => r === p || r.startsWith(`${p}-`);
56
+ return of(ref, branch) && !foreign.some((x) => of(x, branch) && of(ref, x));
57
+ }
58
+
59
+ /** Where the workspace and its repos are, and the other workspaces'
60
+ * branches (whose topics are not this one's). */
61
+ async function context(ctx, fresh) {
62
+ const { ws } = await ctx.resolveWorkspace();
63
+ const [list, status] = await Promise.all([
64
+ ctx.api.get("/api/workspaces"),
65
+ ctx.api.get(fresh ? "/api/status?fresh" : "/api/status"),
66
+ ]);
67
+ const foreign = list.filter((w) => w.id !== ws.id).map((w) => w.branch);
68
+ const prsOf = new Map(
69
+ (status[ws.id]?.repos || []).map((r) => [r.full_name, r.prs || []]),
70
+ );
71
+ const repos = ws.repos.map((r) => ({
72
+ ...r,
73
+ dir: path.join(ws.path, r.worktree),
74
+ prs: prsOf.get(r.full_name) || [],
75
+ }));
76
+ return { ws, repos, foreign };
77
+ }
78
+
79
+ const topicDir = (ws, name, repo) =>
80
+ path.join(ws.path, ".worktrees", name, repo.worktree);
81
+
82
+ const link = (ws, repo, name) =>
83
+ `[${name}](/w/${ws.id}/review?section=${repo.worktree}@${name})`;
84
+
85
+ /** The repo whose checkout (or one of whose topic worktrees) holds the
86
+ * cwd, else the workspace's only one. */
87
+ function repoHere(ws, repos) {
88
+ let cwd = process.cwd();
89
+ try {
90
+ cwd = fs.realpathSync(cwd);
91
+ } catch {}
92
+ const real = (p) => {
93
+ try {
94
+ return fs.realpathSync(p);
95
+ } catch {
96
+ return p;
97
+ }
98
+ };
99
+ const root = real(ws.path);
100
+ const inside = (dir) => cwd === dir || cwd.startsWith(dir + path.sep);
101
+ for (const r of repos) if (inside(real(r.dir))) return r;
102
+ const rel = path.relative(path.join(root, ".worktrees"), cwd).split(path.sep);
103
+ if (rel.length >= 2 && rel[0] !== "..") {
104
+ const r = repos.find((x) => x.worktree === rel[1]);
105
+ if (r) return r;
106
+ }
107
+ return repos.length === 1 ? repos[0] : null;
108
+ }
109
+
110
+ function pickRepos(ctx, ws, repos) {
111
+ const named = ctx.has("--repo") ? ctx.flags("--repo") : [];
112
+ if (named.length) {
113
+ return named.map((n) => {
114
+ const r = repos.find((x) => x.worktree === n || x.full_name === n);
115
+ if (!r)
116
+ ctx.die(
117
+ `no repo ${n} in ${ws.name} (its repos: ${repos.map((x) => x.worktree).join(", ")})`,
118
+ );
119
+ return r;
120
+ });
121
+ }
122
+ const here = repoHere(ws, repos);
123
+ if (!here)
124
+ ctx.die(
125
+ `which repo? This workspace has ${repos.map((x) => x.worktree).join(", ")}: pass --repo <name> (repeat it for several)`,
126
+ );
127
+ return [here];
128
+ }
129
+
130
+ function installFor(dir) {
131
+ for (const [file, cmd] of INSTALLERS)
132
+ if (fs.existsSync(path.join(dir, file))) return cmd;
133
+ return null;
134
+ }
135
+
136
+ /** The PR a branch has: an open one, else the latest. */
137
+ function prFor(repo, branch) {
138
+ const mine = repo.prs.filter((p) => p.headRefName === branch);
139
+ return (
140
+ mine.find((p) => p.state === "OPEN") ||
141
+ mine.sort((a, b) =>
142
+ String(b.updatedAt || "").localeCompare(String(a.updatedAt || "")),
143
+ )[0] ||
144
+ null
145
+ );
146
+ }
147
+
148
+ /** Each worktree of a repo's cache, by the branch it has out. */
149
+ function worktreesOf(dir) {
150
+ const out = git(dir, ["worktree", "list", "--porcelain"]).out;
151
+ const map = new Map();
152
+ let at = null;
153
+ for (const line of out.split("\n")) {
154
+ if (line.startsWith("worktree ")) at = line.slice(9);
155
+ else if (line.startsWith("branch refs/heads/") && at)
156
+ map.set(line.slice(18), at);
157
+ }
158
+ return map;
159
+ }
160
+
161
+ const dirtyIn = (dir) =>
162
+ git(dir, ["status", "--porcelain"], { allowFail: true })
163
+ .out.split("\n")
164
+ .filter(Boolean);
165
+
166
+ function fetch(ctx, repo) {
167
+ ctx.log(`fetching ${repo.full_name}…`);
168
+ git(repo.dir, ["fetch", "--prune", "--quiet", "origin"]);
169
+ }
170
+
171
+ // -- new ---------------------------------------------------------------------
172
+
173
+ async function topicNew(ctx) {
174
+ const [name] = ctx.parse(["--repo", "--from"]);
175
+ if (!name)
176
+ ctx.die("usage: work topic new <name> [--repo <repo>…] [--from <ref>]");
177
+ if (!NAME.test(name) || name.includes(".."))
178
+ ctx.die(
179
+ `"${name}" is not a topic name: letters, digits, \`-\`, \`_\`, and \`.\` for a group (\`<group>.<topic>\`)`,
180
+ );
181
+ const { ws, repos } = await context(ctx, false);
182
+ const branch = `${ws.branch}-${name}`;
183
+ const from = ctx.has("--from") ? ctx.flag("--from") : null;
184
+ const picked = pickRepos(ctx, ws, repos);
185
+ for (const repo of picked) {
186
+ if (
187
+ !git(repo.dir, ["check-ref-format", "--branch", branch], {
188
+ allowFail: true,
189
+ }).ok
190
+ )
191
+ ctx.die(`${branch} is not a branch name git takes`);
192
+ }
193
+ // Every refusal before anything is made: half a topic is worse than none.
194
+ const plans = [];
195
+ for (const repo of picked) {
196
+ fetch(ctx, repo);
197
+ const dest = topicDir(ws, name, repo);
198
+ if (has(repo.dir, `refs/heads/${branch}`))
199
+ ctx.die(
200
+ `${repo.worktree}: ${branch} already exists${worktreesOf(repo.dir).get(branch) ? ` (at ${worktreesOf(repo.dir).get(branch)})` : ""} — pick another name, or work in it`,
201
+ );
202
+ if (has(repo.dir, `refs/remotes/origin/${branch}`))
203
+ ctx.die(
204
+ `${repo.worktree}: ${branch} is already on origin — check it out there (git -C ${repo.dir} worktree add ${dest} ${branch}) rather than starting it again`,
205
+ );
206
+ if (fs.existsSync(dest)) ctx.die(`${dest} already exists`);
207
+ const start = from ?? `origin/${repo.base_branch}`;
208
+ if (!has(repo.dir, `${start}^{commit}`))
209
+ ctx.die(`${repo.worktree}: no ${start} to start from`);
210
+ plans.push({ repo, dest, start });
211
+ }
212
+ const made = [];
213
+ for (const { repo, dest, start } of plans) {
214
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
215
+ // --no-track: its upstream is set by the first push, to its own name on
216
+ // origin, never to the base it was cut from.
217
+ git(repo.dir, ["worktree", "add", "--no-track", "-b", branch, dest, start]);
218
+ const install = installFor(dest);
219
+ const stacked = from && from !== `origin/${repo.base_branch}` ? from : null;
220
+ made.push({
221
+ repo: repo.worktree,
222
+ branch,
223
+ path: dest,
224
+ base: repo.base_branch,
225
+ from: start,
226
+ stacked,
227
+ install,
228
+ link: link(ws, repo, name),
229
+ });
230
+ }
231
+ if (ctx.has("--install")) {
232
+ for (const m of made) {
233
+ if (!m.install) continue;
234
+ ctx.log(`${m.repo}: ${m.install}`);
235
+ const r = spawnSync(m.install, {
236
+ cwd: m.path,
237
+ shell: true,
238
+ // Under --json stdout is the JSON alone: the install talks on stderr.
239
+ stdio: ["ignore", ctx.json ? 2 : "inherit", "inherit"],
240
+ });
241
+ m.installed = r.status === 0;
242
+ if (!m.installed) ctx.warn(`${m.repo}: \`${m.install}\` failed`);
243
+ }
244
+ }
245
+ ctx.out(made, (rows) => {
246
+ for (const m of rows) {
247
+ console.log(`${m.repo}/ on ${m.branch}`);
248
+ console.log(` path: ${m.path}`);
249
+ console.log(` from: ${m.from}`);
250
+ if (m.stacked)
251
+ console.log(
252
+ ` stacked: on ${m.stacked}, not origin/${m.base} — its PR's base is that branch, and it rebases when that one merges`,
253
+ );
254
+ if (m.install)
255
+ console.log(
256
+ ` install: ${m.install}${m.installed === undefined ? " (run it there, or pass --install)" : m.installed ? " (done)" : " (failed)"}`,
257
+ );
258
+ console.log(` review: ${m.link}`);
259
+ }
260
+ });
261
+ if (made.some((m) => m.installed === false)) process.exitCode = 1;
262
+ }
263
+
264
+ // -- list --------------------------------------------------------------------
265
+
266
+ /** Every topic of the workspace in one repo: local branches and origin's,
267
+ * this workspace's (not a longer-named workspace's), with where each
268
+ * stands against origin/<base>. */
269
+ function topicsIn(ws, repo, foreign) {
270
+ const prefix = `${ws.branch}-`;
271
+ const refs = git(repo.dir, [
272
+ "for-each-ref",
273
+ "--format=%(refname)",
274
+ `refs/heads/${prefix}*`,
275
+ `refs/remotes/origin/${prefix}*`,
276
+ ]).out.split("\n");
277
+ const branches = new Set();
278
+ for (const ref of refs) {
279
+ const b = ref.replace(/^refs\/(heads|remotes\/origin)\//, "");
280
+ if (b && owns(b, ws.branch, foreign)) branches.add(b);
281
+ }
282
+ const trees = worktreesOf(repo.dir);
283
+ const base = `origin/${repo.base_branch}`;
284
+ const haveBase = has(repo.dir, base);
285
+ return [...branches].sort().map((branch) => {
286
+ const name = branch.slice(prefix.length);
287
+ const local = oid(repo.dir, `refs/heads/${branch}`);
288
+ const remote = oid(repo.dir, `refs/remotes/origin/${branch}`);
289
+ const tip = local || remote;
290
+ let ahead = null;
291
+ let behind = null;
292
+ if (haveBase) {
293
+ const [b, a] = git(repo.dir, [
294
+ "rev-list",
295
+ "--left-right",
296
+ "--count",
297
+ `${base}...${tip}`,
298
+ ]).out.split(/\s+/);
299
+ ahead = Number(a);
300
+ behind = Number(b);
301
+ }
302
+ const worktree = trees.get(branch) || null;
303
+ const pr = prFor(repo, branch);
304
+ return {
305
+ topic: name,
306
+ repo: repo.worktree,
307
+ branch,
308
+ base,
309
+ worktree,
310
+ ahead,
311
+ behind,
312
+ dirty: worktree ? dirtyIn(worktree).length : null,
313
+ local: !!local,
314
+ onOrigin: !!remote,
315
+ pushed: !!remote && (!local || local === remote),
316
+ // Commits here origin's copy lacks (all of them, with none there).
317
+ unpushed:
318
+ local && remote
319
+ ? Number(
320
+ git(repo.dir, ["rev-list", "--count", `${remote}..${local}`]).out,
321
+ )
322
+ : local
323
+ ? ahead
324
+ : 0,
325
+ pr: pr && {
326
+ number: pr.number,
327
+ state: pr.state,
328
+ draft: pr.isDraft,
329
+ url: pr.url,
330
+ title: pr.title,
331
+ },
332
+ merged: pr?.state === "MERGED",
333
+ // Nothing of its own past the base: either merged as it was, or a
334
+ // topic with no commits yet. Either way, nothing is lost by
335
+ // putting its worktree away.
336
+ contained: haveBase && ahead === 0,
337
+ link: link(ws, repo, name),
338
+ };
339
+ });
340
+ }
341
+
342
+ async function topicList(ctx) {
343
+ const { ws, repos, foreign } = await context(ctx, ctx.has("--fresh"));
344
+ const all = repos.flatMap((repo) => topicsIn(ws, repo, foreign));
345
+ // Put away: merged (or nothing past its base) with no worktree left —
346
+ // its branch kept, as the after-merge chores leave it. Listed on asking.
347
+ const away = (t) => !t.worktree && (t.merged || t.contained);
348
+ const rows = ctx.has("--all") ? all : all.filter((t) => !away(t));
349
+ const hidden = all.length - rows.length;
350
+ ctx.out(rows, (list) => {
351
+ if (!list.length)
352
+ console.log(
353
+ `no open topic branches in ${ws.name} (start one: work topic new <name>)`,
354
+ );
355
+ for (const t of list) {
356
+ const state = t.merged
357
+ ? "merged"
358
+ : t.contained
359
+ ? `nothing past ${t.base}`
360
+ : `${t.ahead} ahead, ${t.behind} behind`;
361
+ const flags = [
362
+ t.dirty ? `${t.dirty} uncommitted` : null,
363
+ t.local && !t.onOrigin && t.ahead ? "not pushed" : null,
364
+ t.local && t.onOrigin && t.unpushed ? `${t.unpushed} unpushed` : null,
365
+ t.local && t.onOrigin && !t.unpushed && !t.pushed
366
+ ? "origin has newer"
367
+ : null,
368
+ !t.local ? "only on origin" : null,
369
+ ].filter(Boolean);
370
+ const pr = t.pr
371
+ ? ` #${t.pr.number} ${t.pr.state === "OPEN" ? (t.pr.draft ? "draft" : "open") : t.pr.state.toLowerCase()}`
372
+ : "";
373
+ console.log(
374
+ `${t.repo}@${t.topic} ${state}${pr}${flags.length ? ` (${flags.join(", ")})` : ""}`,
375
+ );
376
+ if (t.worktree) console.log(` ${t.worktree}`);
377
+ console.log(` ${t.link}`);
378
+ }
379
+ if (hidden)
380
+ console.log(
381
+ `${hidden} put away (merged, worktree removed): --all lists them`,
382
+ );
383
+ });
384
+ }
385
+
386
+ // -- done --------------------------------------------------------------------
387
+
388
+ /** Why a merged-or-contained topic's local branch still holds work its
389
+ * merge did not take, or null when it holds none: commits past the merged
390
+ * PR's head, or past what origin has of it. */
391
+ function leftover(repo, t, pr) {
392
+ const local = oid(repo.dir, `refs/heads/${t.branch}`);
393
+ if (!local || t.contained) return null;
394
+ let head = pr?.headRefOid || null;
395
+ if (head && !has(repo.dir, `${head}^{commit}`)) {
396
+ // A squash-merged PR's head lives on in GitHub's pull ref.
397
+ git(
398
+ repo.dir,
399
+ ["fetch", "--quiet", "origin", `refs/pull/${pr.number}/head`],
400
+ { allowFail: true },
401
+ );
402
+ if (!has(repo.dir, `${head}^{commit}`)) head = null;
403
+ }
404
+ const against =
405
+ head || oid(repo.dir, `refs/remotes/origin/${t.branch}`) || null;
406
+ if (!against)
407
+ return `can't tell what of ${t.branch} its merge took (no PR head or origin copy here)`;
408
+ if (contains(repo.dir, against, local)) return null;
409
+ const extra = git(repo.dir, [
410
+ "log",
411
+ "--format=%h %s",
412
+ `${against}..${local}`,
413
+ ]).out;
414
+ return `${t.branch} has commits its merge did not take:\n ${extra.split("\n").join("\n ")}`;
415
+ }
416
+
417
+ async function topicDone(ctx) {
418
+ const [name] = ctx.parse([]);
419
+ if (!name) ctx.die("usage: work topic done <name> [--rebase]");
420
+ const { ws, repos, foreign } = await context(ctx, true);
421
+ const branch = `${ws.branch}-${name}`;
422
+ const result = {
423
+ topic: name,
424
+ removed: [],
425
+ kept: [],
426
+ refused: [],
427
+ rebase: [],
428
+ };
429
+ const touched = repos.filter(
430
+ (r) =>
431
+ has(r.dir, `refs/heads/${branch}`) ||
432
+ has(r.dir, `refs/remotes/origin/${branch}`),
433
+ );
434
+ if (!touched.length)
435
+ ctx.die(`no topic ${name} in ${ws.name} (see: work topic list)`);
436
+ for (const repo of touched) {
437
+ fetch(ctx, repo);
438
+ const t = topicsIn(ws, repo, foreign).find((x) => x.branch === branch);
439
+ if (!t) continue;
440
+ const pr = prFor(repo, branch);
441
+ if (!t.merged && !t.contained) {
442
+ result.refused.push({
443
+ repo: repo.worktree,
444
+ why: `${branch} is not merged${t.pr ? ` (#${t.pr.number} is ${t.pr.state.toLowerCase()})` : ""} and has ${t.ahead} commit(s) its base does not`,
445
+ });
446
+ continue;
447
+ }
448
+ const why = leftover(repo, t, pr);
449
+ if (why) {
450
+ result.refused.push({ repo: repo.worktree, why });
451
+ continue;
452
+ }
453
+ if (t.worktree) {
454
+ const dirty = dirtyIn(t.worktree);
455
+ if (dirty.length) {
456
+ result.refused.push({
457
+ repo: repo.worktree,
458
+ why: `${t.worktree} has ${dirty.length} uncommitted change(s):\n ${dirty.slice(0, 10).join("\n ")}`,
459
+ });
460
+ continue;
461
+ }
462
+ git(repo.dir, ["worktree", "remove", t.worktree]);
463
+ // The topic's own folder, once its last repo's worktree is gone.
464
+ try {
465
+ fs.rmdirSync(path.dirname(t.worktree));
466
+ } catch {}
467
+ result.removed.push({ repo: repo.worktree, path: t.worktree });
468
+ } else {
469
+ result.kept.push({ repo: repo.worktree, why: "no worktree to remove" });
470
+ }
471
+ // The other open topics a squash-merge left on the old fork point.
472
+ const mergedTip = oid(repo.dir, `refs/heads/${branch}`);
473
+ for (const other of topicsIn(ws, repo, foreign)) {
474
+ if (other.branch === branch || other.merged || other.contained) continue;
475
+ if (!other.local || !other.behind) continue;
476
+ // Cut from the merged topic (a stack): only its own commits move.
477
+ const onto =
478
+ mergedTip && contains(repo.dir, other.branch, mergedTip) && !t.contained
479
+ ? mergedTip
480
+ : null;
481
+ const entry = {
482
+ repo: repo.worktree,
483
+ branch: other.branch,
484
+ worktree: other.worktree,
485
+ behind: other.behind,
486
+ command: onto
487
+ ? `git rebase --onto origin/${repo.base_branch} ${onto.slice(0, 12)}`
488
+ : `git rebase origin/${repo.base_branch}`,
489
+ state: "needs rebase",
490
+ };
491
+ if (ctx.has("--rebase")) {
492
+ if (!other.worktree) entry.state = "skipped: no worktree";
493
+ else if (dirtyIn(other.worktree).length)
494
+ entry.state = "skipped: uncommitted changes";
495
+ else {
496
+ const args = onto
497
+ ? ["rebase", "--onto", `origin/${repo.base_branch}`, onto]
498
+ : ["rebase", `origin/${repo.base_branch}`];
499
+ const r = git(other.worktree, args, { allowFail: true });
500
+ if (r.ok) entry.state = "rebased";
501
+ else {
502
+ // A rebase that stopped part-way is put back as it was; one that
503
+ // never started (no identity to commit as, a lock) says why.
504
+ const conflicted = /CONFLICT|could not apply/i.test(
505
+ `${r.out}\n${r.err}`,
506
+ );
507
+ git(other.worktree, ["rebase", "--abort"], { allowFail: true });
508
+ entry.state = conflicted
509
+ ? "conflict: left as it was"
510
+ : `failed: ${
511
+ `${r.err}\n${r.out}`
512
+ .split(/[\r\n]+/)
513
+ .map((l) => l.trim())
514
+ .filter((l) => l && !/^Rebasing \(/.test(l))[0] ||
515
+ "git rebase failed"
516
+ }`;
517
+ }
518
+ }
519
+ }
520
+ result.rebase.push(entry);
521
+ }
522
+ }
523
+ ctx.out(result, (r) => {
524
+ for (const x of r.removed)
525
+ console.log(`${x.repo}: removed ${x.path} (branch kept)`);
526
+ for (const x of r.kept) console.log(`${x.repo}: ${x.why}`);
527
+ for (const x of r.refused)
528
+ console.log(`${x.repo}: left as it was — ${x.why}`);
529
+ for (const x of r.rebase)
530
+ console.log(
531
+ `${x.repo}: ${x.branch} ${x.behind} behind — ${x.state}${x.state === "needs rebase" ? ` (in ${x.worktree || "its checkout"}: ${x.command}, or work topic done ${name} --rebase)` : ""}`,
532
+ );
533
+ });
534
+ if (
535
+ result.refused.length ||
536
+ result.rebase.some((x) => /^(conflict|failed)/.test(x.state))
537
+ )
538
+ process.exitCode = 1;
539
+ }
540
+
541
+ // -- command -----------------------------------------------------------------
542
+
543
+ const HELP = `work topic new <name> [--repo <repo>]… [--from <ref>] [--install]
544
+ start a topic branch, <ws-branch>-<name>, in its own worktree at
545
+ <ws>/.worktrees/<name>/<repo>, from origin/<base> (or <ref>: a stack).
546
+ The repo is the one the cwd is in, else the workspace's only one, else
547
+ --repo (repeat it for several). Prints each path, the install command
548
+ its lockfile wants (--install runs it) and the review link.
549
+ \`<group>.<topic>\` folds topics into one review chip.
550
+ work topic list [--all] [--fresh] [--json]
551
+ this workspace's open topic branches: worktree, ahead/behind its base,
552
+ uncommitted and unpushed work, PR, merged, review link. --all adds
553
+ the ones put away (merged, worktree removed); --fresh asks GitHub
554
+ for PRs again rather than the last minutes' answer.
555
+ work topic done <name> [--rebase] [--json]
556
+ after its PR merged (or with nothing past its base): fetch --prune,
557
+ remove its worktree (only if clean, and only if the merge took every
558
+ commit), keep the branch, and list the other open topics to rebase
559
+ onto origin/<base> — --rebase does it where the worktree is clean,
560
+ aborting on conflict. Never touches the workspace's own checkout.`;
561
+
562
+ export const commands = [
563
+ {
564
+ name: "topic",
565
+ summary: "start, list and put away topic branches (work help topic)",
566
+ help: HELP,
567
+ async run(ctx) {
568
+ const [verb] = ctx.positional;
569
+ const sub = { ...ctx, positional: ctx.positional.slice(1) };
570
+ const parse = ctx.parse;
571
+ sub.parse = (valued) => parse(valued).slice(1);
572
+ try {
573
+ if (verb === "new") return await topicNew(sub);
574
+ if (verb === "list" || verb === "ls" || !verb)
575
+ return await topicList(sub);
576
+ if (verb === "done") return await topicDone(sub);
577
+ } catch (err) {
578
+ ctx.die(err instanceof Error ? err.message : String(err));
579
+ }
580
+ ctx.die(
581
+ `no \`work topic ${verb}\`: new, list or done (see: work help topic)`,
582
+ );
583
+ },
584
+ },
585
+ ];