@dylanpiercey/work 0.2.16 → 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 (519) hide show
  1. package/README.md +16 -2
  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 +405 -82
  21. package/dist/_BAJGOEa1.js +168 -0
  22. package/dist/{_cZI5p-2V.js → _BlLEM8du.js} +80 -48
  23. package/dist/_t9VsALwo.js +2 -0
  24. package/dist/index.mjs +3548 -2613
  25. package/dist/public/assets/{_BwIqXHWp.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/{_BtCVvK2k.js → _4Sj6a8Ya.js} +1 -1
  30. package/dist/public/assets/_4Sj6a8Ya.js.br +0 -0
  31. package/dist/public/assets/{_Hu1xz4Lu.js → _5tN48iuJ.js} +1 -1
  32. package/dist/public/assets/_5tN48iuJ.js.br +0 -0
  33. package/dist/public/assets/{_gIdf7Xw5.js → _8_DzkMgB.js} +1 -1
  34. package/dist/public/assets/_8_DzkMgB.js.br +0 -0
  35. package/dist/public/assets/{_DSw-4lX2.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/{_BAZwH6Ve.js → _B-H6ABVZ.js} +1 -1
  40. package/dist/public/assets/_B-H6ABVZ.js.br +0 -0
  41. package/dist/public/assets/{_CNramc89.js → _B-iOtMg6.js} +1 -1
  42. package/dist/public/assets/_B-iOtMg6.js.br +0 -0
  43. package/dist/public/assets/{_DYH0eCbn.js → _B0FhzjB2.js} +1 -1
  44. package/dist/public/assets/_B0FhzjB2.js.br +0 -0
  45. package/dist/public/assets/{_BdIL1kV4.js → _B26gPL08.js} +1 -1
  46. package/dist/public/assets/_B26gPL08.js.br +0 -0
  47. package/dist/public/assets/{_BMrsOtya.js → _B2DHvTZR.js} +1 -1
  48. package/dist/public/assets/{_BCuTz32B.js → _B3Z1kRpD.js} +1 -1
  49. package/dist/public/assets/_B3Z1kRpD.js.br +0 -0
  50. package/dist/public/assets/{_G9Zy20B_.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/{_HlShuyJR.js → _B4hn3NNW.js} +1 -1
  54. package/dist/public/assets/_B4hn3NNW.js.br +0 -0
  55. package/dist/public/assets/{_o-w-GRlW.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/{_C1gXJmH-.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/{_B4jkmqvY.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/{_DDDY02_q.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/{_CkOLwn-O.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/{_qiP5PaTJ.js → _BIUaA4Nl.js} +1 -1
  82. package/dist/public/assets/_BIUaA4Nl.js.br +0 -0
  83. package/dist/public/assets/{_D4JoXfrO.js → _BJpBnuvv.js} +1 -1
  84. package/dist/public/assets/_BJpBnuvv.js.br +0 -0
  85. package/dist/public/assets/{_D5-Xbg1R.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/{_DYUAPXDQ.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/{_C7n1c5sG.js → _BXHggM8M.js} +1 -1
  94. package/dist/public/assets/_BXHggM8M.js.br +0 -0
  95. package/dist/public/assets/{_DSUWiRRK.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/{_Bfa3k3Hb.js → _BkGAUdMd.js} +1 -1
  112. package/dist/public/assets/_BkGAUdMd.js.br +0 -0
  113. package/dist/public/assets/{_5fKzV_xm.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/{_C4lapxL9.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/{_BA4Iu42n.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/{_B015R9rm.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/{_DRfZHwBJ.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/{_BBbmRU3k.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/{_DVOLp04e.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/{_Bl5WXdoB.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/{_CQIzzFRZ.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/{_CLaHO3fE.js → _CIDjLk1A.js} +1 -1
  147. package/dist/public/assets/_CIDjLk1A.js.br +0 -0
  148. package/dist/public/assets/{_CBcx1kOh.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/{_D9_2hMWP.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/{_CVxQyHtq.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/{_Dy3vjhI9.js → _CX5VOdNU.js} +1 -1
  160. package/dist/public/assets/_CX5VOdNU.js.br +0 -0
  161. package/dist/public/assets/{_CfM615kO.js → _CYcgwayF.js} +1 -1
  162. package/dist/public/assets/_CYcgwayF.js.br +0 -0
  163. package/dist/public/assets/{_hvh7Kn8G.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/{_x9gsUMHG.js → _CcINcTUt.js} +1 -1
  170. package/dist/public/assets/_CcINcTUt.js.br +0 -0
  171. package/dist/public/assets/{_Bfh0n3cD.js → _CcflXOTj.js} +1 -1
  172. package/dist/public/assets/_CcflXOTj.js.br +0 -0
  173. package/dist/public/assets/{_BjiY-VzO.js → _Cfts4mFJ.js} +1 -1
  174. package/dist/public/assets/_Cfts4mFJ.js.br +0 -0
  175. package/dist/public/assets/{_YRsH_Jn5.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/{_Cxhfb-mP.js → _CpVeIwQ4.js} +1 -1
  181. package/dist/public/assets/_CpVeIwQ4.js.br +0 -0
  182. package/dist/public/assets/{_BO_dGrB_.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/{_aY3LCTY6.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/{_CPakGo19.js → _DB7_Yora.js} +1 -1
  193. package/dist/public/assets/_DB7_Yora.js.br +0 -0
  194. package/dist/public/assets/{_CHWlI8eM.js → _DDWIDZyP.js} +1 -1
  195. package/dist/public/assets/_DDWIDZyP.js.br +0 -0
  196. package/dist/public/assets/{_C634i9Iw.js → _DG3L8-RO.js} +1 -1
  197. package/dist/public/assets/_DG3L8-RO.js.br +0 -0
  198. package/dist/public/assets/{_D5woWAW8.js → _DGRgLaG-.js} +1 -1
  199. package/dist/public/assets/_DGRgLaG-.js.br +0 -0
  200. package/dist/public/assets/{_DW7N6KAK.js → _DG_QOtsx.js} +1 -1
  201. package/dist/public/assets/_DG_QOtsx.js.br +0 -0
  202. package/dist/public/assets/{_B5aDrGbO.js → _DHYIZWev.js} +11 -11
  203. package/dist/public/assets/_DHYIZWev.js.br +0 -0
  204. package/dist/public/assets/{_8n8ba3Xr.js → _DJwY_zBh.js} +1 -1
  205. package/dist/public/assets/_DJwY_zBh.js.br +0 -0
  206. package/dist/public/assets/{_DLbIAQvl.js → _DKlB-yMq.js} +1 -1
  207. package/dist/public/assets/_DKlB-yMq.js.br +0 -0
  208. package/dist/public/assets/{_CrJYhl4C.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/{_CvHnOFNG.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/{_Brgs9TFt.js → _DRpsUHP5.js} +1 -1
  216. package/dist/public/assets/_DRpsUHP5.js.br +0 -0
  217. package/dist/public/assets/{_CJDXsoZG.js → _DUH5o_iN.js} +1 -1
  218. package/dist/public/assets/_DUH5o_iN.js.br +0 -0
  219. package/dist/public/assets/{_B-Kvhca4.js → _DUmYv8Kf.js} +1 -1
  220. package/dist/public/assets/_DUmYv8Kf.js.br +0 -0
  221. package/dist/public/assets/{_vGfofQqO.js → _DXvsvUmJ.js} +1 -1
  222. package/dist/public/assets/_DXvsvUmJ.js.br +0 -0
  223. package/dist/public/assets/{_Cbsfq4Xq.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/{_BzlH6kTq.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/{_DO4kss9P.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/{_C94vR51w.js → _Dh9434oc.js} +1 -1
  238. package/dist/public/assets/{_9nMouJiN.js → _DjYscMjF.js} +1 -1
  239. package/dist/public/assets/_DjYscMjF.js.br +0 -0
  240. package/dist/public/assets/{_De1k3VnO.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/{_P2VZNfpY.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/{_oGfeAy8g.js → _GK8r-gEu.js} +1 -1
  253. package/dist/public/assets/_GK8r-gEu.js.br +0 -0
  254. package/dist/public/assets/{_Dt7EUvlj.js → _HpxvfbM2.js} +1 -1
  255. package/dist/public/assets/_HpxvfbM2.js.br +0 -0
  256. package/dist/public/assets/{_DGB38_-Q.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/{_BpLGw_gQ.js → _WOU8TCkO.js} +1 -1
  260. package/dist/public/assets/_WOU8TCkO.js.br +0 -0
  261. package/dist/public/assets/{_qf-8mlHT.js → _XWw1j8vS.js} +1 -1
  262. package/dist/public/assets/_XWw1j8vS.js.br +0 -0
  263. package/dist/public/assets/{_DVeUDVeu.js → _YDyNvOET.js} +1 -1
  264. package/dist/public/assets/_YDyNvOET.js.br +0 -0
  265. package/dist/public/assets/{_CQzhv_mX.js → _f8D86HXZ.js} +1 -1
  266. package/dist/public/assets/_f8D86HXZ.js.br +0 -0
  267. package/dist/public/assets/{_IIk2IzWK.js → _hbXd8Ogo.js} +1 -1
  268. package/dist/public/assets/_hbXd8Ogo.js.br +0 -0
  269. package/dist/public/assets/{_rkCbTrLW.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/{_Gjew_pmJ.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/{_DVevGBhv.js → _o8mJsMHw.js} +1 -1
  278. package/dist/public/assets/_o8mJsMHw.js.br +0 -0
  279. package/dist/public/assets/{_DoIAcib0.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/{_Df1iTAvl.js → _rv9KtZHz.js} +1 -1
  283. package/dist/public/assets/_rv9KtZHz.js.br +0 -0
  284. package/dist/public/assets/{_CzuoYQDG.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/{_DuaMWtx-.js → _vG7bXn-C.js} +1 -1
  290. package/dist/public/assets/_vG7bXn-C.js.br +0 -0
  291. package/dist/public/assets/{_DkvRY2VF.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/{_BcWxCrBn.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/lib/setup/firewall.d.mts +5 -1
  316. package/lib/setup/firewall.mjs +23 -4
  317. package/package.json +1 -1
  318. package/dist/_Bhsm2MDa.js +0 -2
  319. package/dist/_Bktr58J-.js +0 -167
  320. package/dist/public/assets/_-LGAXy6L.js +0 -1
  321. package/dist/public/assets/_1fMDVNSL.js +0 -1
  322. package/dist/public/assets/_4MxLuDH4.js +0 -1
  323. package/dist/public/assets/_5fKzV_xm.js.br +0 -0
  324. package/dist/public/assets/_6uAQn-KZ.js +0 -1
  325. package/dist/public/assets/_6yZQC8Sj.js +0 -1
  326. package/dist/public/assets/_8JhYgpHe.js +0 -1
  327. package/dist/public/assets/_8n8ba3Xr.js.br +0 -0
  328. package/dist/public/assets/_9nMouJiN.js.br +0 -0
  329. package/dist/public/assets/_B-Kvhca4.js.br +0 -0
  330. package/dist/public/assets/_B4jkmqvY.js.br +0 -0
  331. package/dist/public/assets/_B5aDrGbO.js.br +0 -0
  332. package/dist/public/assets/_B71hDPcu.js +0 -1
  333. package/dist/public/assets/_B7XpTOeJ.js +0 -1
  334. package/dist/public/assets/_BA4Iu42n.js.br +0 -0
  335. package/dist/public/assets/_BAZwH6Ve.js.br +0 -0
  336. package/dist/public/assets/_BBZdAtMC.js +0 -1
  337. package/dist/public/assets/_BBbmRU3k.js.br +0 -0
  338. package/dist/public/assets/_BCuTz32B.js.br +0 -0
  339. package/dist/public/assets/_BFZ4oi5n.js +0 -1
  340. package/dist/public/assets/_BGd-I5rD.js +0 -1
  341. package/dist/public/assets/_BHp0OMqQ.js +0 -1
  342. package/dist/public/assets/_BO_dGrB_.js.br +0 -0
  343. package/dist/public/assets/_BOloI1-O.js +0 -1
  344. package/dist/public/assets/_BPD5OqbS.js +0 -1
  345. package/dist/public/assets/_BXZtuyZh.js +0 -1
  346. package/dist/public/assets/_B_aWrUZM.js +0 -1
  347. package/dist/public/assets/_B_wTYBYf.js +0 -1
  348. package/dist/public/assets/_Bbqj4oLG.js +0 -1
  349. package/dist/public/assets/_BdIL1kV4.js.br +0 -0
  350. package/dist/public/assets/_Bfa3k3Hb.js.br +0 -0
  351. package/dist/public/assets/_Bfh0n3cD.js.br +0 -0
  352. package/dist/public/assets/_BjiY-VzO.js.br +0 -0
  353. package/dist/public/assets/_Bl5WXdoB.js.br +0 -0
  354. package/dist/public/assets/_BnsBjEIT.js +0 -1
  355. package/dist/public/assets/_BnsBjEIT.js.br +0 -0
  356. package/dist/public/assets/_BpL0bstc.js +0 -2
  357. package/dist/public/assets/_BpL0bstc.js.br +0 -0
  358. package/dist/public/assets/_BpLGw_gQ.js.br +0 -0
  359. package/dist/public/assets/_BqiRi4qy.js +0 -1
  360. package/dist/public/assets/_Brgs9TFt.js.br +0 -0
  361. package/dist/public/assets/_BtCVvK2k.js.br +0 -0
  362. package/dist/public/assets/_BtEgXsuK.js +0 -1
  363. package/dist/public/assets/_BtEgXsuK.js.br +0 -0
  364. package/dist/public/assets/_BtN42R2m.js +0 -1
  365. package/dist/public/assets/_Bv9d1C-N.js +0 -1
  366. package/dist/public/assets/_BwIqXHWp.js.br +0 -0
  367. package/dist/public/assets/_BwvpgNRf.js +0 -1
  368. package/dist/public/assets/_Bx9nWm8r.js +0 -1
  369. package/dist/public/assets/_Bx9nWm8r.js.br +0 -0
  370. package/dist/public/assets/_BybJoj1B.js +0 -1
  371. package/dist/public/assets/_Bync_PH8.js +0 -1
  372. package/dist/public/assets/_Bync_PH8.js.br +0 -0
  373. package/dist/public/assets/_BzlH6kTq.js.br +0 -0
  374. package/dist/public/assets/_C1gXJmH-.js.br +0 -0
  375. package/dist/public/assets/_C24Yukj9.js +0 -1
  376. package/dist/public/assets/_C4lapxL9.js.br +0 -0
  377. package/dist/public/assets/_C634i9Iw.js.br +0 -0
  378. package/dist/public/assets/_C7n1c5sG.js.br +0 -0
  379. package/dist/public/assets/_CBMRDyF5.js +0 -1
  380. package/dist/public/assets/_CBcx1kOh.js.br +0 -0
  381. package/dist/public/assets/_CDLK6m8H.js +0 -1
  382. package/dist/public/assets/_CGmPVd6x.js +0 -1
  383. package/dist/public/assets/_CHWlI8eM.js.br +0 -0
  384. package/dist/public/assets/_CIiNyABw.js +0 -1
  385. package/dist/public/assets/_CJDXsoZG.js.br +0 -0
  386. package/dist/public/assets/_CKpKOqv6.js +0 -1
  387. package/dist/public/assets/_CLaHO3fE.js.br +0 -0
  388. package/dist/public/assets/_CMbRT0RN.js +0 -1
  389. package/dist/public/assets/_CNiymwwH.js +0 -1
  390. package/dist/public/assets/_CNramc89.js.br +0 -0
  391. package/dist/public/assets/_COjSvk7K.js +0 -1
  392. package/dist/public/assets/_CPakGo19.js.br +0 -0
  393. package/dist/public/assets/_CQIzzFRZ.js.br +0 -0
  394. package/dist/public/assets/_CQzhv_mX.js.br +0 -0
  395. package/dist/public/assets/_CRfRDlj2.js +0 -1
  396. package/dist/public/assets/_CSp3_AhQ.js +0 -1
  397. package/dist/public/assets/_CU-yRMtu.js +0 -6
  398. package/dist/public/assets/_CU-yRMtu.js.br +0 -0
  399. package/dist/public/assets/_CUPj7ARi.js +0 -1
  400. package/dist/public/assets/_CVBq_iEC.js +0 -1
  401. package/dist/public/assets/_CVxQyHtq.js.br +0 -0
  402. package/dist/public/assets/_CYEdfiVK.js +0 -1
  403. package/dist/public/assets/_CYNWm9iq.js +0 -1
  404. package/dist/public/assets/_Ca-c93LZ.js +0 -1
  405. package/dist/public/assets/_Ca4BTBEZ.js +0 -1
  406. package/dist/public/assets/_Ca4BTBEZ.js.br +0 -0
  407. package/dist/public/assets/_CbWILCnD.js +0 -1
  408. package/dist/public/assets/_Cbjd3Q9T.js +0 -2
  409. package/dist/public/assets/_Cbjd3Q9T.js.br +0 -0
  410. package/dist/public/assets/_Cbsfq4Xq.js.br +0 -0
  411. package/dist/public/assets/_CfM615kO.js.br +0 -0
  412. package/dist/public/assets/_Cfge4b__.js +0 -1
  413. package/dist/public/assets/_Cg7H9vg0.js +0 -1
  414. package/dist/public/assets/_CkOLwn-O.js.br +0 -0
  415. package/dist/public/assets/_ClJiup1H.js +0 -1
  416. package/dist/public/assets/_CpU9EtMF.js +0 -1
  417. package/dist/public/assets/_CpU9EtMF.js.br +0 -0
  418. package/dist/public/assets/_Cq8bC13s.js +0 -1
  419. package/dist/public/assets/_CrJYhl4C.js.br +0 -0
  420. package/dist/public/assets/_CvHnOFNG.js.br +0 -0
  421. package/dist/public/assets/_Cxhfb-mP.js.br +0 -0
  422. package/dist/public/assets/_CzSlKec6.js +0 -1
  423. package/dist/public/assets/_CzuoYQDG.js.br +0 -0
  424. package/dist/public/assets/_D4JoXfrO.js.br +0 -0
  425. package/dist/public/assets/_D5-Xbg1R.js.br +0 -0
  426. package/dist/public/assets/_D5woWAW8.js.br +0 -0
  427. package/dist/public/assets/_D99Wlj0O.js +0 -1
  428. package/dist/public/assets/_D9_2hMWP.js.br +0 -0
  429. package/dist/public/assets/_DC_EInc4.js +0 -1
  430. package/dist/public/assets/_DCgPMOAm.js +0 -1
  431. package/dist/public/assets/_DDDY02_q.js.br +0 -0
  432. package/dist/public/assets/_DDuN0b6I.js +0 -1
  433. package/dist/public/assets/_DEm5vgv_.js +0 -1
  434. package/dist/public/assets/_DFBqmRUc.js +0 -1
  435. package/dist/public/assets/_DGB38_-Q.js.br +0 -0
  436. package/dist/public/assets/_DHwe--xJ.js +0 -1
  437. package/dist/public/assets/_DK_ROILU.js +0 -1
  438. package/dist/public/assets/_DLUV6ZQ4.js +0 -1
  439. package/dist/public/assets/_DLbIAQvl.js.br +0 -0
  440. package/dist/public/assets/_DMWoxRSc.js +0 -16
  441. package/dist/public/assets/_DMWoxRSc.js.br +0 -0
  442. package/dist/public/assets/_DNekcg31.js +0 -1
  443. package/dist/public/assets/_DO4kss9P.js.br +0 -0
  444. package/dist/public/assets/_DOpQqgoi.js +0 -1
  445. package/dist/public/assets/_DRDoh9gX.js +0 -1
  446. package/dist/public/assets/_DRfZHwBJ.js.br +0 -0
  447. package/dist/public/assets/_DSUWiRRK.js.br +0 -0
  448. package/dist/public/assets/_DSw-4lX2.js.br +0 -0
  449. package/dist/public/assets/_DUQz9QdQ.js +0 -1
  450. package/dist/public/assets/_DUb2rGXm.js +0 -1
  451. package/dist/public/assets/_DUb2rGXm.js.br +0 -0
  452. package/dist/public/assets/_DVOLp04e.js.br +0 -0
  453. package/dist/public/assets/_DVeUDVeu.js.br +0 -0
  454. package/dist/public/assets/_DVevGBhv.js.br +0 -0
  455. package/dist/public/assets/_DW7N6KAK.js.br +0 -0
  456. package/dist/public/assets/_DXn7VNFu.js +0 -1
  457. package/dist/public/assets/_DYH0eCbn.js.br +0 -0
  458. package/dist/public/assets/_DYUAPXDQ.js.br +0 -0
  459. package/dist/public/assets/_DbODstMe.js +0 -1
  460. package/dist/public/assets/_Dc7kGfRQ.js +0 -1
  461. package/dist/public/assets/_De1k3VnO.js.br +0 -0
  462. package/dist/public/assets/_Df1iTAvl.js.br +0 -0
  463. package/dist/public/assets/_DgYlRNTv.js +0 -1
  464. package/dist/public/assets/_DjQSx1cs.js +0 -1
  465. package/dist/public/assets/_DkvRY2VF.js.br +0 -0
  466. package/dist/public/assets/_DoIAcib0.js.br +0 -0
  467. package/dist/public/assets/_Dt7EUvlj.js.br +0 -0
  468. package/dist/public/assets/_DuaMWtx-.js.br +0 -0
  469. package/dist/public/assets/_Dy3vjhI9.js.br +0 -0
  470. package/dist/public/assets/_G9Zy20B_.js.br +0 -0
  471. package/dist/public/assets/_Gjew_pmJ.js.br +0 -0
  472. package/dist/public/assets/_HCEgTdm1.js +0 -1
  473. package/dist/public/assets/_Hak9GZSq.js +0 -1
  474. package/dist/public/assets/_HlShuyJR.js.br +0 -0
  475. package/dist/public/assets/_Hu1xz4Lu.js.br +0 -0
  476. package/dist/public/assets/_IIk2IzWK.js.br +0 -0
  477. package/dist/public/assets/_JCQQIgqJ.js +0 -1
  478. package/dist/public/assets/_LzgGPkGH.js +0 -1
  479. package/dist/public/assets/_N96Oqzt6.js +0 -1
  480. package/dist/public/assets/_OSMLzC7w.js +0 -1
  481. package/dist/public/assets/_P2VZNfpY.js.br +0 -0
  482. package/dist/public/assets/_Tj4gwE2h.js +0 -1
  483. package/dist/public/assets/_UgBaqmVR.js +0 -1
  484. package/dist/public/assets/_VV8USAwR.js +0 -1
  485. package/dist/public/assets/_XWoqVB1l.js +0 -1
  486. package/dist/public/assets/_YRsH_Jn5.js.br +0 -0
  487. package/dist/public/assets/_aY3LCTY6.js.br +0 -0
  488. package/dist/public/assets/_b3iaRNRr.js +0 -1
  489. package/dist/public/assets/_cKcWLQPj.js +0 -1
  490. package/dist/public/assets/_cVTJMvgA.js +0 -1
  491. package/dist/public/assets/_fTrEfQgl.js +0 -1
  492. package/dist/public/assets/_gIdf7Xw5.js.br +0 -0
  493. package/dist/public/assets/_hlPeODrN.js +0 -1
  494. package/dist/public/assets/_hvh7Kn8G.js.br +0 -0
  495. package/dist/public/assets/_joWx_YVF.js +0 -1
  496. package/dist/public/assets/_k1dlXWES.js +0 -1
  497. package/dist/public/assets/_kPqXo7z9.js +0 -86
  498. package/dist/public/assets/_kPqXo7z9.js.br +0 -0
  499. package/dist/public/assets/_o-w-GRlW.js.br +0 -0
  500. package/dist/public/assets/_oGfeAy8g.js.br +0 -0
  501. package/dist/public/assets/_qf-8mlHT.js.br +0 -0
  502. package/dist/public/assets/_qiP5PaTJ.js.br +0 -0
  503. package/dist/public/assets/_rkCbTrLW.js.br +0 -0
  504. package/dist/public/assets/_vGfofQqO.js.br +0 -0
  505. package/dist/public/assets/_x9gsUMHG.js.br +0 -0
  506. package/dist/public/assets/_xh9Gonv9.js +0 -1
  507. package/dist/public/assets/files-view-BAyLKvRK.js +0 -2
  508. package/dist/public/assets/manage-workspace-B2x5MV3x.js +0 -2
  509. package/dist/public/assets/new-BYYj4BqJ.js +0 -1
  510. package/dist/public/assets/new-workspace-Der44YSz.js +0 -2
  511. package/dist/public/assets/review-view-DKfw2IXZ.js +0 -2
  512. package/dist/public/assets/settings-BYYj4BqJ.js +0 -1
  513. package/dist/public/assets/settings-page-C2QJm3t_.js +0 -2
  514. package/dist/public/assets/styles-Dh_Zl9g6.css +0 -1
  515. package/dist/public/assets/styles-Dh_Zl9g6.css.br +0 -0
  516. package/dist/public/assets/w-BYYj4BqJ.js +0 -1
  517. package/dist/public/assets/w._-BYYj4BqJ.js +0 -1
  518. package/dist/public/assets/w._-DiowwH3T.js +0 -1
  519. package/dist/public/assets/w._-jtiTmGZc.js +0 -1
package/bin/work.mjs CHANGED
@@ -5,18 +5,27 @@
5
5
  // (--tailscale serves work on the tailnet with `tailscale serve`, which
6
6
  // it otherwise offers when Tailscale is logged in; --no-tailscale skips that;
7
7
  // --firewall adds a `work` rule for direct connections' UDP port to a host
8
- // firewall that likely drops it, or on macOS lets the service's node in,
9
- // which it otherwise offers; --no-firewall skips that)
8
+ // firewall that may drop it, or on macOS lets the service's node in,
9
+ // which it otherwise offers; --no-firewall skips that;
10
+ // --allow-host <name> / --disallow-host <name>, repeated, put one name
11
+ // in or out of WORK_ALLOWED_HOSTS and keep the rest; --json prints
12
+ // what it did as JSON on stdout, its progress on stderr)
10
13
  // work doctor report machine readiness (--json, --fix to install what's missing)
11
14
  // work start run the server in the foreground
12
- // work update upgrade the npm package and refresh the service
15
+ // work update upgrade the npm package and refresh the service (--yes, --json as setup's)
13
16
  // work usage print agent plan/quota usage
14
17
  // work workspaces list workspaces (name, branch, sessions, ports)
15
- // work workspace [id|name] one workspace: repos, PRs, sessions, procs (default: the current one)
18
+ // work workspace [id|name] one workspace: repos, PRs, sessions, procs (default: the current one;
19
+ // --fresh asks GitHub again rather than the PR cache, ~2 min old)
16
20
  // work repos [id|name] a workspace's repos with base branch and open PRs
17
- // work procs [id|name] a workspace's running processes and ports
18
- // work review [id|name] pending review comments as markdown (--json for rows)
19
- // work export [file] write a backup manifest (workspaces, settings, skills, tool pins)
21
+ // work procs [id|name] a workspace's running processes and ports, each port with
22
+ // the link that opens it from the user's browser (through the
23
+ // address it last reached work by)
24
+ // work review [id|name] pending review comments as markdown (--json for rows;
25
+ // --take also clears exactly those from the review, as its
26
+ // Send does, once acted on, so a later Send cannot repeat them)
27
+ // work export [file] write a backup manifest (workspaces, settings, skills, tool pins;
28
+ // --json says what it wrote)
20
29
  // work restore <file> bring a manifest's workspaces and machine setup back (--yes to run;
21
30
  // --allow <spec|host>, repeated, for each tool outside work's catalog
22
31
  // or new GitHub host it names — skipped otherwise)
@@ -26,7 +35,13 @@
26
35
  //
27
36
  // The read commands ask the running server (same data as the UI) and take
28
37
  // --json for machine-readable output — agents inside a workspace should
29
- // use them instead of poking at the filesystem or GitHub.
38
+ // use them instead of poking at the filesystem or GitHub. Nothing prompts
39
+ // under --json, --non-interactive or WORK_NONINTERACTIVE=1: a question is
40
+ // answered no, as --yes answers setup's optional ones.
41
+ //
42
+ // Commands beyond these live one file each under bin/cli/ (a file there
43
+ // exports `commands`; see bin/cli/README.md), found at run time, and
44
+ // `work help` lists them all: this header's own lines, then theirs.
30
45
  import { execFileSync, spawnSync } from "node:child_process";
31
46
  import fs from "node:fs";
32
47
  import os from "node:os";
@@ -45,6 +60,7 @@ import {
45
60
  import { allowHost, stateText } from "../lib/setup/tailscale.mjs";
46
61
  import {
47
62
  applyPlan,
63
+ dropText,
48
64
  firewallProbe,
49
65
  serviceNode,
50
66
  } from "../lib/setup/firewall.mjs";
@@ -54,6 +70,7 @@ import { dialHost, listenStream } from "../lib/setup/listen.mjs";
54
70
  import { commentMarkdown } from "../lib/review/markdown.mjs";
55
71
  import { formatInvite, parseInvite } from "../lib/share/manifest.mjs";
56
72
  import { duration, pctText } from "../lib/usage/format.mjs";
73
+ import { locOf, portBlockedAt, portUrlAt } from "../lib/ports/url.mjs";
57
74
 
58
75
  const pkgRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
59
76
  const pkg = JSON.parse(
@@ -62,7 +79,10 @@ const pkg = JSON.parse(
62
79
  const distEntry = path.join(pkgRoot, "dist", "index.mjs");
63
80
  const appPort = process.env.WORK_APP_PORT || "7171";
64
81
 
65
- const log = (msg) => console.log(`==> ${msg}`);
82
+ // Under --json, stdout is the JSON alone: what a command says along the
83
+ // way goes to stderr.
84
+ const log = (msg) => (jsonOut ? console.error : console.log)(`==> ${msg}`);
85
+ const say = (msg) => (jsonOut ? console.error : console.log)(msg);
66
86
  const warn = (msg) => console.warn(`warning: ${msg}`);
67
87
  const die = (msg) => {
68
88
  console.error(`error: ${msg}`);
@@ -78,6 +98,12 @@ const sh = (cmd, args, opts = {}) =>
78
98
  const [, , command = "help", ...args] = process.argv;
79
99
  const yes = args.includes("--yes") || args.includes("-y");
80
100
  const jsonOut = args.includes("--json");
101
+ /** Whether a question may be asked at all: an agent's shell often has a
102
+ * controlling terminal it cannot answer on, and a prompt there hangs it. */
103
+ const interactive =
104
+ !jsonOut &&
105
+ !args.includes("--non-interactive") &&
106
+ !process.env.WORK_NONINTERACTIVE;
81
107
  /** `--name <value>` style flags take the next arg; keep it out of the
82
108
  * positionals so `work join --name x` still reads stdin. */
83
109
  const VALUED = new Set(["--name", "--allow"]);
@@ -100,7 +126,20 @@ const flagValues = (flag) =>
100
126
  return [args[i + 1]];
101
127
  });
102
128
 
103
- switch (command) {
129
+ // A command of bin/cli/ (`workspace create`: a word of the core's, then
130
+ // its own) is looked for first, so one can extend a core command with
131
+ // verbs of its own without a case here.
132
+ const cli = await cliCommand(command, positional[0]);
133
+
134
+ switch (cli ? "" : command) {
135
+ case "":
136
+ await cli.cmd.run(cliContext(cli.words));
137
+ break;
138
+ case "help":
139
+ case "--help":
140
+ case "-h":
141
+ await helpCmd(positional[0]);
142
+ break;
104
143
  case "start":
105
144
  await start();
106
145
  break;
@@ -168,9 +207,10 @@ switch (command) {
168
207
  console.log(pkg.version);
169
208
  break;
170
209
  default:
171
- console.log(
172
- `work ${pkg.version}\n\nusage: work <setup|install-service|doctor|start|update|usage|workspaces|workspace|repos|procs|review|export|restore|share|join|version> [--json]`,
173
- );
210
+ // A typo must fail: an agent reads the exit code, and usage on stdout
211
+ // with 0 read as the command having run.
212
+ console.error(`error: no command \`${command}\` (see: work help)`);
213
+ process.exitCode = 2;
174
214
  }
175
215
 
176
216
  async function start() {
@@ -231,18 +271,38 @@ function serverUrl() {
231
271
  return `http://${dialHost(env.WORK_LISTEN)}:${env.PORT}`;
232
272
  }
233
273
 
234
- async function apiGet(path) {
274
+ /** One request to the running server: its JSON, or the command dies with
275
+ * the server's own reason (`{error}`), never only its status. */
276
+ async function apiCall(method, path, body, timeoutMs = 30_000) {
235
277
  const port = serviceEnv().PORT;
278
+ let res;
236
279
  try {
237
- const res = await fetch(`${serverUrl()}${path}`, {
238
- signal: AbortSignal.timeout(30_000),
280
+ res = await fetch(`${serverUrl()}${path}`, {
281
+ method,
282
+ headers:
283
+ body === undefined ? undefined : { "content-type": "application/json" },
284
+ body: body === undefined ? undefined : JSON.stringify(body),
285
+ signal: AbortSignal.timeout(timeoutMs),
239
286
  });
240
- if (!res.ok) die(`server answered ${res.status} for ${path}`);
241
- return await res.json();
242
- } catch (err) {
243
- if (err?.message?.startsWith("server answered")) throw err;
287
+ } catch {
244
288
  die(`no work server on port ${port}. Start one with: work start`);
245
289
  }
290
+ const text = await res.text();
291
+ let data = null;
292
+ try {
293
+ data = text ? JSON.parse(text) : null;
294
+ } catch {}
295
+ if (!res.ok || (data && typeof data === "object" && data.error)) {
296
+ die(
297
+ (data && data.error) ||
298
+ `server answered ${res.status} for ${method} ${path}`,
299
+ );
300
+ }
301
+ return data;
302
+ }
303
+
304
+ function apiGet(path) {
305
+ return apiCall("GET", path);
246
306
  }
247
307
 
248
308
  /**
@@ -254,7 +314,7 @@ async function apiGet(path) {
254
314
  async function resolveWorkspace(arg) {
255
315
  const [list, status] = await Promise.all([
256
316
  apiGet("/api/workspaces"),
257
- apiGet("/api/status"),
317
+ apiGet(statusPath()),
258
318
  ]);
259
319
  let ws;
260
320
  if (arg) {
@@ -293,10 +353,17 @@ function pad(s, n) {
293
353
  return String(s ?? "").padEnd(n);
294
354
  }
295
355
 
356
+ /** The status snapshot, or with --fresh one that asks GitHub again: a PR
357
+ * made a moment ago (`gh pr create`) is otherwise missing for as long as
358
+ * the PR cache keeps its last answer. */
359
+ function statusPath() {
360
+ return args.includes("--fresh") ? "/api/status?fresh" : "/api/status";
361
+ }
362
+
296
363
  async function workspacesCmd() {
297
364
  const [list, status] = await Promise.all([
298
365
  apiGet("/api/workspaces"),
299
- apiGet("/api/status"),
366
+ apiGet(statusPath()),
300
367
  ]);
301
368
  const rows = list.map((w) => ({ ...w, ...(status[w.id] || {}) }));
302
369
  if (jsonOut) return console.log(JSON.stringify(rows, null, 2));
@@ -322,21 +389,26 @@ function prLine(pr) {
322
389
  : pr.isDraft
323
390
  ? "draft"
324
391
  : "open";
325
- return `#${pr.number} ${state} ${pr.title} ${pr.url}`;
392
+ return `#${pr.number} ${state} ${pr.title}${pr.headRefName ? ` (${pr.headRefName})` : ""} ${pr.url}`;
326
393
  }
327
394
 
328
395
  async function workspaceCmd() {
329
396
  const { ws, status } = await resolveWorkspace(positional[0]);
330
397
  const procs = await apiGet(`/api/workspaces/${ws.id}/procs`).catch(() => []);
331
- const [sessions] = await Promise.all([
398
+ const [sessions, linkPort] = await Promise.all([
332
399
  apiGet(`/api/workspaces/${ws.id}/sessions`).catch(() => []),
400
+ portLinker(),
333
401
  ]);
402
+ const me = process.env.WORK_SESSION_ID;
334
403
  const out = {
335
404
  ...ws,
336
- ports: status.ports,
405
+ ports: (status.ports || []).map((p) => ({
406
+ ...p,
407
+ ...linkPort(p.port, p.local),
408
+ })),
337
409
  repos: mergeRepos(ws, status),
338
- sessions,
339
- procs,
410
+ sessions: sessions.map((s) => (s.id === me ? { ...s, self: true } : s)),
411
+ procs: procs.map((p) => withUrls(p, linkPort)),
340
412
  };
341
413
  if (jsonOut) return console.log(JSON.stringify(out, null, 2));
342
414
  console.log(`${ws.name} (${ws.status})`);
@@ -351,16 +423,58 @@ async function workspaceCmd() {
351
423
  console.log(` sessions:`);
352
424
  for (const s of sessions) {
353
425
  const phase = s.state?.phase || (s.state?.alive ? "idle" : "off");
354
- console.log(` ${pad(s.agent, 10)} ${pad(phase, 8)} ${s.title || ""}`);
426
+ console.log(
427
+ ` ${pad(s.id, 9)} ${pad(s.agent, 10)} ${pad(phase, 8)} ${s.title || ""}${s.id === me ? " (this session)" : ""}`,
428
+ );
355
429
  }
356
430
  }
357
431
  if (procs.length) {
358
432
  console.log(` running:`);
359
- for (const p of procs)
360
- console.log(
361
- ` ${pad(p.pid, 7)} ${p.cmd}${p.ports?.length ? " " + p.ports.map((x) => `:${x}`).join(" ") : ""}`,
362
- );
363
- }
433
+ for (const p of out.procs)
434
+ console.log(` ${pad(p.pid, 7)} ${p.cmd}${portsText(p)}`);
435
+ }
436
+ }
437
+
438
+ /** How a port opens for the user: through the address their browser last
439
+ * reached work by (`/api/boot`'s `origin`) as the page's port chip would
440
+ * (lib/ports/url), or this machine's own when no browser has yet. `blocked`
441
+ * says why that link cannot work (a port bound to loopback, a page on a
442
+ * direct address). */
443
+ async function portLinker() {
444
+ const boot = await apiGet("/api/boot");
445
+ const loc = boot.origin ? locOf(boot.origin) : null;
446
+ return (port, local) => {
447
+ if (!loc) return { url: `http://localhost:${port}` };
448
+ const blocked = portBlockedAt(port, local, loc);
449
+ return {
450
+ url: portUrlAt(port, loc, boot.portHosts),
451
+ ...(blocked && { blocked }),
452
+ };
453
+ };
454
+ }
455
+
456
+ /** A process with each of its ports' links (`urls`, by port). */
457
+ function withUrls(p, linkPort) {
458
+ if (!p.ports?.length) return p;
459
+ const local = new Set(p.local || []);
460
+ return {
461
+ ...p,
462
+ urls: Object.fromEntries(
463
+ p.ports.map((port) => [port, linkPort(port, local.has(port))]),
464
+ ),
465
+ };
466
+ }
467
+
468
+ /** A process's ports for a line of text, each with where it opens. */
469
+ function portsText(p) {
470
+ if (!p.ports?.length) return "";
471
+ return ` ${p.ports
472
+ .map((port) => {
473
+ const link = p.urls?.[port];
474
+ if (!link) return `:${port}`;
475
+ return `:${port} ${link.url}${link.blocked ? ` (${link.blocked})` : ""}`;
476
+ })
477
+ .join(" ")}`;
364
478
  }
365
479
 
366
480
  /** Workspace repos joined with the status snapshot's PRs per repo. */
@@ -386,13 +500,15 @@ async function reposCmd() {
386
500
 
387
501
  async function procsCmd() {
388
502
  const { ws } = await resolveWorkspace(positional[0]);
389
- const procs = await apiGet(`/api/workspaces/${ws.id}/procs`);
503
+ const [raw, linkPort] = await Promise.all([
504
+ apiGet(`/api/workspaces/${ws.id}/procs`),
505
+ portLinker(),
506
+ ]);
507
+ const procs = raw.map((p) => withUrls(p, linkPort));
390
508
  if (jsonOut) return console.log(JSON.stringify(procs, null, 2));
391
509
  if (!procs.length) return console.log("nothing running");
392
510
  for (const p of procs)
393
- console.log(
394
- `${pad(p.pid, 7)} ${p.cmd}${p.ports?.length ? " " + p.ports.map((x) => `:${x}`).join(" ") : ""}`,
395
- );
511
+ console.log(`${pad(p.pid, 7)} ${p.cmd}${portsText(p)}`);
396
512
  }
397
513
 
398
514
  /** Comments left on the review page but not yet sent: an agent can pull
@@ -401,9 +517,23 @@ async function procsCmd() {
401
517
  async function reviewCmd() {
402
518
  const { ws } = await resolveWorkspace(positional[0]);
403
519
  const { comments = [] } = await apiGet(`/api/workspaces/${ws.id}/review`);
404
- if (jsonOut) return console.log(JSON.stringify(comments, null, 2));
405
- if (!comments.length) return console.log("no pending review comments");
406
- console.log(commentMarkdown(comments, await reviewSections(ws.id, comments)));
520
+ if (jsonOut) console.log(JSON.stringify(comments, null, 2));
521
+ else if (!comments.length) console.log("no pending review comments");
522
+ else
523
+ console.log(
524
+ commentMarkdown(comments, await reviewSections(ws.id, comments)),
525
+ );
526
+ // Taken, the comments leave the review as a Send takes them: exactly the
527
+ // ones printed, so one written meanwhile stays for the next look.
528
+ if (args.includes("--take") && comments.length) {
529
+ await apiPost(`/api/workspaces/${ws.id}/review`, {
530
+ action: "clear",
531
+ ids: comments.map((c) => c.id),
532
+ });
533
+ console.error(
534
+ `took ${comments.length} comment${comments.length === 1 ? "" : "s"} off the review`,
535
+ );
536
+ }
407
537
  }
408
538
 
409
539
  /** The diff's sections, which say which lane or PR a comment's lines are
@@ -412,41 +542,25 @@ async function reviewCmd() {
412
542
  * comments then read as the checkout's rather than failing the command. */
413
543
  async function reviewSections(wsId, comments) {
414
544
  if (!comments.some((c) => c.head)) return null;
415
- const port = serviceEnv().PORT;
416
545
  try {
417
- const res = await fetch(
418
- `http://127.0.0.1:${port}/api/workspaces/${wsId}/diff`,
419
- { signal: AbortSignal.timeout(60_000) },
420
- );
546
+ const res = await fetch(`${serverUrl()}/api/workspaces/${wsId}/diff`, {
547
+ signal: AbortSignal.timeout(60_000),
548
+ });
421
549
  return res.ok ? ((await res.json()).sections ?? null) : null;
422
550
  } catch {
423
551
  return null;
424
552
  }
425
553
  }
426
554
 
427
- async function apiPost(path, body) {
428
- const port = serviceEnv().PORT;
429
- let res;
430
- try {
431
- res = await fetch(`${serverUrl()}${path}`, {
432
- method: "POST",
433
- headers: { "content-type": "application/json" },
434
- body: body === undefined ? undefined : JSON.stringify(body),
435
- signal: AbortSignal.timeout(15 * 60_000),
436
- });
437
- } catch {
438
- die(`no work server on port ${port}. Start one with: work start`);
439
- }
440
- const data = await res.json().catch(() => ({}));
441
- if (!res.ok || data.error)
442
- die(data.error || `server answered ${res.status} for ${path}`);
443
- return data;
555
+ /** A write: as long as a clone can take (a create, a restore). */
556
+ function apiPost(path, body) {
557
+ return apiCall("POST", path, body ?? {}, 15 * 60_000);
444
558
  }
445
559
 
446
560
  /** One line per workspace/repo of what the file will not carry. */
447
561
  function printPreflight(found) {
448
562
  for (const w of found.workspaces) {
449
- console.log(w.name);
563
+ say(w.name);
450
564
  for (const r of w.repos) {
451
565
  const parts = [];
452
566
  for (const b of r.unpushed)
@@ -455,10 +569,10 @@ function printPreflight(found) {
455
569
  if (r.untracked.length) parts.push(`${r.untracked.length} untracked`);
456
570
  if (r.ignored.length) parts.push(`${r.ignored.length} ignored`);
457
571
  if (r.more) parts.push(`+${r.more} more`);
458
- console.log(` ${r.worktree}/ ${parts.join(", ")}`);
459
- for (const f of r.modified) console.log(` M ${f}`);
460
- for (const f of r.untracked) console.log(` ? ${f}`);
461
- for (const f of r.ignored) console.log(` ! ${f}`);
572
+ say(` ${r.worktree}/ ${parts.join(", ")}`);
573
+ for (const f of r.modified) say(` M ${f}`);
574
+ for (const f of r.untracked) say(` ? ${f}`);
575
+ for (const f of r.ignored) say(` ! ${f}`);
462
576
  }
463
577
  }
464
578
  }
@@ -476,7 +590,7 @@ async function exportCmd() {
476
590
  );
477
591
  }
478
592
  if (found.workspaces.length) {
479
- console.log("not in the backup:");
593
+ say("not in the backup:");
480
594
  printPreflight(found);
481
595
  if (!yes) {
482
596
  console.log("");
@@ -497,6 +611,18 @@ async function exportCmd() {
497
611
  "work-backup.json";
498
612
  fs.writeFileSync(name, await res.text());
499
613
  log(`wrote ${name}`);
614
+ if (jsonOut)
615
+ console.log(
616
+ JSON.stringify(
617
+ {
618
+ file: path.resolve(name),
619
+ contents: c ?? null,
620
+ left_out: found.workspaces,
621
+ },
622
+ null,
623
+ 2,
624
+ ),
625
+ );
500
626
  }
501
627
 
502
628
  function readManifest(file) {
@@ -657,6 +783,8 @@ function until(iso) {
657
783
  }
658
784
 
659
785
  async function setup() {
786
+ // First: a bad name fails before anything is touched.
787
+ editAllowedHosts();
660
788
  await bridge();
661
789
  const d = await doctor(doctorOpts());
662
790
  await fix(d);
@@ -669,13 +797,30 @@ async function setup() {
669
797
  const port = serviceEnv().PORT;
670
798
  if (!tailnet) maybeTunnel(port);
671
799
  if (running) {
672
- if (await waitForServer()) log(`done — http://localhost:${port}`);
673
- else
800
+ const answered = await waitForServer();
801
+ if (answered) log(`done — http://localhost:${port}`);
802
+ else {
674
803
  warn(
675
804
  `service started but nothing answered on port ${port} within 30s — check the logs`,
676
805
  );
806
+ // A setup that left no server is a failed one, to whatever ran it.
807
+ process.exitCode = 1;
808
+ }
677
809
  if (tailnet)
678
810
  log(`on your phone and other devices on the tailnet: ${tailnet}`);
811
+ if (jsonOut)
812
+ console.log(
813
+ JSON.stringify(
814
+ {
815
+ ok: answered,
816
+ version: pkg.version,
817
+ url: `http://localhost:${port}`,
818
+ tailnet: tailnet || null,
819
+ },
820
+ null,
821
+ 2,
822
+ ),
823
+ );
679
824
  } else {
680
825
  // A fresh install's shell may not have mise on PATH yet — name the shim.
681
826
  const shim = path.join(
@@ -693,6 +838,34 @@ async function setup() {
693
838
  }
694
839
  }
695
840
 
841
+ /** `--allow-host <name>` / `--disallow-host <name>`, each repeatable: one
842
+ * name in or out of the service's WORK_ALLOWED_HOSTS, the rest kept —
843
+ * `WORK_ALLOWED_HOSTS=… work setup` replaces the whole list, which an
844
+ * agent adding one proxy's name would otherwise have to read back first.
845
+ * Applied before anything writes the service, which carries what this
846
+ * leaves in the environment (`serviceEnv`). */
847
+ function editAllowedHosts() {
848
+ const allow = flagValues("--allow-host");
849
+ const disallow = flagValues("--disallow-host");
850
+ if (!allow.length && !disallow.length) return;
851
+ for (const name of [...allow, ...disallow]) {
852
+ if (!/^[A-Za-z0-9.-]+$/.test(name))
853
+ die(
854
+ `not a host name: ${name} (one name per flag, e.g. work.example.com)`,
855
+ );
856
+ }
857
+ const before = serviceEnv().WORK_ALLOWED_HOSTS || "";
858
+ let next = before;
859
+ for (const name of allow) next = allowHost(next, name, true);
860
+ for (const name of disallow) next = allowHost(next, name, false);
861
+ process.env.WORK_ALLOWED_HOSTS = next;
862
+ log(
863
+ next === before
864
+ ? `WORK_ALLOWED_HOSTS unchanged: ${next || "(none)"}`
865
+ : `WORK_ALLOWED_HOSTS=${next || "(none)"}`,
866
+ );
867
+ }
868
+
696
869
  /** Bring a database from before 0.1 to the baseline this release starts
697
870
  * from (lib/setup/bridge.mjs), before anything touches the service: the
698
871
  * server it would restart refuses such a database. Run with the settings
@@ -744,22 +917,35 @@ async function update() {
744
917
  // `--prefer-online`: npm serves `latest` from a cached packument for a
745
918
  // few minutes after a publish, so an update right behind a release
746
919
  // reinstalled the version already running.
747
- sh("npm", [
748
- "install",
749
- "-g",
750
- "--prefer-online",
751
- ...allowScriptsArgs(),
752
- "@dylanpiercey/work@latest",
753
- ]);
920
+ // Under --json, npm's own output goes to stderr: stdout is setup's JSON.
921
+ const stdio = jsonOut ? ["inherit", 2, 2] : "inherit";
922
+ sh(
923
+ "npm",
924
+ [
925
+ "install",
926
+ "-g",
927
+ "--prefer-online",
928
+ ...allowScriptsArgs(),
929
+ "@dylanpiercey/work@latest",
930
+ ],
931
+ { stdio },
932
+ );
754
933
  // Re-exec the (possibly new) CLI to finish with fresh code.
755
934
  sh("work", [
756
935
  "setup",
757
936
  ...(yes ? ["--yes"] : []),
758
937
  ...args.filter((a) =>
759
- ["--tailscale", "--no-tailscale", "--firewall", "--no-firewall"].includes(
760
- a,
761
- ),
938
+ [
939
+ "--tailscale",
940
+ "--no-tailscale",
941
+ "--firewall",
942
+ "--no-firewall",
943
+ "--json",
944
+ "--non-interactive",
945
+ ].includes(a),
762
946
  ),
947
+ ...flagValues("--allow-host").flatMap((h) => ["--allow-host", h]),
948
+ ...flagValues("--disallow-host").flatMap((h) => ["--disallow-host", h]),
763
949
  ]);
764
950
  }
765
951
 
@@ -975,7 +1161,7 @@ async function fix(d) {
975
1161
  }
976
1162
  log(`installing ${tool.label} via ${tool.mise ? "mise" : "gh"}`);
977
1163
  try {
978
- await installTool(tool.id, { log: (l) => console.log(` ${l}`) });
1164
+ await installTool(tool.id, { log: (l) => say(` ${l}`) });
979
1165
  } catch (err) {
980
1166
  if (tool.required) die(`${tool.label} install failed: ${err.message}`);
981
1167
  warn(`${tool.label} install failed (non-fatal)`);
@@ -1641,7 +1827,7 @@ async function firewall() {
1641
1827
  if (asked) log(`${f.what} already admits UDP ${f.port}`);
1642
1828
  return;
1643
1829
  }
1644
- const drops = `${f.what} ${f.exact ? "drops" : "likely drops"} UDP ${f.port}${f.why ? ` (${f.why})` : ""}, which direct connections from your other devices use`;
1830
+ const drops = `${dropText(f)}, which direct connections from your other devices use`;
1645
1831
  if (!f.plan.length) {
1646
1832
  if (asked || !yes) warn(`${drops}: ${f.fix}`);
1647
1833
  return;
@@ -1740,6 +1926,7 @@ function allowTailnet(serve) {
1740
1926
  * controlling terminal, since the curl installer pipes its script into the
1741
1927
  * shell this runs under. Null with no terminal at all. */
1742
1928
  function terminal() {
1929
+ if (!interactive) return null;
1743
1930
  if (process.stdin.isTTY) return process.stdin;
1744
1931
  try {
1745
1932
  return new tty.ReadStream(fs.openSync("/dev/tty", "r"));
@@ -1762,3 +1949,139 @@ async function confirm(input, question) {
1762
1949
  rl.close();
1763
1950
  }
1764
1951
  }
1952
+
1953
+ // -- commands of bin/cli/ and help ------------------------------------------
1954
+
1955
+ /** Every command a file of bin/cli/ exports (`_`-prefixed files are its
1956
+ * helpers), loaded once. (Kept on the function: the dispatch above runs
1957
+ * before a top-level `let` here would be initialised.) */
1958
+ async function cliCommands() {
1959
+ if (cliCommands.cache) return cliCommands.cache;
1960
+ const dir = path.join(pkgRoot, "bin", "cli");
1961
+ const files = fs.existsSync(dir)
1962
+ ? fs
1963
+ .readdirSync(dir)
1964
+ .filter((f) => f.endsWith(".mjs") && !f.startsWith("_"))
1965
+ .sort()
1966
+ : [];
1967
+ const all = [];
1968
+ for (const f of files) {
1969
+ const mod = await import(path.join(dir, f));
1970
+ for (const c of mod.commands || []) all.push(c);
1971
+ }
1972
+ cliCommands.cache = all;
1973
+ return all;
1974
+ }
1975
+
1976
+ /** A core command's other name, which a bin/cli/ command's first word may
1977
+ * be spelled with (`work ws create` is `workspace create`). */
1978
+ function canonical(word) {
1979
+ return word === "ws" ? "workspace" : word;
1980
+ }
1981
+
1982
+ /** The bin/cli/ command `argv` names: two words (`workspace create`)
1983
+ * before one, so a verb of its own wins over the word alone. */
1984
+ async function cliCommand(first, second) {
1985
+ const all = await cliCommands();
1986
+ const head = canonical(first);
1987
+ const names = (c) => [c.name, ...(c.aliases || [])];
1988
+ if (second) {
1989
+ const two = `${head} ${second}`;
1990
+ const cmd = all.find((c) => names(c).includes(two));
1991
+ if (cmd) return { cmd, words: 2 };
1992
+ }
1993
+ const cmd = all.find((c) => names(c).includes(head));
1994
+ return cmd ? { cmd, words: 1 } : null;
1995
+ }
1996
+
1997
+ /** What a bin/cli/ command is handed: its own argv, the CLI's flags, and
1998
+ * the helpers every command shares — the running server (`api`), which
1999
+ * workspace is meant, and how to say something or fail. */
2000
+ function cliContext(words) {
2001
+ const own = args.slice(0);
2002
+ // The words that named it are not its positionals.
2003
+ const pos = positional.slice(words - 1);
2004
+ return {
2005
+ pkg,
2006
+ args: own,
2007
+ positional: pos,
2008
+ json: jsonOut,
2009
+ yes,
2010
+ has: (flag) => args.includes(flag),
2011
+ flag: flagValue,
2012
+ flags: flagValues,
2013
+ /** Positionals with `valued` flags' values left out (`--agent x`). */
2014
+ parse(valued = []) {
2015
+ const set = new Set([...VALUED, ...valued]);
2016
+ return args
2017
+ .filter(
2018
+ (a, i) => a === "-" || (!a.startsWith("-") && !set.has(args[i - 1])),
2019
+ )
2020
+ .slice(words - 1);
2021
+ },
2022
+ api: {
2023
+ get: (p) => apiCall("GET", p),
2024
+ post: (p, body) => apiPost(p, body),
2025
+ put: (p, body) => apiCall("PUT", p, body ?? {}),
2026
+ patch: (p, body) => apiCall("PATCH", p, body ?? {}),
2027
+ del: (p, body) => apiCall("DELETE", p, body),
2028
+ },
2029
+ serverUrl,
2030
+ resolveWorkspace,
2031
+ /** `data` as JSON under --json, else what `text` prints. */
2032
+ out(data, text) {
2033
+ if (jsonOut) console.log(JSON.stringify(data, null, 2));
2034
+ else text?.(data);
2035
+ },
2036
+ pad,
2037
+ log,
2038
+ warn,
2039
+ die,
2040
+ };
2041
+ }
2042
+
2043
+ /** The header's own lines for the core commands: `work <name>` and what
2044
+ * continues it, as written at the top of this file. */
2045
+ function coreHelp() {
2046
+ const src = fs.readFileSync(fileURLToPath(import.meta.url), "utf8");
2047
+ const lines = [];
2048
+ for (const line of src.split("\n").slice(1)) {
2049
+ if (!line.startsWith("//")) break;
2050
+ const text = line.replace(/^\/\/ ?/, "");
2051
+ // The list starts at its first `work …` line and ends at a blank one.
2052
+ if (!lines.length && !/^ {2}work /.test(text)) continue;
2053
+ if (!text.trim()) break;
2054
+ lines.push(text.replace(/^ {2}/, ""));
2055
+ }
2056
+ return lines;
2057
+ }
2058
+
2059
+ /** `work help`: every command, the core's then bin/cli/'s; `work help
2060
+ * <cmd>` that one's whole text. */
2061
+ async function helpCmd(topic) {
2062
+ const all = await cliCommands();
2063
+ const core = coreHelp();
2064
+ if (topic) {
2065
+ const head = canonical(topic);
2066
+ const own = all.filter(
2067
+ (c) => c.name === head || c.name.startsWith(`${head} `),
2068
+ );
2069
+ const start = core.findIndex((l) => new RegExp(`^work ${head}\\b`).test(l));
2070
+ if (start < 0 && !own.length)
2071
+ die(`no command \`${topic}\` (see: work help)`);
2072
+ if (start >= 0) {
2073
+ let end = start + 1;
2074
+ while (end < core.length && !core[end].startsWith("work ")) end++;
2075
+ console.log(core.slice(start, end).join("\n"));
2076
+ }
2077
+ for (const c of own) console.log(`\n${c.help || c.summary}`);
2078
+ return;
2079
+ }
2080
+ console.log(`work ${pkg.version}\n`);
2081
+ console.log(core.join("\n"));
2082
+ for (const c of all) console.log(`work ${c.name.padEnd(22)} ${c.summary}`);
2083
+ console.log(
2084
+ "\nwork help <command> for one command's whole text. Most take --json;",
2085
+ );
2086
+ console.log("under it stdout is JSON alone and progress goes to stderr.");
2087
+ }