@poncho-ai/cli 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (516) hide show
  1. package/.turbo/turbo-build.log +19 -0
  2. package/.turbo/turbo-test.log +389 -0
  3. package/CHANGELOG.md +17 -0
  4. package/LICENSE +21 -0
  5. package/dist/chunk-22OMLQUR.js +1249 -0
  6. package/dist/chunk-24JFN5RM.js +1887 -0
  7. package/dist/chunk-24TAT3US.js +2137 -0
  8. package/dist/chunk-26YBLT7G.js +997 -0
  9. package/dist/chunk-2EJIC6UW.js +1893 -0
  10. package/dist/chunk-2JNCF37R.js +1156 -0
  11. package/dist/chunk-2LVMUHJX.js +1874 -0
  12. package/dist/chunk-2OVSD65B.js +1269 -0
  13. package/dist/chunk-2RQ45LI6.js +1251 -0
  14. package/dist/chunk-2SMIRDLI.js +1854 -0
  15. package/dist/chunk-2UXPHBFI.js +1862 -0
  16. package/dist/chunk-2VFM7SSZ.js +1135 -0
  17. package/dist/chunk-2ZAUADNG.js +1456 -0
  18. package/dist/chunk-2ZNUT5WA.js +1862 -0
  19. package/dist/chunk-33ZQ7WTP.js +1834 -0
  20. package/dist/chunk-34ARQX3O.js +1156 -0
  21. package/dist/chunk-3BEWSRFW.js +1893 -0
  22. package/dist/chunk-3DVE5AG6.js +1862 -0
  23. package/dist/chunk-3KE6MHO6.js +1608 -0
  24. package/dist/chunk-3MOLPB7Z.js +997 -0
  25. package/dist/chunk-3OZZOYAZ.js +1884 -0
  26. package/dist/chunk-3VJYNZEF.js +2181 -0
  27. package/dist/chunk-3W27LOUH.js +997 -0
  28. package/dist/chunk-3WMAW74D.js +1163 -0
  29. package/dist/chunk-3Z4AHBPF.js +1569 -0
  30. package/dist/chunk-43NK6MB4.js +1977 -0
  31. package/dist/chunk-4E5M2IGA.js +1300 -0
  32. package/dist/chunk-4FVI4LVI.js +1862 -0
  33. package/dist/chunk-4GNQQJUP.js +1156 -0
  34. package/dist/chunk-4PGZFTVC.js +1234 -0
  35. package/dist/chunk-4QE2HDNC.js +1355 -0
  36. package/dist/chunk-4S2EL4ED.js +1135 -0
  37. package/dist/chunk-536SSOJ3.js +1797 -0
  38. package/dist/chunk-5CNEGIC5.js +997 -0
  39. package/dist/chunk-5CWN43YL.js +1147 -0
  40. package/dist/chunk-5HZCYTUZ.js +997 -0
  41. package/dist/chunk-5ICNG6RX.js +1885 -0
  42. package/dist/chunk-5OUIRXMN.js +997 -0
  43. package/dist/chunk-5T34JOWH.js +1460 -0
  44. package/dist/chunk-5XBAIQX3.js +1862 -0
  45. package/dist/chunk-65AIX3CS.js +1441 -0
  46. package/dist/chunk-67NBW4NG.js +1355 -0
  47. package/dist/chunk-6AFIL35M.js +1414 -0
  48. package/dist/chunk-6B3XMBKA.js +1716 -0
  49. package/dist/chunk-6CEJO4OM.js +1242 -0
  50. package/dist/chunk-6DQZUP3B.js +1460 -0
  51. package/dist/chunk-6ET624OE.js +1441 -0
  52. package/dist/chunk-6I7WFMAU.js +1862 -0
  53. package/dist/chunk-6MOKAYCL.js +1449 -0
  54. package/dist/chunk-6RFUALWB.js +1845 -0
  55. package/dist/chunk-73E57JUS.js +1231 -0
  56. package/dist/chunk-73SU7GT4.js +816 -0
  57. package/dist/chunk-74IRETUF.js +997 -0
  58. package/dist/chunk-77BYFMUN.js +1924 -0
  59. package/dist/chunk-7DAC2XE5.js +1460 -0
  60. package/dist/chunk-7GBQ4YSB.js +1024 -0
  61. package/dist/chunk-7TOJGUQ5.js +1803 -0
  62. package/dist/chunk-7W7KPLEG.js +1163 -0
  63. package/dist/chunk-7Y7ZXEN2.js +817 -0
  64. package/dist/chunk-A5WKH7H2.js +852 -0
  65. package/dist/chunk-A775UYQB.js +1886 -0
  66. package/dist/chunk-AC4OGTSK.js +1313 -0
  67. package/dist/chunk-ACCRUQ6J.js +1271 -0
  68. package/dist/chunk-AEAZZFTT.js +1886 -0
  69. package/dist/chunk-AIAC5Z55.js +1147 -0
  70. package/dist/chunk-AN34PI2R.js +1238 -0
  71. package/dist/chunk-APIA7MHJ.js +1355 -0
  72. package/dist/chunk-AQGIIT7R.js +1347 -0
  73. package/dist/chunk-ATXKV2NH.js +2111 -0
  74. package/dist/chunk-AVBKQZYR.js +1010 -0
  75. package/dist/chunk-AWIXDCZF.js +1329 -0
  76. package/dist/chunk-AXFHQBKT.js +2128 -0
  77. package/dist/chunk-AYDSTU4P.js +1156 -0
  78. package/dist/chunk-AZ35PK7E.js +1271 -0
  79. package/dist/chunk-BE6HB4IO.js +2155 -0
  80. package/dist/chunk-BIX2FI3S.js +1625 -0
  81. package/dist/chunk-BPPM5YPG.js +997 -0
  82. package/dist/chunk-BRCIVYKE.js +1420 -0
  83. package/dist/chunk-BRK2KKTF.js +1236 -0
  84. package/dist/chunk-BU7R2TVT.js +1862 -0
  85. package/dist/chunk-BXJQ4G5F.js +1901 -0
  86. package/dist/chunk-C3KTCROR.js +1128 -0
  87. package/dist/chunk-C42IGDJW.js +1032 -0
  88. package/dist/chunk-CBRPO2FE.js +1886 -0
  89. package/dist/chunk-CDNITKC6.js +1163 -0
  90. package/dist/chunk-CDVWUDFM.js +1389 -0
  91. package/dist/chunk-CH2IE453.js +1147 -0
  92. package/dist/chunk-CIHC46FS.js +1010 -0
  93. package/dist/chunk-CIYO754K.js +1687 -0
  94. package/dist/chunk-CJFNJ7U3.js +918 -0
  95. package/dist/chunk-CJN66CJY.js +1024 -0
  96. package/dist/chunk-CLJFTDJQ.js +1667 -0
  97. package/dist/chunk-CLNCOQNI.js +997 -0
  98. package/dist/chunk-CN4AUQL4.js +1460 -0
  99. package/dist/chunk-CQYGUBY6.js +1854 -0
  100. package/dist/chunk-CRJUMKVB.js +1862 -0
  101. package/dist/chunk-CZHUYI2J.js +997 -0
  102. package/dist/chunk-DIBSIWSR.js +1256 -0
  103. package/dist/chunk-DJGC3R4O.js +1836 -0
  104. package/dist/chunk-DJR2PEAQ.js +1884 -0
  105. package/dist/chunk-DKE7NWBK.js +1862 -0
  106. package/dist/chunk-DXYDN2OS.js +1147 -0
  107. package/dist/chunk-DZ3FEUJ7.js +1147 -0
  108. package/dist/chunk-EC47SFY3.js +1113 -0
  109. package/dist/chunk-ECAALEAK.js +1239 -0
  110. package/dist/chunk-ECEYIAQZ.js +997 -0
  111. package/dist/chunk-EKX7AV7O.js +1024 -0
  112. package/dist/chunk-EN6CTYUN.js +634 -0
  113. package/dist/chunk-EXCH47WX.js +1460 -0
  114. package/dist/chunk-EY2JOCTM.js +1862 -0
  115. package/dist/chunk-EYHB3LTH.js +1818 -0
  116. package/dist/chunk-F2AC5PKU.js +1446 -0
  117. package/dist/chunk-F5RCUJ62.js +1156 -0
  118. package/dist/chunk-F6OM65VA.js +1460 -0
  119. package/dist/chunk-FAEJ5CQU.js +997 -0
  120. package/dist/chunk-FB7X4KBF.js +1460 -0
  121. package/dist/chunk-FBSEEW3H.js +1862 -0
  122. package/dist/chunk-FBYY3TE5.js +1862 -0
  123. package/dist/chunk-FEA3GBGG.js +997 -0
  124. package/dist/chunk-FHPRGTOJ.js +1131 -0
  125. package/dist/chunk-FLAY6YWY.js +1389 -0
  126. package/dist/chunk-FNXIVJ3B.js +997 -0
  127. package/dist/chunk-FWRVG7RM.js +2160 -0
  128. package/dist/chunk-G47UW452.js +916 -0
  129. package/dist/chunk-G6V5O5AV.js +997 -0
  130. package/dist/chunk-GACQBIOO.js +1613 -0
  131. package/dist/chunk-GBFHLBWT.js +881 -0
  132. package/dist/chunk-GLJLTQMZ.js +997 -0
  133. package/dist/chunk-GN7DDBAT.js +1872 -0
  134. package/dist/chunk-GPTI42MM.js +1355 -0
  135. package/dist/chunk-GPXGCPVY.js +1834 -0
  136. package/dist/chunk-GRASQSCU.js +1886 -0
  137. package/dist/chunk-GU2WWG5C.js +1314 -0
  138. package/dist/chunk-GW3SAYT3.js +1679 -0
  139. package/dist/chunk-GZ4F2VI5.js +1447 -0
  140. package/dist/chunk-GZYXND4U.js +1322 -0
  141. package/dist/chunk-H27BRPVI.js +1862 -0
  142. package/dist/chunk-H4JRTOW7.js +1862 -0
  143. package/dist/chunk-HDS72SRU.js +1138 -0
  144. package/dist/chunk-HEUGSUL5.js +757 -0
  145. package/dist/chunk-HMZN5GPS.js +1024 -0
  146. package/dist/chunk-HN5SVGQO.js +1163 -0
  147. package/dist/chunk-HN6VQ5FI.js +242 -0
  148. package/dist/chunk-HNTQ66EL.js +2054 -0
  149. package/dist/chunk-HSDL3YK5.js +1134 -0
  150. package/dist/chunk-HVMIMERW.js +997 -0
  151. package/dist/chunk-HVWCQS2B.js +1834 -0
  152. package/dist/chunk-HVYMSOXQ.js +2156 -0
  153. package/dist/chunk-HYQITTK3.js +1862 -0
  154. package/dist/chunk-I4CCYPAI.js +1239 -0
  155. package/dist/chunk-IDG3X4UL.js +1229 -0
  156. package/dist/chunk-IDVRTQJ3.js +1514 -0
  157. package/dist/chunk-IEER23NN.js +1862 -0
  158. package/dist/chunk-IKLRYA4W.js +785 -0
  159. package/dist/chunk-IQBUSFBY.js +1270 -0
  160. package/dist/chunk-IUXQDZIR.js +1862 -0
  161. package/dist/chunk-IV74RJFV.js +1862 -0
  162. package/dist/chunk-IVEMIXXO.js +1854 -0
  163. package/dist/chunk-J4PNCGSP.js +1460 -0
  164. package/dist/chunk-J7ULLJUI.js +1862 -0
  165. package/dist/chunk-J7WHTBOM.js +983 -0
  166. package/dist/chunk-JBN6D7EG.js +1757 -0
  167. package/dist/chunk-JDKSD54C.js +1366 -0
  168. package/dist/chunk-JGWTOS7A.js +1507 -0
  169. package/dist/chunk-JHJ6FMSI.js +1804 -0
  170. package/dist/chunk-JJSHCP32.js +1282 -0
  171. package/dist/chunk-JLMAMC3V.js +1625 -0
  172. package/dist/chunk-JMTF7VV5.js +1919 -0
  173. package/dist/chunk-JO56GKTV.js +1133 -0
  174. package/dist/chunk-JRN3P6CS.js +1441 -0
  175. package/dist/chunk-JTKPRRSM.js +1024 -0
  176. package/dist/chunk-JVTFCTGO.js +1868 -0
  177. package/dist/chunk-JXH452LK.js +956 -0
  178. package/dist/chunk-K4SW5SP4.js +1147 -0
  179. package/dist/chunk-KANVHOQK.js +1880 -0
  180. package/dist/chunk-KBJZJWPZ.js +1143 -0
  181. package/dist/chunk-KFC4ZVRH.js +1798 -0
  182. package/dist/chunk-KHG6MSLS.js +1885 -0
  183. package/dist/chunk-KJJE6V5N.js +1886 -0
  184. package/dist/chunk-KKLEILZP.js +1868 -0
  185. package/dist/chunk-KW5QXXEN.js +1641 -0
  186. package/dist/chunk-KWMTK4N7.js +1147 -0
  187. package/dist/chunk-KYOW6LIV.js +1231 -0
  188. package/dist/chunk-KYWIUH3M.js +1355 -0
  189. package/dist/chunk-KZEA3HJL.js +1833 -0
  190. package/dist/chunk-L3KONBJE.js +1355 -0
  191. package/dist/chunk-L47B3OMM.js +1156 -0
  192. package/dist/chunk-LCQFWI6S.js +997 -0
  193. package/dist/chunk-LFIUZUI5.js +1138 -0
  194. package/dist/chunk-LHSLPQR4.js +1854 -0
  195. package/dist/chunk-LJGVAOFP.js +647 -0
  196. package/dist/chunk-LKJDYQPA.js +1687 -0
  197. package/dist/chunk-LOY6PWTR.js +1336 -0
  198. package/dist/chunk-LPFN3GNV.js +1147 -0
  199. package/dist/chunk-LPM7AN7S.js +1147 -0
  200. package/dist/chunk-LU6ES63L.js +1249 -0
  201. package/dist/chunk-M73DE7AU.js +1389 -0
  202. package/dist/chunk-M7OJ52WK.js +1448 -0
  203. package/dist/chunk-MCSM3DAE.js +1024 -0
  204. package/dist/chunk-MEP7OYUL.js +1417 -0
  205. package/dist/chunk-MFH6MVWX.js +1441 -0
  206. package/dist/chunk-MIHU3TFJ.js +1156 -0
  207. package/dist/chunk-MIU5FMSV.js +1329 -0
  208. package/dist/chunk-MJQMHH7Z.js +1976 -0
  209. package/dist/chunk-MLR2HUUY.js +1255 -0
  210. package/dist/chunk-MSBZHMUV.js +997 -0
  211. package/dist/chunk-MV4DZQRB.js +1163 -0
  212. package/dist/chunk-MWBLZDYK.js +1854 -0
  213. package/dist/chunk-N2MEPDSA.js +675 -0
  214. package/dist/chunk-N36ATUZM.js +1863 -0
  215. package/dist/chunk-NEHLM4WN.js +1610 -0
  216. package/dist/chunk-NHOJZ7FZ.js +1138 -0
  217. package/dist/chunk-NLPQBHHH.js +1282 -0
  218. package/dist/chunk-NMY3FWV7.js +1236 -0
  219. package/dist/chunk-NN2WDNCO.js +1138 -0
  220. package/dist/chunk-NNC7LH2Y.js +1258 -0
  221. package/dist/chunk-NQRWXPJ5.js +1275 -0
  222. package/dist/chunk-NR3G3D6Q.js +1238 -0
  223. package/dist/chunk-NRUAFOL3.js +2121 -0
  224. package/dist/chunk-NSCG7F6H.js +1862 -0
  225. package/dist/chunk-NXHVG7ZI.js +1113 -0
  226. package/dist/chunk-NYKPTBXA.js +1258 -0
  227. package/dist/chunk-O4AE4MFX.js +920 -0
  228. package/dist/chunk-O7GTMG3C.js +1467 -0
  229. package/dist/chunk-OBUP5UIM.js +997 -0
  230. package/dist/chunk-ONI2DTTL.js +1156 -0
  231. package/dist/chunk-OQHLTSVD.js +1776 -0
  232. package/dist/chunk-OVV6SHTA.js +997 -0
  233. package/dist/chunk-PAPAMVNI.js +1156 -0
  234. package/dist/chunk-PH7OXFMJ.js +997 -0
  235. package/dist/chunk-PHFLPSZU.js +1608 -0
  236. package/dist/chunk-PTVSK5DV.js +1854 -0
  237. package/dist/chunk-PUHWX6PD.js +1156 -0
  238. package/dist/chunk-PYDU2HN2.js +1803 -0
  239. package/dist/chunk-Q2AMIXBY.js +1250 -0
  240. package/dist/chunk-Q2EARVB7.js +1414 -0
  241. package/dist/chunk-Q4HFSYSN.js +1024 -0
  242. package/dist/chunk-Q65PNALY.js +1024 -0
  243. package/dist/chunk-QBQNHCYH.js +1791 -0
  244. package/dist/chunk-QGI55HK3.js +1433 -0
  245. package/dist/chunk-QIAODEAT.js +1862 -0
  246. package/dist/chunk-QLRJ2X3B.js +1800 -0
  247. package/dist/chunk-QTLVBIBL.js +1147 -0
  248. package/dist/chunk-QUPLQ7O4.js +1862 -0
  249. package/dist/chunk-QZGUSWLN.js +1415 -0
  250. package/dist/chunk-R7N3T7YR.js +1130 -0
  251. package/dist/chunk-RFS3GC46.js +239 -0
  252. package/dist/chunk-RJA7HIQO.js +1823 -0
  253. package/dist/chunk-RJLU3F7G.js +1156 -0
  254. package/dist/chunk-RLBXW5AY.js +1156 -0
  255. package/dist/chunk-RMKHLIMU.js +1147 -0
  256. package/dist/chunk-RS7F3BLO.js +1255 -0
  257. package/dist/chunk-RYLDHQQT.js +997 -0
  258. package/dist/chunk-RYYTEAQ7.js +1147 -0
  259. package/dist/chunk-S2KNKYQJ.js +1815 -0
  260. package/dist/chunk-S5R5IZJH.js +2056 -0
  261. package/dist/chunk-S5S7OBDZ.js +1355 -0
  262. package/dist/chunk-S6YKNJQG.js +1868 -0
  263. package/dist/chunk-SBDVWSWM.js +1251 -0
  264. package/dist/chunk-SBXSREFV.js +1237 -0
  265. package/dist/chunk-SDZJV47M.js +1238 -0
  266. package/dist/chunk-SMBILO75.js +1024 -0
  267. package/dist/chunk-SUSLSP3P.js +1456 -0
  268. package/dist/chunk-T4GPZ2AG.js +1128 -0
  269. package/dist/chunk-TA7EDJXT.js +1460 -0
  270. package/dist/chunk-TAW2ISST.js +1024 -0
  271. package/dist/chunk-TB6KKWS2.js +1138 -0
  272. package/dist/chunk-TBZCEX5O.js +1355 -0
  273. package/dist/chunk-TDOPKFNW.js +756 -0
  274. package/dist/chunk-TGY3JSDQ.js +1460 -0
  275. package/dist/chunk-THOIGUSY.js +666 -0
  276. package/dist/chunk-TLSGQKLO.js +1836 -0
  277. package/dist/chunk-TQ2QEU3G.js +1460 -0
  278. package/dist/chunk-TRJODOZM.js +1232 -0
  279. package/dist/chunk-TSBMRYLQ.js +1717 -0
  280. package/dist/chunk-TUL3R6KB.js +1024 -0
  281. package/dist/chunk-TWCD6YPP.js +2129 -0
  282. package/dist/chunk-TWQDGVLI.js +1323 -0
  283. package/dist/chunk-TZQLWQTW.js +769 -0
  284. package/dist/chunk-U22K555L.js +1803 -0
  285. package/dist/chunk-U3VXLQTW.js +1707 -0
  286. package/dist/chunk-U5QM4SRB.js +2054 -0
  287. package/dist/chunk-UCZLOOAW.js +997 -0
  288. package/dist/chunk-UDA2OZLK.js +1242 -0
  289. package/dist/chunk-UDASJ4IC.js +1355 -0
  290. package/dist/chunk-UGU2KSOQ.js +1113 -0
  291. package/dist/chunk-UJHB3CLA.js +1130 -0
  292. package/dist/chunk-UNUTAECX.js +1238 -0
  293. package/dist/chunk-UUUJVWXA.js +1806 -0
  294. package/dist/chunk-UV4WM7Q5.js +2039 -0
  295. package/dist/chunk-UXPGPOQ3.js +2113 -0
  296. package/dist/chunk-UYVWNYFB.js +1434 -0
  297. package/dist/chunk-UYWI4MPU.js +1460 -0
  298. package/dist/chunk-V2FNBN3P.js +997 -0
  299. package/dist/chunk-V6CF5XXG.js +1862 -0
  300. package/dist/chunk-V6UUV2SZ.js +1156 -0
  301. package/dist/chunk-VARTDMWQ.js +1862 -0
  302. package/dist/chunk-VC7ZYKMP.js +1156 -0
  303. package/dist/chunk-VCJNX77B.js +2038 -0
  304. package/dist/chunk-VDE2I72J.js +650 -0
  305. package/dist/chunk-VEO7FKEL.js +1156 -0
  306. package/dist/chunk-VIJYIU7E.js +2124 -0
  307. package/dist/chunk-VJX4WETG.js +1136 -0
  308. package/dist/chunk-VO3QDFU2.js +1276 -0
  309. package/dist/chunk-VOX2Q2V2.js +1933 -0
  310. package/dist/chunk-W7J5XM2X.js +1862 -0
  311. package/dist/chunk-WONM6P4N.js +1862 -0
  312. package/dist/chunk-WW576PYD.js +1862 -0
  313. package/dist/chunk-XCZCCA2D.js +997 -0
  314. package/dist/chunk-XHQOG4X6.js +1871 -0
  315. package/dist/chunk-XIYLHBWA.js +1163 -0
  316. package/dist/chunk-XKZ6XWSE.js +1907 -0
  317. package/dist/chunk-XLHKOBSF.js +1815 -0
  318. package/dist/chunk-XMMFUBB5.js +1270 -0
  319. package/dist/chunk-XQLK777K.js +1442 -0
  320. package/dist/chunk-XRN47M65.js +997 -0
  321. package/dist/chunk-XVBKUEXA.js +1441 -0
  322. package/dist/chunk-XY4ISIAV.js +1639 -0
  323. package/dist/chunk-Y2SOII6F.js +1156 -0
  324. package/dist/chunk-Y5TJU6YZ.js +1163 -0
  325. package/dist/chunk-YBPCMSUU.js +1147 -0
  326. package/dist/chunk-YDAZ3YZT.js +1004 -0
  327. package/dist/chunk-YH2QPUWO.js +1621 -0
  328. package/dist/chunk-YJX4O5CY.js +1355 -0
  329. package/dist/chunk-YNJMS3VK.js +997 -0
  330. package/dist/chunk-YNRZMOC3.js +997 -0
  331. package/dist/chunk-YNUF5JNP.js +1163 -0
  332. package/dist/chunk-YO7TJ6SG.js +2135 -0
  333. package/dist/chunk-YTUUFYVS.js +1842 -0
  334. package/dist/chunk-YXCOG54V.js +997 -0
  335. package/dist/chunk-Z7V254BA.js +1432 -0
  336. package/dist/chunk-ZBHRR3RS.js +1256 -0
  337. package/dist/chunk-ZM47X5PT.js +1236 -0
  338. package/dist/chunk-ZO6JUCLC.js +917 -0
  339. package/dist/chunk-ZOF4ERNI.js +2039 -0
  340. package/dist/chunk-ZOTRZN3T.js +1238 -0
  341. package/dist/chunk-ZPBA4JGE.js +1234 -0
  342. package/dist/chunk-ZTKGRHNV.js +1138 -0
  343. package/dist/chunk-ZW2JM2OY.js +997 -0
  344. package/dist/chunk-ZXYHUC7C.js +1722 -0
  345. package/dist/cli.d.ts +1 -0
  346. package/dist/cli.js +8 -0
  347. package/dist/index.d.ts +47 -0
  348. package/dist/index.js +37 -0
  349. package/dist/run-interactive-ink-2CDKFV6C.js +783 -0
  350. package/dist/run-interactive-ink-2JULLCIS.js +461 -0
  351. package/dist/run-interactive-ink-2KIHEGXT.js +451 -0
  352. package/dist/run-interactive-ink-2LX2NZRL.js +737 -0
  353. package/dist/run-interactive-ink-2OH6AV3C.js +756 -0
  354. package/dist/run-interactive-ink-2PLJ5XST.js +423 -0
  355. package/dist/run-interactive-ink-2ZI75VMK.js +462 -0
  356. package/dist/run-interactive-ink-3BHGPZ4B.js +423 -0
  357. package/dist/run-interactive-ink-3IUV3456.js +777 -0
  358. package/dist/run-interactive-ink-3QZK5PWY.js +168 -0
  359. package/dist/run-interactive-ink-3VLL4FNN.js +423 -0
  360. package/dist/run-interactive-ink-47Y2KAFZ.js +668 -0
  361. package/dist/run-interactive-ink-4CBTS636.js +423 -0
  362. package/dist/run-interactive-ink-4CHDJRAK.js +423 -0
  363. package/dist/run-interactive-ink-4E6GM5ST.js +423 -0
  364. package/dist/run-interactive-ink-4EKHIHGU.js +462 -0
  365. package/dist/run-interactive-ink-4NO7O233.js +744 -0
  366. package/dist/run-interactive-ink-55IZU2EZ.js +741 -0
  367. package/dist/run-interactive-ink-5A7BER6J.js +423 -0
  368. package/dist/run-interactive-ink-5NEFKF3R.js +423 -0
  369. package/dist/run-interactive-ink-5TCBH3Z4.js +462 -0
  370. package/dist/run-interactive-ink-5WOLWMGH.js +747 -0
  371. package/dist/run-interactive-ink-66JFIG2P.js +462 -0
  372. package/dist/run-interactive-ink-6R77DKEV.js +744 -0
  373. package/dist/run-interactive-ink-7B6HI7XT.js +679 -0
  374. package/dist/run-interactive-ink-7ICZH4E3.js +423 -0
  375. package/dist/run-interactive-ink-7LWBJYQE.js +423 -0
  376. package/dist/run-interactive-ink-7Y7MVKPA.js +438 -0
  377. package/dist/run-interactive-ink-A264MR35.js +423 -0
  378. package/dist/run-interactive-ink-A3NCKFFM.js +423 -0
  379. package/dist/run-interactive-ink-A4FBMTZ5.js +563 -0
  380. package/dist/run-interactive-ink-A7YPXRXR.js +462 -0
  381. package/dist/run-interactive-ink-AAER2EXR.js +451 -0
  382. package/dist/run-interactive-ink-ADVQJ2GF.js +423 -0
  383. package/dist/run-interactive-ink-ALV34HZF.js +451 -0
  384. package/dist/run-interactive-ink-ARDK3CO6.js +462 -0
  385. package/dist/run-interactive-ink-AVMVDBQK.js +462 -0
  386. package/dist/run-interactive-ink-AWV7ZOTC.js +423 -0
  387. package/dist/run-interactive-ink-AWZLJJLH.js +423 -0
  388. package/dist/run-interactive-ink-B25V52JO.js +462 -0
  389. package/dist/run-interactive-ink-BC6RGDCH.js +451 -0
  390. package/dist/run-interactive-ink-BMWLPUEU.js +451 -0
  391. package/dist/run-interactive-ink-BRT2MMN6.js +423 -0
  392. package/dist/run-interactive-ink-CB42OWV4.js +572 -0
  393. package/dist/run-interactive-ink-COZSBQND.js +777 -0
  394. package/dist/run-interactive-ink-CQFV44HM.js +451 -0
  395. package/dist/run-interactive-ink-CQMG45KQ.js +462 -0
  396. package/dist/run-interactive-ink-CQP3B7JM.js +669 -0
  397. package/dist/run-interactive-ink-CUCLNJCF.js +451 -0
  398. package/dist/run-interactive-ink-DEEZYQK5.js +423 -0
  399. package/dist/run-interactive-ink-DF5P6WZX.js +423 -0
  400. package/dist/run-interactive-ink-DFITKRY4.js +423 -0
  401. package/dist/run-interactive-ink-DG6TTEQQ.js +462 -0
  402. package/dist/run-interactive-ink-DH7ECECB.js +438 -0
  403. package/dist/run-interactive-ink-DMTUJHP6.js +423 -0
  404. package/dist/run-interactive-ink-DNSBSWLT.js +451 -0
  405. package/dist/run-interactive-ink-DV3TZEM3.js +742 -0
  406. package/dist/run-interactive-ink-E4AYCUDK.js +451 -0
  407. package/dist/run-interactive-ink-E4GPBTSL.js +462 -0
  408. package/dist/run-interactive-ink-ECVTPOIE.js +462 -0
  409. package/dist/run-interactive-ink-ELPCCGT3.js +423 -0
  410. package/dist/run-interactive-ink-ELWVRJZS.js +451 -0
  411. package/dist/run-interactive-ink-ENBMPAV7.js +462 -0
  412. package/dist/run-interactive-ink-EOVWUC3C.js +451 -0
  413. package/dist/run-interactive-ink-EVHWEXM4.js +451 -0
  414. package/dist/run-interactive-ink-F7SBCWE3.js +423 -0
  415. package/dist/run-interactive-ink-FCHXZ3JW.js +423 -0
  416. package/dist/run-interactive-ink-G27WWB5V.js +423 -0
  417. package/dist/run-interactive-ink-GDFTNYRC.js +462 -0
  418. package/dist/run-interactive-ink-GHGZAYSM.js +533 -0
  419. package/dist/run-interactive-ink-GK4IVHVT.js +684 -0
  420. package/dist/run-interactive-ink-GNCZNR6W.js +423 -0
  421. package/dist/run-interactive-ink-GQ53M5SW.js +605 -0
  422. package/dist/run-interactive-ink-GT7R7X2P.js +762 -0
  423. package/dist/run-interactive-ink-GWZTEIEZ.js +462 -0
  424. package/dist/run-interactive-ink-HA45VNUD.js +703 -0
  425. package/dist/run-interactive-ink-HB44RGFJ.js +423 -0
  426. package/dist/run-interactive-ink-HCVGKG23.js +462 -0
  427. package/dist/run-interactive-ink-HYKJ4PZ3.js +462 -0
  428. package/dist/run-interactive-ink-IGEBXARA.js +423 -0
  429. package/dist/run-interactive-ink-IGU7UVL5.js +462 -0
  430. package/dist/run-interactive-ink-JNVKOJRV.js +462 -0
  431. package/dist/run-interactive-ink-JYON5JQQ.js +461 -0
  432. package/dist/run-interactive-ink-K47CRELE.js +423 -0
  433. package/dist/run-interactive-ink-KCHMEHVH.js +547 -0
  434. package/dist/run-interactive-ink-KEB6ENSZ.js +423 -0
  435. package/dist/run-interactive-ink-KEWSKPTE.js +451 -0
  436. package/dist/run-interactive-ink-KJUHMADH.js +423 -0
  437. package/dist/run-interactive-ink-KW5NPJ32.js +423 -0
  438. package/dist/run-interactive-ink-L3EDWKF6.js +687 -0
  439. package/dist/run-interactive-ink-L4BCC6WG.js +462 -0
  440. package/dist/run-interactive-ink-LQPEZ6PR.js +520 -0
  441. package/dist/run-interactive-ink-LQXS5GMO.js +253 -0
  442. package/dist/run-interactive-ink-M4SOBC5E.js +817 -0
  443. package/dist/run-interactive-ink-MJGAQA2R.js +423 -0
  444. package/dist/run-interactive-ink-MPJB6PCJ.js +451 -0
  445. package/dist/run-interactive-ink-MXWZBG3F.js +461 -0
  446. package/dist/run-interactive-ink-N4XIVCWV.js +562 -0
  447. package/dist/run-interactive-ink-ND3PWHDU.js +462 -0
  448. package/dist/run-interactive-ink-NJTAWS3L.js +462 -0
  449. package/dist/run-interactive-ink-OBWWSIZ5.js +423 -0
  450. package/dist/run-interactive-ink-OD7YJBYI.js +423 -0
  451. package/dist/run-interactive-ink-OE23JGIN.js +451 -0
  452. package/dist/run-interactive-ink-OJ4EUMR5.js +462 -0
  453. package/dist/run-interactive-ink-OOC74RCY.js +423 -0
  454. package/dist/run-interactive-ink-P2PHTOX6.js +462 -0
  455. package/dist/run-interactive-ink-PE3XWCVU.js +423 -0
  456. package/dist/run-interactive-ink-PJBWWQF3.js +423 -0
  457. package/dist/run-interactive-ink-PLLZW6BV.js +678 -0
  458. package/dist/run-interactive-ink-PTXNQZ57.js +451 -0
  459. package/dist/run-interactive-ink-PWN5Q6T6.js +423 -0
  460. package/dist/run-interactive-ink-PZK4RD77.js +423 -0
  461. package/dist/run-interactive-ink-QBSXVTCG.js +462 -0
  462. package/dist/run-interactive-ink-QHOQB55Q.js +727 -0
  463. package/dist/run-interactive-ink-QIAC6ZMT.js +451 -0
  464. package/dist/run-interactive-ink-QLLGPIUL.js +462 -0
  465. package/dist/run-interactive-ink-R5W3ZEMY.js +818 -0
  466. package/dist/run-interactive-ink-RHDW3EHS.js +462 -0
  467. package/dist/run-interactive-ink-RLRKPNTS.js +668 -0
  468. package/dist/run-interactive-ink-RORQKBWV.js +425 -0
  469. package/dist/run-interactive-ink-RS6OZ66I.js +423 -0
  470. package/dist/run-interactive-ink-RVGRYBNQ.js +684 -0
  471. package/dist/run-interactive-ink-S35BKUZB.js +423 -0
  472. package/dist/run-interactive-ink-SS6RAQDE.js +423 -0
  473. package/dist/run-interactive-ink-SVP37E33.js +451 -0
  474. package/dist/run-interactive-ink-T53KH7FU.js +423 -0
  475. package/dist/run-interactive-ink-TCQUCJVS.js +423 -0
  476. package/dist/run-interactive-ink-TLUBKTTN.js +423 -0
  477. package/dist/run-interactive-ink-U2BAAHUU.js +438 -0
  478. package/dist/run-interactive-ink-U2KXGJ5S.js +451 -0
  479. package/dist/run-interactive-ink-U73PEMAO.js +423 -0
  480. package/dist/run-interactive-ink-UFTOTXIX.js +669 -0
  481. package/dist/run-interactive-ink-UJNQ54ZU.js +451 -0
  482. package/dist/run-interactive-ink-V63D5IV5.js +423 -0
  483. package/dist/run-interactive-ink-VLBITT4H.js +451 -0
  484. package/dist/run-interactive-ink-VSAX3XFR.js +462 -0
  485. package/dist/run-interactive-ink-WBJOY622.js +729 -0
  486. package/dist/run-interactive-ink-WERR64KP.js +451 -0
  487. package/dist/run-interactive-ink-WHTQ5OV6.js +732 -0
  488. package/dist/run-interactive-ink-WJBK4XIO.js +423 -0
  489. package/dist/run-interactive-ink-WQLCJ34D.js +462 -0
  490. package/dist/run-interactive-ink-WRKQJIAG.js +733 -0
  491. package/dist/run-interactive-ink-XG3P25DM.js +423 -0
  492. package/dist/run-interactive-ink-XPVJ22HP.js +462 -0
  493. package/dist/run-interactive-ink-XS5I2CGI.js +423 -0
  494. package/dist/run-interactive-ink-XVK7DXPB.js +785 -0
  495. package/dist/run-interactive-ink-YB3USTSB.js +706 -0
  496. package/dist/run-interactive-ink-YCBRQCG2.js +423 -0
  497. package/dist/run-interactive-ink-YOPSMTYJ.js +423 -0
  498. package/dist/run-interactive-ink-YYPCL65X.js +665 -0
  499. package/dist/run-interactive-ink-YYZT5L4Z.js +462 -0
  500. package/dist/run-interactive-ink-ZBPYRTJK.js +462 -0
  501. package/dist/run-interactive-ink-ZCCKFR2A.js +451 -0
  502. package/dist/run-interactive-ink-ZKYQ4CJW.js +423 -0
  503. package/dist/run-interactive-ink-ZMO2352Q.js +685 -0
  504. package/dist/run-interactive-ink-ZTDQ773P.js +423 -0
  505. package/dist/run-interactive-ink-ZWH74XDY.js +674 -0
  506. package/package.json +50 -0
  507. package/src/cli.ts +4 -0
  508. package/src/index.ts +1800 -0
  509. package/src/init-feature-context.ts +153 -0
  510. package/src/init-onboarding.ts +529 -0
  511. package/src/interactive-ink.tsx +5 -0
  512. package/src/run-interactive-ink.ts +618 -0
  513. package/src/web-ui.ts +1975 -0
  514. package/test/cli.test.ts +587 -0
  515. package/test/init-onboarding.contract.test.ts +48 -0
  516. package/tsconfig.json +9 -0
package/src/index.ts ADDED
@@ -0,0 +1,1800 @@
1
+ import { spawn } from "node:child_process";
2
+ import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { existsSync } from "node:fs";
4
+ import {
5
+ createServer,
6
+ type IncomingMessage,
7
+ type Server,
8
+ type ServerResponse,
9
+ } from "node:http";
10
+ import { dirname, relative, resolve } from "node:path";
11
+ import { createRequire } from "node:module";
12
+ import { fileURLToPath } from "node:url";
13
+ import {
14
+ AgentHarness,
15
+ TelemetryEmitter,
16
+ createConversationStore,
17
+ loadPonchoConfig,
18
+ resolveStateConfig,
19
+ type PonchoConfig,
20
+ type ConversationStore,
21
+ } from "@poncho-ai/harness";
22
+ import type { AgentEvent, Message, RunInput } from "@poncho-ai/sdk";
23
+ import { Command } from "commander";
24
+ import dotenv from "dotenv";
25
+ import YAML from "yaml";
26
+ import {
27
+ LoginRateLimiter,
28
+ SessionStore,
29
+ getRequestIp,
30
+ inferConversationTitle,
31
+ parseCookies,
32
+ renderIconSvg,
33
+ renderManifest,
34
+ renderServiceWorker,
35
+ renderWebUiHtml,
36
+ setCookie,
37
+ verifyPassphrase,
38
+ } from "./web-ui.js";
39
+ import {
40
+ runInitOnboarding,
41
+ type InitOnboardingOptions,
42
+ } from "./init-onboarding.js";
43
+ import {
44
+ consumeFirstRunIntro,
45
+ initializeOnboardingMarker,
46
+ } from "./init-feature-context.js";
47
+
48
+ const __dirname = dirname(fileURLToPath(import.meta.url));
49
+ const require = createRequire(import.meta.url);
50
+
51
+ const writeJson = (response: ServerResponse, statusCode: number, payload: unknown) => {
52
+ response.writeHead(statusCode, { "Content-Type": "application/json" });
53
+ response.end(JSON.stringify(payload));
54
+ };
55
+
56
+ const writeHtml = (response: ServerResponse, statusCode: number, payload: string) => {
57
+ response.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" });
58
+ response.end(payload);
59
+ };
60
+
61
+ const readRequestBody = async (request: IncomingMessage): Promise<unknown> => {
62
+ const chunks: Buffer[] = [];
63
+ for await (const chunk of request) {
64
+ chunks.push(Buffer.from(chunk));
65
+ }
66
+ const body = Buffer.concat(chunks).toString("utf8");
67
+ return body.length > 0 ? (JSON.parse(body) as unknown) : {};
68
+ };
69
+
70
+ const resolveHarnessEnvironment = (): "development" | "staging" | "production" => {
71
+ const value = (process.env.PONCHO_ENV ?? process.env.NODE_ENV ?? "development").toLowerCase();
72
+ if (value === "production" || value === "staging") {
73
+ return value;
74
+ }
75
+ return "development";
76
+ };
77
+
78
+ const listenOnAvailablePort = async (
79
+ server: Server,
80
+ preferredPort: number,
81
+ ): Promise<number> =>
82
+ await new Promise<number>((resolveListen, rejectListen) => {
83
+ let currentPort = preferredPort;
84
+
85
+ const tryListen = (): void => {
86
+ const onListening = (): void => {
87
+ server.off("error", onError);
88
+ const address = server.address();
89
+ if (address && typeof address === "object" && typeof address.port === "number") {
90
+ resolveListen(address.port);
91
+ return;
92
+ }
93
+ resolveListen(currentPort);
94
+ };
95
+
96
+ const onError = (error: unknown): void => {
97
+ server.off("listening", onListening);
98
+ if (
99
+ typeof error === "object" &&
100
+ error !== null &&
101
+ "code" in error &&
102
+ (error as { code?: string }).code === "EADDRINUSE"
103
+ ) {
104
+ currentPort += 1;
105
+ if (currentPort > 65535) {
106
+ rejectListen(
107
+ new Error(
108
+ "No available ports found from the requested port up to 65535.",
109
+ ),
110
+ );
111
+ return;
112
+ }
113
+ setImmediate(tryListen);
114
+ return;
115
+ }
116
+ rejectListen(error);
117
+ };
118
+
119
+ server.once("listening", onListening);
120
+ server.once("error", onError);
121
+ server.listen(currentPort);
122
+ };
123
+
124
+ tryListen();
125
+ });
126
+
127
+ const readJsonFile = async <T>(path: string): Promise<T | undefined> => {
128
+ try {
129
+ const content = await readFile(path, "utf8");
130
+ return JSON.parse(content) as T;
131
+ } catch {
132
+ return undefined;
133
+ }
134
+ };
135
+
136
+ const parseParams = (values: string[]): Record<string, string> => {
137
+ const params: Record<string, string> = {};
138
+ for (const value of values) {
139
+ const [key, ...rest] = value.split("=");
140
+ if (!key) {
141
+ continue;
142
+ }
143
+ params[key] = rest.join("=");
144
+ }
145
+ return params;
146
+ };
147
+
148
+ const AGENT_TEMPLATE = (
149
+ name: string,
150
+ options: { modelProvider: "anthropic" | "openai"; modelName: string },
151
+ ): string => `---
152
+ name: ${name}
153
+ description: A helpful Poncho assistant
154
+ model:
155
+ provider: ${options.modelProvider}
156
+ name: ${options.modelName}
157
+ temperature: 0.2
158
+ limits:
159
+ maxSteps: 50
160
+ timeout: 300
161
+ ---
162
+
163
+ # {{name}}
164
+
165
+ You are **{{name}}**, a helpful assistant built with Poncho.
166
+
167
+ Working directory: {{runtime.workingDir}}
168
+ Environment: {{runtime.environment}}
169
+
170
+ ## Task Guidance
171
+
172
+ - Use tools when needed
173
+ - Explain your reasoning clearly
174
+ - Ask clarifying questions when requirements are ambiguous
175
+ - For setup/configuration/skills/MCP questions, proactively read \`README.md\` with \`read_file\` before answering.
176
+ - Prefer concrete commands and examples from \`README.md\` over assumptions.
177
+ - Never claim a file/tool change unless the corresponding tool call actually succeeded
178
+
179
+ ## Default Capabilities in a Fresh Project
180
+
181
+ - Built-in tools: \`list_directory\` and \`read_file\`
182
+ - \`write_file\` is available in development, and disabled by default in production
183
+ - A starter local skill is included (\`starter-echo\`)
184
+ - Bash/shell commands are **not** available unless you install and enable a shell tool/skill
185
+ - Git operations are only available if a git-capable tool/skill is configured
186
+ `;
187
+
188
+ /**
189
+ * Resolve the monorepo packages root if we're running from a local dev build.
190
+ * Returns the absolute path to the `packages/` directory, or null when
191
+ * running from an npm-installed copy.
192
+ */
193
+ const resolveLocalPackagesRoot = (): string | null => {
194
+ // __dirname is packages/cli/dist — the monorepo root is three levels up
195
+ const candidate = resolve(__dirname, "..", "..", "harness", "package.json");
196
+ if (existsSync(candidate)) {
197
+ return resolve(__dirname, "..", "..");
198
+ }
199
+ return null;
200
+ };
201
+
202
+ /**
203
+ * Build dependency specifiers for the scaffolded project.
204
+ * In dev mode we use `file:` paths so pnpm can resolve local packages;
205
+ * in production we point at the npm registry.
206
+ */
207
+ const resolveCoreDeps = (
208
+ projectDir: string,
209
+ ): { harness: string; sdk: string } => {
210
+ const packagesRoot = resolveLocalPackagesRoot();
211
+ if (packagesRoot) {
212
+ const harnessAbs = resolve(packagesRoot, "harness");
213
+ const sdkAbs = resolve(packagesRoot, "sdk");
214
+ return {
215
+ harness: `link:${relative(projectDir, harnessAbs)}`,
216
+ sdk: `link:${relative(projectDir, sdkAbs)}`,
217
+ };
218
+ }
219
+ return { harness: "^0.1.0", sdk: "^0.1.0" };
220
+ };
221
+
222
+ const PACKAGE_TEMPLATE = (name: string, projectDir: string): string => {
223
+ const deps = resolveCoreDeps(projectDir);
224
+ return JSON.stringify(
225
+ {
226
+ name,
227
+ private: true,
228
+ type: "module",
229
+ dependencies: {
230
+ "@poncho-ai/harness": deps.harness,
231
+ "@poncho-ai/sdk": deps.sdk,
232
+ },
233
+ },
234
+ null,
235
+ 2,
236
+ );
237
+ };
238
+
239
+ const README_TEMPLATE = (name: string): string => `# ${name}
240
+
241
+ An AI agent built with [Poncho](https://github.com/cesr/poncho-ai).
242
+
243
+ ## Prerequisites
244
+
245
+ - Node.js 20+
246
+ - npm (or pnpm/yarn)
247
+ - Anthropic or OpenAI API key
248
+
249
+ ## Quick Start
250
+
251
+ \`\`\`bash
252
+ npm install
253
+ # If you didn't enter an API key during init:
254
+ cp .env.example .env
255
+ # Then edit .env and add your API key
256
+ poncho dev
257
+ \`\`\`
258
+
259
+ Open \`http://localhost:3000\` for the web UI.
260
+
261
+ On your first interactive session, the agent introduces its configurable capabilities.
262
+
263
+ ## Common Commands
264
+
265
+ \`\`\`bash
266
+ # Local web UI + API server
267
+ poncho dev
268
+
269
+ # Local interactive CLI
270
+ poncho run --interactive
271
+
272
+ # One-off run
273
+ poncho run "Your task here"
274
+
275
+ # Run tests
276
+ poncho test
277
+
278
+ # List available tools
279
+ poncho tools
280
+ \`\`\`
281
+
282
+ ## Add Skills
283
+
284
+ Install skills from a local path or remote repository, then verify discovery:
285
+
286
+ \`\`\`bash
287
+ # Install skills into ./skills
288
+ poncho add <repo-or-path>
289
+
290
+ # Verify loaded tools
291
+ poncho tools
292
+ \`\`\`
293
+
294
+ After adding skills, run \`poncho dev\` or \`poncho run --interactive\` and ask the agent to use them.
295
+
296
+ ## Configure MCP Servers (Remote)
297
+
298
+ Connect remote MCP servers and expose their tools to the agent:
299
+
300
+ \`\`\`bash
301
+ # Add remote MCP server
302
+ poncho mcp add --url wss://mcp.example.com/github --name github --env GITHUB_TOKEN
303
+
304
+ # List configured servers
305
+ poncho mcp list
306
+
307
+ # Remove a server
308
+ poncho mcp remove github
309
+ \`\`\`
310
+
311
+ Set required secrets in \`.env\` (for example, \`GITHUB_TOKEN=...\`).
312
+
313
+ ## Configuration
314
+
315
+ Core files:
316
+
317
+ - \`AGENT.md\`: behavior, model selection, runtime guidance
318
+ - \`poncho.config.js\`: runtime config (storage, auth, telemetry, MCP, tools)
319
+ - \`.env\`: secrets and environment variables
320
+
321
+ Example \`poncho.config.js\`:
322
+
323
+ \`\`\`javascript
324
+ export default {
325
+ storage: {
326
+ provider: "local", // local | memory | redis | upstash | dynamodb
327
+ memory: {
328
+ enabled: true,
329
+ maxRecallConversations: 20,
330
+ },
331
+ },
332
+ auth: {
333
+ required: false,
334
+ },
335
+ telemetry: {
336
+ enabled: true,
337
+ },
338
+ tools: {
339
+ defaults: {
340
+ list_directory: true,
341
+ read_file: true,
342
+ write_file: true, // still gated by environment/policy
343
+ },
344
+ byEnvironment: {
345
+ production: {
346
+ read_file: false, // example override
347
+ },
348
+ },
349
+ },
350
+ };
351
+ \`\`\`
352
+
353
+ ## Project Structure
354
+
355
+ \`\`\`
356
+ ${name}/
357
+ ├── AGENT.md # Agent definition and system prompt
358
+ ├── poncho.config.js # Configuration (MCP servers, auth, etc.)
359
+ ├── package.json # Dependencies
360
+ ├── .env.example # Environment variables template
361
+ ├── tests/
362
+ │ └── basic.yaml # Test suite
363
+ └── skills/
364
+ └── starter/
365
+ ├── SKILL.md
366
+ └── scripts/
367
+ └── starter-echo.ts
368
+ \`\`\`
369
+
370
+ ## Deployment
371
+
372
+ \`\`\`bash
373
+ # Build for Vercel
374
+ poncho build vercel
375
+ cd .poncho-build/vercel && vercel deploy --prod
376
+
377
+ # Build for Docker
378
+ poncho build docker
379
+ docker build -t ${name} .
380
+ \`\`\`
381
+
382
+ For full reference:
383
+ https://github.com/cesr/poncho-ai
384
+ `;
385
+
386
+ const ENV_TEMPLATE = "ANTHROPIC_API_KEY=sk-ant-...\n";
387
+ const GITIGNORE_TEMPLATE =
388
+ ".env\nnode_modules\ndist\n.poncho-build\n.poncho/\ninteractive-session.json\n";
389
+ const VERCEL_RUNTIME_DEPENDENCIES: Record<string, string> = {
390
+ "@anthropic-ai/sdk": "^0.74.0",
391
+ "@aws-sdk/client-dynamodb": "^3.988.0",
392
+ "@latitude-data/telemetry": "^2.0.2",
393
+ commander: "^12.0.0",
394
+ dotenv: "^16.4.0",
395
+ jiti: "^2.6.1",
396
+ mustache: "^4.2.0",
397
+ openai: "^6.3.0",
398
+ redis: "^5.10.0",
399
+ ws: "^8.18.0",
400
+ yaml: "^2.8.1",
401
+ };
402
+ const TEST_TEMPLATE = `tests:
403
+ - name: "Basic sanity"
404
+ task: "What is 2 + 2?"
405
+ expect:
406
+ contains: "4"
407
+ `;
408
+
409
+ const SKILL_TEMPLATE = `---
410
+ name: starter-skill
411
+ description: Starter local skill template
412
+ ---
413
+
414
+ # Starter Skill
415
+
416
+ This is a starter local skill created by \`poncho init\`.
417
+
418
+ ## Authoring Notes
419
+
420
+ - Put executable JavaScript/TypeScript files in \`scripts/\`.
421
+ - Ask the agent to call \`run_skill_script\` with \`skill\`, \`script\`, and optional \`input\`.
422
+ `;
423
+
424
+ const SKILL_TOOL_TEMPLATE = `export default async function run(input) {
425
+ const message = typeof input?.message === "string" ? input.message : "";
426
+ return { echoed: message };
427
+ }
428
+ `;
429
+
430
+ const ensureFile = async (path: string, content: string): Promise<void> => {
431
+ await mkdir(dirname(path), { recursive: true });
432
+ await writeFile(path, content, { encoding: "utf8", flag: "wx" });
433
+ };
434
+
435
+ const copyIfExists = async (sourcePath: string, destinationPath: string): Promise<void> => {
436
+ try {
437
+ await access(sourcePath);
438
+ } catch {
439
+ return;
440
+ }
441
+ await mkdir(dirname(destinationPath), { recursive: true });
442
+ await cp(sourcePath, destinationPath, { recursive: true });
443
+ };
444
+
445
+ const resolveCliEntrypoint = async (): Promise<string> => {
446
+ const sourceEntrypoint = resolve(packageRoot, "src", "index.ts");
447
+ try {
448
+ await access(sourceEntrypoint);
449
+ return sourceEntrypoint;
450
+ } catch {
451
+ return resolve(packageRoot, "dist", "index.js");
452
+ }
453
+ };
454
+
455
+ const buildVercelHandlerBundle = async (outDir: string): Promise<void> => {
456
+ const { build: esbuild } = await import("esbuild");
457
+ const cliEntrypoint = await resolveCliEntrypoint();
458
+ const tempEntry = resolve(outDir, "api", "_entry.js");
459
+ await writeFile(
460
+ tempEntry,
461
+ `import { createRequestHandler } from ${JSON.stringify(cliEntrypoint)};
462
+ let handlerPromise;
463
+ export default async function handler(req, res) {
464
+ try {
465
+ if (!handlerPromise) {
466
+ handlerPromise = createRequestHandler({ workingDir: process.cwd() });
467
+ }
468
+ const requestHandler = await handlerPromise;
469
+ await requestHandler(req, res);
470
+ } catch (error) {
471
+ console.error("Handler error:", error);
472
+ if (!res.headersSent) {
473
+ res.writeHead(500, { "Content-Type": "application/json" });
474
+ res.end(JSON.stringify({ error: "Internal server error", message: error?.message || "Unknown error" }));
475
+ }
476
+ }
477
+ }
478
+ `,
479
+ "utf8",
480
+ );
481
+ await esbuild({
482
+ entryPoints: [tempEntry],
483
+ bundle: true,
484
+ platform: "node",
485
+ format: "esm",
486
+ target: "node20",
487
+ outfile: resolve(outDir, "api", "index.js"),
488
+ sourcemap: false,
489
+ legalComments: "none",
490
+ external: [
491
+ ...Object.keys(VERCEL_RUNTIME_DEPENDENCIES),
492
+ "@anthropic-ai/sdk/*",
493
+ "child_process",
494
+ "fs",
495
+ "fs/promises",
496
+ "http",
497
+ "https",
498
+ "path",
499
+ "module",
500
+ "url",
501
+ "readline",
502
+ "readline/promises",
503
+ "crypto",
504
+ "stream",
505
+ "events",
506
+ "util",
507
+ "os",
508
+ "zlib",
509
+ "net",
510
+ "tls",
511
+ "dns",
512
+ "assert",
513
+ "buffer",
514
+ "timers",
515
+ "timers/promises",
516
+ "node:child_process",
517
+ "node:fs",
518
+ "node:fs/promises",
519
+ "node:http",
520
+ "node:https",
521
+ "node:path",
522
+ "node:module",
523
+ "node:url",
524
+ "node:readline",
525
+ "node:readline/promises",
526
+ "node:crypto",
527
+ "node:stream",
528
+ "node:events",
529
+ "node:util",
530
+ "node:os",
531
+ "node:zlib",
532
+ "node:net",
533
+ "node:tls",
534
+ "node:dns",
535
+ "node:assert",
536
+ "node:buffer",
537
+ "node:timers",
538
+ "node:timers/promises",
539
+ ],
540
+ });
541
+ };
542
+
543
+ const renderConfigFile = (config: PonchoConfig): string =>
544
+ `export default ${JSON.stringify(config, null, 2)}\n`;
545
+
546
+ const writeConfigFile = async (workingDir: string, config: PonchoConfig): Promise<void> => {
547
+ const serialized = renderConfigFile(config);
548
+ await writeFile(resolve(workingDir, "poncho.config.js"), serialized, "utf8");
549
+ };
550
+
551
+ const gitInit = (cwd: string): Promise<boolean> =>
552
+ new Promise((resolve) => {
553
+ const child = spawn("git", ["init"], { cwd, stdio: "ignore" });
554
+ child.on("error", () => resolve(false));
555
+ child.on("close", (code) => resolve(code === 0));
556
+ });
557
+
558
+ export const initProject = async (
559
+ projectName: string,
560
+ options?: {
561
+ workingDir?: string;
562
+ onboarding?: InitOnboardingOptions;
563
+ envExampleOverride?: string;
564
+ },
565
+ ): Promise<void> => {
566
+ const baseDir = options?.workingDir ?? process.cwd();
567
+ const projectDir = resolve(baseDir, projectName);
568
+ await mkdir(projectDir, { recursive: true });
569
+
570
+ const onboardingOptions: InitOnboardingOptions = options?.onboarding ?? {
571
+ yes: true,
572
+ interactive: false,
573
+ };
574
+ const onboarding = await runInitOnboarding(onboardingOptions);
575
+
576
+ const G = "\x1b[32m";
577
+ const D = "\x1b[2m";
578
+ const B = "\x1b[1m";
579
+ const CY = "\x1b[36m";
580
+ const YW = "\x1b[33m";
581
+ const R = "\x1b[0m";
582
+
583
+ process.stdout.write("\n");
584
+
585
+ const scaffoldFiles: Array<{ path: string; content: string }> = [
586
+ { path: "AGENT.md", content: AGENT_TEMPLATE(projectName, { modelProvider: onboarding.agentModel.provider, modelName: onboarding.agentModel.name }) },
587
+ { path: "poncho.config.js", content: renderConfigFile(onboarding.config) },
588
+ { path: "package.json", content: PACKAGE_TEMPLATE(projectName, projectDir) },
589
+ { path: "README.md", content: README_TEMPLATE(projectName) },
590
+ { path: ".env.example", content: options?.envExampleOverride ?? onboarding.envExample ?? ENV_TEMPLATE },
591
+ { path: ".gitignore", content: GITIGNORE_TEMPLATE },
592
+ { path: "tests/basic.yaml", content: TEST_TEMPLATE },
593
+ { path: "skills/starter/SKILL.md", content: SKILL_TEMPLATE },
594
+ { path: "skills/starter/scripts/starter-echo.ts", content: SKILL_TOOL_TEMPLATE },
595
+ ];
596
+ if (onboarding.envFile) {
597
+ scaffoldFiles.push({ path: ".env", content: onboarding.envFile });
598
+ }
599
+
600
+ for (const file of scaffoldFiles) {
601
+ await ensureFile(resolve(projectDir, file.path), file.content);
602
+ process.stdout.write(` ${D}+${R} ${D}${file.path}${R}\n`);
603
+ }
604
+
605
+ await initializeOnboardingMarker(projectDir, {
606
+ allowIntro: !(onboardingOptions.yes ?? false),
607
+ });
608
+
609
+ process.stdout.write("\n");
610
+
611
+ // Install dependencies so subsequent commands (e.g. `poncho add`) succeed.
612
+ try {
613
+ await runPnpmInstall(projectDir);
614
+ process.stdout.write(` ${G}✓${R} ${D}Installed dependencies${R}\n`);
615
+ } catch {
616
+ process.stdout.write(
617
+ ` ${YW}!${R} Could not install dependencies — run ${D}pnpm install${R} manually\n`,
618
+ );
619
+ }
620
+
621
+ const gitOk = await gitInit(projectDir);
622
+ if (gitOk) {
623
+ process.stdout.write(` ${G}✓${R} ${D}Initialized git${R}\n`);
624
+ }
625
+
626
+ process.stdout.write(` ${G}✓${R} ${B}${projectName}${R} is ready\n`);
627
+ process.stdout.write("\n");
628
+ process.stdout.write(` ${B}Get started${R}\n`);
629
+ process.stdout.write("\n");
630
+ process.stdout.write(` ${D}$${R} cd ${projectName}\n`);
631
+ process.stdout.write("\n");
632
+ process.stdout.write(` ${CY}Web UI${R} ${D}$${R} poncho dev\n`);
633
+ process.stdout.write(` ${CY}CLI interactive${R} ${D}$${R} poncho run --interactive\n`);
634
+ process.stdout.write("\n");
635
+ if (onboarding.envNeedsUserInput) {
636
+ process.stdout.write(
637
+ ` ${YW}!${R} Make sure you add your keys to the ${B}.env${R} file.\n`,
638
+ );
639
+ }
640
+ process.stdout.write(` ${D}The agent will introduce itself on your first session.${R}\n`);
641
+ process.stdout.write("\n");
642
+ };
643
+
644
+ export const updateAgentGuidance = async (workingDir: string): Promise<boolean> => {
645
+ const agentPath = resolve(workingDir, "AGENT.md");
646
+ const content = await readFile(agentPath, "utf8");
647
+ const guidanceSectionPattern =
648
+ /\n## Configuration Assistant Context[\s\S]*?(?=\n## |\n# |$)|\n## Skill Authoring Guidance[\s\S]*?(?=\n## |\n# |$)/g;
649
+ const normalized = content.replace(/\s+$/g, "");
650
+ const updated = normalized.replace(guidanceSectionPattern, "").replace(/\n{3,}/g, "\n\n");
651
+ if (updated === normalized) {
652
+ process.stdout.write("AGENT.md does not contain deprecated embedded local guidance.\n");
653
+ return false;
654
+ }
655
+ await writeFile(agentPath, `${updated}\n`, "utf8");
656
+ process.stdout.write("Removed deprecated embedded local guidance from AGENT.md.\n");
657
+ return true;
658
+ };
659
+
660
+ const formatSseEvent = (event: AgentEvent): string =>
661
+ `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`;
662
+
663
+ export type RequestHandler = (
664
+ request: IncomingMessage,
665
+ response: ServerResponse,
666
+ ) => Promise<void>;
667
+
668
+ export const createRequestHandler = async (options?: {
669
+ workingDir?: string;
670
+ }): Promise<RequestHandler> => {
671
+ const workingDir = options?.workingDir ?? process.cwd();
672
+ dotenv.config({ path: resolve(workingDir, ".env") });
673
+ const config = await loadPonchoConfig(workingDir);
674
+ let agentName = "Agent";
675
+ let agentModelProvider = "anthropic";
676
+ let agentModelName = "claude-opus-4-5";
677
+ try {
678
+ const agentMd = await readFile(resolve(workingDir, "AGENT.md"), "utf8");
679
+ const nameMatch = agentMd.match(/^name:\s*(.+)$/m);
680
+ const providerMatch = agentMd.match(/^\s{2}provider:\s*(.+)$/m);
681
+ const modelMatch = agentMd.match(/^\s{2}name:\s*(.+)$/m);
682
+ if (nameMatch?.[1]) {
683
+ agentName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
684
+ }
685
+ if (providerMatch?.[1]) {
686
+ agentModelProvider = providerMatch[1].trim().replace(/^["']|["']$/g, "");
687
+ }
688
+ if (modelMatch?.[1]) {
689
+ agentModelName = modelMatch[1].trim().replace(/^["']|["']$/g, "");
690
+ }
691
+ } catch {}
692
+ const harness = new AgentHarness({ workingDir });
693
+ await harness.initialize();
694
+ const telemetry = new TelemetryEmitter(config?.telemetry);
695
+ const conversationStore = createConversationStore(resolveStateConfig(config), { workingDir });
696
+ const sessionStore = new SessionStore();
697
+ const loginRateLimiter = new LoginRateLimiter();
698
+ const passphrase = process.env.AGENT_UI_PASSPHRASE ?? "";
699
+ const isProduction = resolveHarnessEnvironment() === "production";
700
+ const requireUiAuth = passphrase.length > 0;
701
+ const secureCookies = isProduction;
702
+
703
+ return async (request: IncomingMessage, response: ServerResponse) => {
704
+ if (!request.url || !request.method) {
705
+ writeJson(response, 404, { error: "Not found" });
706
+ return;
707
+ }
708
+ const [pathname] = request.url.split("?");
709
+
710
+ if (request.method === "GET" && (pathname === "/" || pathname.startsWith("/c/"))) {
711
+ writeHtml(response, 200, renderWebUiHtml({ agentName }));
712
+ return;
713
+ }
714
+
715
+ if (pathname === "/manifest.json" && request.method === "GET") {
716
+ response.writeHead(200, { "Content-Type": "application/manifest+json" });
717
+ response.end(renderManifest({ agentName }));
718
+ return;
719
+ }
720
+
721
+ if (pathname === "/sw.js" && request.method === "GET") {
722
+ response.writeHead(200, {
723
+ "Content-Type": "application/javascript",
724
+ "Service-Worker-Allowed": "/",
725
+ });
726
+ response.end(renderServiceWorker());
727
+ return;
728
+ }
729
+
730
+ if (pathname === "/icon.svg" && request.method === "GET") {
731
+ response.writeHead(200, { "Content-Type": "image/svg+xml" });
732
+ response.end(renderIconSvg({ agentName }));
733
+ return;
734
+ }
735
+
736
+ if ((pathname === "/icon-192.png" || pathname === "/icon-512.png") && request.method === "GET") {
737
+ // Redirect to SVG — browsers that support PWA icons will use the SVG
738
+ response.writeHead(302, { Location: "/icon.svg" });
739
+ response.end();
740
+ return;
741
+ }
742
+
743
+ if (pathname === "/health" && request.method === "GET") {
744
+ writeJson(response, 200, { status: "ok" });
745
+ return;
746
+ }
747
+
748
+ const cookies = parseCookies(request);
749
+ const sessionId = cookies.poncho_session;
750
+ const session = sessionId ? sessionStore.get(sessionId) : undefined;
751
+ const ownerId = session?.ownerId ?? "local-owner";
752
+ const requiresCsrfValidation =
753
+ request.method !== "GET" && request.method !== "HEAD" && request.method !== "OPTIONS";
754
+
755
+ if (pathname === "/api/auth/session" && request.method === "GET") {
756
+ if (!requireUiAuth) {
757
+ writeJson(response, 200, { authenticated: true, csrfToken: "" });
758
+ return;
759
+ }
760
+ if (!session) {
761
+ writeJson(response, 200, { authenticated: false });
762
+ return;
763
+ }
764
+ writeJson(response, 200, {
765
+ authenticated: true,
766
+ sessionId: session.sessionId,
767
+ ownerId: session.ownerId,
768
+ csrfToken: session.csrfToken,
769
+ });
770
+ return;
771
+ }
772
+
773
+ if (pathname === "/api/auth/login" && request.method === "POST") {
774
+ if (!requireUiAuth) {
775
+ writeJson(response, 200, { authenticated: true, csrfToken: "" });
776
+ return;
777
+ }
778
+ const ip = getRequestIp(request);
779
+ const canAttempt = loginRateLimiter.canAttempt(ip);
780
+ if (!canAttempt.allowed) {
781
+ writeJson(response, 429, {
782
+ code: "AUTH_RATE_LIMIT",
783
+ message: "Too many failed login attempts. Try again later.",
784
+ retryAfterSeconds: canAttempt.retryAfterSeconds,
785
+ });
786
+ return;
787
+ }
788
+ const body = (await readRequestBody(request)) as { passphrase?: string };
789
+ const provided = body.passphrase ?? "";
790
+ if (!verifyPassphrase(provided, passphrase)) {
791
+ const failure = loginRateLimiter.registerFailure(ip);
792
+ writeJson(response, 401, {
793
+ code: "AUTH_ERROR",
794
+ message: "Invalid passphrase",
795
+ retryAfterSeconds: failure.retryAfterSeconds,
796
+ });
797
+ return;
798
+ }
799
+ loginRateLimiter.registerSuccess(ip);
800
+ const createdSession = sessionStore.create(ownerId);
801
+ setCookie(response, "poncho_session", createdSession.sessionId, {
802
+ httpOnly: true,
803
+ secure: secureCookies,
804
+ sameSite: "Lax",
805
+ path: "/",
806
+ maxAge: 60 * 60 * 8,
807
+ });
808
+ writeJson(response, 200, {
809
+ authenticated: true,
810
+ csrfToken: createdSession.csrfToken,
811
+ });
812
+ return;
813
+ }
814
+
815
+ if (pathname === "/api/auth/logout" && request.method === "POST") {
816
+ if (session?.sessionId) {
817
+ sessionStore.delete(session.sessionId);
818
+ }
819
+ setCookie(response, "poncho_session", "", {
820
+ httpOnly: true,
821
+ secure: secureCookies,
822
+ sameSite: "Lax",
823
+ path: "/",
824
+ maxAge: 0,
825
+ });
826
+ writeJson(response, 200, { ok: true });
827
+ return;
828
+ }
829
+
830
+ if (pathname.startsWith("/api/")) {
831
+ if (requireUiAuth && !session) {
832
+ writeJson(response, 401, {
833
+ code: "AUTH_ERROR",
834
+ message: "Authentication required",
835
+ });
836
+ return;
837
+ }
838
+ if (
839
+ requireUiAuth &&
840
+ requiresCsrfValidation &&
841
+ pathname !== "/api/auth/login" &&
842
+ request.headers["x-csrf-token"] !== session?.csrfToken
843
+ ) {
844
+ writeJson(response, 403, {
845
+ code: "CSRF_ERROR",
846
+ message: "Invalid CSRF token",
847
+ });
848
+ return;
849
+ }
850
+ }
851
+
852
+ if (pathname === "/api/conversations" && request.method === "GET") {
853
+ const conversations = await conversationStore.list(ownerId);
854
+ writeJson(response, 200, {
855
+ conversations: conversations.map((conversation) => ({
856
+ conversationId: conversation.conversationId,
857
+ title: conversation.title,
858
+ runtimeRunId: conversation.runtimeRunId,
859
+ ownerId: conversation.ownerId,
860
+ tenantId: conversation.tenantId,
861
+ createdAt: conversation.createdAt,
862
+ updatedAt: conversation.updatedAt,
863
+ messageCount: conversation.messages.length,
864
+ })),
865
+ });
866
+ return;
867
+ }
868
+
869
+ if (pathname === "/api/conversations" && request.method === "POST") {
870
+ const body = (await readRequestBody(request)) as { title?: string };
871
+ const conversation = await conversationStore.create(ownerId, body.title);
872
+ const introMessage = await consumeFirstRunIntro(workingDir, {
873
+ agentName,
874
+ provider: agentModelProvider,
875
+ model: agentModelName,
876
+ config,
877
+ });
878
+ if (introMessage) {
879
+ conversation.messages = [{ role: "assistant", content: introMessage }];
880
+ await conversationStore.update(conversation);
881
+ }
882
+ writeJson(response, 201, { conversation });
883
+ return;
884
+ }
885
+
886
+ const conversationPathMatch = pathname.match(/^\/api\/conversations\/([^/]+)$/);
887
+ if (conversationPathMatch) {
888
+ const conversationId = decodeURIComponent(conversationPathMatch[1] ?? "");
889
+ const conversation = await conversationStore.get(conversationId);
890
+ if (!conversation || conversation.ownerId !== ownerId) {
891
+ writeJson(response, 404, {
892
+ code: "CONVERSATION_NOT_FOUND",
893
+ message: "Conversation not found",
894
+ });
895
+ return;
896
+ }
897
+ if (request.method === "GET") {
898
+ writeJson(response, 200, { conversation });
899
+ return;
900
+ }
901
+ if (request.method === "PATCH") {
902
+ const body = (await readRequestBody(request)) as { title?: string };
903
+ if (!body.title || body.title.trim().length === 0) {
904
+ writeJson(response, 400, {
905
+ code: "VALIDATION_ERROR",
906
+ message: "title is required",
907
+ });
908
+ return;
909
+ }
910
+ const updated = await conversationStore.rename(conversationId, body.title);
911
+ writeJson(response, 200, { conversation: updated });
912
+ return;
913
+ }
914
+ if (request.method === "DELETE") {
915
+ await conversationStore.delete(conversationId);
916
+ writeJson(response, 200, { ok: true });
917
+ return;
918
+ }
919
+ }
920
+
921
+ const conversationMessageMatch = pathname.match(/^\/api\/conversations\/([^/]+)\/messages$/);
922
+ if (conversationMessageMatch && request.method === "POST") {
923
+ const conversationId = decodeURIComponent(conversationMessageMatch[1] ?? "");
924
+ const conversation = await conversationStore.get(conversationId);
925
+ if (!conversation || conversation.ownerId !== ownerId) {
926
+ writeJson(response, 404, {
927
+ code: "CONVERSATION_NOT_FOUND",
928
+ message: "Conversation not found",
929
+ });
930
+ return;
931
+ }
932
+ const body = (await readRequestBody(request)) as {
933
+ message?: string;
934
+ parameters?: Record<string, unknown>;
935
+ };
936
+ const messageText = body.message?.trim() ?? "";
937
+ if (!messageText) {
938
+ writeJson(response, 400, {
939
+ code: "VALIDATION_ERROR",
940
+ message: "message is required",
941
+ });
942
+ return;
943
+ }
944
+ if (
945
+ conversation.messages.length === 0 &&
946
+ (conversation.title === "New conversation" || conversation.title.trim().length === 0)
947
+ ) {
948
+ conversation.title = inferConversationTitle(messageText);
949
+ }
950
+ response.writeHead(200, {
951
+ "Content-Type": "text/event-stream",
952
+ "Cache-Control": "no-cache",
953
+ Connection: "keep-alive",
954
+ });
955
+ let latestRunId = conversation.runtimeRunId ?? "";
956
+ let assistantResponse = "";
957
+ const toolTimeline: string[] = [];
958
+ try {
959
+ const recallCorpus = (await conversationStore.list(ownerId))
960
+ .filter((item) => item.conversationId !== conversationId)
961
+ .slice(0, 20)
962
+ .map((item) => ({
963
+ conversationId: item.conversationId,
964
+ title: item.title,
965
+ updatedAt: item.updatedAt,
966
+ content: item.messages
967
+ .slice(-6)
968
+ .map((message) => `${message.role}: ${message.content}`)
969
+ .join("\n")
970
+ .slice(0, 2000),
971
+ }))
972
+ .filter((item) => item.content.length > 0);
973
+
974
+ for await (const event of harness.run({
975
+ task: messageText,
976
+ parameters: {
977
+ ...(body.parameters ?? {}),
978
+ __conversationRecallCorpus: recallCorpus,
979
+ __activeConversationId: conversationId,
980
+ },
981
+ messages: conversation.messages,
982
+ })) {
983
+ if (event.type === "run:started") {
984
+ latestRunId = event.runId;
985
+ }
986
+ if (event.type === "model:chunk") {
987
+ assistantResponse += event.content;
988
+ }
989
+ if (event.type === "tool:started") {
990
+ toolTimeline.push(`- start \`${event.tool}\``);
991
+ }
992
+ if (event.type === "tool:completed") {
993
+ toolTimeline.push(`- done \`${event.tool}\` (${event.duration}ms)`);
994
+ }
995
+ if (event.type === "tool:error") {
996
+ toolTimeline.push(`- error \`${event.tool}\`: ${event.error}`);
997
+ }
998
+ if (event.type === "tool:approval:required") {
999
+ toolTimeline.push(`- approval required \`${event.tool}\``);
1000
+ }
1001
+ if (event.type === "tool:approval:granted") {
1002
+ toolTimeline.push(`- approval granted (${event.approvalId})`);
1003
+ }
1004
+ if (event.type === "tool:approval:denied") {
1005
+ toolTimeline.push(`- approval denied (${event.approvalId})`);
1006
+ }
1007
+ if (
1008
+ event.type === "run:completed" &&
1009
+ assistantResponse.length === 0 &&
1010
+ event.result.response
1011
+ ) {
1012
+ assistantResponse = event.result.response;
1013
+ }
1014
+ await telemetry.emit(event);
1015
+ response.write(formatSseEvent(event));
1016
+ }
1017
+ conversation.messages = [
1018
+ ...conversation.messages,
1019
+ { role: "user", content: messageText },
1020
+ {
1021
+ role: "assistant",
1022
+ content: assistantResponse,
1023
+ metadata:
1024
+ toolTimeline.length > 0
1025
+ ? ({ toolActivity: toolTimeline } as Message["metadata"])
1026
+ : undefined,
1027
+ },
1028
+ ];
1029
+ conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
1030
+ conversation.updatedAt = Date.now();
1031
+ await conversationStore.update(conversation);
1032
+ } catch (error) {
1033
+ response.write(
1034
+ formatSseEvent({
1035
+ type: "run:error",
1036
+ runId: latestRunId || "run_unknown",
1037
+ error: {
1038
+ code: "RUN_ERROR",
1039
+ message: error instanceof Error ? error.message : "Unknown error",
1040
+ },
1041
+ }),
1042
+ );
1043
+ } finally {
1044
+ response.end();
1045
+ }
1046
+ return;
1047
+ }
1048
+
1049
+ writeJson(response, 404, { error: "Not found" });
1050
+ };
1051
+ };
1052
+
1053
+ export const startDevServer = async (
1054
+ port: number,
1055
+ options?: { workingDir?: string },
1056
+ ): Promise<Server> => {
1057
+ const handler = await createRequestHandler(options);
1058
+ const server = createServer(handler);
1059
+ const actualPort = await listenOnAvailablePort(server, port);
1060
+ if (actualPort !== port) {
1061
+ process.stdout.write(`Port ${port} is in use, switched to ${actualPort}.\n`);
1062
+ }
1063
+ process.stdout.write(`Poncho dev server running at http://localhost:${actualPort}\n`);
1064
+
1065
+ const shutdown = () => {
1066
+ server.close();
1067
+ // Force-close any lingering connections so the port is freed immediately
1068
+ server.closeAllConnections?.();
1069
+ process.exit(0);
1070
+ };
1071
+ process.on("SIGINT", shutdown);
1072
+ process.on("SIGTERM", shutdown);
1073
+
1074
+ return server;
1075
+ };
1076
+
1077
+ export const runOnce = async (
1078
+ task: string,
1079
+ options: {
1080
+ params: Record<string, string>;
1081
+ json: boolean;
1082
+ filePaths: string[];
1083
+ workingDir?: string;
1084
+ },
1085
+ ): Promise<void> => {
1086
+ const workingDir = options.workingDir ?? process.cwd();
1087
+ dotenv.config({ path: resolve(workingDir, ".env") });
1088
+ const config = await loadPonchoConfig(workingDir);
1089
+ const harness = new AgentHarness({ workingDir });
1090
+ const telemetry = new TelemetryEmitter(config?.telemetry);
1091
+ await harness.initialize();
1092
+
1093
+ const fileBlobs = await Promise.all(
1094
+ options.filePaths.map(async (path) => {
1095
+ const content = await readFile(resolve(workingDir, path), "utf8");
1096
+ return `# File: ${path}\n${content}`;
1097
+ }),
1098
+ );
1099
+
1100
+ const input: RunInput = {
1101
+ task: fileBlobs.length > 0 ? `${task}\n\n${fileBlobs.join("\n\n")}` : task,
1102
+ parameters: options.params,
1103
+ };
1104
+
1105
+ if (options.json) {
1106
+ const output = await harness.runToCompletion(input);
1107
+ for (const event of output.events) {
1108
+ await telemetry.emit(event);
1109
+ }
1110
+ process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
1111
+ return;
1112
+ }
1113
+
1114
+ for await (const event of harness.run(input)) {
1115
+ await telemetry.emit(event);
1116
+ if (event.type === "model:chunk") {
1117
+ process.stdout.write(event.content);
1118
+ }
1119
+ if (event.type === "run:error") {
1120
+ process.stderr.write(`\nError: ${event.error.message}\n`);
1121
+ }
1122
+ if (event.type === "run:completed") {
1123
+ process.stdout.write("\n");
1124
+ }
1125
+ }
1126
+ };
1127
+
1128
+ export const runInteractive = async (
1129
+ workingDir: string,
1130
+ params: Record<string, string>,
1131
+ ): Promise<void> => {
1132
+ dotenv.config({ path: resolve(workingDir, ".env") });
1133
+ const config = await loadPonchoConfig(workingDir);
1134
+
1135
+ // Approval bridge: the harness calls this handler which creates a pending
1136
+ // promise. The Ink UI picks up the pending request and shows a Y/N prompt.
1137
+ // The user's response resolves the promise.
1138
+ type ApprovalRequest = {
1139
+ tool: string;
1140
+ input: Record<string, unknown>;
1141
+ approvalId: string;
1142
+ resolve: (approved: boolean) => void;
1143
+ };
1144
+ let pendingApproval: ApprovalRequest | null = null;
1145
+ let onApprovalRequest: ((req: ApprovalRequest) => void) | null = null;
1146
+
1147
+ const approvalHandler = async (request: {
1148
+ tool: string;
1149
+ input: Record<string, unknown>;
1150
+ runId: string;
1151
+ step: number;
1152
+ approvalId: string;
1153
+ }): Promise<boolean> => {
1154
+ return new Promise<boolean>((resolveApproval) => {
1155
+ const req: ApprovalRequest = {
1156
+ tool: request.tool,
1157
+ input: request.input,
1158
+ approvalId: request.approvalId,
1159
+ resolve: resolveApproval,
1160
+ };
1161
+ pendingApproval = req;
1162
+ if (onApprovalRequest) {
1163
+ onApprovalRequest(req);
1164
+ }
1165
+ });
1166
+ };
1167
+
1168
+ const harness = new AgentHarness({
1169
+ workingDir,
1170
+ environment: resolveHarnessEnvironment(),
1171
+ approvalHandler,
1172
+ });
1173
+ await harness.initialize();
1174
+ try {
1175
+ const { runInteractiveInk } = await import("./run-interactive-ink.js");
1176
+ await (
1177
+ runInteractiveInk as (input: {
1178
+ harness: AgentHarness;
1179
+ params: Record<string, string>;
1180
+ workingDir: string;
1181
+ config?: PonchoConfig;
1182
+ conversationStore: ConversationStore;
1183
+ onSetApprovalCallback?: (cb: (req: ApprovalRequest) => void) => void;
1184
+ }) => Promise<void>
1185
+ )({
1186
+ harness,
1187
+ params,
1188
+ workingDir,
1189
+ config,
1190
+ conversationStore: createConversationStore(resolveStateConfig(config), { workingDir }),
1191
+ onSetApprovalCallback: (cb: (req: ApprovalRequest) => void) => {
1192
+ onApprovalRequest = cb;
1193
+ // If there's already a pending request, fire it immediately
1194
+ if (pendingApproval) {
1195
+ cb(pendingApproval);
1196
+ }
1197
+ },
1198
+ });
1199
+ } finally {
1200
+ await harness.shutdown();
1201
+ }
1202
+ };
1203
+
1204
+ export const listTools = async (workingDir: string): Promise<void> => {
1205
+ dotenv.config({ path: resolve(workingDir, ".env") });
1206
+ const harness = new AgentHarness({ workingDir });
1207
+ await harness.initialize();
1208
+ const tools = harness.listTools();
1209
+
1210
+ if (tools.length === 0) {
1211
+ process.stdout.write("No tools registered.\n");
1212
+ return;
1213
+ }
1214
+
1215
+ process.stdout.write("Available tools:\n");
1216
+ for (const tool of tools) {
1217
+ process.stdout.write(`- ${tool.name}: ${tool.description}\n`);
1218
+ }
1219
+ };
1220
+
1221
+ const runPnpmInstall = async (workingDir: string): Promise<void> =>
1222
+ await new Promise<void>((resolveInstall, rejectInstall) => {
1223
+ const child = spawn("pnpm", ["install"], {
1224
+ cwd: workingDir,
1225
+ stdio: "inherit",
1226
+ env: process.env,
1227
+ });
1228
+ child.on("exit", (code) => {
1229
+ if (code === 0) {
1230
+ resolveInstall();
1231
+ return;
1232
+ }
1233
+ rejectInstall(new Error(`pnpm install failed with exit code ${code ?? -1}`));
1234
+ });
1235
+ });
1236
+
1237
+ const runInstallCommand = async (
1238
+ workingDir: string,
1239
+ packageNameOrPath: string,
1240
+ ): Promise<void> =>
1241
+ await new Promise<void>((resolveInstall, rejectInstall) => {
1242
+ const child = spawn("pnpm", ["add", packageNameOrPath], {
1243
+ cwd: workingDir,
1244
+ stdio: "inherit",
1245
+ env: process.env,
1246
+ });
1247
+ child.on("exit", (code) => {
1248
+ if (code === 0) {
1249
+ resolveInstall();
1250
+ return;
1251
+ }
1252
+ rejectInstall(new Error(`pnpm add failed with exit code ${code ?? -1}`));
1253
+ });
1254
+ });
1255
+
1256
+ /**
1257
+ * Resolve the installed npm package name from a package specifier.
1258
+ * Handles local paths, scoped packages, and GitHub shorthand (e.g.
1259
+ * "vercel-labs/agent-skills" installs as "agent-skills").
1260
+ */
1261
+ const resolveInstalledPackageName = (packageNameOrPath: string): string | null => {
1262
+ if (packageNameOrPath.startsWith(".") || packageNameOrPath.startsWith("/")) {
1263
+ return null; // local path — handled separately
1264
+ }
1265
+ // Scoped package: @scope/name
1266
+ if (packageNameOrPath.startsWith("@")) {
1267
+ return packageNameOrPath;
1268
+ }
1269
+ // GitHub shorthand: owner/repo — npm installs as the repo name
1270
+ if (packageNameOrPath.includes("/")) {
1271
+ return packageNameOrPath.split("/").pop() ?? packageNameOrPath;
1272
+ }
1273
+ return packageNameOrPath;
1274
+ };
1275
+
1276
+ /**
1277
+ * Locate the root directory of an installed skill package.
1278
+ * Handles local paths, normal npm packages, and GitHub repos (which may
1279
+ * lack a root package.json).
1280
+ */
1281
+ const resolveSkillRoot = (
1282
+ workingDir: string,
1283
+ packageNameOrPath: string,
1284
+ ): string => {
1285
+ // Local path
1286
+ if (packageNameOrPath.startsWith(".") || packageNameOrPath.startsWith("/")) {
1287
+ return resolve(workingDir, packageNameOrPath);
1288
+ }
1289
+
1290
+ const moduleName =
1291
+ resolveInstalledPackageName(packageNameOrPath) ?? packageNameOrPath;
1292
+
1293
+ // Try require.resolve first (works for packages with a package.json)
1294
+ try {
1295
+ const packageJsonPath = require.resolve(`${moduleName}/package.json`, {
1296
+ paths: [workingDir],
1297
+ });
1298
+ return resolve(packageJsonPath, "..");
1299
+ } catch {
1300
+ // Fall back to looking in node_modules directly (GitHub repos may lack
1301
+ // a root package.json)
1302
+ const candidate = resolve(workingDir, "node_modules", moduleName);
1303
+ if (existsSync(candidate)) {
1304
+ return candidate;
1305
+ }
1306
+ throw new Error(
1307
+ `Could not locate installed package "${moduleName}" in ${workingDir}`,
1308
+ );
1309
+ }
1310
+ };
1311
+
1312
+ /**
1313
+ * Recursively check whether a directory (or any immediate sub-directory
1314
+ * tree) contains at least one SKILL.md file.
1315
+ */
1316
+ const findSkillManifest = async (dir: string, depth = 2): Promise<boolean> => {
1317
+ try {
1318
+ await access(resolve(dir, "SKILL.md"));
1319
+ return true;
1320
+ } catch {
1321
+ // Not found at this level — look one level deeper (e.g. skills/<name>/SKILL.md)
1322
+ }
1323
+ if (depth <= 0) return false;
1324
+ try {
1325
+ const { readdir } = await import("node:fs/promises");
1326
+ const entries = await readdir(dir, { withFileTypes: true });
1327
+ for (const entry of entries) {
1328
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
1329
+ const found = await findSkillManifest(resolve(dir, entry.name), depth - 1);
1330
+ if (found) return true;
1331
+ }
1332
+ }
1333
+ } catch {
1334
+ // ignore read errors
1335
+ }
1336
+ return false;
1337
+ };
1338
+
1339
+ const validateSkillPackage = async (
1340
+ workingDir: string,
1341
+ packageNameOrPath: string,
1342
+ ): Promise<void> => {
1343
+ const skillRoot = resolveSkillRoot(workingDir, packageNameOrPath);
1344
+ const hasSkill = await findSkillManifest(skillRoot);
1345
+ if (!hasSkill) {
1346
+ throw new Error(`Skill validation failed: no SKILL.md found in ${skillRoot}`);
1347
+ }
1348
+ };
1349
+
1350
+ export const addSkill = async (workingDir: string, packageNameOrPath: string): Promise<void> => {
1351
+ await runInstallCommand(workingDir, packageNameOrPath);
1352
+ await validateSkillPackage(workingDir, packageNameOrPath);
1353
+ process.stdout.write(`Added skill: ${packageNameOrPath}\n`);
1354
+ };
1355
+
1356
+ export const runTests = async (
1357
+ workingDir: string,
1358
+ filePath?: string,
1359
+ ): Promise<{ passed: number; failed: number }> => {
1360
+ dotenv.config({ path: resolve(workingDir, ".env") });
1361
+ const testFilePath = filePath ?? resolve(workingDir, "tests", "basic.yaml");
1362
+ const content = await readFile(testFilePath, "utf8");
1363
+ const parsed = YAML.parse(content) as {
1364
+ tests?: Array<{
1365
+ name: string;
1366
+ task: string;
1367
+ expect?: {
1368
+ contains?: string;
1369
+ refusal?: boolean;
1370
+ toolCalled?: string;
1371
+ maxSteps?: number;
1372
+ maxTokens?: number;
1373
+ };
1374
+ }>;
1375
+ };
1376
+ const tests = parsed.tests ?? [];
1377
+
1378
+ const harness = new AgentHarness({ workingDir });
1379
+ await harness.initialize();
1380
+
1381
+ let passed = 0;
1382
+ let failed = 0;
1383
+
1384
+ for (const testCase of tests) {
1385
+ try {
1386
+ const output = await harness.runToCompletion({ task: testCase.task });
1387
+ const response = output.result.response ?? "";
1388
+ const events = output.events;
1389
+ const expectation = testCase.expect ?? {};
1390
+ const checks: boolean[] = [];
1391
+
1392
+ if (expectation.contains) {
1393
+ checks.push(response.includes(expectation.contains));
1394
+ }
1395
+ if (typeof expectation.maxSteps === "number") {
1396
+ checks.push(output.result.steps <= expectation.maxSteps);
1397
+ }
1398
+ if (typeof expectation.maxTokens === "number") {
1399
+ checks.push(
1400
+ output.result.tokens.input + output.result.tokens.output <= expectation.maxTokens,
1401
+ );
1402
+ }
1403
+ if (expectation.refusal) {
1404
+ checks.push(
1405
+ response.toLowerCase().includes("can't") || response.toLowerCase().includes("cannot"),
1406
+ );
1407
+ }
1408
+ if (expectation.toolCalled) {
1409
+ checks.push(
1410
+ events.some(
1411
+ (event) => event.type === "tool:started" && event.tool === expectation.toolCalled,
1412
+ ),
1413
+ );
1414
+ }
1415
+
1416
+ const ok = checks.length === 0 ? output.result.status === "completed" : checks.every(Boolean);
1417
+ if (ok) {
1418
+ passed += 1;
1419
+ process.stdout.write(`PASS ${testCase.name}\n`);
1420
+ } else {
1421
+ failed += 1;
1422
+ process.stdout.write(`FAIL ${testCase.name}\n`);
1423
+ }
1424
+ } catch (error) {
1425
+ failed += 1;
1426
+ process.stdout.write(
1427
+ `FAIL ${testCase.name} (${error instanceof Error ? error.message : "Unknown test error"})\n`,
1428
+ );
1429
+ }
1430
+ }
1431
+
1432
+ process.stdout.write(`\nTest summary: ${passed} passed, ${failed} failed\n`);
1433
+ return { passed, failed };
1434
+ };
1435
+
1436
+ export const buildTarget = async (workingDir: string, target: string): Promise<void> => {
1437
+ const outDir = resolve(workingDir, ".poncho-build", target);
1438
+ await mkdir(outDir, { recursive: true });
1439
+ const serverEntrypoint = `import { startDevServer } from "@poncho-ai/cli";
1440
+
1441
+ const port = Number.parseInt(process.env.PORT ?? "3000", 10);
1442
+ await startDevServer(Number.isNaN(port) ? 3000 : port, { workingDir: process.cwd() });
1443
+ `;
1444
+ const runtimePackageJson = JSON.stringify(
1445
+ {
1446
+ name: "poncho-runtime-bundle",
1447
+ private: true,
1448
+ type: "module",
1449
+ scripts: {
1450
+ start: "node server.js",
1451
+ },
1452
+ dependencies: {
1453
+ "@poncho-ai/cli": "^0.1.0",
1454
+ },
1455
+ },
1456
+ null,
1457
+ 2,
1458
+ );
1459
+
1460
+ if (target === "vercel") {
1461
+ await mkdir(resolve(outDir, "api"), { recursive: true });
1462
+ await copyIfExists(resolve(workingDir, "AGENT.md"), resolve(outDir, "AGENT.md"));
1463
+ await copyIfExists(
1464
+ resolve(workingDir, "poncho.config.js"),
1465
+ resolve(outDir, "poncho.config.js"),
1466
+ );
1467
+ await copyIfExists(resolve(workingDir, "skills"), resolve(outDir, "skills"));
1468
+ await copyIfExists(resolve(workingDir, "tests"), resolve(outDir, "tests"));
1469
+ await writeFile(
1470
+ resolve(outDir, "vercel.json"),
1471
+ JSON.stringify(
1472
+ {
1473
+ version: 2,
1474
+ functions: {
1475
+ "api/index.js": {
1476
+ includeFiles: "{AGENT.md,poncho.config.js,skills/**,tests/**}",
1477
+ },
1478
+ },
1479
+ routes: [{ src: "/(.*)", dest: "/api/index.js" }],
1480
+ },
1481
+ null,
1482
+ 2,
1483
+ ),
1484
+ "utf8",
1485
+ );
1486
+ await buildVercelHandlerBundle(outDir);
1487
+ await writeFile(
1488
+ resolve(outDir, "package.json"),
1489
+ JSON.stringify(
1490
+ {
1491
+ private: true,
1492
+ type: "module",
1493
+ engines: {
1494
+ node: "20.x",
1495
+ },
1496
+ dependencies: VERCEL_RUNTIME_DEPENDENCIES,
1497
+ },
1498
+ null,
1499
+ 2,
1500
+ ),
1501
+ "utf8",
1502
+ );
1503
+ } else if (target === "docker") {
1504
+ await writeFile(
1505
+ resolve(outDir, "Dockerfile"),
1506
+ `FROM node:20-slim
1507
+ WORKDIR /app
1508
+ COPY package.json package.json
1509
+ COPY AGENT.md AGENT.md
1510
+ COPY poncho.config.js poncho.config.js
1511
+ COPY skills skills
1512
+ COPY tests tests
1513
+ COPY .env.example .env.example
1514
+ RUN corepack enable && npm install -g @poncho-ai/cli
1515
+ COPY server.js server.js
1516
+ EXPOSE 3000
1517
+ CMD ["node","server.js"]
1518
+ `,
1519
+ "utf8",
1520
+ );
1521
+ await writeFile(resolve(outDir, "server.js"), serverEntrypoint, "utf8");
1522
+ await writeFile(resolve(outDir, "package.json"), runtimePackageJson, "utf8");
1523
+ } else if (target === "lambda") {
1524
+ await writeFile(
1525
+ resolve(outDir, "lambda-handler.js"),
1526
+ `import { startDevServer } from "@poncho-ai/cli";
1527
+ let serverPromise;
1528
+ export const handler = async (event = {}) => {
1529
+ if (!serverPromise) {
1530
+ serverPromise = startDevServer(0, { workingDir: process.cwd() });
1531
+ }
1532
+ const body = JSON.stringify({
1533
+ status: "ready",
1534
+ route: event.rawPath ?? event.path ?? "/",
1535
+ });
1536
+ return { statusCode: 200, headers: { "content-type": "application/json" }, body };
1537
+ };
1538
+ `,
1539
+ "utf8",
1540
+ );
1541
+ await writeFile(resolve(outDir, "package.json"), runtimePackageJson, "utf8");
1542
+ } else if (target === "fly") {
1543
+ await writeFile(
1544
+ resolve(outDir, "fly.toml"),
1545
+ `app = "poncho-app"
1546
+ [env]
1547
+ PORT = "3000"
1548
+ [http_service]
1549
+ internal_port = 3000
1550
+ force_https = true
1551
+ auto_start_machines = true
1552
+ auto_stop_machines = "stop"
1553
+ min_machines_running = 0
1554
+ `,
1555
+ "utf8",
1556
+ );
1557
+ await writeFile(
1558
+ resolve(outDir, "Dockerfile"),
1559
+ `FROM node:20-slim
1560
+ WORKDIR /app
1561
+ COPY package.json package.json
1562
+ COPY AGENT.md AGENT.md
1563
+ COPY poncho.config.js poncho.config.js
1564
+ COPY skills skills
1565
+ COPY tests tests
1566
+ RUN npm install -g @poncho-ai/cli
1567
+ COPY server.js server.js
1568
+ EXPOSE 3000
1569
+ CMD ["node","server.js"]
1570
+ `,
1571
+ "utf8",
1572
+ );
1573
+ await writeFile(resolve(outDir, "server.js"), serverEntrypoint, "utf8");
1574
+ await writeFile(resolve(outDir, "package.json"), runtimePackageJson, "utf8");
1575
+ } else {
1576
+ throw new Error(`Unsupported build target: ${target}`);
1577
+ }
1578
+
1579
+ process.stdout.write(`Build artifacts generated at ${outDir}\n`);
1580
+ };
1581
+
1582
+ const normalizeMcpName = (entry: { url?: string; name?: string }): string =>
1583
+ entry.name ?? entry.url ?? `mcp_${Date.now()}`;
1584
+
1585
+ export const mcpAdd = async (
1586
+ workingDir: string,
1587
+ options: {
1588
+ url?: string;
1589
+ name?: string;
1590
+ envVars?: string[];
1591
+ },
1592
+ ): Promise<void> => {
1593
+ const config = (await loadPonchoConfig(workingDir)) ?? { mcp: [] };
1594
+ const mcp = [...(config.mcp ?? [])];
1595
+ if (!options.url) {
1596
+ throw new Error("Remote MCP only: provide --url for a remote MCP server.");
1597
+ }
1598
+ mcp.push({
1599
+ name: options.name ?? normalizeMcpName({ url: options.url }),
1600
+ url: options.url,
1601
+ env: options.envVars ?? [],
1602
+ });
1603
+
1604
+ await writeConfigFile(workingDir, { ...config, mcp });
1605
+ process.stdout.write("MCP server added.\n");
1606
+ };
1607
+
1608
+ export const mcpList = async (workingDir: string): Promise<void> => {
1609
+ const config = await loadPonchoConfig(workingDir);
1610
+ const mcp = config?.mcp ?? [];
1611
+ if (mcp.length === 0) {
1612
+ process.stdout.write("No MCP servers configured.\n");
1613
+ return;
1614
+ }
1615
+ process.stdout.write("Configured MCP servers:\n");
1616
+ for (const entry of mcp) {
1617
+ process.stdout.write(`- ${entry.name ?? entry.url} (remote: ${entry.url})\n`);
1618
+ }
1619
+ };
1620
+
1621
+ export const mcpRemove = async (workingDir: string, name: string): Promise<void> => {
1622
+ const config = (await loadPonchoConfig(workingDir)) ?? { mcp: [] };
1623
+ const before = config.mcp ?? [];
1624
+ const filtered = before.filter((entry) => normalizeMcpName(entry) !== name);
1625
+ await writeConfigFile(workingDir, { ...config, mcp: filtered });
1626
+ process.stdout.write(`Removed MCP server: ${name}\n`);
1627
+ };
1628
+
1629
+ export const buildCli = (): Command => {
1630
+ const program = new Command();
1631
+ program
1632
+ .name("poncho")
1633
+ .description("CLI for building and running Poncho agents")
1634
+ .version("0.1.0");
1635
+
1636
+ program
1637
+ .command("init")
1638
+ .argument("<name>", "project name")
1639
+ .option("--yes", "accept defaults and skip prompts", false)
1640
+ .description("Scaffold a new Poncho project")
1641
+ .action(async (name: string, options: { yes: boolean }) => {
1642
+ await initProject(name, {
1643
+ onboarding: {
1644
+ yes: options.yes,
1645
+ interactive:
1646
+ !options.yes && process.stdin.isTTY === true && process.stdout.isTTY === true,
1647
+ },
1648
+ });
1649
+ });
1650
+
1651
+ program
1652
+ .command("dev")
1653
+ .description("Run local development server")
1654
+ .option("--port <port>", "server port", "3000")
1655
+ .action(async (options: { port: string }) => {
1656
+ const port = Number.parseInt(options.port, 10);
1657
+ await startDevServer(Number.isNaN(port) ? 3000 : port);
1658
+ });
1659
+
1660
+ program
1661
+ .command("run")
1662
+ .argument("[task]", "task to run")
1663
+ .description("Execute the agent once")
1664
+ .option("--param <keyValue>", "parameter key=value", (value, all: string[]) => {
1665
+ all.push(value);
1666
+ return all;
1667
+ }, [] as string[])
1668
+ .option("--file <path>", "include file contents", (value, all: string[]) => {
1669
+ all.push(value);
1670
+ return all;
1671
+ }, [] as string[])
1672
+ .option("--json", "output json", false)
1673
+ .option("--interactive", "run in interactive mode", false)
1674
+ .action(
1675
+ async (
1676
+ task: string | undefined,
1677
+ options: { param: string[]; file: string[]; json: boolean; interactive: boolean },
1678
+ ) => {
1679
+ const params = parseParams(options.param);
1680
+ if (options.interactive) {
1681
+ await runInteractive(process.cwd(), params);
1682
+ return;
1683
+ }
1684
+ if (!task) {
1685
+ throw new Error("Task is required unless --interactive is used.");
1686
+ }
1687
+ await runOnce(task, {
1688
+ params,
1689
+ json: options.json,
1690
+ filePaths: options.file,
1691
+ });
1692
+ },
1693
+ );
1694
+
1695
+ program
1696
+ .command("tools")
1697
+ .description("List all tools available to the agent")
1698
+ .action(async () => {
1699
+ await listTools(process.cwd());
1700
+ });
1701
+
1702
+ program
1703
+ .command("add")
1704
+ .argument("<packageOrPath>", "skill package name/path")
1705
+ .description("Add a skill package and validate SKILL.md")
1706
+ .action(async (packageOrPath: string) => {
1707
+ await addSkill(process.cwd(), packageOrPath);
1708
+ });
1709
+
1710
+ program
1711
+ .command("update-agent")
1712
+ .description("Remove deprecated embedded local guidance from AGENT.md")
1713
+ .action(async () => {
1714
+ await updateAgentGuidance(process.cwd());
1715
+ });
1716
+
1717
+ program
1718
+ .command("test")
1719
+ .argument("[file]", "test file path (yaml)")
1720
+ .description("Run yaml-defined agent tests")
1721
+ .action(async (file?: string) => {
1722
+ const testFile = file ? resolve(process.cwd(), file) : undefined;
1723
+ const result = await runTests(process.cwd(), testFile);
1724
+ if (result.failed > 0) {
1725
+ process.exitCode = 1;
1726
+ }
1727
+ });
1728
+
1729
+ program
1730
+ .command("build")
1731
+ .argument("<target>", "vercel|docker|lambda|fly")
1732
+ .description("Generate build artifacts for deployment target")
1733
+ .action(async (target: string) => {
1734
+ await buildTarget(process.cwd(), target);
1735
+ });
1736
+
1737
+ const mcpCommand = program.command("mcp").description("Manage MCP servers");
1738
+ mcpCommand
1739
+ .command("add")
1740
+ .requiredOption("--url <url>", "remote MCP url")
1741
+ .option("--name <name>", "server name")
1742
+ .option("--env <name>", "env variable (repeatable)", (value, all: string[]) => {
1743
+ all.push(value);
1744
+ return all;
1745
+ }, [] as string[])
1746
+ .action(
1747
+ async (
1748
+ options: {
1749
+ url?: string;
1750
+ name?: string;
1751
+ env: string[];
1752
+ },
1753
+ ) => {
1754
+ await mcpAdd(process.cwd(), {
1755
+ url: options.url,
1756
+ name: options.name,
1757
+ envVars: options.env,
1758
+ });
1759
+ },
1760
+ );
1761
+
1762
+ mcpCommand
1763
+ .command("list")
1764
+ .description("List configured MCP servers")
1765
+ .action(async () => {
1766
+ await mcpList(process.cwd());
1767
+ });
1768
+
1769
+ mcpCommand
1770
+ .command("remove")
1771
+ .argument("<name>", "server name")
1772
+ .description("Remove an MCP server by name")
1773
+ .action(async (name: string) => {
1774
+ await mcpRemove(process.cwd(), name);
1775
+ });
1776
+
1777
+ return program;
1778
+ };
1779
+
1780
+ export const main = async (argv: string[] = process.argv): Promise<void> => {
1781
+ try {
1782
+ await buildCli().parseAsync(argv);
1783
+ } catch (error) {
1784
+ if (
1785
+ typeof error === "object" &&
1786
+ error !== null &&
1787
+ "code" in error &&
1788
+ (error as { code?: string }).code === "EADDRINUSE"
1789
+ ) {
1790
+ const message = "Port is already in use. Try `poncho dev --port 3001` or stop the process using port 3000.";
1791
+ process.stderr.write(`${message}\n`);
1792
+ process.exitCode = 1;
1793
+ return;
1794
+ }
1795
+ process.stderr.write(`${error instanceof Error ? error.message : "Unknown CLI error"}\n`);
1796
+ process.exitCode = 1;
1797
+ }
1798
+ };
1799
+
1800
+ export const packageRoot = resolve(__dirname, "..");