autohand-cli 0.8.3 → 0.9.1

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 (546) hide show
  1. package/README.md +191 -74
  2. package/dist/{AgentRegistry-ODDXPAFR.js → AgentRegistry-GOV7FN2O.js} +2 -2
  3. package/dist/AgentRegistry-J2C6JBZ3.cjs +10 -0
  4. package/dist/{AutomodeManager-EOVHGRPP.js → AutomodeManager-APLLPEYJ.js} +31 -17
  5. package/dist/{AutomodeManager-6ATBE7Q5.cjs → AutomodeManager-MPSXT2RH.cjs} +38 -24
  6. package/dist/CommunitySkillsCache-22BB3J6H.cjs +8 -0
  7. package/dist/{CommunitySkillsCache-QLE57BN5.js → CommunitySkillsCache-ZACDPD4J.js} +2 -2
  8. package/dist/GitHubRegistryFetcher-CYJLF2XL.cjs +7 -0
  9. package/dist/{GitHubRegistryFetcher-NT5GFZXS.js → GitHubRegistryFetcher-J2BWPXJF.js} +1 -1
  10. package/dist/{HookManager-B2F35M27.js → HookManager-2S6AHOC3.js} +1 -1
  11. package/dist/{HookManager-PFAFE3FK.cjs → HookManager-TVEQI5ZU.cjs} +2 -2
  12. package/dist/{ImportWizard-OHRKBANZ.js → ImportWizard-AEBMAKXE.js} +33 -9
  13. package/dist/{ImportWizard-CJRZPPHL.cjs → ImportWizard-AF2XL6DS.cjs} +37 -13
  14. package/dist/LearnAdvisor-KKEQ5QCV.js +9 -0
  15. package/dist/LearnAdvisor-Q7RAYLPF.cjs +9 -0
  16. package/dist/{McpClientManager-NQ3EW2IF.cjs → McpClientManager-BNSKLHIN.cjs} +2 -2
  17. package/dist/{McpClientManager-VBIMGKXU.js → McpClientManager-SL35BR24.js} +1 -1
  18. package/dist/MemoryManager-PMNAEZMB.cjs +8 -0
  19. package/dist/{MemoryManager-2LAT7IHS.js → MemoryManager-QZRLTUB5.js} +2 -2
  20. package/dist/NVIDIAProvider-C3FWQOBZ.cjs +14 -0
  21. package/dist/NVIDIAProvider-SJPY3ONR.js +14 -0
  22. package/dist/{PermissionManager-7NQHRCHY.js → PermissionManager-MT7KBNNR.js} +3 -3
  23. package/dist/PermissionManager-URCNP5EC.cjs +11 -0
  24. package/dist/{ProjectProfiler-UG42W6WD.cjs → ProjectProfiler-CDAE7ECW.cjs} +1 -0
  25. package/dist/{ProjectProfiler-P4QJQWWA.js → ProjectProfiler-NZTJDRHD.js} +1 -0
  26. package/dist/ProviderFactory-56N3ECE4.cjs +12 -0
  27. package/dist/{ProviderFactory-2IYJ5OPW.js → ProviderFactory-RB4RVAF6.js} +4 -1
  28. package/dist/SessionManager-2IZGWASQ.cjs +10 -0
  29. package/dist/{SessionManager-4U4JFQ3C.js → SessionManager-554PHVPC.js} +2 -2
  30. package/dist/SkillsRegistry-GMRVONOQ.cjs +9 -0
  31. package/dist/{SkillsRegistry-RFEINXRT.js → SkillsRegistry-NHHMIUYJ.js} +2 -2
  32. package/dist/SubAgent-4GOR2FIJ.js +13 -0
  33. package/dist/SubAgent-ZJMDH4FI.cjs +13 -0
  34. package/dist/{SyncApiClient-B5RT2ECG.js → SyncApiClient-G3FM65IV.js} +1 -1
  35. package/dist/SyncApiClient-ODBDECOG.cjs +11 -0
  36. package/dist/about-3USFSGOH.js +15 -0
  37. package/dist/about-QR52U5DD.cjs +15 -0
  38. package/dist/acp-5Y2W6OZP.js +1556 -0
  39. package/dist/acp-GSYE75OB.cjs +1556 -0
  40. package/dist/actionExecutor-KHUU3Z3Q.cjs +28 -0
  41. package/dist/actionExecutor-PLQJZLZV.js +28 -0
  42. package/dist/add-dir-BTAD5OZS.cjs +11 -0
  43. package/dist/{add-dir-TUJM3PG5.js → add-dir-HCERJDBR.js} +3 -2
  44. package/dist/agent-B3EVT7NN.cjs +126 -0
  45. package/dist/agent-SATQ3WZ3.js +126 -0
  46. package/dist/agents/builtin/code-cleaner.md +1 -1
  47. package/dist/agents/builtin/docs-writer.md +1 -1
  48. package/dist/agents/builtin/researcher.md +2 -2
  49. package/dist/agents/builtin/reviewer.md +1 -1
  50. package/dist/agents/builtin/tester.md +1 -1
  51. package/dist/agents/builtin/todo-resolver.md +1 -1
  52. package/dist/agents-44CRJGM7.cjs +18 -0
  53. package/dist/agents-NN4ZAJ7R.js +18 -0
  54. package/dist/agents-new-6D6G4QS6.cjs +17 -0
  55. package/dist/agents-new-7GAQ25EK.js +17 -0
  56. package/dist/{autoSkill-LQEVQFIH.js → autoSkill-66PKCYTW.js} +2 -2
  57. package/dist/autoSkill-VQLGBELN.cjs +20 -0
  58. package/dist/automode-45DWV4TN.cjs +10 -0
  59. package/dist/{automode-R6P3VHLS.js → automode-BUF3VGXU.js} +2 -2
  60. package/dist/browserToolBridge-BXRQB4B4.cjs +58 -0
  61. package/dist/browserToolBridge-F5N66PE7.js +58 -0
  62. package/dist/chrome-Q2U73HZB.cjs +50 -0
  63. package/dist/chrome-Q54OALOK.cjs +21 -0
  64. package/dist/chrome-Y3T7EIHG.js +50 -0
  65. package/dist/chrome-ZNJ46OYW.js +21 -0
  66. package/dist/chromeSkill-53TH55PM.js +105 -0
  67. package/dist/chromeSkill-THW7N4IY.cjs +105 -0
  68. package/dist/{chunk-HXGBSJL5.cjs → chunk-22SCR3EV.cjs} +2 -2
  69. package/dist/chunk-26G7YGOW.js +367 -0
  70. package/dist/{chunk-3L53OA4E.cjs → chunk-2ESQR35E.cjs} +10 -10
  71. package/dist/chunk-2H5O745H.js +63 -0
  72. package/dist/{chunk-ULDM2SNB.js → chunk-2IW5YFJ4.js} +48 -47
  73. package/dist/{chunk-U55TWFJI.cjs → chunk-2VHB43IX.cjs} +24 -6
  74. package/dist/chunk-3EDDDZS2.cjs +5 -0
  75. package/dist/{chunk-X5VSP65C.cjs → chunk-3ICORF7M.cjs} +4 -4
  76. package/dist/chunk-3OF56EMA.cjs +197 -0
  77. package/dist/chunk-3UC6DMFI.cjs +93 -0
  78. package/dist/{chunk-64H4FRM3.cjs → chunk-3UHRCHKB.cjs} +32 -34
  79. package/dist/{chunk-JCLYQ2JC.js → chunk-3ZOOH44N.js} +12 -6
  80. package/dist/{chunk-TUD3Z3BD.js → chunk-44ZWOBTI.js} +136 -55
  81. package/dist/chunk-4567QNRB.js +444 -0
  82. package/dist/chunk-45CHNQM6.cjs +517 -0
  83. package/dist/chunk-46MTALKD.js +44 -0
  84. package/dist/{chunk-WHE2SWHU.js → chunk-4E7SHPNQ.js} +2 -2
  85. package/dist/chunk-4LMUDS2K.js +124 -0
  86. package/dist/{chunk-W7RYQJLO.cjs → chunk-4XFHP2SN.cjs} +46 -15
  87. package/dist/chunk-4XVLNOFA.js +360 -0
  88. package/dist/{chunk-GBHDROGL.js → chunk-575PXZX7.js} +16 -4
  89. package/dist/chunk-5A7ZL64N.js +139 -0
  90. package/dist/chunk-5GCQ5UMV.cjs +85 -0
  91. package/dist/chunk-5JTTM5SC.js +59 -0
  92. package/dist/{chunk-IETRBBMP.cjs → chunk-5K3CDSWZ.cjs} +108 -31
  93. package/dist/{chunk-KLWJIPU2.cjs → chunk-5SFV2NCY.cjs} +20 -16
  94. package/dist/chunk-5ZNYX4VH.cjs +71 -0
  95. package/dist/{chunk-A4IJHHV7.cjs → chunk-63ANVAOJ.cjs} +52 -11
  96. package/dist/{chunk-MYISNQH4.js → chunk-65LRPW5W.js} +1 -1
  97. package/dist/chunk-6AYJCDTA.js +360 -0
  98. package/dist/{chunk-X2MSVKDV.js → chunk-6HF6XSCC.js} +2 -2
  99. package/dist/{chunk-AEJH23FO.cjs → chunk-6JWCGQJU.cjs} +6 -6
  100. package/dist/chunk-6OECOAK5.cjs +360 -0
  101. package/dist/chunk-6R25D2H5.js +121 -0
  102. package/dist/chunk-7DJJGKM6.js +77 -0
  103. package/dist/{chunk-VG34MG2U.js → chunk-7IRQ7JEH.js} +9 -6
  104. package/dist/chunk-7MY4JHER.js +27 -0
  105. package/dist/chunk-7PJKTSRN.js +152 -0
  106. package/dist/{chunk-SKYG33B2.cjs → chunk-7PT646AL.cjs} +3 -3
  107. package/dist/chunk-7WAUCLLQ.js +229 -0
  108. package/dist/{chunk-U46VYPLR.cjs → chunk-7XTUL7ZD.cjs} +9 -9
  109. package/dist/chunk-AE2ILW25.cjs +152 -0
  110. package/dist/{chunk-T2M64VHA.cjs → chunk-AOKXYGBQ.cjs} +61 -41
  111. package/dist/chunk-AQ5PVHOC.cjs +113 -0
  112. package/dist/{chunk-N2BLVUPM.cjs → chunk-AZ47LKD5.cjs} +3 -3
  113. package/dist/chunk-B4HSNOIH.cjs +354 -0
  114. package/dist/chunk-B76FJKEJ.cjs +184 -0
  115. package/dist/chunk-BP37FVBM.js +85 -0
  116. package/dist/chunk-BQU3HAE7.js +21 -0
  117. package/dist/chunk-BYE7RQFZ.cjs +121 -0
  118. package/dist/chunk-C46B72VK.js +1034 -0
  119. package/dist/{chunk-HIVRCQS2.js → chunk-C5QIQQ37.js} +56 -25
  120. package/dist/chunk-CCIDL4SA.js +19931 -0
  121. package/dist/{chunk-XVUHNWMX.js → chunk-CECH6QQP.js} +17 -19
  122. package/dist/chunk-CFAWTLSC.js +13 -0
  123. package/dist/{chunk-ZYQMLKOK.cjs → chunk-CK4HAHVY.cjs} +212 -70
  124. package/dist/chunk-CO2AADYU.cjs +360 -0
  125. package/dist/chunk-CRTCZ4WL.cjs +19931 -0
  126. package/dist/chunk-CSTUTDTH.js +75 -0
  127. package/dist/{chunk-K2C56QGS.cjs → chunk-D546G7DA.cjs} +1650 -479
  128. package/dist/{chunk-CNBKZEX5.cjs → chunk-DA7NBAJK.cjs} +49 -17
  129. package/dist/{chunk-G4CAKI3V.js → chunk-DGBPWODD.js} +7 -2
  130. package/dist/chunk-DHUQZ53B.cjs +29 -0
  131. package/dist/{chunk-RGR6ME5J.cjs → chunk-DI57A4BX.cjs} +25 -28
  132. package/dist/{chunk-5K6NGAVM.js → chunk-DNSD7DVJ.js} +67 -52
  133. package/dist/{chunk-RDF37HKB.cjs → chunk-DSSCERHI.cjs} +3 -2
  134. package/dist/{chunk-NAJ4IZKD.cjs → chunk-EA2MS25U.cjs} +8 -6
  135. package/dist/{chunk-SJAVBCOA.js → chunk-EEHWALXA.js} +94 -3
  136. package/dist/chunk-ES2HJQ4N.js +37 -0
  137. package/dist/{chunk-G27PQQFD.js → chunk-EYHKL4QC.js} +1 -1
  138. package/dist/{chunk-SV2WA57F.js → chunk-FBQJZKFX.js} +38 -15
  139. package/dist/chunk-FFBDRUO5.cjs +59 -0
  140. package/dist/{chunk-N7LI55V4.js → chunk-FFHSCM5M.js} +15 -8
  141. package/dist/{chunk-SAHBLB3E.js → chunk-FP46B4X3.js} +208 -3
  142. package/dist/chunk-FQVG6ZHF.js +197 -0
  143. package/dist/{chunk-J2GSFVUV.cjs → chunk-GAOPJ5S3.cjs} +74 -59
  144. package/dist/{chunk-S52YW5ZQ.js → chunk-GNBBIAMD.js} +16 -19
  145. package/dist/chunk-GSDV75A5.js +715 -0
  146. package/dist/chunk-GWY26SUD.cjs +63 -0
  147. package/dist/{chunk-TUAITHWL.js → chunk-H426KOGY.js} +2 -1
  148. package/dist/{chunk-EZMINVLU.js → chunk-H4D2Q2AL.js} +5 -2
  149. package/dist/chunk-HC2Q6A3E.cjs +4967 -0
  150. package/dist/{chunk-3WVJEL7K.cjs → chunk-HDK2EHVH.cjs} +3 -1
  151. package/dist/{chunk-5IXII4HX.cjs → chunk-HG3ZASBH.cjs} +23 -16
  152. package/dist/chunk-HK5A54AV.cjs +280 -0
  153. package/dist/{chunk-LQGVEP3E.js → chunk-HQUSEWT4.js} +45 -13
  154. package/dist/chunk-HSWWM6AM.js +2340 -0
  155. package/dist/chunk-HTU4W3GB.js +183 -0
  156. package/dist/{chunk-HLHTG5ZU.cjs → chunk-HVCIB4SW.cjs} +14 -12
  157. package/dist/chunk-HXHE2KAO.js +385 -0
  158. package/dist/{chunk-LJFUXC56.cjs → chunk-HXQREVTA.cjs} +9 -6
  159. package/dist/{chunk-SLQAYV3W.js → chunk-HZWU6IO3.js} +11 -2
  160. package/dist/chunk-I2ZK3AOD.js +866 -0
  161. package/dist/chunk-IEPLQ47J.js +719 -0
  162. package/dist/chunk-J5HE6CUM.cjs +124 -0
  163. package/dist/{chunk-LIEXWM2M.js → chunk-JAQO6XDB.js} +19 -1
  164. package/dist/chunk-JB3JCYBJ.js +113 -0
  165. package/dist/chunk-JCUHCH37.cjs +247 -0
  166. package/dist/{chunk-FPHU2ES6.cjs → chunk-JM3ZTQUK.cjs} +19 -9
  167. package/dist/{chunk-NDMIPTV4.js → chunk-JMJRFSNJ.js} +8 -3
  168. package/dist/{chunk-ZLSGXMGQ.js → chunk-JMN3GZU6.js} +2 -0
  169. package/dist/chunk-JPFRI4L4.js +4967 -0
  170. package/dist/{chunk-BVKXEQVG.cjs → chunk-JV5HVCHU.cjs} +22 -10
  171. package/dist/chunk-KA2U6MY2.js +388 -0
  172. package/dist/{chunk-SEKD5FH3.cjs → chunk-KASXKDF2.cjs} +8 -3
  173. package/dist/chunk-KJ26T7GM.cjs +367 -0
  174. package/dist/chunk-KM7R5YPS.cjs +385 -0
  175. package/dist/{chunk-MBBY4ZIK.js → chunk-KMP6Y3LJ.js} +4 -1
  176. package/dist/chunk-KWXGDSYT.js +184 -0
  177. package/dist/{chunk-C5IJIM2V.cjs → chunk-L46OUKW5.cjs} +68 -37
  178. package/dist/{chunk-6UJMCWRY.js → chunk-L6OH44HL.js} +19 -9
  179. package/dist/chunk-LANGD5CO.js +280 -0
  180. package/dist/{chunk-APIXPPMT.js → chunk-LEZEWKPA.js} +1004 -39
  181. package/dist/{chunk-ULQ6MDSJ.cjs → chunk-LFDPTJYF.cjs} +43 -35
  182. package/dist/chunk-LJD7KR3L.cjs +44 -0
  183. package/dist/{chunk-NCC6ETZS.js → chunk-LUMV3DB2.js} +41 -33
  184. package/dist/{chunk-QXH5RCFI.js → chunk-M5DK524Z.js} +2 -2
  185. package/dist/chunk-M7B7RRBK.cjs +1034 -0
  186. package/dist/chunk-MANJ67D3.js +3897 -0
  187. package/dist/{chunk-HPHJ73GU.cjs → chunk-MGNXABSU.cjs} +9 -9
  188. package/dist/{chunk-6ZCULLCA.js → chunk-MMKZSJUN.js} +1 -1
  189. package/dist/{chunk-DVUHHH3B.cjs → chunk-MQQH4XQH.cjs} +4 -4
  190. package/dist/chunk-MUNUUFU7.cjs +21 -0
  191. package/dist/{chunk-DVZOENQ7.cjs → chunk-MZN6JBRZ.cjs} +9 -4
  192. package/dist/{chunk-OOKY3HPZ.js → chunk-NB6M6ESM.js} +50 -9
  193. package/dist/{chunk-BYONM7UM.js → chunk-NBGOIFKP.js} +265 -18
  194. package/dist/chunk-NHWHEW7M.cjs +75 -0
  195. package/dist/chunk-NQZH5CR2.cjs +468 -0
  196. package/dist/{chunk-EGPXJERY.cjs → chunk-NV6LCD2I.cjs} +119 -28
  197. package/dist/chunk-NVATU4V6.cjs +44 -0
  198. package/dist/{chunk-YZXUDM5X.js → chunk-NZ3ZWLUW.js} +204 -62
  199. package/dist/chunk-NZ7GCK7Y.cjs +92 -0
  200. package/dist/{chunk-VEKDGU2Q.cjs → chunk-NZQFKXM3.cjs} +49 -25
  201. package/dist/chunk-O5GI6SR3.js +59 -0
  202. package/dist/{chunk-56SWIDEL.cjs → chunk-OCATAKXV.cjs} +57 -56
  203. package/dist/{chunk-SKV2F3NM.js → chunk-OIGYWOL6.js} +1 -1
  204. package/dist/{chunk-IFFXSTOM.cjs → chunk-ONVYXTUT.cjs} +3 -3
  205. package/dist/chunk-OR67YXQK.cjs +13 -0
  206. package/dist/chunk-OV3T5MUH.cjs +715 -0
  207. package/dist/{chunk-OHUZKDGX.js → chunk-P4FUTSVK.js} +3 -3
  208. package/dist/chunk-P6SLT2F4.cjs +139 -0
  209. package/dist/{chunk-TNZRZQ7Q.js → chunk-PB7W7R72.js} +3 -78
  210. package/dist/chunk-PCM3N3CL.cjs +37 -0
  211. package/dist/{chunk-2AA5MFES.js → chunk-PGFLPURU.js} +8 -5
  212. package/dist/{chunk-DJDE4DTT.cjs → chunk-PIXQ2AVM.cjs} +25 -19
  213. package/dist/chunk-PNIAIOMZ.cjs +229 -0
  214. package/dist/{chunk-HTLINWX6.cjs → chunk-PQBYFEBL.cjs} +16 -13
  215. package/dist/{chunk-22D2CNTP.cjs → chunk-PSAH4ZQB.cjs} +5 -2
  216. package/dist/{chunk-3VTAFAL2.js → chunk-PTTZI4QZ.js} +16 -12
  217. package/dist/{chunk-WPVWQSL7.cjs → chunk-PY73W5MQ.cjs} +16 -13
  218. package/dist/{chunk-EGMZDTSL.js → chunk-PZZMIYII.js} +10 -2
  219. package/dist/{chunk-HBZU3RBZ.js → chunk-Q3BS6FPM.js} +34 -10
  220. package/dist/chunk-QBYGMMDD.js +517 -0
  221. package/dist/chunk-QLZOMZO5.cjs +388 -0
  222. package/dist/{chunk-ADUFCS4Q.cjs → chunk-QQ64HEHP.cjs} +160 -79
  223. package/dist/{chunk-L3TWPROA.js → chunk-QRQF3556.js} +44 -24
  224. package/dist/{chunk-YHD6TUIR.cjs → chunk-QW2RW2GY.cjs} +2 -0
  225. package/dist/chunk-RAKO7UN7.js +114 -0
  226. package/dist/{chunk-7TQH3CL4.cjs → chunk-RBXBH6EB.cjs} +78 -35
  227. package/dist/chunk-RFUKZIJF.cjs +78 -0
  228. package/dist/chunk-RS2E32YB.js +71 -0
  229. package/dist/{chunk-OLSBBZW6.cjs → chunk-S5JNQIGL.cjs} +5 -5
  230. package/dist/{chunk-FTYY5JJD.js → chunk-S5QKRA2V.js} +2 -2
  231. package/dist/chunk-SD3NTC7D.cjs +77 -0
  232. package/dist/{chunk-KPELYZ6L.js → chunk-SDLY4X3G.js} +2 -2
  233. package/dist/{chunk-IDFF5J2E.js → chunk-SFFEKZGC.js} +38 -7
  234. package/dist/{chunk-EGFT4PGW.js → chunk-SHRLFX6F.js} +8 -3
  235. package/dist/{chunk-3WICOC33.js → chunk-SNDDZG5H.js} +124 -84
  236. package/dist/{chunk-CZXGCVTR.cjs → chunk-SOVR7S3T.cjs} +2 -2
  237. package/dist/chunk-SPQ7QIQ6.js +78 -0
  238. package/dist/{chunk-XRZEUWKF.js → chunk-SRMGWMQO.js} +1 -1
  239. package/dist/{chunk-R33VKSH5.cjs → chunk-SVVP4UUZ.cjs} +11 -11
  240. package/dist/{chunk-7BTSG4ME.cjs → chunk-SY2P3Z5W.cjs} +1004 -39
  241. package/dist/chunk-SZD45IDG.js +468 -0
  242. package/dist/{chunk-3O3MOK5C.cjs → chunk-TE4SYTWR.cjs} +1114 -142
  243. package/dist/chunk-TH26BQJG.js +101 -0
  244. package/dist/{chunk-QNGEW5TC.js → chunk-TR6NECEZ.js} +1 -1
  245. package/dist/{chunk-YGN4CQIP.js → chunk-TYBPTKFT.js} +1 -1
  246. package/dist/{chunk-GJH7XMSK.js → chunk-U2QUMKCB.js} +8 -6
  247. package/dist/{chunk-47CKWKEX.cjs → chunk-U6PLSPMD.cjs} +9 -4
  248. package/dist/chunk-UOQECODR.js +34 -0
  249. package/dist/chunk-UR27UDTB.js +354 -0
  250. package/dist/chunk-UWZ4G3SQ.js +93 -0
  251. package/dist/{chunk-Y72HH2TF.cjs → chunk-V3SZLWEQ.cjs} +33 -24
  252. package/dist/chunk-V4HGZSKQ.cjs +183 -0
  253. package/dist/{chunk-3PDTTAKJ.js → chunk-V5ELP2XE.js} +19 -12
  254. package/dist/{chunk-3K2ESU53.cjs → chunk-VKOXFT4L.cjs} +2 -2
  255. package/dist/{chunk-XTB6VJVQ.cjs → chunk-VLGGMQUN.cjs} +6 -6
  256. package/dist/chunk-VQHSYAPZ.cjs +3897 -0
  257. package/dist/chunk-VUGOOGHB.js +400 -0
  258. package/dist/chunk-W5QLA6WP.cjs +866 -0
  259. package/dist/chunk-WBU4Q4GS.cjs +400 -0
  260. package/dist/{chunk-KWXVKLQ5.cjs → chunk-WGNMOVMT.cjs} +7 -82
  261. package/dist/chunk-WJYFLQ7G.cjs +27 -0
  262. package/dist/{chunk-RKJTGGMU.cjs → chunk-WKRDBCP2.cjs} +221 -16
  263. package/dist/{chunk-ZSPXQYG2.js → chunk-WNJXIACY.js} +7 -5
  264. package/dist/{chunk-AYS2ASM7.js → chunk-WPE2XHVX.js} +1 -1
  265. package/dist/chunk-WTB7AFL6.cjs +101 -0
  266. package/dist/{chunk-JYTDYJVW.js → chunk-X3TI5TJJ.js} +1 -1
  267. package/dist/chunk-X5P6QQOB.cjs +719 -0
  268. package/dist/{chunk-245KJE5Y.cjs → chunk-X7XMITIL.cjs} +14 -6
  269. package/dist/chunk-XF2WIKHR.cjs +34 -0
  270. package/dist/{chunk-DV2ZHK7B.cjs → chunk-XNIMSVS6.cjs} +49 -26
  271. package/dist/chunk-XW3XCK4E.cjs +59 -0
  272. package/dist/{chunk-N23UAW4I.js → chunk-XZVDYC5U.js} +7 -2
  273. package/dist/chunk-Y5BDE24P.cjs +444 -0
  274. package/dist/{chunk-NAGQ2PDC.js → chunk-YC5OBZQU.js} +1604 -433
  275. package/dist/chunk-YISPVAXO.cjs +2340 -0
  276. package/dist/{chunk-KQGPTCQJ.js → chunk-YQDI753V.js} +68 -25
  277. package/dist/{chunk-UJ2JSM6H.js → chunk-YVQI26H4.js} +2 -0
  278. package/dist/{chunk-NNPAM4HC.cjs → chunk-YWH55BWK.cjs} +15 -6
  279. package/dist/{chunk-X2YOZQIP.cjs → chunk-YWTIXHU6.cjs} +266 -19
  280. package/dist/{chunk-XX2ZO7DS.js → chunk-YXM6SA7P.js} +25 -16
  281. package/dist/chunk-YZWE7XSM.js +5 -0
  282. package/dist/chunk-ZASDSY7P.cjs +114 -0
  283. package/dist/{chunk-2UC22DJU.js → chunk-ZNGRLCFQ.js} +30 -4
  284. package/dist/{chunk-KANW6OYC.cjs → chunk-ZNYZB7XY.cjs} +34 -8
  285. package/dist/{chunk-NA6WQDYW.js → chunk-ZPLCAXJW.js} +1088 -116
  286. package/dist/{chunk-7UOUW76C.js → chunk-ZQAT5VT5.js} +101 -24
  287. package/dist/chunk-ZUYYHKQA.js +44 -0
  288. package/dist/clear-QJXU25IF.cjs +12 -0
  289. package/dist/{clear-A3N4GK2S.js → clear-Y5TO3RZA.js} +3 -3
  290. package/dist/communityInstaller-LXMVKVAV.cjs +22 -0
  291. package/dist/{communityInstaller-LOP2EDH5.js → communityInstaller-RVL4STPS.js} +8 -5
  292. package/dist/completion-JX4T2ONW.cjs +17 -0
  293. package/dist/completion-SE3XYG74.js +17 -0
  294. package/dist/config-3224PRW4.cjs +21 -0
  295. package/dist/{config-HPJPKTO6.js → config-7KPHKFTP.js} +7 -4
  296. package/dist/{constants-LISJW3DD.js → constants-EJFAWJQI.js} +1 -1
  297. package/dist/constants-MCCGUU5F.cjs +21 -0
  298. package/dist/{defaultHooks-IHSJR2AX.cjs → defaultHooks-2V2CQL63.cjs} +25 -10
  299. package/dist/{defaultHooks-KUKHK3AG.js → defaultHooks-TMHDU3FS.js} +25 -10
  300. package/dist/export-BJVIDZC4.js +15 -0
  301. package/dist/export-KU4557HK.cjs +15 -0
  302. package/dist/feature-K3LG5IJP.cjs +14 -0
  303. package/dist/feature-V4JF5TNJ.js +14 -0
  304. package/dist/features-5OJTLKS7.js +23 -0
  305. package/dist/features-VCBJQXCX.cjs +23 -0
  306. package/dist/feedback-D4437VE4.js +18 -0
  307. package/dist/feedback-VZPEHGFY.cjs +18 -0
  308. package/dist/fffSearchProvider-2YCNKOYD.js +412 -0
  309. package/dist/fffSearchProvider-W6627E2V.cjs +412 -0
  310. package/dist/{filesystem-Z7BWAWMZ.js → filesystem-L6DQKGWK.js} +3 -2
  311. package/dist/filesystem-PGUPCMVK.cjs +11 -0
  312. package/dist/go-X4E6BCD6.js +12 -0
  313. package/dist/go-XREVFS5I.cjs +12 -0
  314. package/dist/goal-4M3J6JS2.cjs +16 -0
  315. package/dist/goal-EI66BV7W.js +16 -0
  316. package/dist/help-GGFS7WHY.cjs +12 -0
  317. package/dist/{help-GFQXNZOK.js → help-MLIROWKM.js} +2 -2
  318. package/dist/{history-E3N6BJP7.js → history-GQJ6RZD6.js} +2 -2
  319. package/dist/history-J4LUIOSI.cjs +14 -0
  320. package/dist/hooks-FSRIUS6A.cjs +18 -0
  321. package/dist/hooks-PPFHCF7T.js +18 -0
  322. package/dist/i18n-CI6VFXL5.cjs +33 -0
  323. package/dist/{i18n-SY7QRM22.js → i18n-Q7UZJRPL.js} +1 -1
  324. package/dist/ide-BUSVE54P.js +15 -0
  325. package/dist/ide-YR27BPGM.cjs +15 -0
  326. package/dist/immediateCommandRouter-MTEHZXQX.js +15 -0
  327. package/dist/immediateCommandRouter-ROXU3MWT.cjs +15 -0
  328. package/dist/{import-W7SVLSTC.js → import-3VBKI6BN.js} +3 -3
  329. package/dist/{import-ADI37ZUR.cjs → import-C7S6UJMW.cjs} +3 -3
  330. package/dist/import-DBK4OCDF.cjs +10 -0
  331. package/dist/{import-KGKKZ3B7.js → import-U47DXCA7.js} +2 -2
  332. package/dist/index.cjs +837 -23011
  333. package/dist/index.d.cts +95 -0
  334. package/dist/index.d.ts +95 -0
  335. package/dist/index.js +864 -23038
  336. package/dist/init-OXTYS72D.cjs +10 -0
  337. package/dist/{init-7MFK626E.js → init-Q2O4R42R.js} +2 -2
  338. package/dist/inkMode-VUE6ZDLD.cjs +7 -0
  339. package/dist/inkMode-WBNFOSAT.js +7 -0
  340. package/dist/inputPrompt-3CFZDUBH.js +90 -0
  341. package/dist/inputPrompt-JMACL4EB.cjs +90 -0
  342. package/dist/language-EK3M6UBV.cjs +22 -0
  343. package/dist/language-Q3RUGGOW.js +22 -0
  344. package/dist/learn-3DSX7DYG.cjs +23 -0
  345. package/dist/learn-OPOH5L43.js +23 -0
  346. package/dist/login-GST7MWXJ.cjs +27 -0
  347. package/dist/login-Q7DZKAX4.js +27 -0
  348. package/dist/logout-2G7OUK7I.cjs +24 -0
  349. package/dist/logout-FNBBL2UI.js +24 -0
  350. package/dist/mcp-AH3MMUBU.js +21 -0
  351. package/dist/mcp-DW7R63IB.cjs +21 -0
  352. package/dist/{mcp-install-2FEROZTL.js → mcp-install-222PCKSW.js} +18 -10
  353. package/dist/{mcp-install-WM6BQRI5.cjs → mcp-install-IA4ZS2SV.cjs} +22 -14
  354. package/dist/memory-6NX3DAIY.cjs +10 -0
  355. package/dist/{memory-J73WZH2I.js → memory-VB46T5H3.js} +2 -2
  356. package/dist/model-64NAELFS.cjs +10 -0
  357. package/dist/{model-AES267IN.js → model-SJJG64AM.js} +2 -2
  358. package/dist/new-DU5SOOOY.cjs +12 -0
  359. package/dist/{new-5CLF3MKH.js → new-ERZ5C6CN.js} +3 -3
  360. package/dist/onboarding-2U2BV2KE.cjs +36 -0
  361. package/dist/onboarding-ZQXMPSMJ.js +36 -0
  362. package/dist/permissions-7ACNFK7A.js +10 -0
  363. package/dist/permissions-GSNNV7RJ.cjs +10 -0
  364. package/dist/plan-XEJMOT55.cjs +13 -0
  365. package/dist/{plan-65HMS5HQ.js → plan-YYUAXPTL.js} +3 -1
  366. package/dist/pr-review-CW6J7P62.cjs +9 -0
  367. package/dist/pr-review-YZSBQVT2.js +9 -0
  368. package/dist/{quit-XDZYRSPU.js → quit-WY6T267G.js} +2 -2
  369. package/dist/quit-XIRE2KRE.cjs +10 -0
  370. package/dist/rawMode-6W5AXAKI.cjs +7 -0
  371. package/dist/rawMode-GFNLXQPU.js +7 -0
  372. package/dist/{registry-PTHWERKC.js → registry-4DXUDKJN.js} +29 -44
  373. package/dist/{registry-IVT4G2RT.cjs → registry-UEO3ETZ7.cjs} +65 -80
  374. package/dist/repeat-P4FAPE3Y.cjs +17 -0
  375. package/dist/{repeat-EVCWUL6Z.js → repeat-RALE6AUO.js} +7 -3
  376. package/dist/resume-RQZ3WXBP.cjs +17 -0
  377. package/dist/resume-XSXZMDMD.js +17 -0
  378. package/dist/review-QHP2KP4Q.js +9 -0
  379. package/dist/review-UWHWQHCB.cjs +9 -0
  380. package/dist/ripgrep-67SCU2BA.cjs +9 -0
  381. package/dist/ripgrep-VHJQQ55W.js +9 -0
  382. package/dist/rpc-XH7QLN4H.js +3874 -0
  383. package/dist/rpc-YF54T7JU.cjs +3874 -0
  384. package/dist/search-VXXFGZWU.cjs +21 -0
  385. package/dist/search-YAWGX7P7.js +21 -0
  386. package/dist/{sessions-6GWEBMKS.js → sessions-NJYHJOEL.js} +2 -2
  387. package/dist/sessions-OSG3UJEZ.cjs +10 -0
  388. package/dist/settings-SO2UIGWV.cjs +42 -0
  389. package/dist/settings-ZXUQH3DO.js +42 -0
  390. package/dist/setup-KG7EGKF5.js +30 -0
  391. package/dist/setup-RSOPCQ57.cjs +30 -0
  392. package/dist/share-WFAGZ5PY.js +17 -0
  393. package/dist/share-ZU3SGACF.cjs +17 -0
  394. package/dist/skills-KBVAQAD2.cjs +29 -0
  395. package/dist/skills-NBTNDVAY.js +29 -0
  396. package/dist/{skills-ZZCIAS7C.js → skills-OB6RDW7D.js} +10 -7
  397. package/dist/{skills-PG542VEB.cjs → skills-ZROBG3RZ.cjs} +13 -10
  398. package/dist/{skills-install-SRC3Z2MS.js → skills-install-BHTIEMKH.js} +21 -70
  399. package/dist/{skills-install-67DOBPJC.cjs → skills-install-ILX6QVEF.cjs} +34 -83
  400. package/dist/skills-new-B45VQ2PP.cjs +18 -0
  401. package/dist/skills-new-YMRP2HNO.js +18 -0
  402. package/dist/slashCommands-53VYIBJU.js +105 -0
  403. package/dist/slashCommands-BG2RGGZ6.cjs +105 -0
  404. package/dist/status-CQ2IUOVK.cjs +24 -0
  405. package/dist/status-E7IGNVPC.js +24 -0
  406. package/dist/summarizer-DGPHE5IQ.js +17 -0
  407. package/dist/summarizer-JNXLUAQG.cjs +17 -0
  408. package/dist/sync-7C25MOT2.js +22 -0
  409. package/dist/{sync-VU2NSJ4O.js → sync-OCJN4ZSO.js} +3 -3
  410. package/dist/sync-XRP46IVG.cjs +40 -0
  411. package/dist/sync-ZMFVE7T4.cjs +22 -0
  412. package/dist/{teammate-SXRVXNQV.cjs → teammate-D77B6QRT.cjs} +31 -9
  413. package/dist/{teammate-SD26GR37.js → teammate-EZCMHOIL.js} +30 -8
  414. package/dist/templates-ARG2VRWW.cjs +11 -0
  415. package/dist/templates-UGVZV3KJ.js +11 -0
  416. package/dist/theme-KKRDE6P7.cjs +22 -0
  417. package/dist/theme-XF7XIWBQ.js +22 -0
  418. package/dist/tools-3PPTTKFV.js +9 -0
  419. package/dist/tools-THIQA7WC.cjs +9 -0
  420. package/dist/ui/questionModal.cjs +8 -5
  421. package/dist/ui/questionModal.js +7 -4
  422. package/dist/{undo-OL2EDBRY.js → undo-GNUTFXCW.js} +2 -2
  423. package/dist/undo-U4KN7QQM.cjs +10 -0
  424. package/dist/usage-QSTNSDAO.js +24 -0
  425. package/dist/usage-YDEMQBNQ.cjs +24 -0
  426. package/dist/web-3BA2WV37.cjs +37 -0
  427. package/dist/web-6FYGBX5K.js +37 -0
  428. package/dist/workspaceSafety-MDJGHK6D.cjs +9 -0
  429. package/dist/workspaceSafety-XOUMUBVB.js +9 -0
  430. package/dist/yolo-GF2YD7ZI.js +9 -0
  431. package/dist/yolo-OGDA7HNC.cjs +9 -0
  432. package/dist/yoloMode-3DJDA75U.cjs +17 -0
  433. package/dist/yoloMode-4JOOSU26.js +17 -0
  434. package/package.json +51 -49
  435. package/dist/AgentRegistry-HRPN6ZOF.cjs +0 -10
  436. package/dist/CommunitySkillsCache-KE435RAR.cjs +0 -8
  437. package/dist/GitHubRegistryFetcher-I45SESIL.cjs +0 -7
  438. package/dist/LearnAdvisor-FLBA6FDD.js +0 -9
  439. package/dist/LearnAdvisor-GG3CXQF3.cjs +0 -9
  440. package/dist/MemoryManager-2LQPIYVE.cjs +0 -8
  441. package/dist/PermissionManager-X57BXHJ6.cjs +0 -11
  442. package/dist/ProviderFactory-KPJOGQWF.cjs +0 -9
  443. package/dist/SessionManager-YBJAZXNO.cjs +0 -10
  444. package/dist/SkillsRegistry-7O72A6TZ.cjs +0 -9
  445. package/dist/SubAgent-JT4HZHN7.js +0 -11
  446. package/dist/SubAgent-VPNYDWAU.cjs +0 -11
  447. package/dist/SyncApiClient-HQXJL5BT.cjs +0 -11
  448. package/dist/about-4DB5KTHW.js +0 -12
  449. package/dist/about-LXAOXZFT.cjs +0 -12
  450. package/dist/actionExecutor-23JB2WUC.js +0 -19
  451. package/dist/actionExecutor-X5UEZSKH.cjs +0 -19
  452. package/dist/add-dir-YC37DMSF.cjs +0 -10
  453. package/dist/agents-YF3BSUU5.js +0 -12
  454. package/dist/agents-ZOUHPMYR.cjs +0 -12
  455. package/dist/agents-new-HSH4GQPG.js +0 -14
  456. package/dist/agents-new-VYUDOCE7.cjs +0 -14
  457. package/dist/autoSkill-X5W52WOE.cjs +0 -20
  458. package/dist/automode-SJGM7VEI.cjs +0 -10
  459. package/dist/chunk-ALYU6VTM.js +0 -105
  460. package/dist/chunk-B53A2NM2.js +0 -2030
  461. package/dist/chunk-BJXSNT46.js +0 -100
  462. package/dist/chunk-CB4E2T5N.cjs +0 -312
  463. package/dist/chunk-DNUOXBHL.js +0 -539
  464. package/dist/chunk-EFX2QSZX.cjs +0 -33
  465. package/dist/chunk-GCXYXLRA.cjs +0 -111
  466. package/dist/chunk-H2ZRHQQV.js +0 -33
  467. package/dist/chunk-HNRPK5MY.cjs +0 -85
  468. package/dist/chunk-HVKOZ2VP.cjs +0 -115
  469. package/dist/chunk-JJLYWH5Y.cjs +0 -100
  470. package/dist/chunk-LWUJFGOZ.js +0 -115
  471. package/dist/chunk-MERYP6AM.cjs +0 -539
  472. package/dist/chunk-MZAPWNAC.cjs +0 -207
  473. package/dist/chunk-NTSDP2WB.js +0 -226
  474. package/dist/chunk-OUZQXMHL.cjs +0 -226
  475. package/dist/chunk-PGESAU2W.cjs +0 -2030
  476. package/dist/chunk-SYVYLZZF.cjs +0 -24
  477. package/dist/chunk-SZOLA6FR.js +0 -111
  478. package/dist/chunk-VWDHR4HV.js +0 -168
  479. package/dist/chunk-Y45G6ZO5.cjs +0 -168
  480. package/dist/chunk-YRLYSQEQ.cjs +0 -105
  481. package/dist/chunk-ZYVS43MU.js +0 -312
  482. package/dist/clear-GK4IEUUS.cjs +0 -12
  483. package/dist/communityInstaller-XXC7RLE4.cjs +0 -19
  484. package/dist/completion-HWABSAEL.js +0 -14
  485. package/dist/completion-IUUVQG4D.cjs +0 -14
  486. package/dist/config-HF7WOLZF.cjs +0 -18
  487. package/dist/constants-PEO3P2SJ.cjs +0 -21
  488. package/dist/export-QJAV2FCZ.js +0 -12
  489. package/dist/export-XSRFXGWU.cjs +0 -12
  490. package/dist/feedback-4TCIL3ML.cjs +0 -15
  491. package/dist/feedback-SJ6VVEY3.js +0 -15
  492. package/dist/filesystem-W56QZUJF.cjs +0 -10
  493. package/dist/help-ISBVQL3S.cjs +0 -12
  494. package/dist/history-XQ4GTSFU.cjs +0 -14
  495. package/dist/hooks-CJNKJ5PF.js +0 -13
  496. package/dist/hooks-UTMBTAXT.cjs +0 -13
  497. package/dist/i18n-N7QQ7A5M.cjs +0 -33
  498. package/dist/ide-RTA4UJV4.js +0 -12
  499. package/dist/ide-VWVOLIFF.cjs +0 -12
  500. package/dist/immediateCommandRouter-BW34JNXL.js +0 -9
  501. package/dist/immediateCommandRouter-SHOVNB5X.cjs +0 -9
  502. package/dist/import-ZLJVONXH.cjs +0 -10
  503. package/dist/init-TBKAB4LZ.cjs +0 -10
  504. package/dist/language-MDSHEXHB.cjs +0 -18
  505. package/dist/language-PXTQSHIG.js +0 -18
  506. package/dist/learn-623TW5EK.cjs +0 -20
  507. package/dist/learn-BCPV7GM2.js +0 -20
  508. package/dist/localProjectPermissions-BHQXEWZJ.cjs +0 -18
  509. package/dist/localProjectPermissions-GMOUYQM6.js +0 -18
  510. package/dist/login-QMVEETWJ.js +0 -20
  511. package/dist/login-QYMXAL3K.cjs +0 -20
  512. package/dist/logout-2CMTDAOJ.js +0 -18
  513. package/dist/logout-ZOHVZAUK.cjs +0 -18
  514. package/dist/mcp-IUVKK65S.js +0 -18
  515. package/dist/mcp-TXC7PYG3.cjs +0 -18
  516. package/dist/memory-WRIHDEPK.cjs +0 -10
  517. package/dist/model-RLP75SF5.cjs +0 -10
  518. package/dist/new-HLSFL6A4.cjs +0 -12
  519. package/dist/permissions-GP6FTGZ2.js +0 -13
  520. package/dist/permissions-O6EKKPOG.cjs +0 -13
  521. package/dist/plan-MCAXDIM2.cjs +0 -11
  522. package/dist/quit-TQX6GXA5.cjs +0 -10
  523. package/dist/repeat-BSPS5TWD.cjs +0 -13
  524. package/dist/resume-2GOPDLL4.cjs +0 -13
  525. package/dist/resume-37IUVDA6.js +0 -13
  526. package/dist/search-7KUSHIBL.cjs +0 -17
  527. package/dist/search-OJGDRIMA.js +0 -17
  528. package/dist/sessions-CYYCHSQG.cjs +0 -10
  529. package/dist/settings-2D7CAO66.cjs +0 -30
  530. package/dist/settings-BXR6SBJP.js +0 -30
  531. package/dist/share-BXQY5IQU.js +0 -14
  532. package/dist/share-OSFXZBGS.cjs +0 -14
  533. package/dist/skills-FL6O6AOM.cjs +0 -26
  534. package/dist/skills-PNKQZRNK.js +0 -26
  535. package/dist/skills-new-XFMEHHIF.cjs +0 -15
  536. package/dist/skills-new-ZNZPHUKS.js +0 -15
  537. package/dist/slashCommands-LLCNPK3X.js +0 -76
  538. package/dist/slashCommands-RXZZS6RE.cjs +0 -76
  539. package/dist/status-BCECUJXT.cjs +0 -11
  540. package/dist/status-EFO7MQU3.js +0 -11
  541. package/dist/sync-IJYJ6KKM.js +0 -18
  542. package/dist/sync-LFT6SBPF.cjs +0 -18
  543. package/dist/sync-U7SDPBNZ.cjs +0 -40
  544. package/dist/theme-AWBHSG7J.cjs +0 -18
  545. package/dist/theme-TQLBPJ2E.js +0 -18
  546. package/dist/undo-IBBGP7A2.cjs +0 -10
@@ -0,0 +1,3897 @@
1
+ import {
2
+ executeShellCommandAsync,
3
+ getPrimaryShellCommandSuggestion,
4
+ getShellCommandSuggestions,
5
+ isShellCommand,
6
+ parseShellCommand
7
+ } from "./chunk-VUGOOGHB.js";
8
+ import {
9
+ formatPlanModeToggleMessage,
10
+ getPlanModeManager
11
+ } from "./chunk-LUMV3DB2.js";
12
+ import {
13
+ safeSetRawMode
14
+ } from "./chunk-BQU3HAE7.js";
15
+ import {
16
+ disableBracketedPaste,
17
+ enableBracketedPaste,
18
+ getContentDisplay,
19
+ stripAnsiCodes
20
+ } from "./chunk-CSTUTDTH.js";
21
+ import {
22
+ getTheme,
23
+ hexToRgb,
24
+ isThemeInitialized,
25
+ themedFg
26
+ } from "./chunk-JAQO6XDB.js";
27
+ import {
28
+ __require
29
+ } from "./chunk-QGM4M3NI.js";
30
+
31
+ // src/ui/inputPrompt.ts
32
+ import chalk2 from "chalk";
33
+ import readline2 from "readline";
34
+ import { EventEmitter } from "events";
35
+ import { existsSync, readFileSync } from "fs";
36
+ import { basename, extname } from "path";
37
+ import os from "os";
38
+
39
+ // src/ui/terminalResize.ts
40
+ var TerminalResizeWatcher = class {
41
+ stream;
42
+ handler;
43
+ disposed = false;
44
+ debounceTimer;
45
+ constructor(stream, callback, debounceMs = 80) {
46
+ this.stream = stream;
47
+ this.handler = () => {
48
+ if (this.disposed) {
49
+ return;
50
+ }
51
+ if (this.debounceTimer) {
52
+ clearTimeout(this.debounceTimer);
53
+ }
54
+ this.debounceTimer = setTimeout(() => {
55
+ this.debounceTimer = void 0;
56
+ if (!this.disposed) {
57
+ callback();
58
+ }
59
+ }, debounceMs);
60
+ };
61
+ if (this.stream && typeof this.stream.on === "function") {
62
+ this.stream.on("resize", this.handler);
63
+ }
64
+ }
65
+ dispose() {
66
+ if (this.disposed) {
67
+ return;
68
+ }
69
+ this.disposed = true;
70
+ if (this.debounceTimer) {
71
+ clearTimeout(this.debounceTimer);
72
+ this.debounceTimer = void 0;
73
+ }
74
+ if (this.stream && typeof this.stream.off === "function") {
75
+ this.stream.off("resize", this.handler);
76
+ }
77
+ }
78
+ };
79
+
80
+ // src/ui/mentionPreview.ts
81
+ import chalk from "chalk";
82
+ import readline from "readline";
83
+
84
+ // src/ui/mentionFilter.ts
85
+ var MENTION_SUGGESTION_LIMIT = 8;
86
+ function buildFileMentionSuggestions(files, seed, limit = MENTION_SUGGESTION_LIMIT) {
87
+ const trimmedSeed = seed.trim();
88
+ if (!trimmedSeed) {
89
+ return files.slice(0, limit);
90
+ }
91
+ const normalizedSeed = trimmedSeed.toLowerCase().replace(/\\/g, "/");
92
+ const wantsPathPrefix = normalizedSeed.includes("/");
93
+ const ranked = [];
94
+ files.forEach((file, index) => {
95
+ const normalizedPath = file.toLowerCase().replace(/\\/g, "/");
96
+ const filenameLower = normalizedPath.split("/").pop() ?? normalizedPath;
97
+ const pathStartsWith = normalizedPath.startsWith(normalizedSeed);
98
+ const pathContains = normalizedPath.includes(normalizedSeed);
99
+ const filenameContains = filenameLower.includes(normalizedSeed);
100
+ const exactFilename = filenameLower === normalizedSeed;
101
+ if (wantsPathPrefix && !pathContains) {
102
+ return;
103
+ }
104
+ if (!pathContains && !filenameContains) {
105
+ return;
106
+ }
107
+ let rank;
108
+ if (exactFilename) {
109
+ rank = 0;
110
+ } else if (filenameContains) {
111
+ rank = 1;
112
+ } else if (pathStartsWith) {
113
+ rank = 2;
114
+ } else {
115
+ rank = 3;
116
+ }
117
+ ranked.push({ file, rank, index });
118
+ });
119
+ return ranked.sort((a, b) => a.rank - b.rank || a.index - b.index).slice(0, limit).map((entry) => entry.file);
120
+ }
121
+ function buildSkillMentionSuggestions(skills, seed, limit = MENTION_SUGGESTION_LIMIT) {
122
+ const trimmedSeed = seed.trim();
123
+ if (!trimmedSeed) {
124
+ const sorted = [...skills].sort((a, b) => {
125
+ if (a.isActive !== b.isActive) return a.isActive ? -1 : 1;
126
+ return a.name.localeCompare(b.name);
127
+ });
128
+ return sorted.slice(0, limit).map((skill) => skill.name);
129
+ }
130
+ const normalizedSeed = trimmedSeed.toLowerCase();
131
+ const ranked = [];
132
+ skills.forEach((skill, index) => {
133
+ const nameLower = skill.name.toLowerCase();
134
+ const descLower = skill.description.toLowerCase();
135
+ const nameStartsWith = nameLower.startsWith(normalizedSeed);
136
+ const nameContains = nameLower.includes(normalizedSeed);
137
+ const descContains = descLower.includes(normalizedSeed);
138
+ if (!nameContains && !descContains) {
139
+ return;
140
+ }
141
+ let rank;
142
+ if (nameStartsWith) {
143
+ rank = 0;
144
+ } else if (nameContains) {
145
+ rank = 1;
146
+ } else {
147
+ rank = 2;
148
+ }
149
+ if (skill.isActive) {
150
+ rank -= 0.5;
151
+ }
152
+ ranked.push({ name: skill.name, rank, index });
153
+ });
154
+ return ranked.sort((a, b) => a.rank - b.rank || a.index - b.index).slice(0, limit).map((entry) => entry.name);
155
+ }
156
+
157
+ // src/ui/mentionPreview.ts
158
+ function padVisibleRight(text, width) {
159
+ if (width <= 0) {
160
+ return "";
161
+ }
162
+ const visibleLength = text.replace(/\u001b\[[0-9;]*m/g, "").length;
163
+ if (visibleLength >= width) {
164
+ return text;
165
+ }
166
+ return `${text}${" ".repeat(width - visibleLength)}`;
167
+ }
168
+ function truncateVisible(text, width) {
169
+ if (width <= 0) {
170
+ return "";
171
+ }
172
+ const plain = text.replace(/\u001b\[[0-9;]*m/g, "");
173
+ if (plain.length <= width) {
174
+ return text;
175
+ }
176
+ if (width === 1) {
177
+ return "\u2026";
178
+ }
179
+ return `${plain.slice(0, width - 1)}\u2026`;
180
+ }
181
+ function getFilenameColumnWidth(entries, width) {
182
+ const longestFilename = entries.reduce((max, entry) => {
183
+ const normalized = entry.replace(/\\/g, "/");
184
+ const filename = normalized.split("/").pop() || normalized;
185
+ return Math.max(max, filename.length);
186
+ }, 0);
187
+ const availableWidth = Math.max(12, width - 2);
188
+ return Math.max(12, Math.min(longestFilename, Math.floor(availableWidth * 0.32), 24));
189
+ }
190
+ function formatFileSuggestionLine(entry, isSelected, width, filenameColumnWidth) {
191
+ const normalized = entry.replace(/\\/g, "/");
192
+ const parts = normalized.split("/");
193
+ const filename = parts.pop() || normalized;
194
+ const dir = parts.join("/");
195
+ const pointer = isSelected ? chalk.cyan("\u25B8") : " ";
196
+ const basePrefix = `${pointer} `;
197
+ const gap = " ";
198
+ const availableWidth = Math.max(12, width - basePrefix.length);
199
+ const filenameWidth = Math.min(filenameColumnWidth, Math.max(1, availableWidth - gap.length));
200
+ const pathWidth = Math.max(0, availableWidth - gap.length - filenameWidth);
201
+ const visibleFilename = truncateVisible(filename, filenameWidth);
202
+ const visiblePath = truncateVisible(dir, pathWidth);
203
+ const styledFilename = isSelected ? chalk.cyan(visibleFilename) : chalk.white(visibleFilename);
204
+ const styledPath = visiblePath ? chalk.gray(visiblePath) : "";
205
+ return `${basePrefix}${padVisibleRight(styledFilename, filenameWidth)}${styledPath ? `${gap}${styledPath}` : ""}`;
206
+ }
207
+ function formatSkillSuggestionLine(skill, isSelected, width) {
208
+ const name = `$${skill.name}`;
209
+ const description = skill.description;
210
+ const pointer = isSelected ? chalk.cyan("\u25B8") : " ";
211
+ const basePrefix = `${pointer} `;
212
+ const gap = " ";
213
+ const availableWidth = Math.max(12, width - basePrefix.length);
214
+ const nameWidth = Math.max(12, Math.min(Math.floor(availableWidth * 0.32), 30));
215
+ const descWidth = Math.max(0, availableWidth - gap.length - nameWidth);
216
+ const visibleName = truncateVisible(name, nameWidth);
217
+ const visibleDesc = description ? truncateVisible(description, descWidth) : "";
218
+ const styledName = isSelected ? chalk.cyan(visibleName) : chalk.white(visibleName);
219
+ const styledDesc = visibleDesc ? chalk.gray(visibleDesc) : "";
220
+ return `${basePrefix}${padVisibleRight(styledName, nameWidth)}${styledDesc ? `${gap}${styledDesc}` : ""}`;
221
+ }
222
+ var MentionPreview = class {
223
+ constructor(rl, filesProvider, slashCommands, output, skillsProvider, onFileSuggestionAccepted) {
224
+ this.rl = rl;
225
+ this.filesProvider = filesProvider;
226
+ this.slashCommands = slashCommands;
227
+ this.output = output;
228
+ this.onFileSuggestionAccepted = onFileSuggestionAccepted;
229
+ const input = rl.input;
230
+ safeEmitKeypressEvents(input);
231
+ this.keypressHandler = this.handleKeypress.bind(this);
232
+ this.skillsProvider = skillsProvider;
233
+ input.prependListener("keypress", this.keypressHandler);
234
+ }
235
+ rl;
236
+ filesProvider;
237
+ slashCommands;
238
+ output;
239
+ onFileSuggestionAccepted;
240
+ suggestionLines = 0;
241
+ keypressHandler = null;
242
+ slashMatches = [];
243
+ skillMatches = [];
244
+ fileSuggestions = [];
245
+ mode = null;
246
+ activeIndex = 0;
247
+ disposed = false;
248
+ suspended = false;
249
+ lastSuggestions = [];
250
+ tabJustHandled = false;
251
+ completionJustHandled = false;
252
+ skillsProvider;
253
+ // Dynamic offset from cursor to suggestion area, accounting for multi-line content
254
+ get suggestionOffset() {
255
+ const contentLines = getLastRenderedContentLines();
256
+ const cursorRow = getLastRenderedCursorRow();
257
+ const contentLinesBelow = contentLines - 1 - cursorRow;
258
+ return contentLinesBelow + PROMPT_LINES_BELOW_INPUT + STATUS_LINE_COUNT + 1;
259
+ }
260
+ dispose() {
261
+ const input = this.rl.input;
262
+ if (this.keypressHandler) {
263
+ input.off("keypress", this.keypressHandler);
264
+ }
265
+ this.disposed = true;
266
+ this.clear();
267
+ }
268
+ reset() {
269
+ this.clear();
270
+ this.tabJustHandled = false;
271
+ }
272
+ handleResize() {
273
+ if (this.disposed || this.suspended || !this.suggestionLines) {
274
+ return;
275
+ }
276
+ this.clear(false);
277
+ this.render(this.lastSuggestions);
278
+ }
279
+ setSuspended(suspended) {
280
+ if (this.suspended === suspended) {
281
+ return;
282
+ }
283
+ this.suspended = suspended;
284
+ if (suspended) {
285
+ this.clear();
286
+ }
287
+ }
288
+ handleKeypress(_str, key) {
289
+ if (this.disposed || this.suspended) {
290
+ return;
291
+ }
292
+ const isAcceptKey = this.isTabKey(_str, key) || key?.name === "right" || key?.name === "return" || key?.name === "enter";
293
+ const beforeCursor = this.rl.line.slice(0, this.rl.cursor);
294
+ if ((key?.name === "return" || key?.name === "enter") && this.slashCommands.some((command) => command.command === beforeCursor.trim())) {
295
+ return;
296
+ }
297
+ if (isAcceptKey || key?.name === "down" || key?.name === "up") {
298
+ this.updateSuggestions();
299
+ }
300
+ if (key?.name === "escape") {
301
+ this.reset();
302
+ return;
303
+ }
304
+ if (isAcceptKey && (key?.name !== "right" || this.rl.cursor === this.rl.line.length)) {
305
+ if (this.mode === "file" && this.fileSuggestions.length) {
306
+ this.tabJustHandled = true;
307
+ this.completionJustHandled = true;
308
+ this.insertFileSuggestion(beforeCursor, this.fileSuggestions[this.activeIndex]);
309
+ return;
310
+ }
311
+ if (this.mode === "slash" && this.slashMatches.length) {
312
+ const selected = this.slashMatches[this.activeIndex];
313
+ if ((key?.name === "return" || key?.name === "enter") && selected && beforeCursor.trim() === selected.command) {
314
+ return;
315
+ }
316
+ this.tabJustHandled = true;
317
+ this.completionJustHandled = true;
318
+ this.insertSlashSuggestion(beforeCursor, selected ?? this.slashMatches[0]);
319
+ return;
320
+ }
321
+ if (this.mode === "skill" && this.skillMatches.length) {
322
+ this.tabJustHandled = true;
323
+ this.completionJustHandled = true;
324
+ this.insertSkillSuggestion(beforeCursor, this.skillMatches[this.activeIndex]);
325
+ return;
326
+ }
327
+ const mentionMatch = this.isTabKey(_str, key) ? this.matchMention(beforeCursor) : null;
328
+ if (mentionMatch) {
329
+ const seed = mentionMatch[1] ?? "";
330
+ const suggestions = this.filter(seed);
331
+ if (suggestions.length) {
332
+ this.mode = "file";
333
+ this.fileSuggestions = suggestions;
334
+ this.activeIndex = this.getPreservedSelectionIndex(
335
+ this.lastSuggestions,
336
+ suggestions,
337
+ this.activeIndex
338
+ );
339
+ this.tabJustHandled = true;
340
+ this.completionJustHandled = true;
341
+ this.insertFileSuggestion(beforeCursor, suggestions[this.activeIndex] ?? suggestions[0]);
342
+ }
343
+ }
344
+ return;
345
+ }
346
+ if ((key?.name === "down" || key?.name === "up") && this.mode && this.lastSuggestions.length) {
347
+ const delta = key.name === "down" ? 1 : -1;
348
+ const length = this.lastSuggestions.length;
349
+ this.activeIndex = (this.activeIndex + delta + length) % length;
350
+ this.render(this.lastSuggestions);
351
+ return;
352
+ }
353
+ setImmediate(() => this.updateSuggestions());
354
+ }
355
+ updateSuggestions() {
356
+ if (this.disposed || this.suspended) {
357
+ return;
358
+ }
359
+ const beforeCursor = this.rl.line.slice(0, this.rl.cursor);
360
+ if (beforeCursor.startsWith("/")) {
361
+ const seed2 = beforeCursor.slice(1);
362
+ const slashSuggestions = this.filterSlash(seed2);
363
+ if (slashSuggestions.length) {
364
+ this.mode = "slash";
365
+ this.activeIndex = this.getPreservedSelectionIndex(
366
+ this.lastSuggestions,
367
+ slashSuggestions,
368
+ this.activeIndex
369
+ );
370
+ } else {
371
+ this.mode = null;
372
+ }
373
+ this.render(slashSuggestions);
374
+ return;
375
+ }
376
+ this.slashMatches = [];
377
+ const skillMatch = /\$([A-Za-z0-9_-]*)$/.exec(beforeCursor);
378
+ if (skillMatch) {
379
+ this.fileSuggestions = [];
380
+ const seed2 = skillMatch[1] ?? "";
381
+ const skillNames = this.filterSkills(seed2);
382
+ if (skillNames.length) {
383
+ this.mode = "skill";
384
+ this.skillMatches = this.filterSkillsInfo(seed2);
385
+ this.activeIndex = Math.min(this.activeIndex, this.skillMatches.length - 1);
386
+ } else {
387
+ this.mode = null;
388
+ this.skillMatches = [];
389
+ }
390
+ this.render(skillNames);
391
+ return;
392
+ }
393
+ this.skillMatches = [];
394
+ const match = this.matchMention(beforeCursor);
395
+ if (!match) {
396
+ this.mode = null;
397
+ this.fileSuggestions = [];
398
+ this.render([]);
399
+ return;
400
+ }
401
+ const seed = match[1];
402
+ const suggestions = this.filter(seed ?? "");
403
+ if (suggestions.length) {
404
+ this.mode = "file";
405
+ this.fileSuggestions = suggestions;
406
+ this.activeIndex = this.getPreservedSelectionIndex(
407
+ this.lastSuggestions,
408
+ suggestions,
409
+ this.activeIndex
410
+ );
411
+ } else {
412
+ this.mode = null;
413
+ this.fileSuggestions = [];
414
+ }
415
+ this.render(suggestions);
416
+ }
417
+ filter(seed) {
418
+ return buildFileMentionSuggestions(this.filesProvider(), seed, MENTION_SUGGESTION_LIMIT);
419
+ }
420
+ filterSkills(seed) {
421
+ return buildSkillMentionSuggestions(this.skillsProvider(), seed, MENTION_SUGGESTION_LIMIT);
422
+ }
423
+ filterSkillsInfo(seed) {
424
+ const allSkills = this.skillsProvider();
425
+ const skillNames = buildSkillMentionSuggestions(allSkills, seed, MENTION_SUGGESTION_LIMIT);
426
+ return allSkills.filter((s) => skillNames.includes(s.name));
427
+ }
428
+ consumeHandledTab() {
429
+ const handled = this.tabJustHandled;
430
+ this.tabJustHandled = false;
431
+ return handled;
432
+ }
433
+ consumeHandledCompletion() {
434
+ const handled = this.completionJustHandled;
435
+ this.completionJustHandled = false;
436
+ this.tabJustHandled = false;
437
+ return handled;
438
+ }
439
+ getPreservedSelectionIndex(previousSuggestions, nextSuggestions, previousIndex) {
440
+ if (!nextSuggestions.length) {
441
+ return 0;
442
+ }
443
+ const previousSelection = previousSuggestions[previousIndex];
444
+ if (!previousSelection) {
445
+ return 0;
446
+ }
447
+ const nextIndex = nextSuggestions.indexOf(previousSelection);
448
+ if (nextIndex >= 0) {
449
+ return nextIndex;
450
+ }
451
+ return Math.min(previousIndex, nextSuggestions.length - 1);
452
+ }
453
+ matchMention(beforeCursor) {
454
+ return /@([A-Za-z0-9_./\\-]*)$/.exec(beforeCursor);
455
+ }
456
+ isTabKey(str, key) {
457
+ if (key?.name === "backtab" || key?.sequence === "\x1B[Z" || str === "\x1B[Z" || key?.shift) {
458
+ return false;
459
+ }
460
+ return key?.name === "tab" || key?.sequence === " " || str === " ";
461
+ }
462
+ filterSlash(seed) {
463
+ const normalized = seed.toLowerCase();
464
+ let matches = this.slashCommands.filter((cmd) => cmd.command.replace("/", "").toLowerCase().startsWith(normalized));
465
+ if (matches.length === 0) {
466
+ matches = this.slashCommands.filter((cmd) => cmd.command.replace("/", "").toLowerCase().includes(normalized));
467
+ }
468
+ this.slashMatches = matches.slice(0, 5);
469
+ return this.slashMatches.map((cmd) => {
470
+ const detail = cmd.description ? chalk.gray(` - ${cmd.description}`) : "";
471
+ return `${cmd.command}${detail}`;
472
+ });
473
+ }
474
+ render(suggestions) {
475
+ if (this.disposed || this.suspended) {
476
+ return;
477
+ }
478
+ this.lastSuggestions = [...suggestions];
479
+ this.clear(false);
480
+ if (!suggestions.length) {
481
+ return;
482
+ }
483
+ const filenameColumnWidth = this.mode === "file" ? getFilenameColumnWidth(suggestions, getPromptBlockWidth(this.output.columns)) : 0;
484
+ const suggestionLines = suggestions.map((entry, idx) => {
485
+ const isSelected = this.mode && idx === this.activeIndex;
486
+ if (this.mode === "file") {
487
+ return formatFileSuggestionLine(
488
+ entry,
489
+ Boolean(isSelected),
490
+ getPromptBlockWidth(this.output.columns),
491
+ filenameColumnWidth
492
+ );
493
+ }
494
+ if (this.mode === "skill") {
495
+ const skills = this.skillsProvider();
496
+ const skillInfo = skills.find((s) => s.name === entry);
497
+ if (skillInfo) {
498
+ return formatSkillSuggestionLine(skillInfo, Boolean(isSelected), getPromptBlockWidth(this.output.columns));
499
+ }
500
+ }
501
+ const pointer = isSelected ? chalk.cyan("\u25B8") : " ";
502
+ const text = isSelected ? chalk.cyan(entry) : entry;
503
+ return `${pointer} ${text}`;
504
+ });
505
+ const lines = suggestionLines;
506
+ readline.moveCursor(this.output, 0, this.suggestionOffset);
507
+ readline.cursorTo(this.output, 0);
508
+ for (const line of lines) {
509
+ readline.clearLine(this.output, 0);
510
+ this.output.write(`${line}
511
+ `);
512
+ }
513
+ this.suggestionLines = lines.length;
514
+ readline.moveCursor(this.output, 0, -(this.suggestionLines + this.suggestionOffset));
515
+ readline.cursorTo(this.output, 0);
516
+ const rlAny = this.rl;
517
+ const cursorPos = rlAny.cursor ?? rlAny.line.length;
518
+ const width = getPromptBlockWidth(this.output.columns);
519
+ const state = buildPromptRenderState(rlAny.line, cursorPos, width);
520
+ this.output.write(state.lineText);
521
+ readline.cursorTo(this.output, state.cursorColumn);
522
+ }
523
+ clear(reprompt = true) {
524
+ if (!this.suggestionLines) {
525
+ return;
526
+ }
527
+ readline.moveCursor(this.output, 0, this.suggestionOffset);
528
+ for (let i = 0; i < this.suggestionLines; i++) {
529
+ readline.clearLine(this.output, 0);
530
+ if (i < this.suggestionLines - 1) {
531
+ readline.moveCursor(this.output, 0, 1);
532
+ }
533
+ }
534
+ readline.moveCursor(this.output, 0, -(this.suggestionLines + this.suggestionOffset - 1));
535
+ this.suggestionLines = 0;
536
+ if (reprompt && !this.disposed) {
537
+ const rlAny = this.rl;
538
+ readline.cursorTo(this.output, 0);
539
+ const cursorPos = rlAny.cursor ?? rlAny.line.length;
540
+ const width = getPromptBlockWidth(this.output.columns);
541
+ const state = buildPromptRenderState(rlAny.line, cursorPos, width);
542
+ this.output.write(state.lineText);
543
+ readline.cursorTo(this.output, state.cursorColumn);
544
+ }
545
+ }
546
+ insertFileSuggestion(beforeCursor, file) {
547
+ const match = /@([A-Za-z0-9_./\\-]*)$/.exec(beforeCursor);
548
+ if (!match) {
549
+ return;
550
+ }
551
+ const start = match.index;
552
+ const afterCursor = this.rl.line.slice(this.rl.cursor);
553
+ const prefix = this.rl.line.slice(0, start);
554
+ const replacement = `@${file} `;
555
+ const newLine = prefix + replacement + afterCursor;
556
+ const newCursorPos = prefix.length + replacement.length;
557
+ if (this.onFileSuggestionAccepted) {
558
+ this.onFileSuggestionAccepted(newLine, newCursorPos);
559
+ } else {
560
+ this.rl.line = newLine;
561
+ this.rl.cursor = newCursorPos;
562
+ }
563
+ this.mode = null;
564
+ this.fileSuggestions = [];
565
+ this.lastSuggestions = [];
566
+ this.clear();
567
+ if (typeof this.rl._refreshLine === "function") {
568
+ this.rl._refreshLine();
569
+ } else {
570
+ readline.cursorTo(this.output, 0);
571
+ const width = getPromptBlockWidth(this.output.columns);
572
+ const state = buildPromptRenderState(newLine, newCursorPos, width);
573
+ this.output.write(state.lineText);
574
+ readline.cursorTo(this.output, state.cursorColumn);
575
+ }
576
+ }
577
+ insertSlashSuggestion(beforeCursor, command) {
578
+ const afterCursor = this.rl.line.slice(this.rl.cursor);
579
+ const replacement = `${command.command} `;
580
+ const newLine = replacement + afterCursor;
581
+ const newCursorPos = replacement.length;
582
+ if (this.onFileSuggestionAccepted) {
583
+ this.onFileSuggestionAccepted(newLine, newCursorPos);
584
+ } else {
585
+ this.rl.line = newLine;
586
+ this.rl.cursor = newCursorPos;
587
+ }
588
+ this.mode = null;
589
+ this.slashMatches = [];
590
+ this.lastSuggestions = [];
591
+ this.clear();
592
+ }
593
+ insertSkillSuggestion(beforeCursor, skill) {
594
+ const match = /\$([A-Za-z0-9_-]*)$/.exec(beforeCursor);
595
+ if (!match) {
596
+ return;
597
+ }
598
+ const start = match.index;
599
+ const afterCursor = this.rl.line.slice(this.rl.cursor);
600
+ const prefix = this.rl.line.slice(0, start);
601
+ const replacement = `$${skill.name} `;
602
+ const newLine = prefix + replacement + afterCursor;
603
+ const newCursorPos = prefix.length + replacement.length;
604
+ if (this.onFileSuggestionAccepted) {
605
+ this.onFileSuggestionAccepted(newLine, newCursorPos);
606
+ } else {
607
+ this.rl.line = newLine;
608
+ this.rl.cursor = newCursorPos;
609
+ }
610
+ this.mode = null;
611
+ this.skillMatches = [];
612
+ this.lastSuggestions = [];
613
+ this.clear();
614
+ if (typeof this.rl._refreshLine === "function") {
615
+ this.rl._refreshLine();
616
+ } else {
617
+ readline.cursorTo(this.output, 0);
618
+ const width = getPromptBlockWidth(this.output.columns);
619
+ const state = buildPromptRenderState(newLine, newCursorPos, width);
620
+ this.output.write(state.lineText);
621
+ readline.cursorTo(this.output, state.cursorColumn);
622
+ }
623
+ }
624
+ };
625
+
626
+ // src/utils/imageCompression.ts
627
+ var sharpConstructor;
628
+ async function getSharp() {
629
+ if (!sharpConstructor) {
630
+ const mod = await import("sharp");
631
+ sharpConstructor = mod.default;
632
+ }
633
+ return sharpConstructor;
634
+ }
635
+ var IMAGE_TARGET_RAW_SIZE = 3.75 * 1024 * 1024;
636
+ var IMAGE_MAX_DIMENSION = 2e3;
637
+ async function compressImageBuffer(imageBuffer, maxBytes = IMAGE_TARGET_RAW_SIZE, originalMediaType) {
638
+ if (imageBuffer.length === 0) {
639
+ throw new Error("Image buffer is empty");
640
+ }
641
+ const sharp = await getSharp();
642
+ const fallbackFormat = (originalMediaType?.split("/")[1] || "jpeg").replace("jpg", "jpeg");
643
+ const metadata = await sharp(imageBuffer).metadata();
644
+ const format = metadata.format || fallbackFormat;
645
+ if (imageBuffer.length <= maxBytes) {
646
+ return {
647
+ base64: imageBuffer.toString("base64"),
648
+ mediaType: `image/${format === "jpg" ? "jpeg" : format}`,
649
+ originalSize: imageBuffer.length
650
+ };
651
+ }
652
+ if (maxBytes <= 1024 * 1024) {
653
+ const budgetDimension2 = Math.max(300, Math.min(1200, Math.round(Math.sqrt(maxBytes))));
654
+ for (const quality of [70, 50, 35, 20]) {
655
+ const jpegBuf = await sharp(imageBuffer).resize(budgetDimension2, budgetDimension2, { fit: "inside", withoutEnlargement: true }).jpeg({ quality }).toBuffer();
656
+ if (jpegBuf.length <= maxBytes) {
657
+ return {
658
+ base64: jpegBuf.toString("base64"),
659
+ mediaType: "image/jpeg",
660
+ originalSize: imageBuffer.length
661
+ };
662
+ }
663
+ }
664
+ }
665
+ const budgetDimension = Math.max(400, Math.min(IMAGE_MAX_DIMENSION, Math.round(Math.sqrt(maxBytes * 1.5))));
666
+ const estimatedScale = Math.min(
667
+ Math.sqrt(Math.max(maxBytes, 1) / imageBuffer.length),
668
+ budgetDimension / Math.max(metadata.width ?? budgetDimension, metadata.height ?? budgetDimension)
669
+ );
670
+ const preferredStart = Math.min(1, Math.max(0.1, estimatedScale * 1.1));
671
+ const scalingFactors = Array.from(/* @__PURE__ */ new Set([
672
+ preferredStart,
673
+ Math.max(0.1, preferredStart * 0.75),
674
+ Math.max(0.1, preferredStart * 0.5)
675
+ ])).sort((a, b) => b - a);
676
+ const w = metadata.width ?? IMAGE_MAX_DIMENSION;
677
+ const h = metadata.height ?? IMAGE_MAX_DIMENSION;
678
+ for (const factor of scalingFactors) {
679
+ const newW = Math.round(w * factor);
680
+ const newH = Math.round(h * factor);
681
+ const resized = sharp(imageBuffer).resize(newW, newH, { fit: "inside", withoutEnlargement: true });
682
+ if (format === "png") {
683
+ resized.png({ compressionLevel: 9, palette: true });
684
+ } else if (format === "jpeg" || format === "jpg") {
685
+ resized.jpeg({ quality: 80 });
686
+ } else if (format === "webp") {
687
+ resized.webp({ quality: 80 });
688
+ }
689
+ const buf = await resized.toBuffer();
690
+ if (buf.length <= maxBytes) {
691
+ return {
692
+ base64: buf.toString("base64"),
693
+ mediaType: `image/${format === "jpg" ? "jpeg" : format}`,
694
+ originalSize: imageBuffer.length
695
+ };
696
+ }
697
+ }
698
+ const palettePng = await sharp(imageBuffer).resize(800, 800, { fit: "inside", withoutEnlargement: true }).png({ compressionLevel: 9, palette: true, colors: 64 }).toBuffer();
699
+ if (palettePng.length <= maxBytes) {
700
+ return {
701
+ base64: palettePng.toString("base64"),
702
+ mediaType: "image/png",
703
+ originalSize: imageBuffer.length
704
+ };
705
+ }
706
+ const jpeg = await sharp(imageBuffer).resize(600, 600, { fit: "inside", withoutEnlargement: true }).jpeg({ quality: 50 }).toBuffer();
707
+ if (jpeg.length <= maxBytes) {
708
+ return {
709
+ base64: jpeg.toString("base64"),
710
+ mediaType: "image/jpeg",
711
+ originalSize: imageBuffer.length
712
+ };
713
+ }
714
+ const ultra = await sharp(imageBuffer).resize(400, 400, { fit: "inside", withoutEnlargement: true }).jpeg({ quality: 20 }).toBuffer();
715
+ return {
716
+ base64: ultra.toString("base64"),
717
+ mediaType: "image/jpeg",
718
+ originalSize: imageBuffer.length
719
+ };
720
+ }
721
+ async function compressImageBufferWithTargetLimit(imageBuffer, maxTokens, originalMediaType) {
722
+ const maxBase64Chars = Math.floor(maxTokens / 0.125);
723
+ const maxBytes = Math.floor(maxBase64Chars * 0.75);
724
+ return compressImageBuffer(imageBuffer, maxBytes, originalMediaType);
725
+ }
726
+
727
+ // src/core/ImageManager.ts
728
+ var ImageManager = class _ImageManager {
729
+ images = /* @__PURE__ */ new Map();
730
+ counter = 0;
731
+ /**
732
+ * Maximum image size before compression (3.75MB raw target, cc-src's IMAGE_TARGET_RAW_SIZE).
733
+ * Accounts for base64 4/3 expansion to stay under 5MB API limit.
734
+ */
735
+ static MAX_IMAGE_SIZE = IMAGE_TARGET_RAW_SIZE;
736
+ /**
737
+ * Add a new image attachment (sync — compression happens lazily in toOpenAIFormat).
738
+ * @param data - Raw image data as Buffer
739
+ * @param mimeType - Image MIME type
740
+ * @param filename - Optional original filename
741
+ * @returns Sequential image ID starting from 1
742
+ */
743
+ add(data, mimeType, filename) {
744
+ const id = ++this.counter;
745
+ this.images.set(id, {
746
+ id,
747
+ data,
748
+ mimeType,
749
+ filename
750
+ });
751
+ return id;
752
+ }
753
+ /**
754
+ * Add a new image attachment without compression (for internal use)
755
+ * @param data - Raw image data as Buffer
756
+ * @param mimeType - Image MIME type
757
+ * @param filename - Optional original filename
758
+ * @returns Sequential image ID starting from 1
759
+ */
760
+ addRaw(data, mimeType, filename) {
761
+ const id = ++this.counter;
762
+ this.images.set(id, {
763
+ id,
764
+ data,
765
+ mimeType,
766
+ filename
767
+ });
768
+ return id;
769
+ }
770
+ /**
771
+ * Get image by ID
772
+ * @param id - Image ID
773
+ * @returns Image attachment or undefined if not found
774
+ */
775
+ get(id) {
776
+ return this.images.get(id);
777
+ }
778
+ /**
779
+ * Get all images in order they were added
780
+ * @returns Array of image attachments sorted by ID
781
+ */
782
+ getAll() {
783
+ return Array.from(this.images.values()).sort((a, b) => a.id - b.id);
784
+ }
785
+ /**
786
+ * Clear all images and reset counter (called on /new or session end)
787
+ */
788
+ clear() {
789
+ this.images.clear();
790
+ this.counter = 0;
791
+ }
792
+ /**
793
+ * Get number of images currently stored
794
+ */
795
+ count() {
796
+ return this.images.size;
797
+ }
798
+ /**
799
+ * Convert all images to Claude vision API format
800
+ * @returns Array of Claude image content objects
801
+ */
802
+ toClaudeFormat() {
803
+ return this.getAll().map((img) => ({
804
+ type: "image",
805
+ source: {
806
+ type: "base64",
807
+ media_type: img.mimeType,
808
+ data: img.data.toString("base64")
809
+ }
810
+ }));
811
+ }
812
+ /**
813
+ * Convert all images to OpenAI vision API format.
814
+ * Images exceeding the target size are properly compressed (not truncated).
815
+ * @param tokenLimit - Optional token budget for image compression
816
+ * @returns Array of OpenAI image content objects
817
+ */
818
+ async toOpenAIFormat(tokenLimit) {
819
+ const allImages = this.getAll();
820
+ const results = [];
821
+ for (const img of allImages) {
822
+ let base64Data;
823
+ if (tokenLimit) {
824
+ const compressed = await compressImageBufferWithTargetLimit(
825
+ img.data,
826
+ tokenLimit,
827
+ img.mimeType
828
+ );
829
+ base64Data = compressed.base64;
830
+ } else {
831
+ if (img.data.length > _ImageManager.MAX_IMAGE_SIZE) {
832
+ const compressed = await compressImageBufferWithTargetLimit(
833
+ img.data,
834
+ Math.floor(IMAGE_TARGET_RAW_SIZE),
835
+ img.mimeType
836
+ );
837
+ base64Data = compressed.base64;
838
+ } else {
839
+ base64Data = img.data.toString("base64");
840
+ }
841
+ }
842
+ results.push({
843
+ type: "image_url",
844
+ image_url: {
845
+ url: `data:${img.mimeType};base64,${base64Data}`
846
+ }
847
+ });
848
+ }
849
+ return results;
850
+ }
851
+ /**
852
+ * Format placeholder text for display
853
+ * @param id - Image ID
854
+ * @returns Placeholder string like "[Image #1] filename.png" or empty string if not found
855
+ */
856
+ formatPlaceholder(id) {
857
+ const image = this.get(id);
858
+ if (!image) {
859
+ return "";
860
+ }
861
+ if (image.filename) {
862
+ return `[Image #${id}] ${image.filename}`;
863
+ }
864
+ return `[Image #${id}]`;
865
+ }
866
+ };
867
+ function isBase64Image(str) {
868
+ return str.startsWith("data:image/") && str.includes(";base64,");
869
+ }
870
+ function getMimeTypeFromExtension(ext) {
871
+ const normalized = ext.toLowerCase().replace(/^\./, "");
872
+ switch (normalized) {
873
+ case "png":
874
+ return "image/png";
875
+ case "jpg":
876
+ case "jpeg":
877
+ return "image/jpeg";
878
+ case "gif":
879
+ return "image/gif";
880
+ case "webp":
881
+ return "image/webp";
882
+ default:
883
+ return void 0;
884
+ }
885
+ }
886
+ function parseBase64DataUrl(dataUrl) {
887
+ if (!isBase64Image(dataUrl)) {
888
+ return void 0;
889
+ }
890
+ const match = dataUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
891
+ if (!match) {
892
+ return void 0;
893
+ }
894
+ const [, mimeType, base64Data] = match;
895
+ if (mimeType !== "image/png" && mimeType !== "image/jpeg" && mimeType !== "image/gif" && mimeType !== "image/webp") {
896
+ return void 0;
897
+ }
898
+ try {
899
+ const data = Buffer.from(base64Data, "base64");
900
+ return { mimeType, data };
901
+ } catch {
902
+ return void 0;
903
+ }
904
+ }
905
+
906
+ // src/ui/box.ts
907
+ var DEFAULT_BORDER_COLOR = "#8a8a8a";
908
+ var PLAN_BORDER_COLOR = "#ff9d3f";
909
+ var SHELL_BORDER_COLOR = "#000000";
910
+ var SHELL_BOX_BG = "#ffffff";
911
+ var SHELL_BOX_FG = "#000000";
912
+ var FALLBACK_BOX_BG = "#2b2b2b";
913
+ var FALLBACK_BOX_FG = "#a0a0a0";
914
+ var cachedBoxBg = null;
915
+ var cachedBoxFg = null;
916
+ var cachedShellBoxBg = null;
917
+ var cachedShellBoxFg = null;
918
+ var cachedBorderFg = /* @__PURE__ */ new Map();
919
+ var cachedThemeRef = null;
920
+ function ensureCacheValid() {
921
+ const currentTheme = isThemeInitialized() ? getTheme() : null;
922
+ if (currentTheme !== cachedThemeRef) {
923
+ cachedBoxBg = null;
924
+ cachedBoxFg = null;
925
+ cachedShellBoxBg = null;
926
+ cachedShellBoxFg = null;
927
+ cachedBorderFg.clear();
928
+ cachedThemeRef = currentTheme;
929
+ }
930
+ }
931
+ function invalidateBoxColorCache() {
932
+ cachedBoxBg = null;
933
+ cachedBoxFg = null;
934
+ cachedShellBoxBg = null;
935
+ cachedShellBoxFg = null;
936
+ cachedBorderFg.clear();
937
+ }
938
+ function resolveBorderToken(style) {
939
+ if (style === "plan") {
940
+ return "warning";
941
+ }
942
+ if (style === "shell") {
943
+ return "dim";
944
+ }
945
+ return "borderAccent";
946
+ }
947
+ function resolveBorderFallback(style) {
948
+ if (style === "plan") {
949
+ return PLAN_BORDER_COLOR;
950
+ }
951
+ if (style === "shell") {
952
+ return SHELL_BORDER_COLOR;
953
+ }
954
+ return DEFAULT_BORDER_COLOR;
955
+ }
956
+ function hexToAnsiRgb(hex, type) {
957
+ const rgb = hexToRgb(hex);
958
+ if (!rgb) return "";
959
+ const base = type === "fg" ? 38 : 48;
960
+ return `\x1B[${base};2;${rgb.r};${rgb.g};${rgb.b}m`;
961
+ }
962
+ function resolveBoxBg(style = "default") {
963
+ if (style === "shell") {
964
+ if (cachedShellBoxBg !== null) return cachedShellBoxBg;
965
+ const result2 = hexToAnsiRgb(SHELL_BOX_BG, "bg");
966
+ cachedShellBoxBg = result2;
967
+ return result2;
968
+ }
969
+ ensureCacheValid();
970
+ if (cachedBoxBg !== null) return cachedBoxBg;
971
+ if (isThemeInitialized()) {
972
+ try {
973
+ const hex = getTheme().getColor("userMessageBg");
974
+ if (hex) {
975
+ const code = hexToAnsiRgb(hex, "bg");
976
+ if (code) {
977
+ cachedBoxBg = code;
978
+ return code;
979
+ }
980
+ }
981
+ } catch {
982
+ }
983
+ }
984
+ const result = hexToAnsiRgb(FALLBACK_BOX_BG, "bg");
985
+ cachedBoxBg = result;
986
+ return result;
987
+ }
988
+ function resolveBoxFg(style = "default") {
989
+ if (style === "shell") {
990
+ if (cachedShellBoxFg !== null) return cachedShellBoxFg;
991
+ const result2 = hexToAnsiRgb(SHELL_BOX_FG, "fg");
992
+ cachedShellBoxFg = result2;
993
+ return result2;
994
+ }
995
+ ensureCacheValid();
996
+ if (cachedBoxFg !== null) return cachedBoxFg;
997
+ if (isThemeInitialized()) {
998
+ try {
999
+ const hex = getTheme().getColor("userMessageText");
1000
+ if (hex) {
1001
+ const code = hexToAnsiRgb(hex, "fg");
1002
+ if (code) {
1003
+ cachedBoxFg = code;
1004
+ return code;
1005
+ }
1006
+ }
1007
+ } catch {
1008
+ }
1009
+ }
1010
+ const result = hexToAnsiRgb(FALLBACK_BOX_FG, "fg");
1011
+ cachedBoxFg = result;
1012
+ return result;
1013
+ }
1014
+ function resolveBorderFg(style) {
1015
+ if (style === "shell") {
1016
+ const cached2 = cachedBorderFg.get(style);
1017
+ if (cached2 !== void 0) return cached2;
1018
+ const result2 = hexToAnsiRgb(SHELL_BORDER_COLOR, "fg");
1019
+ cachedBorderFg.set(style, result2);
1020
+ return result2;
1021
+ }
1022
+ ensureCacheValid();
1023
+ const cached = cachedBorderFg.get(style);
1024
+ if (cached !== void 0) return cached;
1025
+ if (isThemeInitialized()) {
1026
+ try {
1027
+ const hex = getTheme().getColor(resolveBorderToken(style));
1028
+ if (hex) {
1029
+ const code = hexToAnsiRgb(hex, "fg");
1030
+ if (code) {
1031
+ cachedBorderFg.set(style, code);
1032
+ return code;
1033
+ }
1034
+ }
1035
+ } catch {
1036
+ }
1037
+ }
1038
+ const result = hexToAnsiRgb(resolveBorderFallback(style), "fg");
1039
+ cachedBorderFg.set(style, result);
1040
+ return result;
1041
+ }
1042
+ function drawInputTopBorder(width, style = "default") {
1043
+ const innerWidth = Math.max(0, width - 2);
1044
+ const border = `\u250C${"\u2500".repeat(innerWidth)}\u2510`;
1045
+ return resolveBoxBg(style) + resolveBorderFg(style) + border + RESET_ALL + CLEAR_TO_EOL;
1046
+ }
1047
+ function drawInputBottomBorder(width, style = "default") {
1048
+ const innerWidth = Math.max(0, width - 2);
1049
+ const border = `\u2514${"\u2500".repeat(innerWidth)}\u2518`;
1050
+ return resolveBoxBg(style) + resolveBorderFg(style) + border + RESET_ALL + CLEAR_TO_EOL;
1051
+ }
1052
+ var ANSI_OR_CHAR_PATTERN = /(?:\u001b\[[0-9;]*m)|[\s\S]/g;
1053
+ function getVisibleLength(value) {
1054
+ return stripAnsiCodes(value).length;
1055
+ }
1056
+ function truncateVisible2(value, maxVisible) {
1057
+ if (maxVisible <= 0 || value.length === 0) {
1058
+ return "";
1059
+ }
1060
+ let visible = 0;
1061
+ let out = "";
1062
+ const tokens = value.match(ANSI_OR_CHAR_PATTERN) ?? [];
1063
+ for (const token of tokens) {
1064
+ if (token.startsWith("\x1B[")) {
1065
+ out += token;
1066
+ continue;
1067
+ }
1068
+ if (visible >= maxVisible) {
1069
+ break;
1070
+ }
1071
+ out += token;
1072
+ visible += 1;
1073
+ }
1074
+ return out;
1075
+ }
1076
+ var RESET_ALL = "\x1B[0m";
1077
+ var CLEAR_TO_EOL = "\x1B[K";
1078
+ function stabilizeBoxAnsi(text, bg, fg) {
1079
+ const base = bg + fg;
1080
+ return text.replace(/\x1b\[0m/g, RESET_ALL + base).replace(/\x1b\[49m/g, bg).replace(/\x1b\[39m/g, fg);
1081
+ }
1082
+ function drawInputBox(left, width, right, style = "default") {
1083
+ const normalizedLeft = style === "shell" ? stripAnsiCodes(left) : left;
1084
+ const normalizedRight = style === "shell" && right ? stripAnsiCodes(right) : right;
1085
+ const bg = resolveBoxBg(style);
1086
+ const fg = resolveBoxFg(style);
1087
+ const borderFg = resolveBorderFg(style);
1088
+ const base = bg + fg;
1089
+ const innerWidth = Math.max(0, width - 2);
1090
+ const visLeft = getVisibleLength(normalizedLeft);
1091
+ const lBorder = borderFg + "\u2502" + fg;
1092
+ const rBorder = borderFg + "\u2502";
1093
+ const END = RESET_ALL + CLEAR_TO_EOL;
1094
+ if (!normalizedRight) {
1095
+ const pad = Math.max(0, innerWidth - visLeft);
1096
+ return base + lBorder + stabilizeBoxAnsi(normalizedLeft, bg, fg) + " ".repeat(pad) + rBorder + END;
1097
+ }
1098
+ const visRight = getVisibleLength(normalizedRight);
1099
+ const minGap = 2;
1100
+ const available = innerWidth - visLeft - minGap;
1101
+ if (available <= 0) {
1102
+ const pad = Math.max(0, innerWidth - visLeft);
1103
+ return base + lBorder + stabilizeBoxAnsi(normalizedLeft, bg, fg) + " ".repeat(pad) + rBorder + END;
1104
+ }
1105
+ const clippedRight = visRight > available ? truncateVisible2(normalizedRight, available) : normalizedRight;
1106
+ const clippedRightVis = getVisibleLength(clippedRight);
1107
+ const gap = Math.max(0, innerWidth - visLeft - clippedRightVis);
1108
+ const line = stabilizeBoxAnsi(normalizedLeft, bg, fg) + " ".repeat(gap) + stabilizeBoxAnsi(clippedRight, bg, fg);
1109
+ return base + lBorder + line + rBorder + END;
1110
+ }
1111
+
1112
+ // src/ui/textBufferLayout.ts
1113
+ import stringWidth from "string-width";
1114
+ function calculateLayout(lines, viewportWidth) {
1115
+ const visualLines = [];
1116
+ const logicalToVisualMap = [];
1117
+ const visualToLogicalMap = [];
1118
+ const width = Math.max(viewportWidth, 1);
1119
+ for (let logRow = 0; logRow < lines.length; logRow++) {
1120
+ const line = lines[logRow];
1121
+ const visRows = [];
1122
+ if (line.length === 0) {
1123
+ const visIdx = visualLines.length;
1124
+ visualLines.push("");
1125
+ visRows.push([visIdx, 0]);
1126
+ visualToLogicalMap.push([logRow, 0]);
1127
+ } else {
1128
+ let pos = 0;
1129
+ while (pos < line.length) {
1130
+ const segStart = pos;
1131
+ const visIdx = visualLines.length;
1132
+ let currentWidth = 0;
1133
+ let lastSpacePos = -1;
1134
+ let endPos = pos;
1135
+ while (endPos < line.length) {
1136
+ const ch = line[endPos];
1137
+ const chWidth = stringWidth(ch);
1138
+ if (currentWidth + chWidth > width) {
1139
+ break;
1140
+ }
1141
+ currentWidth += chWidth;
1142
+ endPos++;
1143
+ if (ch === " ") {
1144
+ lastSpacePos = endPos;
1145
+ }
1146
+ }
1147
+ if (endPos < line.length) {
1148
+ if (lastSpacePos > pos) {
1149
+ const segment = line.slice(segStart, lastSpacePos);
1150
+ visualLines.push(segment);
1151
+ visRows.push([visIdx, segStart]);
1152
+ visualToLogicalMap.push([logRow, segStart]);
1153
+ pos = lastSpacePos;
1154
+ } else {
1155
+ const segment = line.slice(segStart, endPos);
1156
+ visualLines.push(segment);
1157
+ visRows.push([visIdx, segStart]);
1158
+ visualToLogicalMap.push([logRow, segStart]);
1159
+ pos = endPos;
1160
+ }
1161
+ } else {
1162
+ const segment = line.slice(segStart, endPos);
1163
+ visualLines.push(segment);
1164
+ visRows.push([visIdx, segStart]);
1165
+ visualToLogicalMap.push([logRow, segStart]);
1166
+ pos = endPos;
1167
+ }
1168
+ }
1169
+ }
1170
+ logicalToVisualMap.push(visRows);
1171
+ }
1172
+ return {
1173
+ visualLines,
1174
+ logicalToVisual: logicalToVisualMap,
1175
+ visualToLogical: visualToLogicalMap
1176
+ };
1177
+ }
1178
+ function logicalToVisual(layout, logRow, logCol) {
1179
+ const spans = layout.logicalToVisual[logRow];
1180
+ if (!spans || spans.length === 0) {
1181
+ return [0, 0];
1182
+ }
1183
+ for (let i = spans.length - 1; i >= 0; i--) {
1184
+ const [visRow2, logColStart2] = spans[i];
1185
+ if (logCol >= logColStart2) {
1186
+ return [visRow2, logCol - logColStart2];
1187
+ }
1188
+ }
1189
+ const [visRow, logColStart] = spans[0];
1190
+ return [visRow, logCol - logColStart];
1191
+ }
1192
+ function visualToLogical(layout, visRow, visCol) {
1193
+ const mapping = layout.visualToLogical[visRow];
1194
+ if (!mapping) {
1195
+ return [0, 0];
1196
+ }
1197
+ const [logRow, logColStart] = mapping;
1198
+ return [logRow, logColStart + visCol];
1199
+ }
1200
+
1201
+ // src/ui/textBuffer.ts
1202
+ function cpLen(s) {
1203
+ return Array.from(s).length;
1204
+ }
1205
+ function cpSlice(s, start, end) {
1206
+ return Array.from(s).slice(start, end).join("");
1207
+ }
1208
+ var CONTROL_CHAR_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
1209
+ var TextBuffer = class {
1210
+ lines;
1211
+ cursorRow;
1212
+ cursorCol;
1213
+ viewportWidth;
1214
+ viewportHeight;
1215
+ preferredCol = null;
1216
+ /** Cached layout result; recomputed lazily when dirty. */
1217
+ cachedLayout = null;
1218
+ /** Becomes `true` when lines or viewport dimensions change. */
1219
+ layoutDirty = true;
1220
+ /** Visual row offset for viewport scrolling. */
1221
+ scrollRow = 0;
1222
+ constructor(viewportWidth, viewportHeight, initialText) {
1223
+ this.viewportWidth = viewportWidth;
1224
+ this.viewportHeight = viewportHeight;
1225
+ if (initialText !== void 0 && initialText !== "") {
1226
+ const normalized = normalizeNewlines(initialText);
1227
+ this.lines = normalized.split("\n");
1228
+ this.cursorRow = this.lines.length - 1;
1229
+ this.cursorCol = cpLen(this.lines[this.cursorRow]);
1230
+ } else {
1231
+ this.lines = [""];
1232
+ this.cursorRow = 0;
1233
+ this.cursorCol = 0;
1234
+ }
1235
+ this.ensureCursorVisible();
1236
+ }
1237
+ // ---------------------------------------------------------------------------
1238
+ // Queries
1239
+ // ---------------------------------------------------------------------------
1240
+ /** Returns a shallow copy of the logical lines array. */
1241
+ getLines() {
1242
+ return [...this.lines];
1243
+ }
1244
+ /** Returns the current cursor row (0-based logical line index). */
1245
+ getCursorRow() {
1246
+ return this.cursorRow;
1247
+ }
1248
+ /** Returns the current cursor column (0-based code-point offset). */
1249
+ getCursorCol() {
1250
+ return this.cursorCol;
1251
+ }
1252
+ /** Returns the number of logical lines. */
1253
+ getLineCount() {
1254
+ return this.lines.length;
1255
+ }
1256
+ /** Returns all lines joined with `\n`. Empty single-line buffer returns `''`. */
1257
+ getText() {
1258
+ if (this.lines.length === 1 && this.lines[0] === "") {
1259
+ return "";
1260
+ }
1261
+ return this.lines.join("\n");
1262
+ }
1263
+ /** Returns `true` when the buffer holds no text (single empty line). */
1264
+ isEmpty() {
1265
+ return this.lines.length === 1 && this.lines[0] === "";
1266
+ }
1267
+ // ---------------------------------------------------------------------------
1268
+ // Cursor positioning
1269
+ // ---------------------------------------------------------------------------
1270
+ /**
1271
+ * Sets the cursor to (row, col), clamping both to valid ranges.
1272
+ * Row is clamped to [0, lineCount-1]. Col is clamped to [0, lineLen].
1273
+ */
1274
+ setCursor(row, col) {
1275
+ row = Math.max(0, Math.min(row, this.lines.length - 1));
1276
+ const lineLen = cpLen(this.lines[row]);
1277
+ col = Math.max(0, Math.min(col, lineLen));
1278
+ this.cursorRow = row;
1279
+ this.cursorCol = col;
1280
+ this.ensureCursorVisible();
1281
+ }
1282
+ // ---------------------------------------------------------------------------
1283
+ // Cursor movement
1284
+ // ---------------------------------------------------------------------------
1285
+ /** Moves cursor left by one code point. Wraps to end of previous line at col 0. */
1286
+ moveLeft() {
1287
+ this.preferredCol = null;
1288
+ if (this.cursorCol > 0) {
1289
+ this.cursorCol--;
1290
+ } else if (this.cursorRow > 0) {
1291
+ this.cursorRow--;
1292
+ this.cursorCol = cpLen(this.lines[this.cursorRow]);
1293
+ }
1294
+ }
1295
+ /** Moves cursor right by one code point. Wraps to start of next line at end of line. */
1296
+ moveRight() {
1297
+ this.preferredCol = null;
1298
+ const lineLen = cpLen(this.lines[this.cursorRow]);
1299
+ if (this.cursorCol < lineLen) {
1300
+ this.cursorCol++;
1301
+ } else if (this.cursorRow < this.lines.length - 1) {
1302
+ this.cursorRow++;
1303
+ this.cursorCol = 0;
1304
+ }
1305
+ }
1306
+ /**
1307
+ * Moves cursor up one visual row. Navigates within wrapped lines as well
1308
+ * as across logical line boundaries. Preserves preferred visual column.
1309
+ */
1310
+ moveUp() {
1311
+ const layout = this.getVisualLayout();
1312
+ const strCol = cpToStrIndex(this.lines[this.cursorRow], this.cursorCol);
1313
+ const [visRow, visCol] = logicalToVisual(layout, this.cursorRow, strCol);
1314
+ if (visRow <= 0) return;
1315
+ if (this.preferredCol === null) {
1316
+ this.preferredCol = visCol;
1317
+ }
1318
+ const targetVisRow = visRow - 1;
1319
+ const targetVisLine = layout.visualLines[targetVisRow];
1320
+ const clampedVisCol = Math.min(this.preferredCol, targetVisLine.length);
1321
+ const [logRow, logStrCol] = visualToLogical(layout, targetVisRow, clampedVisCol);
1322
+ this.cursorRow = logRow;
1323
+ this.cursorCol = strToCpIndex(this.lines[logRow], logStrCol);
1324
+ this.ensureCursorVisible();
1325
+ }
1326
+ /**
1327
+ * Moves cursor down one visual row. Navigates within wrapped lines as well
1328
+ * as across logical line boundaries. Preserves preferred visual column.
1329
+ */
1330
+ moveDown() {
1331
+ const layout = this.getVisualLayout();
1332
+ const strCol = cpToStrIndex(this.lines[this.cursorRow], this.cursorCol);
1333
+ const [visRow, visCol] = logicalToVisual(layout, this.cursorRow, strCol);
1334
+ if (visRow >= layout.visualLines.length - 1) return;
1335
+ if (this.preferredCol === null) {
1336
+ this.preferredCol = visCol;
1337
+ }
1338
+ const targetVisRow = visRow + 1;
1339
+ const targetVisLine = layout.visualLines[targetVisRow];
1340
+ const clampedVisCol = Math.min(this.preferredCol, targetVisLine.length);
1341
+ const [logRow, logStrCol] = visualToLogical(layout, targetVisRow, clampedVisCol);
1342
+ this.cursorRow = logRow;
1343
+ this.cursorCol = strToCpIndex(this.lines[logRow], logStrCol);
1344
+ this.ensureCursorVisible();
1345
+ }
1346
+ /**
1347
+ * Moves cursor left by one word boundary using `Intl.Segmenter`.
1348
+ * Jumps to the start of the previous word-like segment. At column 0,
1349
+ * wraps to the end of the previous logical line.
1350
+ */
1351
+ moveWordLeft() {
1352
+ this.preferredCol = null;
1353
+ if (this.cursorCol === 0) {
1354
+ if (this.cursorRow > 0) {
1355
+ this.cursorRow--;
1356
+ this.cursorCol = cpLen(this.lines[this.cursorRow]);
1357
+ }
1358
+ return;
1359
+ }
1360
+ const line = this.lines[this.cursorRow];
1361
+ const codePoints = Array.from(line);
1362
+ const strCursor = codePoints.slice(0, this.cursorCol).join("").length;
1363
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "word" });
1364
+ const segments = Array.from(segmenter.segment(line));
1365
+ let target = null;
1366
+ for (const seg of segments) {
1367
+ if (seg.isWordLike && seg.index < strCursor) {
1368
+ target = seg;
1369
+ }
1370
+ }
1371
+ if (target !== null) {
1372
+ this.cursorCol = strToCpIndex(line, target.index);
1373
+ } else {
1374
+ this.cursorCol = 0;
1375
+ }
1376
+ this.ensureCursorVisible();
1377
+ }
1378
+ /**
1379
+ * Moves cursor right by one word boundary using `Intl.Segmenter`.
1380
+ * Jumps to the end of the current/next word-like segment. At end of line,
1381
+ * wraps to the start of the next logical line.
1382
+ */
1383
+ moveWordRight() {
1384
+ this.preferredCol = null;
1385
+ const line = this.lines[this.cursorRow];
1386
+ const lineLen = cpLen(line);
1387
+ if (this.cursorCol >= lineLen) {
1388
+ if (this.cursorRow < this.lines.length - 1) {
1389
+ this.cursorRow++;
1390
+ this.cursorCol = 0;
1391
+ }
1392
+ return;
1393
+ }
1394
+ const codePoints = Array.from(line);
1395
+ const strCursor = codePoints.slice(0, this.cursorCol).join("").length;
1396
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "word" });
1397
+ const segments = Array.from(segmenter.segment(line));
1398
+ for (const seg of segments) {
1399
+ const segEnd = seg.index + seg.segment.length;
1400
+ if (seg.isWordLike && segEnd > strCursor) {
1401
+ this.cursorCol = strToCpIndex(line, segEnd);
1402
+ this.ensureCursorVisible();
1403
+ return;
1404
+ }
1405
+ }
1406
+ this.cursorCol = lineLen;
1407
+ this.ensureCursorVisible();
1408
+ }
1409
+ /** Moves cursor to the start of the current line. */
1410
+ moveHome() {
1411
+ this.preferredCol = null;
1412
+ this.cursorCol = 0;
1413
+ }
1414
+ /** Moves cursor to the end of the current line. */
1415
+ moveEnd() {
1416
+ this.preferredCol = null;
1417
+ this.cursorCol = cpLen(this.lines[this.cursorRow]);
1418
+ }
1419
+ // ---------------------------------------------------------------------------
1420
+ // Mutations
1421
+ // ---------------------------------------------------------------------------
1422
+ /**
1423
+ * Inserts text at the current cursor position.
1424
+ *
1425
+ * - Control characters (except `\n`, `\r`, `\t`) are stripped.
1426
+ * - `\r\n` and `\r` are normalized to `\n`.
1427
+ * - Newlines split the current line.
1428
+ * - Cursor advances to the end of the inserted text.
1429
+ */
1430
+ insert(text) {
1431
+ if (text === "") return;
1432
+ this.preferredCol = null;
1433
+ this.layoutDirty = true;
1434
+ const cleaned = text.replace(CONTROL_CHAR_RE, "");
1435
+ const normalized = normalizeNewlines(cleaned);
1436
+ const insertLines = normalized.split("\n");
1437
+ const currentLine = this.lines[this.cursorRow];
1438
+ const before = cpSlice(currentLine, 0, this.cursorCol);
1439
+ const after = cpSlice(currentLine, this.cursorCol);
1440
+ if (insertLines.length === 1) {
1441
+ this.lines[this.cursorRow] = before + insertLines[0] + after;
1442
+ this.cursorCol += cpLen(insertLines[0]);
1443
+ } else {
1444
+ const firstLine = before + insertLines[0];
1445
+ const lastLine = insertLines[insertLines.length - 1] + after;
1446
+ const newLines = [
1447
+ firstLine,
1448
+ ...insertLines.slice(1, -1),
1449
+ lastLine
1450
+ ];
1451
+ this.lines.splice(this.cursorRow, 1, ...newLines);
1452
+ this.cursorRow += insertLines.length - 1;
1453
+ this.cursorCol = cpLen(insertLines[insertLines.length - 1]);
1454
+ }
1455
+ this.ensureCursorVisible();
1456
+ }
1457
+ /**
1458
+ * Deletes the character before the cursor (backspace behavior).
1459
+ *
1460
+ * - At column 0 of a line, merges with the previous line.
1461
+ * - At the start of the buffer, does nothing.
1462
+ */
1463
+ backspace() {
1464
+ this.preferredCol = null;
1465
+ this.layoutDirty = true;
1466
+ if (this.cursorCol > 0) {
1467
+ const line = this.lines[this.cursorRow];
1468
+ this.lines[this.cursorRow] = cpSlice(line, 0, this.cursorCol - 1) + cpSlice(line, this.cursorCol);
1469
+ this.cursorCol--;
1470
+ } else if (this.cursorRow > 0) {
1471
+ const prevLine = this.lines[this.cursorRow - 1];
1472
+ const prevLen = cpLen(prevLine);
1473
+ this.lines[this.cursorRow - 1] = prevLine + this.lines[this.cursorRow];
1474
+ this.lines.splice(this.cursorRow, 1);
1475
+ this.cursorRow--;
1476
+ this.cursorCol = prevLen;
1477
+ }
1478
+ this.ensureCursorVisible();
1479
+ }
1480
+ /**
1481
+ * Deletes the character at the cursor position (forward delete behavior).
1482
+ *
1483
+ * - At the end of a line, merges with the next line.
1484
+ * - At the end of the buffer, does nothing.
1485
+ */
1486
+ delete() {
1487
+ this.preferredCol = null;
1488
+ this.layoutDirty = true;
1489
+ const line = this.lines[this.cursorRow];
1490
+ const lineLen = cpLen(line);
1491
+ if (this.cursorCol < lineLen) {
1492
+ this.lines[this.cursorRow] = cpSlice(line, 0, this.cursorCol) + cpSlice(line, this.cursorCol + 1);
1493
+ } else if (this.cursorRow < this.lines.length - 1) {
1494
+ this.lines[this.cursorRow] = line + this.lines[this.cursorRow + 1];
1495
+ this.lines.splice(this.cursorRow + 1, 1);
1496
+ }
1497
+ this.ensureCursorVisible();
1498
+ }
1499
+ /**
1500
+ * Deletes from cursor to end of current line.
1501
+ * If cursor is already at end of line, merges with the next line (like Delete at EOL).
1502
+ */
1503
+ deleteToEnd() {
1504
+ this.preferredCol = null;
1505
+ this.layoutDirty = true;
1506
+ const line = this.lines[this.cursorRow];
1507
+ const lineLen = cpLen(line);
1508
+ if (this.cursorCol < lineLen) {
1509
+ this.lines[this.cursorRow] = cpSlice(line, 0, this.cursorCol);
1510
+ } else if (this.cursorRow < this.lines.length - 1) {
1511
+ this.lines[this.cursorRow] = line + this.lines[this.cursorRow + 1];
1512
+ this.lines.splice(this.cursorRow + 1, 1);
1513
+ }
1514
+ this.ensureCursorVisible();
1515
+ }
1516
+ /**
1517
+ * Deletes from cursor to start of current line.
1518
+ * Cursor moves to column 0.
1519
+ */
1520
+ deleteToStart() {
1521
+ this.preferredCol = null;
1522
+ this.layoutDirty = true;
1523
+ const line = this.lines[this.cursorRow];
1524
+ if (this.cursorCol > 0) {
1525
+ this.lines[this.cursorRow] = cpSlice(line, this.cursorCol);
1526
+ this.cursorCol = 0;
1527
+ }
1528
+ this.ensureCursorVisible();
1529
+ }
1530
+ /**
1531
+ * Deletes the previous word before the cursor.
1532
+ * Skips trailing spaces, then skips non-space characters.
1533
+ * Uses code-point-safe string indexing.
1534
+ */
1535
+ deletePreviousWord() {
1536
+ this.preferredCol = null;
1537
+ this.layoutDirty = true;
1538
+ if (this.cursorCol === 0) return;
1539
+ const line = this.lines[this.cursorRow];
1540
+ const beforeCursor = cpSlice(line, 0, this.cursorCol);
1541
+ const chars = Array.from(beforeCursor);
1542
+ let i = chars.length;
1543
+ while (i > 0 && chars[i - 1] === " ") {
1544
+ i--;
1545
+ }
1546
+ while (i > 0 && chars[i - 1] !== " ") {
1547
+ i--;
1548
+ }
1549
+ const after = cpSlice(line, this.cursorCol);
1550
+ this.lines[this.cursorRow] = chars.slice(0, i).join("") + after;
1551
+ this.cursorCol = i;
1552
+ this.ensureCursorVisible();
1553
+ }
1554
+ /**
1555
+ * Sets cursor to (row, col) with bounds clamping.
1556
+ * Row is clamped to [0, lineCount-1]. Col is clamped to [0, lineLen].
1557
+ */
1558
+ setCursorPosition(row, col) {
1559
+ row = Math.max(0, Math.min(row, this.lines.length - 1));
1560
+ const lineLen = cpLen(this.lines[row]);
1561
+ col = Math.max(0, Math.min(col, lineLen));
1562
+ this.cursorRow = row;
1563
+ this.cursorCol = col;
1564
+ this.preferredCol = null;
1565
+ this.ensureCursorVisible();
1566
+ }
1567
+ /**
1568
+ * Replaces all buffer content and moves the cursor to the end.
1569
+ */
1570
+ setText(text) {
1571
+ this.layoutDirty = true;
1572
+ const normalized = normalizeNewlines(text);
1573
+ if (normalized === "") {
1574
+ this.lines = [""];
1575
+ this.cursorRow = 0;
1576
+ this.cursorCol = 0;
1577
+ } else {
1578
+ this.lines = normalized.split("\n");
1579
+ this.cursorRow = this.lines.length - 1;
1580
+ this.cursorCol = cpLen(this.lines[this.cursorRow]);
1581
+ }
1582
+ this.ensureCursorVisible();
1583
+ }
1584
+ // ---------------------------------------------------------------------------
1585
+ // Visual layout integration
1586
+ // ---------------------------------------------------------------------------
1587
+ /**
1588
+ * Returns the cached visual layout, recomputing it if dirty.
1589
+ * The layout maps logical lines to word-wrapped visual lines.
1590
+ */
1591
+ getVisualLayout() {
1592
+ if (this.layoutDirty || this.cachedLayout === null) {
1593
+ this.cachedLayout = calculateLayout(this.lines, this.viewportWidth);
1594
+ this.layoutDirty = false;
1595
+ }
1596
+ return this.cachedLayout;
1597
+ }
1598
+ /**
1599
+ * Returns the visual (row, col) position of the cursor after wrapping.
1600
+ * The visual row accounts for word-wrapped lines.
1601
+ */
1602
+ getVisualCursor() {
1603
+ const layout = this.getVisualLayout();
1604
+ const strCol = cpToStrIndex(this.lines[this.cursorRow], this.cursorCol);
1605
+ return logicalToVisual(layout, this.cursorRow, strCol);
1606
+ }
1607
+ /** Returns the total number of visual lines after wrapping. */
1608
+ getVisualLineCount() {
1609
+ return this.getVisualLayout().visualLines.length;
1610
+ }
1611
+ /** Returns the current scroll row offset. */
1612
+ getScrollRow() {
1613
+ return this.scrollRow;
1614
+ }
1615
+ /**
1616
+ * Returns the visual lines currently visible within the viewport.
1617
+ * This is the slice `visualLines[scrollRow .. scrollRow + viewportHeight)`.
1618
+ */
1619
+ getRenderedLines() {
1620
+ const layout = this.getVisualLayout();
1621
+ return layout.visualLines.slice(
1622
+ this.scrollRow,
1623
+ this.scrollRow + this.viewportHeight
1624
+ );
1625
+ }
1626
+ /**
1627
+ * Updates viewport dimensions and marks the layout for recomputation.
1628
+ */
1629
+ setViewport(width, height) {
1630
+ if (this.viewportWidth === width && this.viewportHeight === height) {
1631
+ return;
1632
+ }
1633
+ this.viewportWidth = width;
1634
+ this.viewportHeight = height;
1635
+ this.layoutDirty = true;
1636
+ this.ensureCursorVisible();
1637
+ }
1638
+ // ---------------------------------------------------------------------------
1639
+ // Private: scroll management
1640
+ // ---------------------------------------------------------------------------
1641
+ /**
1642
+ * Adjusts `scrollRow` so the visual cursor is within the visible viewport.
1643
+ * Called after any cursor movement or layout change.
1644
+ */
1645
+ ensureCursorVisible() {
1646
+ const layout = this.getVisualLayout();
1647
+ const strCol = cpToStrIndex(this.lines[this.cursorRow], this.cursorCol);
1648
+ const [visRow] = logicalToVisual(layout, this.cursorRow, strCol);
1649
+ if (visRow < this.scrollRow) {
1650
+ this.scrollRow = visRow;
1651
+ } else if (visRow >= this.scrollRow + this.viewportHeight) {
1652
+ this.scrollRow = visRow - this.viewportHeight + 1;
1653
+ }
1654
+ }
1655
+ };
1656
+ function normalizeNewlines(text) {
1657
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1658
+ }
1659
+ function cpToStrIndex(s, cpCol) {
1660
+ const codePoints = Array.from(s);
1661
+ const clamped = Math.min(cpCol, codePoints.length);
1662
+ return codePoints.slice(0, clamped).join("").length;
1663
+ }
1664
+ function strToCpIndex(s, strIdx) {
1665
+ const prefix = s.slice(0, strIdx);
1666
+ return Array.from(prefix).length;
1667
+ }
1668
+
1669
+ // src/ui/textBufferKeyHandler.ts
1670
+ var CONTROL_CHAR_RE2 = /^[\x00-\x1f\x7f]/;
1671
+ var CSI_ENTER_RESIDUAL_RE = /^(?:\x1b\[|\x1b|\[)?(?:13;?[234]?\d*[u~]|27;[234];13~)$/;
1672
+ function handleTextBufferKey(buffer, str, key) {
1673
+ const name = key.name;
1674
+ const ctrl = key.ctrl === true;
1675
+ const meta = key.meta === true;
1676
+ const shift = key.shift === true;
1677
+ if (name === "return") {
1678
+ if (shift || meta) {
1679
+ buffer.insert("\n");
1680
+ return "handled";
1681
+ }
1682
+ return "submit";
1683
+ }
1684
+ if (name === "backspace") {
1685
+ buffer.backspace();
1686
+ return "handled";
1687
+ }
1688
+ if (name === "delete") {
1689
+ buffer.delete();
1690
+ return "handled";
1691
+ }
1692
+ if (name === "left" && (ctrl || meta)) {
1693
+ buffer.moveWordLeft();
1694
+ return "handled";
1695
+ }
1696
+ if (name === "right" && (ctrl || meta)) {
1697
+ buffer.moveWordRight();
1698
+ return "handled";
1699
+ }
1700
+ if (name === "left") {
1701
+ buffer.moveLeft();
1702
+ return "handled";
1703
+ }
1704
+ if (name === "right") {
1705
+ buffer.moveRight();
1706
+ return "handled";
1707
+ }
1708
+ if (name === "up") {
1709
+ buffer.moveUp();
1710
+ return "handled";
1711
+ }
1712
+ if (name === "down") {
1713
+ buffer.moveDown();
1714
+ return "handled";
1715
+ }
1716
+ if (name === "home") {
1717
+ buffer.moveHome();
1718
+ return "handled";
1719
+ }
1720
+ if (name === "end") {
1721
+ buffer.moveEnd();
1722
+ return "handled";
1723
+ }
1724
+ if (ctrl && name === "a") {
1725
+ buffer.moveHome();
1726
+ return "handled";
1727
+ }
1728
+ if (ctrl && name === "e") {
1729
+ buffer.moveEnd();
1730
+ return "handled";
1731
+ }
1732
+ if (str && !CONTROL_CHAR_RE2.test(str) && !CSI_ENTER_RESIDUAL_RE.test(str)) {
1733
+ buffer.insert(str);
1734
+ return "handled";
1735
+ }
1736
+ return "unhandled";
1737
+ }
1738
+
1739
+ // src/ui/inputPrompt.ts
1740
+ var promptEvents = new EventEmitter();
1741
+ function promptNotify(message) {
1742
+ promptEvents.emit("notify", message);
1743
+ }
1744
+ function promptInterrupt(value) {
1745
+ promptEvents.emit("interrupt", value);
1746
+ }
1747
+ function writePromptShellCommandHeader(output, command) {
1748
+ output.write(`${chalk2.cyan(`You ran ${command}`)}
1749
+ `);
1750
+ }
1751
+ function createPromptShellCommandBlockWriter(output) {
1752
+ let pending = "";
1753
+ let pendingStream = "stdout";
1754
+ let lineIndex = 0;
1755
+ const flushLine = (line, stream) => {
1756
+ const prefix = lineIndex === 0 ? " \u2514 " : " ";
1757
+ output.write(`${prefix}${stream === "stderr" ? chalk2.red(line) : line}
1758
+ `);
1759
+ lineIndex += 1;
1760
+ };
1761
+ const push = (chunk, stream) => {
1762
+ pendingStream = stream;
1763
+ pending += chunk;
1764
+ while (true) {
1765
+ const newlineIndex = pending.indexOf("\n");
1766
+ const carriageIndex = pending.indexOf("\r");
1767
+ const boundaryCandidates = [newlineIndex, carriageIndex].filter((value) => value >= 0);
1768
+ if (boundaryCandidates.length === 0) {
1769
+ break;
1770
+ }
1771
+ const boundaryIndex = Math.min(...boundaryCandidates);
1772
+ const boundaryWidth = pending[boundaryIndex] === "\r" && pending[boundaryIndex + 1] === "\n" ? 2 : 1;
1773
+ const line = pending.slice(0, boundaryIndex);
1774
+ pending = pending.slice(boundaryIndex + boundaryWidth);
1775
+ flushLine(line, stream);
1776
+ }
1777
+ };
1778
+ return {
1779
+ pushStdout(chunk) {
1780
+ push(chunk, "stdout");
1781
+ },
1782
+ pushStderr(chunk) {
1783
+ push(chunk, "stderr");
1784
+ },
1785
+ flush() {
1786
+ if (!pending) {
1787
+ return;
1788
+ }
1789
+ flushLine(pending, pendingStream);
1790
+ pending = "";
1791
+ }
1792
+ };
1793
+ }
1794
+ var PROMPT_PREFIX = `${chalk2.gray("\u203A")} `;
1795
+ var STATUS_LINE_COUNT = 1;
1796
+ var PROMPT_LINES_ABOVE_INPUT = 1;
1797
+ var PROMPT_LINES_BELOW_INPUT = 1;
1798
+ var PROMPT_PLACEHOLDER = "Plan, search, build anything";
1799
+ var PROMPT_INPUT_PREFIX = "\u276F ";
1800
+ var SHIFT_ENTER_RESIDUAL_PATTERN = /^(?:\x1b\[|\x1b|\[)?(?:13;?[234]?\d*[u~]|27;[234];13~)$/;
1801
+ var HOT_TIP_LIMIT = 5;
1802
+ var SLASH_MATCH_EXACT = 0;
1803
+ var SLASH_MATCH_PREFIX = 1;
1804
+ var SLASH_MATCH_WORD_PREFIX = 2;
1805
+ var SLASH_MATCH_SUBSTRING = 3;
1806
+ var SLASH_MATCH_FUZZY = 4;
1807
+ function getHelpOrderedSlashCommands(slashCommands) {
1808
+ return slashCommands.filter((cmd) => cmd.implemented && cmd.command !== "/?").sort((a, b) => a.command.localeCompare(b.command));
1809
+ }
1810
+ function getRankedSlashCommandMatches(seed, slashCommands) {
1811
+ const normalizedSeed = seed.toLowerCase().trim();
1812
+ const orderedCommands = getHelpOrderedSlashCommands(slashCommands);
1813
+ if (!normalizedSeed) {
1814
+ return orderedCommands;
1815
+ }
1816
+ return orderedCommands.map((command, helpOrder) => {
1817
+ const commandName = command.command.slice(1).toLowerCase();
1818
+ const match = rankSlashCommand(commandName, normalizedSeed);
1819
+ return match ? { command, helpOrder, ...match } : null;
1820
+ }).filter((match) => match !== null).sort(
1821
+ (a, b) => a.rank - b.rank || a.firstIndex - b.firstIndex || a.spread - b.spread || a.helpOrder - b.helpOrder
1822
+ ).map((match) => match.command);
1823
+ }
1824
+ function rankSlashCommand(commandName, seed) {
1825
+ if (commandName === seed) {
1826
+ return { rank: SLASH_MATCH_EXACT, firstIndex: 0, spread: seed.length };
1827
+ }
1828
+ if (commandName.startsWith(seed)) {
1829
+ return { rank: SLASH_MATCH_PREFIX, firstIndex: 0, spread: seed.length };
1830
+ }
1831
+ const wordPrefixIndex = findSlashCommandWordPrefix(commandName, seed);
1832
+ if (wordPrefixIndex !== -1) {
1833
+ return { rank: SLASH_MATCH_WORD_PREFIX, firstIndex: wordPrefixIndex, spread: seed.length };
1834
+ }
1835
+ const substringIndex = commandName.indexOf(seed);
1836
+ if (substringIndex !== -1) {
1837
+ return { rank: SLASH_MATCH_SUBSTRING, firstIndex: substringIndex, spread: seed.length };
1838
+ }
1839
+ const fuzzyMatch = findSlashCommandFuzzyMatch(commandName, seed);
1840
+ if (fuzzyMatch) {
1841
+ return { rank: SLASH_MATCH_FUZZY, ...fuzzyMatch };
1842
+ }
1843
+ return null;
1844
+ }
1845
+ function findSlashCommandWordPrefix(commandName, seed) {
1846
+ for (let index = 1; index < commandName.length; index++) {
1847
+ const previous = commandName[index - 1];
1848
+ if ((previous === "-" || previous === "_" || previous === "?") && commandName.startsWith(seed, index)) {
1849
+ return index;
1850
+ }
1851
+ }
1852
+ return -1;
1853
+ }
1854
+ function findSlashCommandFuzzyMatch(commandName, seed) {
1855
+ let searchFrom = 0;
1856
+ let firstIndex = -1;
1857
+ let lastIndex = -1;
1858
+ for (const char of seed) {
1859
+ const index = commandName.indexOf(char, searchFrom);
1860
+ if (index === -1) {
1861
+ return null;
1862
+ }
1863
+ if (firstIndex === -1) {
1864
+ firstIndex = index;
1865
+ }
1866
+ lastIndex = index;
1867
+ searchFrom = index + 1;
1868
+ }
1869
+ return {
1870
+ firstIndex,
1871
+ spread: lastIndex - firstIndex + 1
1872
+ };
1873
+ }
1874
+ var cachedSkillMentions;
1875
+ function resetCachedSkillMentions() {
1876
+ cachedSkillMentions = void 0;
1877
+ }
1878
+ var CONTEXTUAL_HELP_ROWS = [
1879
+ { left: "/ for commands", right: "! for shell commands" },
1880
+ { left: "@ for file paths", right: "tab accepts suggestion" },
1881
+ { left: "$ for skills", right: "tab accepts suggestion" },
1882
+ { left: "? toggles this shortcuts panel", right: "shift + tab toggles plan mode" },
1883
+ { left: "shift + enter inserts newline", right: "alt + enter inserts newline" },
1884
+ { left: "enter submits prompt", right: "ctrl + c clears input / exits" },
1885
+ { left: "esc interrupts active turn", right: "type /, @, or ! to switch mode" }
1886
+ ];
1887
+ function truncatePlainText(value, width) {
1888
+ if (width <= 0) {
1889
+ return "";
1890
+ }
1891
+ if (value.length <= width) {
1892
+ return value;
1893
+ }
1894
+ if (width === 1) {
1895
+ return "\u2026";
1896
+ }
1897
+ return `${value.slice(0, width - 1)}\u2026`;
1898
+ }
1899
+ function buildPromptHotTips(currentLine, files, slashCommands, workspaceRoot, skillsProvider) {
1900
+ const trimmed = currentLine.trim();
1901
+ const mentionMatch = /@([A-Za-z0-9_./\\-]*)$/.exec(currentLine);
1902
+ if (mentionMatch) {
1903
+ const seed = mentionMatch[1] ?? "";
1904
+ const suggestions = buildFileMentionSuggestions(files, seed, HOT_TIP_LIMIT);
1905
+ const mentionTips = suggestions.map((file) => ({
1906
+ label: `Tab -> @${file}`
1907
+ }));
1908
+ return mentionTips.length > 0 ? mentionTips : [{ label: "Type more after @ to filter file paths" }];
1909
+ }
1910
+ const skillMatch = /\$([A-Za-z0-9_-]*)$/.exec(currentLine);
1911
+ if (skillMatch && skillsProvider) {
1912
+ const seed = skillMatch[1] ?? "";
1913
+ const skills = cachedSkillMentions ?? skillsProvider();
1914
+ if (cachedSkillMentions === void 0) {
1915
+ cachedSkillMentions = skills;
1916
+ }
1917
+ const suggestions = buildSkillMentionSuggestions(skills, seed, HOT_TIP_LIMIT);
1918
+ const skillTips = suggestions.map((name) => ({
1919
+ label: `Tab -> $${name}`
1920
+ }));
1921
+ return skillTips.length > 0 ? skillTips : [{ label: "Type more after $ to filter skills" }];
1922
+ }
1923
+ if (trimmed.startsWith("/")) {
1924
+ const slashInput = currentLine.replace(/^\s+/, "");
1925
+ const spaceIdx = slashInput.indexOf(" ");
1926
+ if (spaceIdx !== -1) {
1927
+ const cmdPart = slashInput.slice(0, spaceIdx).toLowerCase();
1928
+ const subSeed = slashInput.slice(spaceIdx + 1).toLowerCase().trim();
1929
+ const parent = slashCommands.find((cmd) => cmd.command.toLowerCase() === cmdPart);
1930
+ if (parent?.subcommands && parent.subcommands.length > 0) {
1931
+ const subMatches = parent.subcommands.filter(
1932
+ (sub) => subSeed === "" ? true : sub.name.toLowerCase().startsWith(subSeed)
1933
+ ).slice(0, HOT_TIP_LIMIT).map((sub) => ({
1934
+ label: `Tab -> ${parent.command} ${sub.name} (${sub.description})`
1935
+ }));
1936
+ if (subMatches.length > 0) {
1937
+ return subMatches;
1938
+ }
1939
+ }
1940
+ }
1941
+ const seed = trimmed.slice(1).toLowerCase();
1942
+ const matches = getRankedSlashCommandMatches(seed, slashCommands).slice(0, HOT_TIP_LIMIT).map((cmd) => ({
1943
+ label: `Tab -> ${cmd.command}${cmd.description ? ` (${cmd.description})` : ""}`
1944
+ }));
1945
+ return matches.length > 0 ? matches : [{ label: "No slash command match. Try /help" }];
1946
+ }
1947
+ if (trimmed.startsWith("!")) {
1948
+ const shellSuggestions = getShellCommandSuggestions(trimmed, {
1949
+ cwd: workspaceRoot,
1950
+ limit: HOT_TIP_LIMIT
1951
+ });
1952
+ return shellSuggestions.length > 0 ? shellSuggestions.map((value) => ({ label: `Tab -> ${value}` })) : [{ label: "Type a shell command after ! (e.g. ! git status)" }];
1953
+ }
1954
+ const defaultFileTip = files.length > 0 ? { label: `Tab -> @${files[0]}` } : { label: "Type @ to mention files" };
1955
+ return [
1956
+ { label: "Tab -> /help" },
1957
+ { label: "Tab -> ! git status" },
1958
+ defaultFileTip,
1959
+ { label: "Type $ for skills" },
1960
+ { label: "Type /, @, or ! to switch suggestion mode" },
1961
+ { label: "Shift+Tab toggles plan mode" }
1962
+ ];
1963
+ }
1964
+ function getPrimaryHotTipSuggestion(currentLine, files, slashCommands, options, workspaceRoot, skillsProvider) {
1965
+ const normalizedOptions = normalizePromptSuggestionOptions(options, workspaceRoot, skillsProvider);
1966
+ const mentionMatch = /@([A-Za-z0-9_./\\-]*)$/.exec(currentLine);
1967
+ if (mentionMatch) {
1968
+ const seed = mentionMatch[1] ?? "";
1969
+ const suggestions = buildFileMentionSuggestions(files, seed, 1);
1970
+ if (suggestions.length === 0) {
1971
+ return null;
1972
+ }
1973
+ const prefix = currentLine.slice(0, mentionMatch.index);
1974
+ const line = `${prefix}@${suggestions[0]} `;
1975
+ return { line, cursor: line.length };
1976
+ }
1977
+ const skillMatch = /\$([A-Za-z0-9_-]*)$/.exec(currentLine);
1978
+ if (skillMatch && normalizedOptions.skillsProvider) {
1979
+ const seed = skillMatch[1] ?? "";
1980
+ const skills = cachedSkillMentions ?? normalizedOptions.skillsProvider();
1981
+ if (cachedSkillMentions === void 0) {
1982
+ cachedSkillMentions = skills;
1983
+ }
1984
+ const suggestions = buildSkillMentionSuggestions(skills, seed, 1);
1985
+ if (suggestions.length === 0) {
1986
+ return null;
1987
+ }
1988
+ const prefix = currentLine.slice(0, skillMatch.index);
1989
+ const line = `${prefix}$${suggestions[0]} `;
1990
+ return { line, cursor: line.length };
1991
+ }
1992
+ const trimmed = currentLine.trim();
1993
+ if (!trimmed) {
1994
+ const nextPromptSuggestion = normalizedOptions.nextPromptSuggestion?.trim();
1995
+ if (nextPromptSuggestion) {
1996
+ return { line: nextPromptSuggestion, cursor: nextPromptSuggestion.length };
1997
+ }
1998
+ return null;
1999
+ }
2000
+ if (trimmed.startsWith("/")) {
2001
+ const slashInput = currentLine.replace(/^\s+/, "");
2002
+ const spaceIdx = slashInput.indexOf(" ");
2003
+ if (spaceIdx !== -1) {
2004
+ const cmdPart = slashInput.slice(0, spaceIdx).toLowerCase();
2005
+ const subSeed = slashInput.slice(spaceIdx + 1).toLowerCase().trim();
2006
+ const parent = slashCommands.find((cmd) => cmd.command.toLowerCase() === cmdPart);
2007
+ if (parent?.subcommands && parent.subcommands.length > 0) {
2008
+ const subMatch = parent.subcommands.find(
2009
+ (sub) => subSeed === "" ? true : sub.name.toLowerCase().startsWith(subSeed)
2010
+ );
2011
+ if (subMatch) {
2012
+ const line2 = `${parent.command} ${subMatch.name} `;
2013
+ return { line: line2, cursor: line2.length };
2014
+ }
2015
+ }
2016
+ return null;
2017
+ }
2018
+ const seed = trimmed.slice(1).toLowerCase();
2019
+ const match = getRankedSlashCommandMatches(seed, slashCommands)[0];
2020
+ if (!match) {
2021
+ return null;
2022
+ }
2023
+ const line = `${match.command} `;
2024
+ return { line, cursor: line.length };
2025
+ }
2026
+ if (trimmed.startsWith("!")) {
2027
+ const suggestion = getPrimaryShellCommandSuggestion(trimmed, { cwd: normalizedOptions.workspaceRoot });
2028
+ if (!suggestion) {
2029
+ return null;
2030
+ }
2031
+ return { line: suggestion, cursor: suggestion.length };
2032
+ }
2033
+ return null;
2034
+ }
2035
+ function normalizePromptSuggestionOptions(options, workspaceRoot, skillsProvider) {
2036
+ if (typeof options === "string") {
2037
+ return {
2038
+ nextPromptSuggestion: options,
2039
+ workspaceRoot,
2040
+ skillsProvider
2041
+ };
2042
+ }
2043
+ return {
2044
+ ...options,
2045
+ workspaceRoot: options?.workspaceRoot ?? workspaceRoot,
2046
+ skillsProvider: options?.skillsProvider ?? skillsProvider
2047
+ };
2048
+ }
2049
+ function getInlineGhostCompletionSuffix(currentLine, files, slashCommands, workspaceRoot, llmSuggestion, skillsProvider) {
2050
+ const trimmed = currentLine.trim();
2051
+ if (!trimmed.startsWith("/") && !trimmed.startsWith("@") && !trimmed.startsWith("!") && !trimmed.startsWith("$")) {
2052
+ return null;
2053
+ }
2054
+ const cleanLlmSuggestion = sanitizeRenderLine(llmSuggestion ?? "");
2055
+ if (cleanLlmSuggestion && cleanLlmSuggestion.startsWith(currentLine) && cleanLlmSuggestion !== currentLine) {
2056
+ return cleanLlmSuggestion.slice(currentLine.length);
2057
+ }
2058
+ const suggestion = getPrimaryHotTipSuggestion(
2059
+ currentLine,
2060
+ files,
2061
+ slashCommands,
2062
+ { workspaceRoot, skillsProvider }
2063
+ );
2064
+ if (!suggestion) {
2065
+ return null;
2066
+ }
2067
+ if (!suggestion.line.startsWith(currentLine)) {
2068
+ return null;
2069
+ }
2070
+ const suffix = suggestion.line.slice(currentLine.length);
2071
+ return suffix.length > 0 ? suffix : null;
2072
+ }
2073
+ function buildContextualHelpPanelLines(currentLine, width, files, slashCommands, skillsProvider) {
2074
+ const panelWidth = Math.max(20, width);
2075
+ const gap = 3;
2076
+ const leftWidth = Math.max(12, Math.floor((panelWidth - gap) / 2));
2077
+ const rightWidth = Math.max(12, panelWidth - leftWidth - gap);
2078
+ const tips = buildPromptHotTips(currentLine, files, slashCommands, void 0, skillsProvider);
2079
+ const primaryTip = tips[0]?.label ?? "Tab -> /help";
2080
+ const secondaryTip = tips[1]?.label ?? "Type /, @, or ! to switch suggestion mode";
2081
+ const formatCell = (value, cellWidth) => {
2082
+ const plain = sanitizeRenderLine(value);
2083
+ return truncatePlainText(plain, cellWidth).padEnd(cellWidth, " ");
2084
+ };
2085
+ const rowLines = CONTEXTUAL_HELP_ROWS.map((row) => {
2086
+ const left = formatCell(row.left, leftWidth);
2087
+ const right = formatCell(row.right, rightWidth);
2088
+ return `${left}${" ".repeat(gap)}${right}`;
2089
+ });
2090
+ const lines = [
2091
+ " ? shortcuts",
2092
+ ...rowLines,
2093
+ "",
2094
+ ` hot tip: ${primaryTip}`,
2095
+ ` tab applies suggestion: ${secondaryTip}`
2096
+ ];
2097
+ return lines.map(
2098
+ (line) => chalk2.bgHex("#2b2b2b").hex("#a8a8a8")(truncatePlainText(line, panelWidth).padEnd(panelWidth, " "))
2099
+ );
2100
+ }
2101
+ function buildContextualPromptStatusLine(currentLine, files, slashCommands, skillsProvider) {
2102
+ const tips = buildPromptHotTips(currentLine, files, slashCommands, void 0, skillsProvider);
2103
+ const primaryTip = tips[0]?.label ?? "Tab -> /help";
2104
+ return `hot tip: ${primaryTip}`;
2105
+ }
2106
+ function buildSlashSuggestionLines(currentLine, width, slashCommands) {
2107
+ const input = currentLine.replace(/^\s+/, "");
2108
+ if (!input.startsWith("/")) {
2109
+ return [];
2110
+ }
2111
+ const panelWidth = Math.max(20, width);
2112
+ const subcommandResult = buildSubcommandSuggestions(input, panelWidth, slashCommands);
2113
+ if (subcommandResult !== null) {
2114
+ return subcommandResult;
2115
+ }
2116
+ const seed = input.slice(1).toLowerCase();
2117
+ const matches = getRankedSlashCommandMatches(seed, slashCommands).slice(0, HOT_TIP_LIMIT);
2118
+ if (matches.length === 0) {
2119
+ return [];
2120
+ }
2121
+ return formatSuggestionLines(
2122
+ matches.map((m) => ({ name: m.command, description: m.description ?? "" })),
2123
+ panelWidth
2124
+ );
2125
+ }
2126
+ function buildSubcommandSuggestions(input, panelWidth, slashCommands) {
2127
+ const spaceIdx = input.indexOf(" ");
2128
+ if (spaceIdx === -1) {
2129
+ return null;
2130
+ }
2131
+ const cmdPart = input.slice(0, spaceIdx).toLowerCase();
2132
+ const subSeed = input.slice(spaceIdx + 1).toLowerCase().trim();
2133
+ const parent = slashCommands.find(
2134
+ (cmd) => cmd.command.toLowerCase() === cmdPart
2135
+ );
2136
+ if (!parent) {
2137
+ return null;
2138
+ }
2139
+ if (!parent.subcommands || parent.subcommands.length === 0) {
2140
+ return [];
2141
+ }
2142
+ const matches = parent.subcommands.filter(
2143
+ (sub) => subSeed === "" ? true : sub.name.toLowerCase().startsWith(subSeed)
2144
+ ).slice(0, HOT_TIP_LIMIT);
2145
+ if (matches.length === 0) {
2146
+ return [];
2147
+ }
2148
+ return formatSuggestionLines(
2149
+ matches.map((m) => ({ name: `${parent.command} ${m.name}`, description: m.description })),
2150
+ panelWidth
2151
+ );
2152
+ }
2153
+ function formatSuggestionLines(entries, panelWidth) {
2154
+ const maxNameLen = Math.min(
2155
+ 24,
2156
+ Math.max(...entries.map((e) => e.name.length))
2157
+ );
2158
+ return entries.map((entry, i) => {
2159
+ const prefix = i === 0 ? " \u25B8 " : " ";
2160
+ const namePadded = entry.name.padEnd(maxNameLen + 1);
2161
+ const content = `${prefix}${namePadded}${entry.description}`;
2162
+ return chalk2.bgHex("#1e1e2e").hex("#cdd6f4")(
2163
+ truncatePlainText(content, panelWidth).padEnd(panelWidth, " ")
2164
+ );
2165
+ });
2166
+ }
2167
+ var PASTED_REFERENCE_PATTERN = /\[Text [Pp]asted(?:\s+\+?\d+\s+(?:chars|lines)|:\s*\d+\s+lines)\]/;
2168
+ function removePastedReferenceFromLine(line) {
2169
+ const match = PASTED_REFERENCE_PATTERN.exec(line);
2170
+ if (!match) {
2171
+ return null;
2172
+ }
2173
+ const start = match.index;
2174
+ const end = start + match[0].length;
2175
+ return {
2176
+ line: `${line.slice(0, start)}${line.slice(end)}`,
2177
+ cursor: start
2178
+ };
2179
+ }
2180
+ function isShiftTabShortcut(str, key) {
2181
+ const sequence = key?.sequence ?? str;
2182
+ return key?.name === "backtab" || key?.name === "tab" && key.shift === true || sequence === "\x1B[Z";
2183
+ }
2184
+ function isPlainTabShortcut(str, key) {
2185
+ if (isShiftTabShortcut(str, key)) {
2186
+ return false;
2187
+ }
2188
+ return key?.name === "tab" || key?.sequence === " " || str === " ";
2189
+ }
2190
+ function isRightArrowAcceptShortcut(key) {
2191
+ return key?.name === "right";
2192
+ }
2193
+ function isShiftEnterSequence(str, key) {
2194
+ if (key?.name === "return" && (key.shift || key.meta)) {
2195
+ return true;
2196
+ }
2197
+ const seq = key?.sequence ?? str ?? "";
2198
+ if (/^\x1b\[13;?[234]?\d*[u~]$/.test(seq)) {
2199
+ return true;
2200
+ }
2201
+ if (/^\x1b\[27;[234];13~$/.test(seq)) {
2202
+ return true;
2203
+ }
2204
+ if (seq === "\x1B\r" || seq === "\x1B\n") {
2205
+ return true;
2206
+ }
2207
+ return false;
2208
+ }
2209
+ function isShiftEnterResidualSequence(sequence) {
2210
+ return SHIFT_ENTER_RESIDUAL_PATTERN.test(sequence ?? "");
2211
+ }
2212
+ function countResidualModifiedEnterSequences(chunk) {
2213
+ if (!chunk) {
2214
+ return 0;
2215
+ }
2216
+ const matches = chunk.match(/(?:13;?[234]?\d*[u~]|27;[234];13~)/g);
2217
+ if (!matches || matches.length === 0) {
2218
+ return 0;
2219
+ }
2220
+ return matches.join("") === chunk ? matches.length : 0;
2221
+ }
2222
+ function countRawModifiedEnterSequences(chunk) {
2223
+ if (!chunk) {
2224
+ return 0;
2225
+ }
2226
+ const matches = chunk.match(/\x1b(?:\[13;?[234]?\d*[u~]|\[27;[234];13~|\r|\n)/g);
2227
+ return matches?.length ?? 0;
2228
+ }
2229
+ function shouldAutoHideShortcutHelp(str, key) {
2230
+ if (isPlainTabShortcut(str, key) || isShiftTabShortcut(str, key)) {
2231
+ return false;
2232
+ }
2233
+ if (key?.ctrl || key?.meta) {
2234
+ return false;
2235
+ }
2236
+ if (key?.name === "escape") {
2237
+ return false;
2238
+ }
2239
+ if (key?.name === "up" || key?.name === "down" || key?.name === "left" || key?.name === "right") {
2240
+ return false;
2241
+ }
2242
+ if (key?.name === "backspace" || key?.name === "delete") {
2243
+ return true;
2244
+ }
2245
+ if (!str) {
2246
+ return false;
2247
+ }
2248
+ return str !== "\r" && str !== "\n";
2249
+ }
2250
+ function sanitizeRenderLine(line) {
2251
+ if (!line) return "";
2252
+ const withoutAnsi = line.replace(/\u001b\[[0-9;]*[A-Za-z]/g, "");
2253
+ return withoutAnsi.replace(/[\x00-\x1F\x7F]/g, "");
2254
+ }
2255
+ function getPromptBlockWidth(columns) {
2256
+ const terminalWidth = Math.max(10, columns ?? 80);
2257
+ return Math.max(10, terminalWidth - 1);
2258
+ }
2259
+ function renderSegment(rawSegment, cursorPos, width, prefix, showPlaceholder, renderOptions, legacyInlineGhostSuffix) {
2260
+ const {
2261
+ placeholderText,
2262
+ nextPromptSuggestion,
2263
+ inlineGhostSuffix
2264
+ } = normalizePromptRenderOptions(renderOptions, legacyInlineGhostSuffix);
2265
+ const sanitizedLine = sanitizeRenderLine(rawSegment);
2266
+ const normalizedLine = sanitizedLine.trim().length === 0 ? "" : sanitizedLine;
2267
+ const innerWidth = Math.max(1, width - 2);
2268
+ const effectiveCursor = Math.max(0, Math.min(normalizedLine.length, cursorPos));
2269
+ const fullInput = `${prefix}${normalizedLine}`;
2270
+ const safeGhostSuffix = sanitizeRenderLine(inlineGhostSuffix ?? "");
2271
+ let visibleText = fullInput;
2272
+ let cursorColumn = prefix.length + effectiveCursor;
2273
+ const fullCursor = prefix.length + effectiveCursor;
2274
+ let ghostFragment = "";
2275
+ if (showPlaceholder && !normalizedLine) {
2276
+ const placeholder = `${prefix}${placeholderText}`;
2277
+ const displayPlaceholder = nextPromptSuggestion?.trim() ? `${prefix}${nextPromptSuggestion}` : placeholder;
2278
+ visibleText = chalk2.gray(displayPlaceholder);
2279
+ cursorColumn = prefix.length;
2280
+ } else if (!normalizedLine) {
2281
+ visibleText = prefix;
2282
+ cursorColumn = prefix.length;
2283
+ } else if (fullInput.length > innerWidth) {
2284
+ const ellipsis = "\u2026";
2285
+ const nearStartThreshold = innerWidth - 1;
2286
+ const nearEndThreshold = innerWidth - 1;
2287
+ if (fullCursor <= nearStartThreshold) {
2288
+ const body = fullInput.slice(0, Math.max(1, innerWidth - 1));
2289
+ visibleText = `${body}${ellipsis}`;
2290
+ cursorColumn = fullCursor;
2291
+ } else if (fullInput.length - fullCursor <= nearEndThreshold) {
2292
+ const start = Math.max(0, fullInput.length - Math.max(1, innerWidth - 1));
2293
+ const body = fullInput.slice(start);
2294
+ visibleText = `${ellipsis}${body}`;
2295
+ cursorColumn = 1 + (fullCursor - start);
2296
+ } else {
2297
+ const windowSize = Math.max(1, innerWidth - 2);
2298
+ const half = Math.floor(windowSize / 2);
2299
+ const minStart = 1;
2300
+ const maxStart = Math.max(minStart, fullInput.length - windowSize - 1);
2301
+ const start = Math.max(minStart, Math.min(maxStart, fullCursor - half));
2302
+ const body = fullInput.slice(start, start + windowSize);
2303
+ visibleText = `${ellipsis}${body}${ellipsis}`;
2304
+ cursorColumn = 1 + (fullCursor - start);
2305
+ }
2306
+ }
2307
+ let styledText = visibleText;
2308
+ if (showPlaceholder && !normalizedLine) {
2309
+ styledText = themedFg("muted", visibleText, (value) => chalk2.gray(value));
2310
+ } else if (visibleText.startsWith(prefix)) {
2311
+ const prefixStyled = themedFg("accent", prefix, (value) => chalk2.gray(value));
2312
+ styledText = `${prefixStyled}${visibleText.slice(prefix.length)}`;
2313
+ } else if (visibleText.startsWith(`\u2026${prefix}`)) {
2314
+ const prefixStyled = themedFg("accent", prefix, (value) => chalk2.gray(value));
2315
+ styledText = `\u2026${prefixStyled}${visibleText.slice(`\u2026${prefix}`.length)}`;
2316
+ }
2317
+ if (normalizedLine && safeGhostSuffix && fullInput.length <= innerWidth && effectiveCursor === normalizedLine.length) {
2318
+ const availableGhostWidth = Math.max(0, innerWidth - fullInput.length);
2319
+ if (availableGhostWidth > 0) {
2320
+ ghostFragment = safeGhostSuffix.slice(0, availableGhostWidth);
2321
+ }
2322
+ }
2323
+ if (ghostFragment) {
2324
+ styledText += themedFg("muted", ghostFragment, (value) => chalk2.gray(value));
2325
+ }
2326
+ return { styledText, cursorColumn };
2327
+ }
2328
+ function normalizePromptRenderOptions(options, legacyInlineGhostSuffix) {
2329
+ if (typeof options === "string") {
2330
+ return {
2331
+ placeholderText: PROMPT_PLACEHOLDER,
2332
+ nextPromptSuggestion: options,
2333
+ inlineGhostSuffix: legacyInlineGhostSuffix ?? ""
2334
+ };
2335
+ }
2336
+ return {
2337
+ placeholderText: options?.placeholderText ?? PROMPT_PLACEHOLDER,
2338
+ nextPromptSuggestion: options?.nextPromptSuggestion ?? "",
2339
+ inlineGhostSuffix: options?.inlineGhostSuffix ?? legacyInlineGhostSuffix ?? ""
2340
+ };
2341
+ }
2342
+ function buildPromptRenderState(currentLine, cursorPos, width, options, inlineGhostSuffix) {
2343
+ const segment = renderSegment(
2344
+ currentLine,
2345
+ cursorPos,
2346
+ width,
2347
+ PROMPT_INPUT_PREFIX,
2348
+ true,
2349
+ options,
2350
+ inlineGhostSuffix
2351
+ );
2352
+ const lineText = drawInputBox(segment.styledText, width);
2353
+ const clampedCursor = Math.max(0, Math.min(width - 1, segment.cursorColumn + 1));
2354
+ return { lineText, cursorColumn: clampedCursor };
2355
+ }
2356
+ function buildMultiLineRenderState(currentLine, cursorPos, width, borderStyle = "default", options, inlineGhostSuffix) {
2357
+ const renderOptions = normalizePromptRenderOptions(options, inlineGhostSuffix);
2358
+ const { segments, separatorLengths } = splitMultilineSegments(currentLine);
2359
+ const innerWidth = Math.max(1, width - 2);
2360
+ const continuationPrefix = " ";
2361
+ const contentWidth = Math.max(1, innerWidth - continuationPrefix.length);
2362
+ if (segments.length <= 1) {
2363
+ const singleSegment = sanitizeRenderLine(segments[0] ?? "");
2364
+ const singleLayout = calculateLayout([singleSegment], contentWidth);
2365
+ if (singleLayout.visualLines.length <= 1) {
2366
+ const seg = renderSegment(
2367
+ currentLine,
2368
+ cursorPos,
2369
+ width,
2370
+ PROMPT_INPUT_PREFIX,
2371
+ true,
2372
+ renderOptions
2373
+ );
2374
+ const lineText = drawInputBox(seg.styledText, width, void 0, borderStyle);
2375
+ const clampedCursor = Math.max(0, Math.min(width - 1, seg.cursorColumn + 1));
2376
+ return { lines: [lineText], cursorRow: 0, cursorColumn: clampedCursor, lineCount: 1 };
2377
+ }
2378
+ }
2379
+ if (currentLine.length === 0) {
2380
+ const seg = renderSegment(
2381
+ currentLine,
2382
+ cursorPos,
2383
+ width,
2384
+ PROMPT_INPUT_PREFIX,
2385
+ true,
2386
+ renderOptions
2387
+ );
2388
+ const lineText = drawInputBox(seg.styledText, width, void 0, borderStyle);
2389
+ const clampedCursor = Math.max(0, Math.min(width - 1, seg.cursorColumn + 1));
2390
+ return { lines: [lineText], cursorRow: 0, cursorColumn: clampedCursor, lineCount: 1 };
2391
+ }
2392
+ let cursorRow = 0;
2393
+ let cursorInSegment = 0;
2394
+ let pos = 0;
2395
+ for (let i = 0; i < segments.length; i++) {
2396
+ const segEnd = pos + segments[i].length;
2397
+ if (cursorPos <= segEnd || i === segments.length - 1) {
2398
+ cursorRow = i;
2399
+ cursorInSegment = Math.max(0, cursorPos - pos);
2400
+ break;
2401
+ }
2402
+ pos = segEnd + (separatorLengths[i] ?? 0);
2403
+ }
2404
+ const lines = [];
2405
+ let visualRowOffset = 0;
2406
+ let overallVisualRow = 0;
2407
+ let hasPromptPrefix = false;
2408
+ let finalCursorColumn = 0;
2409
+ for (let i = 0; i < segments.length; i++) {
2410
+ const sanitizedSegment = sanitizeRenderLine(segments[i] ?? "");
2411
+ const layout = calculateLayout([sanitizedSegment], contentWidth);
2412
+ const wrappedLines = layout.visualLines.length > 0 ? layout.visualLines : [""];
2413
+ if (i === cursorRow) {
2414
+ const [wrappedCursorRow, wrappedCursorCol] = logicalToVisual(
2415
+ layout,
2416
+ 0,
2417
+ Math.max(0, Math.min(sanitizedSegment.length, cursorInSegment))
2418
+ );
2419
+ cursorRow = visualRowOffset + wrappedCursorRow;
2420
+ finalCursorColumn = Math.max(
2421
+ 0,
2422
+ Math.min(width - 1, continuationPrefix.length + wrappedCursorCol + 1)
2423
+ );
2424
+ }
2425
+ for (let j = 0; j < wrappedLines.length; j++) {
2426
+ const prefix = !hasPromptPrefix ? PROMPT_INPUT_PREFIX : continuationPrefix;
2427
+ const prefixStyled = themedFg("accent", prefix, (value) => chalk2.gray(value));
2428
+ const styledText = `${prefixStyled}${wrappedLines[j] ?? ""}`;
2429
+ lines.push(drawInputBox(styledText, width, void 0, borderStyle));
2430
+ hasPromptPrefix = true;
2431
+ overallVisualRow += 1;
2432
+ }
2433
+ visualRowOffset += wrappedLines.length;
2434
+ }
2435
+ return {
2436
+ lines,
2437
+ cursorRow,
2438
+ cursorColumn: finalCursorColumn,
2439
+ lineCount: overallVisualRow
2440
+ };
2441
+ }
2442
+ function splitMultilineSegments(value) {
2443
+ const segments = [];
2444
+ const separatorLengths = [];
2445
+ let segmentStart = 0;
2446
+ let i = 0;
2447
+ while (i < value.length) {
2448
+ if (value.startsWith(NEWLINE_MARKER, i)) {
2449
+ segments.push(value.slice(segmentStart, i));
2450
+ separatorLengths.push(NEWLINE_MARKER.length);
2451
+ i += NEWLINE_MARKER.length;
2452
+ segmentStart = i;
2453
+ continue;
2454
+ }
2455
+ const ch = value[i];
2456
+ if (ch === "\n") {
2457
+ segments.push(value.slice(segmentStart, i));
2458
+ separatorLengths.push(1);
2459
+ i += 1;
2460
+ segmentStart = i;
2461
+ continue;
2462
+ }
2463
+ if (ch === "\r") {
2464
+ const separatorLength = value[i + 1] === "\n" ? 2 : 1;
2465
+ segments.push(value.slice(segmentStart, i));
2466
+ separatorLengths.push(separatorLength);
2467
+ i += separatorLength;
2468
+ segmentStart = i;
2469
+ continue;
2470
+ }
2471
+ i += 1;
2472
+ }
2473
+ segments.push(value.slice(segmentStart));
2474
+ return { segments, separatorLengths };
2475
+ }
2476
+ function formatPromptStatusRow(statusLine, width) {
2477
+ let left;
2478
+ let right;
2479
+ if (typeof statusLine === "object" && statusLine !== null) {
2480
+ left = statusLine.left ?? "";
2481
+ right = statusLine.right || void 0;
2482
+ } else {
2483
+ left = statusLine ?? " ";
2484
+ }
2485
+ const plainLeft = stripAnsiCodes(left);
2486
+ let plainRight = right ? stripAnsiCodes(right) : "";
2487
+ if (plainRight.length > width) {
2488
+ plainRight = truncatePlainText(plainRight, width);
2489
+ }
2490
+ const minGap = plainRight ? 2 : 0;
2491
+ const availableForLeft = Math.max(0, width - plainRight.length - minGap);
2492
+ const clippedLeft = truncatePlainText(plainLeft, availableForLeft);
2493
+ const gap = Math.max(0, width - clippedLeft.length - plainRight.length);
2494
+ const row = `${clippedLeft}${" ".repeat(gap)}${plainRight}`;
2495
+ return themedFg("muted", row.padEnd(width), (value) => chalk2.gray(value));
2496
+ }
2497
+ function createPasteState() {
2498
+ return {
2499
+ isInPaste: false,
2500
+ buffer: "",
2501
+ outputSuppressed: false
2502
+ };
2503
+ }
2504
+ function installReadlineOutputGuard(rl) {
2505
+ const rlWriter = rl;
2506
+ const originalWriteToOutput = typeof rlWriter._writeToOutput === "function" ? rlWriter._writeToOutput.bind(rlWriter) : void 0;
2507
+ if (!originalWriteToOutput) {
2508
+ return {
2509
+ setSuppressed: () => {
2510
+ },
2511
+ restore: () => {
2512
+ }
2513
+ };
2514
+ }
2515
+ let suppressed = false;
2516
+ rlWriter._writeToOutput = (chunk) => {
2517
+ if (!suppressed) {
2518
+ originalWriteToOutput(chunk);
2519
+ }
2520
+ };
2521
+ return {
2522
+ setSuppressed: (nextSuppressed) => {
2523
+ suppressed = nextSuppressed;
2524
+ },
2525
+ restore: () => {
2526
+ rlWriter._writeToOutput = originalWriteToOutput;
2527
+ }
2528
+ };
2529
+ }
2530
+ var IMAGE_FILE_EXTENSIONS = "(?:png|jpg|jpeg|gif|webp)";
2531
+ var IMAGE_FILE_SUFFIX_REGEX = new RegExp(`\\.${IMAGE_FILE_EXTENSIONS}$`, "i");
2532
+ var ASCII_WHITESPACE = "[ \\t\\r\\n]";
2533
+ function createEscapedImagePathRegex(flags) {
2534
+ return new RegExp(
2535
+ `(?:^|${ASCII_WHITESPACE})((\\/|~)(?:[^ \\t\\r\\n\\\\]|\\\\.)+\\.${IMAGE_FILE_EXTENSIONS})(?=${ASCII_WHITESPACE}|$)`,
2536
+ flags
2537
+ );
2538
+ }
2539
+ function createSimpleImagePathRegex(flags) {
2540
+ return new RegExp(
2541
+ `(?:^|${ASCII_WHITESPACE})([^ \\t\\r\\n"']+\\.${IMAGE_FILE_EXTENSIONS})(?=${ASCII_WHITESPACE}|$)`,
2542
+ flags
2543
+ );
2544
+ }
2545
+ function hasPotentialImagePath(text) {
2546
+ return createEscapedImagePathRegex("i").test(text) || createSimpleImagePathRegex("i").test(text);
2547
+ }
2548
+ function writeImageNotice(output, message, announce) {
2549
+ if (!announce || !output) {
2550
+ return;
2551
+ }
2552
+ output.write(chalk2.cyan(`
2553
+ ${message}
2554
+ `));
2555
+ }
2556
+ function normalizeDragPathCandidates(rawPath) {
2557
+ const trimmed = rawPath.trim();
2558
+ if (!trimmed) return [];
2559
+ const unquoted = trimmed.replace(/^"(.*)"$/s, "$1").replace(/^'(.*)'$/s, "$1");
2560
+ const unescaped = unquoted.replace(/\\([ \t\r\n\u00a0\u202f])/g, "$1").replace(/\\([\\'"()])/g, "$1");
2561
+ const broadlyUnescaped = unquoted.replace(/\\(.)/g, "$1");
2562
+ const withHomeExpanded = (value) => {
2563
+ if (value.startsWith("~/")) {
2564
+ return `${os.homedir()}/${value.slice(2)}`;
2565
+ }
2566
+ return value;
2567
+ };
2568
+ const candidates = /* @__PURE__ */ new Set();
2569
+ const push = (value) => {
2570
+ if (!value) return;
2571
+ candidates.add(value);
2572
+ candidates.add(withHomeExpanded(value));
2573
+ candidates.add(value.replace(/\u202f/g, " "));
2574
+ candidates.add(withHomeExpanded(value.replace(/\u202f/g, " ")));
2575
+ candidates.add(value.replace(/\u00a0/g, " "));
2576
+ candidates.add(withHomeExpanded(value.replace(/\u00a0/g, " ")));
2577
+ const withNNBSP = value.replace(/ (?=(?:AM|PM)\.)/gi, "\u202F");
2578
+ if (withNNBSP !== value) {
2579
+ candidates.add(withNNBSP);
2580
+ candidates.add(withHomeExpanded(withNNBSP));
2581
+ }
2582
+ };
2583
+ push(unquoted);
2584
+ push(unescaped);
2585
+ push(broadlyUnescaped);
2586
+ return Array.from(candidates).filter(Boolean);
2587
+ }
2588
+ function processImagesInText(text, onImageDetected, options = {}) {
2589
+ if (!onImageDetected) {
2590
+ return text;
2591
+ }
2592
+ const announce = options.announce ?? true;
2593
+ const output = options.output;
2594
+ let result = text;
2595
+ const replaceImagePath = (rawMatch) => {
2596
+ const candidates = normalizeDragPathCandidates(rawMatch);
2597
+ for (const candidatePath of candidates) {
2598
+ if (!existsSync(candidatePath)) continue;
2599
+ try {
2600
+ const data = readFileSync(candidatePath);
2601
+ const ext = extname(candidatePath);
2602
+ const mimeType = getMimeTypeFromExtension(ext);
2603
+ if (!mimeType) continue;
2604
+ const id = onImageDetected(data, mimeType, basename(candidatePath));
2605
+ result = result.replace(rawMatch, `[Image #${id}]`);
2606
+ writeImageNotice(output, `\u{1F4F7} Loaded image: ${candidatePath} -> [Image #${id}]`, announce);
2607
+ return true;
2608
+ } catch {
2609
+ }
2610
+ }
2611
+ return false;
2612
+ };
2613
+ const base64Regex = /data:image\/[a-z]+;base64,[A-Za-z0-9+/=]+/g;
2614
+ const base64Matches = result.match(base64Regex) || [];
2615
+ for (const dataUrl of base64Matches) {
2616
+ const parsed = parseBase64DataUrl(dataUrl);
2617
+ if (!parsed) continue;
2618
+ const id = onImageDetected(parsed.data, parsed.mimeType);
2619
+ result = result.replace(dataUrl, `[Image #${id}]`);
2620
+ writeImageNotice(output, `\u{1F4F7} Detected base64 image -> [Image #${id}]`, announce);
2621
+ }
2622
+ const quotedPathRegex = new RegExp(`["']([^"']+\\.${IMAGE_FILE_EXTENSIONS})["']`, "gi");
2623
+ let quotedMatch;
2624
+ while ((quotedMatch = quotedPathRegex.exec(text)) !== null) {
2625
+ const fullMatch = quotedMatch[0];
2626
+ if (!result.includes(fullMatch)) continue;
2627
+ replaceImagePath(quotedMatch[1]) || replaceImagePath(fullMatch);
2628
+ }
2629
+ const escapedPathRegex = createEscapedImagePathRegex("gi");
2630
+ let escapedMatch;
2631
+ if (process.env.DEBUG_IMAGES && output) {
2632
+ output.write(chalk2.gray(`
2633
+ [DEBUG] processImagesInText called
2634
+ `));
2635
+ output.write(chalk2.gray(`[DEBUG] Input text: ${JSON.stringify(text)}
2636
+ `));
2637
+ output.write(chalk2.gray(`[DEBUG] Has backslash: ${text.includes("\\")}
2638
+ `));
2639
+ output.write(chalk2.gray(`[DEBUG] Char codes: ${text.slice(0, 50).split("").map((c) => c.charCodeAt(0)).join(",")}
2640
+ `));
2641
+ }
2642
+ while ((escapedMatch = escapedPathRegex.exec(text)) !== null) {
2643
+ const rawPath = escapedMatch[1];
2644
+ if (process.env.DEBUG_IMAGES && output) {
2645
+ output.write(chalk2.gray(`[DEBUG] Regex matched: ${JSON.stringify(rawPath)}
2646
+ `));
2647
+ const candidates = normalizeDragPathCandidates(rawPath);
2648
+ output.write(chalk2.gray(`[DEBUG] Candidate paths: ${JSON.stringify(candidates)}
2649
+ `));
2650
+ }
2651
+ if (!result.includes(rawPath)) continue;
2652
+ replaceImagePath(rawPath);
2653
+ }
2654
+ const simplePathRegex = createSimpleImagePathRegex("gi");
2655
+ let simpleMatch;
2656
+ while ((simpleMatch = simplePathRegex.exec(text)) !== null) {
2657
+ const filePath = simpleMatch[1];
2658
+ if (!result.includes(filePath)) {
2659
+ continue;
2660
+ }
2661
+ replaceImagePath(filePath);
2662
+ }
2663
+ if (!result.includes("[Image #")) {
2664
+ const trimmed = result.trim();
2665
+ if (IMAGE_FILE_SUFFIX_REGEX.test(trimmed)) {
2666
+ replaceImagePath(trimmed);
2667
+ }
2668
+ }
2669
+ return result;
2670
+ }
2671
+ var NEWLINE_MARKER = " \u21B5 ";
2672
+ var NEWLINE_MARKER_REGEX = new RegExp(escapeRegex(NEWLINE_MARKER), "g");
2673
+ function escapeRegex(value) {
2674
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2675
+ }
2676
+ var instrumentedStreams = /* @__PURE__ */ new WeakSet();
2677
+ function safeEmitKeypressEvents(stream) {
2678
+ if (!instrumentedStreams.has(stream)) {
2679
+ readline2.emitKeypressEvents(stream);
2680
+ instrumentedStreams.add(stream);
2681
+ }
2682
+ }
2683
+ function convertNewlineMarkersToNewlines(text) {
2684
+ return text.replace(NEWLINE_MARKER_REGEX, "\n").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2685
+ }
2686
+ async function readInstruction(filesProvider, slashCommands, statusLine, io = {}, onImageDetected, workspaceRoot, initialValue = "", nextPromptSuggestionProvider, resolveShellSuggestion, pendingSuggestion, skillsProvider) {
2687
+ const stdInput = io.input ?? process.stdin;
2688
+ const stdOutput = io.output ?? process.stdout;
2689
+ const keepAlive = setInterval(() => {
2690
+ }, 1e4);
2691
+ try {
2692
+ while (true) {
2693
+ await new Promise((resolve) => process.nextTick(resolve));
2694
+ const result = await promptOnce({
2695
+ filesProvider,
2696
+ slashCommands,
2697
+ statusLine,
2698
+ initialValue,
2699
+ stdInput,
2700
+ stdOutput,
2701
+ onImageDetected,
2702
+ workspaceRoot,
2703
+ nextPromptSuggestionProvider,
2704
+ resolveShellSuggestion,
2705
+ pendingSuggestion,
2706
+ skillsProvider
2707
+ });
2708
+ if (result.kind === "abort") {
2709
+ return "ABORT";
2710
+ }
2711
+ return result.value;
2712
+ }
2713
+ } finally {
2714
+ clearInterval(keepAlive);
2715
+ }
2716
+ }
2717
+ function drainStdin(input) {
2718
+ let chunk;
2719
+ do {
2720
+ chunk = input.read();
2721
+ } while (chunk !== null);
2722
+ }
2723
+ function createReadline(stdInput, stdOutput) {
2724
+ stdOutput.write("\r");
2725
+ safeEmitKeypressEvents(stdInput);
2726
+ try {
2727
+ stdInput.resume();
2728
+ drainStdin(stdInput);
2729
+ } catch {
2730
+ }
2731
+ enableBracketedPaste(stdOutput);
2732
+ try {
2733
+ stdInput.resume();
2734
+ } catch {
2735
+ }
2736
+ let rl;
2737
+ try {
2738
+ rl = readline2.createInterface({
2739
+ input: stdInput,
2740
+ output: stdOutput,
2741
+ prompt: PROMPT_PREFIX,
2742
+ terminal: true,
2743
+ crlfDelay: Infinity,
2744
+ historySize: 100,
2745
+ tabSize: 2
2746
+ });
2747
+ } catch {
2748
+ rl = readline2.createInterface({
2749
+ input: stdInput,
2750
+ output: stdOutput,
2751
+ prompt: PROMPT_PREFIX,
2752
+ terminal: false,
2753
+ crlfDelay: Infinity,
2754
+ historySize: 100,
2755
+ tabSize: 2
2756
+ });
2757
+ }
2758
+ disableReadlineTabBehavior(rl);
2759
+ const input = rl.input;
2760
+ const supportsRawMode = typeof input.setRawMode === "function";
2761
+ if (supportsRawMode && input.isTTY) {
2762
+ safeSetRawMode(input, true);
2763
+ }
2764
+ input.resume();
2765
+ input.setEncoding("utf8");
2766
+ return { rl, input, supportsRawMode };
2767
+ }
2768
+ function leavePromptSurface(output, statusLineCount = STATUS_LINE_COUNT, fromLineEvent = false) {
2769
+ const numContentLines = lastRenderedContentLines;
2770
+ const cursorRow = lastRenderedCursorRow;
2771
+ if (fromLineEvent) {
2772
+ const normalizationRows = PROMPT_LINES_BELOW_INPUT;
2773
+ for (let i = 0; i < normalizationRows; i++) {
2774
+ readline2.moveCursor(output, 0, -1);
2775
+ }
2776
+ }
2777
+ readline2.cursorTo(output, 0);
2778
+ readline2.clearLine(output, 0);
2779
+ for (let i = 0; i < cursorRow + PROMPT_LINES_ABOVE_INPUT; i++) {
2780
+ readline2.moveCursor(output, 0, -1);
2781
+ readline2.clearLine(output, 0);
2782
+ }
2783
+ for (let i = 0; i < cursorRow + PROMPT_LINES_ABOVE_INPUT; i++) {
2784
+ readline2.moveCursor(output, 0, 1);
2785
+ }
2786
+ const belowCount = numContentLines - 1 - cursorRow + PROMPT_LINES_BELOW_INPUT + lastRenderedHelpLines + statusLineCount + lastRenderedSlashLines;
2787
+ for (let i = 0; i < belowCount; i++) {
2788
+ readline2.moveCursor(output, 0, 1);
2789
+ readline2.clearLine(output, 0);
2790
+ }
2791
+ const upToTop = PROMPT_LINES_ABOVE_INPUT + cursorRow + belowCount;
2792
+ readline2.moveCursor(output, 0, -upToTop);
2793
+ readline2.cursorTo(output, 0);
2794
+ }
2795
+ function handlePasteComplete(pasteState, rl, output, renderActivePrompt) {
2796
+ const display = getContentDisplay(pasteState.buffer);
2797
+ const rlAny = rl;
2798
+ const prefix = pasteState.prefixContent || "";
2799
+ if (!pasteState.outputSuppressed) {
2800
+ const newlineCount = (pasteState.buffer.match(/\n/g) || []).length;
2801
+ for (let i = 0; i < newlineCount; i++) {
2802
+ readline2.moveCursor(output, 0, -1);
2803
+ readline2.clearLine(output, 0);
2804
+ }
2805
+ readline2.cursorTo(output, 0);
2806
+ readline2.clearLine(output, 0);
2807
+ }
2808
+ if (display.isPasted) {
2809
+ pasteState.hiddenContent = prefix + display.actual;
2810
+ rlAny.line = prefix + display.visual;
2811
+ rlAny.cursor = rlAny.line.length;
2812
+ } else {
2813
+ rlAny.line = prefix + display.actual;
2814
+ rlAny.cursor = rlAny.line.length;
2815
+ }
2816
+ renderActivePrompt();
2817
+ pasteState.buffer = "";
2818
+ pasteState.outputSuppressed = false;
2819
+ pasteState.prefixContent = void 0;
2820
+ }
2821
+ async function promptOnce(options) {
2822
+ const {
2823
+ filesProvider,
2824
+ slashCommands,
2825
+ statusLine,
2826
+ initialValue,
2827
+ stdInput,
2828
+ stdOutput,
2829
+ onImageDetected,
2830
+ workspaceRoot,
2831
+ nextPromptSuggestionProvider,
2832
+ resolveShellSuggestion,
2833
+ pendingSuggestion,
2834
+ skillsProvider
2835
+ } = options;
2836
+ resetPromptRenderState();
2837
+ const { rl, input, supportsRawMode } = createReadline(stdInput, stdOutput);
2838
+ const tbWidth = Math.max(1, getPromptBlockWidth(stdOutput.columns) - 2);
2839
+ const tbMaxVisibleLines = 10;
2840
+ const initialLine = sanitizeRenderLine(initialValue ?? "");
2841
+ const textBuffer = new TextBuffer(tbWidth, tbMaxVisibleLines, initialLine || void 0);
2842
+ activeTextBuffer = textBuffer;
2843
+ const mentionPreview = new MentionPreview(
2844
+ rl,
2845
+ filesProvider,
2846
+ slashCommands,
2847
+ stdOutput,
2848
+ skillsProvider ?? (() => []),
2849
+ (line, cursorPos) => {
2850
+ textBuffer.setText(line);
2851
+ textBuffer.setCursorPosition(0, cursorPos);
2852
+ syncReadlineFromBuffer();
2853
+ }
2854
+ );
2855
+ const pasteState = createPasteState();
2856
+ let contextualHelpVisible = false;
2857
+ let llmInlineShellSuggestion = null;
2858
+ let chordState = "none";
2859
+ let chordTimeout = null;
2860
+ const applyPlanModePrefix = (line) => {
2861
+ const planPrefix = getPlanModeManager().isEnabled() ? "plan:on" : "plan:off";
2862
+ if (!line) {
2863
+ return planPrefix;
2864
+ }
2865
+ if (line.startsWith("plan:on \xB7 ") || line.startsWith("plan:off \xB7 ")) {
2866
+ const separatorIndex = line.indexOf(" \xB7 ");
2867
+ return `${planPrefix}${line.slice(separatorIndex)}`;
2868
+ }
2869
+ return `${planPrefix} \xB7 ${line}`;
2870
+ };
2871
+ const getActiveStatusLine = () => {
2872
+ if (typeof statusLine === "object" && statusLine !== null) {
2873
+ return statusLine;
2874
+ }
2875
+ return applyPlanModePrefix(statusLine ?? "");
2876
+ };
2877
+ const getCurrentText = () => textBuffer.getText();
2878
+ const syncReadlineFromBuffer = () => {
2879
+ const rlAny = rl;
2880
+ const text = textBuffer.getText();
2881
+ const flat = text.replace(/\n/g, NEWLINE_MARKER);
2882
+ rlAny.line = flat;
2883
+ rlAny.cursor = flat.length;
2884
+ };
2885
+ const getInlineGhostSuffix = () => {
2886
+ if (contextualHelpVisible) {
2887
+ return void 0;
2888
+ }
2889
+ const currentText = getCurrentText();
2890
+ if (!currentText || currentText.includes("\n") || currentText.includes(NEWLINE_MARKER)) {
2891
+ return void 0;
2892
+ }
2893
+ return getInlineGhostCompletionSuffix(
2894
+ currentText,
2895
+ filesProvider(),
2896
+ slashCommands,
2897
+ workspaceRoot,
2898
+ llmInlineShellSuggestion,
2899
+ skillsProvider
2900
+ ) ?? void 0;
2901
+ };
2902
+ const getHelpPanelLines = () => {
2903
+ if (!contextualHelpVisible) {
2904
+ return void 0;
2905
+ }
2906
+ const width = getPromptBlockWidth(stdOutput.columns);
2907
+ return buildContextualHelpPanelLines(getCurrentText(), width, filesProvider(), slashCommands, skillsProvider);
2908
+ };
2909
+ const getSlashSuggestionLines = () => {
2910
+ if (contextualHelpVisible) {
2911
+ return void 0;
2912
+ }
2913
+ const width = getPromptBlockWidth(stdOutput.columns);
2914
+ const lines = buildSlashSuggestionLines(getCurrentText(), width, slashCommands);
2915
+ return lines.length > 0 ? lines : void 0;
2916
+ };
2917
+ let resizeDetectedAt = 0;
2918
+ const renderPromptSurface = (isResize = false, hasExistingPromptBlock = true) => {
2919
+ renderPromptLine(
2920
+ rl,
2921
+ getActiveStatusLine(),
2922
+ stdOutput,
2923
+ isResize,
2924
+ hasExistingPromptBlock,
2925
+ nextPromptSuggestionProvider?.(),
2926
+ getInlineGhostSuffix(),
2927
+ getHelpPanelLines(),
2928
+ getSlashSuggestionLines()
2929
+ );
2930
+ };
2931
+ const resizeWatcher = new TerminalResizeWatcher(stdOutput, () => {
2932
+ resizeDetectedAt = Date.now();
2933
+ const newWidth = Math.max(1, getPromptBlockWidth(stdOutput.columns) - 2);
2934
+ textBuffer.setViewport(newWidth, tbMaxVisibleLines);
2935
+ renderPromptSurface(true, true);
2936
+ mentionPreview.handleResize();
2937
+ });
2938
+ syncReadlineFromBuffer();
2939
+ renderPromptSurface(false, false);
2940
+ if (initialLine.length > 0) {
2941
+ renderPromptSurface(false, true);
2942
+ }
2943
+ return new Promise((resolve) => {
2944
+ let ctrlCCount = 0;
2945
+ let closed = false;
2946
+ let suppressResidualShiftEnterCharsUntil = 0;
2947
+ let inlineImageScanTimeout;
2948
+ let inlineImageRetryCount = 0;
2949
+ const MAX_INLINE_IMAGE_RETRIES = 12;
2950
+ let shellSuggestionRequestId = 0;
2951
+ let inlineShellSuggestionTimeout;
2952
+ let inlineShellSuggestionRequestId = 0;
2953
+ const rlInternal = rl;
2954
+ const originalRefreshLine = typeof rlInternal._refreshLine === "function" ? rlInternal._refreshLine.bind(rlInternal) : void 0;
2955
+ const originalMoveCursor = typeof rlInternal._moveCursor === "function" ? rlInternal._moveCursor.bind(rlInternal) : void 0;
2956
+ const RESIZE_COOLDOWN_MS = 200;
2957
+ const outputGuard = installReadlineOutputGuard(rl);
2958
+ const setContextualHelpVisible = (visible) => {
2959
+ if (contextualHelpVisible === visible) {
2960
+ return;
2961
+ }
2962
+ contextualHelpVisible = visible;
2963
+ mentionPreview.setSuspended(visible);
2964
+ if (visible) {
2965
+ mentionPreview.reset();
2966
+ }
2967
+ if (!closed) {
2968
+ renderPromptSurface(false, true);
2969
+ }
2970
+ };
2971
+ function renderActivePrompt() {
2972
+ renderPromptSurface(false, true);
2973
+ }
2974
+ function isTextBufferCursorAtEnd() {
2975
+ const lines = textBuffer.getLines();
2976
+ const lastLine = lines[lines.length - 1] ?? "";
2977
+ return textBuffer.getCursorRow() === lines.length - 1 && textBuffer.getCursorCol() === Array.from(lastLine).length;
2978
+ }
2979
+ function applyPromptSuggestion(suggestion) {
2980
+ if (!suggestion) {
2981
+ return false;
2982
+ }
2983
+ textBuffer.setText(suggestion.line);
2984
+ syncReadlineFromBuffer();
2985
+ renderActivePrompt();
2986
+ return true;
2987
+ }
2988
+ function getCurrentPrimarySuggestion() {
2989
+ return getPrimaryHotTipSuggestion(
2990
+ getCurrentText(),
2991
+ filesProvider(),
2992
+ slashCommands,
2993
+ {
2994
+ nextPromptSuggestion: nextPromptSuggestionProvider?.(),
2995
+ workspaceRoot,
2996
+ skillsProvider
2997
+ }
2998
+ );
2999
+ }
3000
+ let renderScheduled = false;
3001
+ function scheduleRender() {
3002
+ if (renderScheduled) return;
3003
+ renderScheduled = true;
3004
+ setImmediate(() => {
3005
+ renderScheduled = false;
3006
+ if (!closed && !pasteState.isInPaste) {
3007
+ renderActivePrompt();
3008
+ }
3009
+ });
3010
+ }
3011
+ if (pendingSuggestion) {
3012
+ pendingSuggestion.then(() => {
3013
+ if (!closed && getCurrentText() === "" && nextPromptSuggestionProvider?.()) {
3014
+ scheduleRender();
3015
+ }
3016
+ }).catch(() => {
3017
+ });
3018
+ }
3019
+ const cleanup = () => {
3020
+ if (closed) return;
3021
+ closed = true;
3022
+ activeTextBuffer = null;
3023
+ if (pasteState.timeout) {
3024
+ clearTimeout(pasteState.timeout);
3025
+ pasteState.timeout = void 0;
3026
+ }
3027
+ if (inlineImageScanTimeout) {
3028
+ clearTimeout(inlineImageScanTimeout);
3029
+ inlineImageScanTimeout = void 0;
3030
+ }
3031
+ if (inlineShellSuggestionTimeout) {
3032
+ clearTimeout(inlineShellSuggestionTimeout);
3033
+ inlineShellSuggestionTimeout = void 0;
3034
+ }
3035
+ disableBracketedPaste(stdOutput);
3036
+ stdOutput.write("\x1B[?25h");
3037
+ if (contextualHelpVisible) {
3038
+ contextualHelpVisible = false;
3039
+ }
3040
+ mentionPreview.dispose();
3041
+ resizeWatcher.dispose();
3042
+ promptEvents.off("notify", onPromptNotify);
3043
+ promptEvents.off("interrupt", onPromptInterrupt);
3044
+ input.off("keypress", handleKeypress);
3045
+ input.off("data", handleInputData);
3046
+ if (originalRefreshLine) {
3047
+ rlInternal._refreshLine = originalRefreshLine;
3048
+ }
3049
+ if (originalMoveCursor) {
3050
+ rlInternal._moveCursor = originalMoveCursor;
3051
+ }
3052
+ outputGuard.restore();
3053
+ if (supportsRawMode && input.isTTY) {
3054
+ safeSetRawMode(input, false);
3055
+ }
3056
+ input.pause();
3057
+ rl.close();
3058
+ };
3059
+ const showPromptMessage = (message) => {
3060
+ mentionPreview.reset();
3061
+ if (contextualHelpVisible) {
3062
+ setContextualHelpVisible(false);
3063
+ }
3064
+ leavePromptSurface(stdOutput);
3065
+ stdOutput.write(`${message.replace(/\n+$/g, "")}
3066
+ `);
3067
+ renderPromptSurface(false, false);
3068
+ };
3069
+ const onPromptNotify = (msg) => showPromptMessage(msg);
3070
+ promptEvents.on("notify", onPromptNotify);
3071
+ const onPromptInterrupt = (value) => {
3072
+ if (closed) return;
3073
+ mentionPreview.reset();
3074
+ if (contextualHelpVisible) {
3075
+ setContextualHelpVisible(false);
3076
+ }
3077
+ leavePromptSurface(stdOutput, STATUS_LINE_COUNT);
3078
+ cleanup();
3079
+ resolve({ kind: "submit", value });
3080
+ };
3081
+ promptEvents.on("interrupt", onPromptInterrupt);
3082
+ const refreshLine = () => {
3083
+ renderActivePrompt();
3084
+ };
3085
+ if (typeof rlInternal._refreshLine === "function") {
3086
+ rlInternal._refreshLine = () => {
3087
+ if (!closed && !pasteState.isInPaste) {
3088
+ if (resizeDetectedAt > 0 && Date.now() - resizeDetectedAt < RESIZE_COOLDOWN_MS) {
3089
+ return;
3090
+ }
3091
+ scheduleRender();
3092
+ }
3093
+ };
3094
+ }
3095
+ if (typeof rlInternal._moveCursor === "function") {
3096
+ rlInternal._moveCursor = (...args) => {
3097
+ originalMoveCursor?.(...args);
3098
+ if (!closed && !pasteState.isInPaste) {
3099
+ scheduleRender();
3100
+ }
3101
+ };
3102
+ }
3103
+ const rlTtyWrite = rl;
3104
+ const originalTtyWrite = rlTtyWrite._ttyWrite?.bind(rl);
3105
+ if (originalTtyWrite) {
3106
+ rlTtyWrite._ttyWrite = (s, _key) => {
3107
+ if (pasteState.isInPaste) {
3108
+ return;
3109
+ }
3110
+ if (/^(?:13;?[234]?\d*[u~]|27;[234];13~)$/.test(s)) {
3111
+ textBuffer.insert("\n");
3112
+ syncReadlineFromBuffer();
3113
+ renderActivePrompt();
3114
+ return;
3115
+ }
3116
+ return;
3117
+ };
3118
+ }
3119
+ const applyDetectedImagesToLine = (processedText) => {
3120
+ const display = getContentDisplay(processedText);
3121
+ if (display.isPasted) {
3122
+ pasteState.hiddenContent = display.actual;
3123
+ textBuffer.setText(display.visual);
3124
+ } else {
3125
+ pasteState.hiddenContent = void 0;
3126
+ textBuffer.setText(display.actual);
3127
+ }
3128
+ syncReadlineFromBuffer();
3129
+ refreshLine();
3130
+ };
3131
+ const replaceDroppedImagesInline = () => {
3132
+ if (!onImageDetected) {
3133
+ return false;
3134
+ }
3135
+ const sourceText = pasteState.hiddenContent ?? getCurrentText();
3136
+ if (!sourceText) {
3137
+ inlineImageRetryCount = 0;
3138
+ return false;
3139
+ }
3140
+ const processed = processImagesInText(sourceText, onImageDetected, {
3141
+ announce: false,
3142
+ output: stdOutput
3143
+ });
3144
+ if (processed !== sourceText) {
3145
+ inlineImageRetryCount = 0;
3146
+ applyDetectedImagesToLine(processed);
3147
+ return true;
3148
+ }
3149
+ if (hasPotentialImagePath(sourceText) && inlineImageRetryCount < MAX_INLINE_IMAGE_RETRIES) {
3150
+ inlineImageRetryCount += 1;
3151
+ scheduleInlineImageScan(180);
3152
+ } else if (!hasPotentialImagePath(sourceText)) {
3153
+ inlineImageRetryCount = 0;
3154
+ }
3155
+ return false;
3156
+ };
3157
+ const scheduleInlineImageScan = (delayMs = 75) => {
3158
+ if (!onImageDetected || pasteState.isInPaste) {
3159
+ return;
3160
+ }
3161
+ if (inlineImageScanTimeout) {
3162
+ clearTimeout(inlineImageScanTimeout);
3163
+ }
3164
+ inlineImageScanTimeout = setTimeout(() => {
3165
+ inlineImageScanTimeout = void 0;
3166
+ if (!closed && !pasteState.isInPaste) {
3167
+ replaceDroppedImagesInline();
3168
+ }
3169
+ }, delayMs);
3170
+ };
3171
+ const scheduleInlineShellSuggestion = (delayMs = 120) => {
3172
+ if (!resolveShellSuggestion || pasteState.isInPaste || contextualHelpVisible) {
3173
+ return;
3174
+ }
3175
+ const sourceLine = getCurrentText();
3176
+ const trimmedSource = sourceLine.trim();
3177
+ if (!trimmedSource.startsWith("!") || !trimmedSource.slice(1).trim()) {
3178
+ if (llmInlineShellSuggestion !== null) {
3179
+ llmInlineShellSuggestion = null;
3180
+ renderActivePrompt();
3181
+ }
3182
+ return;
3183
+ }
3184
+ if (inlineShellSuggestionTimeout) {
3185
+ clearTimeout(inlineShellSuggestionTimeout);
3186
+ }
3187
+ inlineShellSuggestionTimeout = setTimeout(() => {
3188
+ inlineShellSuggestionTimeout = void 0;
3189
+ const requestId = ++inlineShellSuggestionRequestId;
3190
+ const lineAtRequest = getCurrentText();
3191
+ resolveShellSuggestion(lineAtRequest).then((suggestion) => {
3192
+ if (closed || requestId !== inlineShellSuggestionRequestId) {
3193
+ return;
3194
+ }
3195
+ const latest = getCurrentText();
3196
+ if (latest !== lineAtRequest) {
3197
+ return;
3198
+ }
3199
+ llmInlineShellSuggestion = suggestion ?? null;
3200
+ renderActivePrompt();
3201
+ }).catch(() => {
3202
+ });
3203
+ }, delayMs);
3204
+ };
3205
+ const handleInputData = (chunk) => {
3206
+ if (closed || pasteState.isInPaste) {
3207
+ return;
3208
+ }
3209
+ const rawText = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk ?? "");
3210
+ const modifiedEnterCount = countRawModifiedEnterSequences(rawText);
3211
+ const residualModifiedEnterCount = countResidualModifiedEnterSequences(rawText);
3212
+ if (modifiedEnterCount > 0) {
3213
+ for (let i = 0; i < modifiedEnterCount; i++) {
3214
+ textBuffer.insert("\n");
3215
+ }
3216
+ suppressResidualShiftEnterCharsUntil = Date.now() + 200;
3217
+ syncReadlineFromBuffer();
3218
+ renderActivePrompt();
3219
+ return;
3220
+ }
3221
+ if (residualModifiedEnterCount > 0) {
3222
+ for (let i = 0; i < residualModifiedEnterCount; i++) {
3223
+ textBuffer.insert("\n");
3224
+ }
3225
+ suppressResidualShiftEnterCharsUntil = Date.now() + 200;
3226
+ syncReadlineFromBuffer();
3227
+ renderActivePrompt();
3228
+ return;
3229
+ }
3230
+ scheduleInlineImageScan();
3231
+ };
3232
+ const handleKeypress = (_str, key) => {
3233
+ if (closed) return;
3234
+ const rawSeq = key?.sequence ?? _str ?? "";
3235
+ if (chordState === "ctrl-x") {
3236
+ chordState = "none";
3237
+ if (chordTimeout) {
3238
+ clearTimeout(chordTimeout);
3239
+ chordTimeout = null;
3240
+ }
3241
+ if (_str === "/") {
3242
+ const currentText = textBuffer.getText();
3243
+ textBuffer.setText("/" + currentText);
3244
+ textBuffer.setCursorPosition(0, 1);
3245
+ syncReadlineFromBuffer();
3246
+ renderActivePrompt();
3247
+ return;
3248
+ }
3249
+ }
3250
+ if (Date.now() < suppressResidualShiftEnterCharsUntil) {
3251
+ if (isShiftEnterSequence(_str, key) || isShiftEnterResidualSequence(rawSeq) || _str && _str.length > 0 && /^[\d;~u]+$/.test(_str)) {
3252
+ return;
3253
+ }
3254
+ }
3255
+ if (key?.sequence === "\x1B[13~") {
3256
+ textBuffer.insert("\n");
3257
+ suppressResidualShiftEnterCharsUntil = Date.now() + 200;
3258
+ syncReadlineFromBuffer();
3259
+ renderActivePrompt();
3260
+ return;
3261
+ }
3262
+ if (key?.name === "paste-start") {
3263
+ pasteState.isInPaste = true;
3264
+ pasteState.buffer = "";
3265
+ pasteState.outputSuppressed = true;
3266
+ outputGuard.setSuppressed(true);
3267
+ if (pasteState.timeout) {
3268
+ clearTimeout(pasteState.timeout);
3269
+ }
3270
+ pasteState.prefixContent = getCurrentText();
3271
+ return;
3272
+ }
3273
+ if (key?.name === "paste-end") {
3274
+ if (pasteState.timeout) {
3275
+ clearTimeout(pasteState.timeout);
3276
+ pasteState.timeout = void 0;
3277
+ }
3278
+ if (pasteState.isInPaste) {
3279
+ pasteState.isInPaste = false;
3280
+ outputGuard.setSuppressed(false);
3281
+ if (onImageDetected && pasteState.buffer) {
3282
+ pasteState.buffer = processImagesInText(
3283
+ pasteState.buffer,
3284
+ onImageDetected,
3285
+ { announce: false, output: stdOutput }
3286
+ );
3287
+ }
3288
+ handlePasteComplete(pasteState, rl, stdOutput, () => {
3289
+ const rlAny = rl;
3290
+ textBuffer.setText(rlAny.line ?? "");
3291
+ syncReadlineFromBuffer();
3292
+ renderActivePrompt();
3293
+ });
3294
+ scheduleInlineImageScan(10);
3295
+ }
3296
+ return;
3297
+ }
3298
+ if (pasteState.isInPaste) {
3299
+ if (_str) {
3300
+ pasteState.buffer += _str;
3301
+ }
3302
+ if (key?.name === "return" || key?.name === "enter") {
3303
+ pasteState.buffer += "\n";
3304
+ }
3305
+ if (pasteState.timeout) {
3306
+ clearTimeout(pasteState.timeout);
3307
+ }
3308
+ pasteState.timeout = setTimeout(() => {
3309
+ if (pasteState.isInPaste && pasteState.buffer) {
3310
+ pasteState.isInPaste = false;
3311
+ outputGuard.setSuppressed(false);
3312
+ if (onImageDetected) {
3313
+ pasteState.buffer = processImagesInText(
3314
+ pasteState.buffer,
3315
+ onImageDetected,
3316
+ { announce: false, output: stdOutput }
3317
+ );
3318
+ }
3319
+ handlePasteComplete(pasteState, rl, stdOutput, () => {
3320
+ const rlAny = rl;
3321
+ textBuffer.setText(rlAny.line ?? "");
3322
+ syncReadlineFromBuffer();
3323
+ renderActivePrompt();
3324
+ });
3325
+ scheduleInlineImageScan(10);
3326
+ }
3327
+ }, 50);
3328
+ return;
3329
+ }
3330
+ if ((key?.name === "backspace" || key?.name === "delete") && pasteState.hiddenContent) {
3331
+ const currentText = getCurrentText();
3332
+ const stripped = removePastedReferenceFromLine(currentText);
3333
+ if (stripped) {
3334
+ textBuffer.setText(stripped.line);
3335
+ } else {
3336
+ textBuffer.setText("");
3337
+ }
3338
+ pasteState.hiddenContent = void 0;
3339
+ syncReadlineFromBuffer();
3340
+ renderActivePrompt();
3341
+ return;
3342
+ }
3343
+ if (!(key?.name === "c" && key.ctrl)) {
3344
+ ctrlCCount = 0;
3345
+ }
3346
+ if (key?.name === "c" && key.ctrl) {
3347
+ const currentInput = getCurrentText();
3348
+ if (currentInput.length > 0) {
3349
+ mentionPreview.reset();
3350
+ if (contextualHelpVisible) {
3351
+ setContextualHelpVisible(false);
3352
+ }
3353
+ textBuffer.setText("");
3354
+ syncReadlineFromBuffer();
3355
+ renderActivePrompt();
3356
+ ctrlCCount = 0;
3357
+ return;
3358
+ }
3359
+ if (ctrlCCount === 0) {
3360
+ ctrlCCount = 1;
3361
+ mentionPreview.reset();
3362
+ if (contextualHelpVisible) {
3363
+ setContextualHelpVisible(false);
3364
+ }
3365
+ showPromptMessage(chalk2.gray("Press Ctrl+C again to exit."));
3366
+ return;
3367
+ }
3368
+ mentionPreview.reset();
3369
+ if (contextualHelpVisible) {
3370
+ setContextualHelpVisible(false);
3371
+ }
3372
+ leavePromptSurface(stdOutput);
3373
+ cleanup();
3374
+ resolve({ kind: "abort" });
3375
+ return;
3376
+ }
3377
+ if (mentionPreview.consumeHandledCompletion()) {
3378
+ syncReadlineFromBuffer();
3379
+ renderActivePrompt();
3380
+ return;
3381
+ }
3382
+ if (isShiftTabShortcut(_str, key)) {
3383
+ const planModeManager = getPlanModeManager();
3384
+ const wasEnabled = planModeManager.isEnabled();
3385
+ planModeManager.handleShiftTab();
3386
+ showPromptMessage(formatPlanModeToggleMessage(!wasEnabled));
3387
+ return;
3388
+ }
3389
+ if (isPlainTabShortcut(_str, key)) {
3390
+ if (mentionPreview.consumeHandledTab()) {
3391
+ return;
3392
+ }
3393
+ const currentInput = getCurrentText();
3394
+ const trimmedInput = currentInput.trim();
3395
+ if (trimmedInput.startsWith("!") && resolveShellSuggestion) {
3396
+ if (llmInlineShellSuggestion && llmInlineShellSuggestion.startsWith(currentInput) && llmInlineShellSuggestion !== currentInput) {
3397
+ textBuffer.setText(llmInlineShellSuggestion);
3398
+ syncReadlineFromBuffer();
3399
+ renderActivePrompt();
3400
+ return;
3401
+ }
3402
+ const requestId = ++shellSuggestionRequestId;
3403
+ const immediateFallback = getPrimaryHotTipSuggestion(
3404
+ currentInput,
3405
+ filesProvider(),
3406
+ slashCommands,
3407
+ {
3408
+ nextPromptSuggestion: nextPromptSuggestionProvider?.(),
3409
+ workspaceRoot,
3410
+ skillsProvider
3411
+ }
3412
+ );
3413
+ let expectedInputAtResponse = currentInput;
3414
+ if (immediateFallback) {
3415
+ textBuffer.setText(immediateFallback.line);
3416
+ syncReadlineFromBuffer();
3417
+ expectedInputAtResponse = immediateFallback.line;
3418
+ renderActivePrompt();
3419
+ }
3420
+ resolveShellSuggestion(currentInput).then((llmSuggestion) => {
3421
+ if (closed || requestId !== shellSuggestionRequestId) {
3422
+ return;
3423
+ }
3424
+ const latest = getCurrentText();
3425
+ if (latest !== expectedInputAtResponse) {
3426
+ return;
3427
+ }
3428
+ if (llmSuggestion) {
3429
+ textBuffer.setText(llmSuggestion);
3430
+ syncReadlineFromBuffer();
3431
+ renderActivePrompt();
3432
+ }
3433
+ }).catch(() => {
3434
+ if (closed || requestId !== shellSuggestionRequestId) {
3435
+ return;
3436
+ }
3437
+ });
3438
+ return;
3439
+ }
3440
+ applyPromptSuggestion(getCurrentPrimarySuggestion());
3441
+ return;
3442
+ }
3443
+ if (isRightArrowAcceptShortcut(key) && isTextBufferCursorAtEnd()) {
3444
+ const currentInput = getCurrentText();
3445
+ const trimmedInput = currentInput.trim();
3446
+ if (!trimmedInput) {
3447
+ applyPromptSuggestion(getCurrentPrimarySuggestion());
3448
+ return;
3449
+ }
3450
+ const inlineGhostSuffix = getInlineGhostSuffix();
3451
+ if (inlineGhostSuffix) {
3452
+ textBuffer.setText(`${currentInput}${inlineGhostSuffix}`);
3453
+ syncReadlineFromBuffer();
3454
+ renderActivePrompt();
3455
+ return;
3456
+ }
3457
+ return;
3458
+ }
3459
+ if (_str === "?" && !key?.ctrl && !key?.meta) {
3460
+ const currentText = getCurrentText();
3461
+ if (currentText.trim() === "" || currentText.trim() === "?") {
3462
+ setContextualHelpVisible(!contextualHelpVisible);
3463
+ return;
3464
+ }
3465
+ }
3466
+ const residualModifiedEnterCount = countResidualModifiedEnterSequences(rawSeq);
3467
+ if (isShiftEnterSequence(_str, key) || isShiftEnterResidualSequence(rawSeq)) {
3468
+ textBuffer.insert("\n");
3469
+ syncReadlineFromBuffer();
3470
+ renderActivePrompt();
3471
+ scheduleInlineImageScan();
3472
+ scheduleInlineShellSuggestion();
3473
+ return;
3474
+ }
3475
+ if (residualModifiedEnterCount > 0) {
3476
+ for (let i = 0; i < residualModifiedEnterCount; i++) {
3477
+ textBuffer.insert("\n");
3478
+ }
3479
+ syncReadlineFromBuffer();
3480
+ renderActivePrompt();
3481
+ scheduleInlineImageScan();
3482
+ scheduleInlineShellSuggestion();
3483
+ return;
3484
+ }
3485
+ if (key?.name === "k" && key.ctrl) {
3486
+ textBuffer.deleteToEnd();
3487
+ syncReadlineFromBuffer();
3488
+ renderActivePrompt();
3489
+ return;
3490
+ }
3491
+ if (key?.name === "u" && key.ctrl) {
3492
+ textBuffer.deleteToStart();
3493
+ syncReadlineFromBuffer();
3494
+ renderActivePrompt();
3495
+ return;
3496
+ }
3497
+ if (key?.name === "w" && key.ctrl) {
3498
+ textBuffer.deletePreviousWord();
3499
+ syncReadlineFromBuffer();
3500
+ renderActivePrompt();
3501
+ return;
3502
+ }
3503
+ if (key?.name === "d" && key.ctrl) {
3504
+ if (textBuffer.getText().length === 0) {
3505
+ process.emit("SIGTERM");
3506
+ return;
3507
+ }
3508
+ textBuffer.delete();
3509
+ syncReadlineFromBuffer();
3510
+ renderActivePrompt();
3511
+ return;
3512
+ }
3513
+ if (key?.name === "l" && key.ctrl) {
3514
+ process.stdout.write("\x1B[2J\x1B[H");
3515
+ renderActivePrompt();
3516
+ return;
3517
+ }
3518
+ if (key?.name === "b" && key.ctrl) {
3519
+ handleTextBufferKey(textBuffer, "", { name: "left" });
3520
+ syncReadlineFromBuffer();
3521
+ renderActivePrompt();
3522
+ return;
3523
+ }
3524
+ if (key?.name === "f" && key.ctrl) {
3525
+ handleTextBufferKey(textBuffer, "", { name: "right" });
3526
+ syncReadlineFromBuffer();
3527
+ renderActivePrompt();
3528
+ return;
3529
+ }
3530
+ if (key?.name === "h" && key.ctrl) {
3531
+ textBuffer.backspace();
3532
+ syncReadlineFromBuffer();
3533
+ renderActivePrompt();
3534
+ return;
3535
+ }
3536
+ if (key?.name === "g" && key.ctrl) {
3537
+ const { writeFileSync, unlinkSync } = __require("fs");
3538
+ const { spawnSync } = __require("child_process");
3539
+ const { tmpdir } = __require("os");
3540
+ const { join } = __require("path");
3541
+ const tmpFile = join(tmpdir(), `autohand-edit-${Date.now()}.txt`);
3542
+ writeFileSync(tmpFile, textBuffer.getText());
3543
+ const editor = process.env.VISUAL || process.env.EDITOR || "vi";
3544
+ spawnSync(editor, [tmpFile], { stdio: "inherit" });
3545
+ try {
3546
+ const content = readFileSync(tmpFile, "utf-8");
3547
+ textBuffer.setText(content.trimEnd());
3548
+ unlinkSync(tmpFile);
3549
+ } catch {
3550
+ }
3551
+ syncReadlineFromBuffer();
3552
+ renderActivePrompt();
3553
+ return;
3554
+ }
3555
+ if (key?.name === "x" && key.ctrl) {
3556
+ chordState = "ctrl-x";
3557
+ chordTimeout = setTimeout(() => {
3558
+ chordState = "none";
3559
+ chordTimeout = null;
3560
+ }, 1e3);
3561
+ return;
3562
+ }
3563
+ const tbResult = handleTextBufferKey(textBuffer, _str, key);
3564
+ if (tbResult === "submit") {
3565
+ const text = textBuffer.getText().trim();
3566
+ if (!text) return;
3567
+ syncReadlineFromBuffer();
3568
+ rl.emit("line", text);
3569
+ return;
3570
+ }
3571
+ if (tbResult === "handled") {
3572
+ syncReadlineFromBuffer();
3573
+ renderActivePrompt();
3574
+ scheduleInlineImageScan();
3575
+ scheduleInlineShellSuggestion();
3576
+ if (contextualHelpVisible && shouldAutoHideShortcutHelp(_str, key)) {
3577
+ setContextualHelpVisible(false);
3578
+ }
3579
+ return;
3580
+ }
3581
+ scheduleInlineImageScan();
3582
+ scheduleInlineShellSuggestion();
3583
+ if (contextualHelpVisible && shouldAutoHideShortcutHelp(_str, key)) {
3584
+ setContextualHelpVisible(false);
3585
+ }
3586
+ scheduleRender();
3587
+ };
3588
+ input.prependListener("data", handleInputData);
3589
+ input.on("keypress", handleKeypress);
3590
+ rl.on("line", (value) => {
3591
+ if (pasteState.isInPaste) {
3592
+ return;
3593
+ }
3594
+ let finalValue = pasteState.hiddenContent || value;
3595
+ pasteState.hiddenContent = void 0;
3596
+ finalValue = convertNewlineMarkersToNewlines(finalValue).trim();
3597
+ finalValue = processImagesInText(finalValue, onImageDetected, {
3598
+ announce: true,
3599
+ output: stdOutput
3600
+ });
3601
+ if (contextualHelpVisible) {
3602
+ setContextualHelpVisible(false);
3603
+ }
3604
+ if (isShellCommand(finalValue)) {
3605
+ const shellCmd = parseShellCommand(finalValue);
3606
+ mentionPreview.reset();
3607
+ leavePromptSurface(stdOutput, STATUS_LINE_COUNT, true);
3608
+ writePromptShellCommandHeader(stdOutput, shellCmd);
3609
+ const writer = createPromptShellCommandBlockWriter(stdOutput);
3610
+ executeShellCommandAsync(shellCmd, workspaceRoot, void 0, {
3611
+ onStdout: (chunk) => writer.pushStdout(chunk),
3612
+ onStderr: (chunk) => writer.pushStderr(chunk)
3613
+ }).then((result) => {
3614
+ writer.flush();
3615
+ if (!result.success && result.error && !result.output) {
3616
+ stdOutput.write(` \u2514 ${chalk2.red(result.error)}
3617
+ `);
3618
+ }
3619
+ textBuffer.setText("");
3620
+ syncReadlineFromBuffer();
3621
+ stdOutput.write("\n");
3622
+ renderPromptLine(rl, getActiveStatusLine(), stdOutput, false, false, nextPromptSuggestionProvider?.());
3623
+ }).catch((error) => {
3624
+ writer.flush();
3625
+ stdOutput.write(` \u2514 ${chalk2.red(error.message)}
3626
+
3627
+ `);
3628
+ textBuffer.setText("");
3629
+ syncReadlineFromBuffer();
3630
+ renderPromptLine(rl, getActiveStatusLine(), stdOutput, false, false, nextPromptSuggestionProvider?.());
3631
+ });
3632
+ return;
3633
+ }
3634
+ mentionPreview.reset();
3635
+ leavePromptSurface(stdOutput, STATUS_LINE_COUNT, true);
3636
+ if (finalValue && !finalValue.startsWith("/")) {
3637
+ stdOutput.write(`${chalk2.gray("press ESC to interrupt")}
3638
+ `);
3639
+ }
3640
+ cleanup();
3641
+ resolve({ kind: "submit", value: finalValue });
3642
+ });
3643
+ rl.on("SIGINT", () => {
3644
+ const currentInput = getCurrentText();
3645
+ if (currentInput.length > 0) {
3646
+ mentionPreview.reset();
3647
+ if (contextualHelpVisible) {
3648
+ setContextualHelpVisible(false);
3649
+ }
3650
+ textBuffer.setText("");
3651
+ syncReadlineFromBuffer();
3652
+ renderActivePrompt();
3653
+ ctrlCCount = 0;
3654
+ return;
3655
+ }
3656
+ if (ctrlCCount === 0) {
3657
+ ctrlCCount = 1;
3658
+ mentionPreview.reset();
3659
+ if (contextualHelpVisible) {
3660
+ setContextualHelpVisible(false);
3661
+ }
3662
+ showPromptMessage(chalk2.gray("Press Ctrl+C again to exit."));
3663
+ return;
3664
+ }
3665
+ mentionPreview.reset();
3666
+ if (contextualHelpVisible) {
3667
+ setContextualHelpVisible(false);
3668
+ }
3669
+ leavePromptSurface(stdOutput);
3670
+ cleanup();
3671
+ resolve({ kind: "abort" });
3672
+ });
3673
+ });
3674
+ }
3675
+ function disableReadlineTabBehavior(rl) {
3676
+ const anyRl = rl;
3677
+ anyRl.completer = (line) => [[], line];
3678
+ if (typeof anyRl._tabComplete === "function") {
3679
+ anyRl._tabComplete = () => {
3680
+ };
3681
+ }
3682
+ }
3683
+ function getComposerBorderStyle(line) {
3684
+ if (/^[\s\u200B-\u200D\uFEFF]*!/u.test(line)) {
3685
+ return "shell";
3686
+ }
3687
+ if (getPlanModeManager().isEnabled()) {
3688
+ return "plan";
3689
+ }
3690
+ return "default";
3691
+ }
3692
+ var lastRenderedContentLines = 1;
3693
+ var lastRenderedCursorRow = 0;
3694
+ var activeTextBuffer = null;
3695
+ function getActiveTextBuffer() {
3696
+ return activeTextBuffer;
3697
+ }
3698
+ function getLastRenderedContentLines() {
3699
+ return lastRenderedContentLines;
3700
+ }
3701
+ function getLastRenderedCursorRow() {
3702
+ return lastRenderedCursorRow;
3703
+ }
3704
+ var lastRenderedPromptWidth = 0;
3705
+ var lastRenderedHelpLines = 0;
3706
+ var lastRenderedSlashLines = 0;
3707
+ function resetPromptRenderState() {
3708
+ lastRenderedContentLines = 1;
3709
+ lastRenderedCursorRow = 0;
3710
+ lastRenderedPromptWidth = 0;
3711
+ lastRenderedHelpLines = 0;
3712
+ lastRenderedSlashLines = 0;
3713
+ }
3714
+ function renderPromptLine(rl, statusLine, output, isResize = false, hasExistingPromptBlock = true, nextPromptSuggestion, inlineGhostSuffix, helpPanelLines, slashSuggestionLines) {
3715
+ invalidateBoxColorCache();
3716
+ const width = getPromptBlockWidth(output.columns);
3717
+ let currentLine;
3718
+ let cursorPos;
3719
+ if (activeTextBuffer) {
3720
+ currentLine = activeTextBuffer.getText();
3721
+ const lines = activeTextBuffer.getLines();
3722
+ const row = activeTextBuffer.getCursorRow();
3723
+ const col = activeTextBuffer.getCursorCol();
3724
+ let flat = 0;
3725
+ for (let i = 0; i < row; i++) {
3726
+ flat += lines[i].length + 1;
3727
+ }
3728
+ flat += col;
3729
+ cursorPos = flat;
3730
+ } else {
3731
+ const rlAny = rl;
3732
+ currentLine = rlAny.line ?? "";
3733
+ cursorPos = rlAny.cursor ?? currentLine.length;
3734
+ }
3735
+ const borderStyle = getComposerBorderStyle(currentLine);
3736
+ const state = buildMultiLineRenderState(
3737
+ currentLine,
3738
+ cursorPos,
3739
+ width,
3740
+ borderStyle,
3741
+ {
3742
+ placeholderText: PROMPT_PLACEHOLDER,
3743
+ nextPromptSuggestion,
3744
+ inlineGhostSuffix
3745
+ }
3746
+ );
3747
+ const topBorder = drawInputTopBorder(width, borderStyle);
3748
+ const bottomBorder = drawInputBottomBorder(width, borderStyle);
3749
+ const statusRow = formatPromptStatusRow(statusLine, width);
3750
+ const widthChanged = lastRenderedPromptWidth > 0 && width !== lastRenderedPromptWidth;
3751
+ const effectiveResize = isResize || widthChanged;
3752
+ rl.setPrompt(PROMPT_PREFIX);
3753
+ output.write("\x1B[?25l");
3754
+ if (effectiveResize && hasExistingPromptBlock) {
3755
+ readline2.cursorTo(output, 0);
3756
+ readline2.clearLine(output, 0);
3757
+ const prevContentLines = lastRenderedContentLines;
3758
+ const prevCursorRow = lastRenderedCursorRow;
3759
+ const upCount = prevCursorRow + PROMPT_LINES_ABOVE_INPUT;
3760
+ for (let i = 0; i < upCount; i++) {
3761
+ readline2.moveCursor(output, 0, -1);
3762
+ readline2.clearLine(output, 0);
3763
+ }
3764
+ const clearContentLines = Math.max(prevContentLines, state.lineCount);
3765
+ const downCount = clearContentLines + PROMPT_LINES_BELOW_INPUT + lastRenderedHelpLines + STATUS_LINE_COUNT + lastRenderedSlashLines;
3766
+ for (let i = 0; i < downCount; i++) {
3767
+ readline2.moveCursor(output, 0, 1);
3768
+ readline2.clearLine(output, 0);
3769
+ }
3770
+ readline2.moveCursor(output, 0, -downCount);
3771
+ readline2.cursorTo(output, 0);
3772
+ } else if (hasExistingPromptBlock) {
3773
+ const prevContentLines = lastRenderedContentLines;
3774
+ const prevCursorRow = lastRenderedCursorRow;
3775
+ readline2.cursorTo(output, 0);
3776
+ readline2.clearLine(output, 0);
3777
+ const upCount = prevCursorRow + PROMPT_LINES_ABOVE_INPUT;
3778
+ for (let i = 0; i < upCount; i++) {
3779
+ readline2.moveCursor(output, 0, -1);
3780
+ readline2.clearLine(output, 0);
3781
+ }
3782
+ const clearContentLines = Math.max(prevContentLines, state.lineCount);
3783
+ const downCount = clearContentLines + PROMPT_LINES_BELOW_INPUT + lastRenderedHelpLines + STATUS_LINE_COUNT + lastRenderedSlashLines;
3784
+ for (let i = 0; i < downCount; i++) {
3785
+ readline2.moveCursor(output, 0, 1);
3786
+ readline2.clearLine(output, 0);
3787
+ }
3788
+ for (let i = 0; i < downCount; i++) {
3789
+ readline2.moveCursor(output, 0, -1);
3790
+ }
3791
+ readline2.cursorTo(output, 0);
3792
+ } else {
3793
+ readline2.cursorTo(output, 0);
3794
+ const blockSize = PROMPT_LINES_ABOVE_INPUT + state.lineCount + PROMPT_LINES_BELOW_INPUT + (helpPanelLines?.length ?? 0) + STATUS_LINE_COUNT + (slashSuggestionLines?.length ?? 0);
3795
+ for (let i = 0; i < blockSize; i++) {
3796
+ readline2.clearLine(output, 0);
3797
+ if (i < blockSize - 1) {
3798
+ readline2.moveCursor(output, 0, 1);
3799
+ }
3800
+ }
3801
+ readline2.moveCursor(output, 0, -(blockSize - 1));
3802
+ readline2.cursorTo(output, 0);
3803
+ }
3804
+ const helpLines = helpPanelLines ?? [];
3805
+ const slashLines = slashSuggestionLines ?? [];
3806
+ let buf = `${topBorder}
3807
+ `;
3808
+ for (const line of state.lines) {
3809
+ buf += `${line}
3810
+ `;
3811
+ }
3812
+ buf += `${bottomBorder}
3813
+ `;
3814
+ for (const hl of helpLines) {
3815
+ buf += `${hl}
3816
+ `;
3817
+ }
3818
+ buf += statusRow;
3819
+ for (const sl of slashLines) {
3820
+ buf += `
3821
+ ${sl}`;
3822
+ }
3823
+ output.write(buf);
3824
+ const moveUp = PROMPT_LINES_BELOW_INPUT + helpLines.length + STATUS_LINE_COUNT + slashLines.length + (state.lineCount - 1 - state.cursorRow);
3825
+ readline2.moveCursor(output, 0, -moveUp);
3826
+ readline2.cursorTo(output, state.cursorColumn);
3827
+ output.write("\x1B[?25h");
3828
+ lastRenderedContentLines = state.lineCount;
3829
+ lastRenderedCursorRow = state.cursorRow;
3830
+ lastRenderedPromptWidth = width;
3831
+ lastRenderedHelpLines = helpLines.length;
3832
+ lastRenderedSlashLines = slashLines.length;
3833
+ }
3834
+
3835
+ export {
3836
+ buildFileMentionSuggestions,
3837
+ buildSkillMentionSuggestions,
3838
+ ImageManager,
3839
+ drawInputTopBorder,
3840
+ drawInputBottomBorder,
3841
+ drawInputBox,
3842
+ TextBuffer,
3843
+ handleTextBufferKey,
3844
+ promptNotify,
3845
+ promptInterrupt,
3846
+ STATUS_LINE_COUNT,
3847
+ PROMPT_LINES_ABOVE_INPUT,
3848
+ PROMPT_LINES_BELOW_INPUT,
3849
+ PROMPT_PLACEHOLDER,
3850
+ PROMPT_INPUT_PREFIX,
3851
+ getHelpOrderedSlashCommands,
3852
+ getRankedSlashCommandMatches,
3853
+ resetCachedSkillMentions,
3854
+ buildPromptHotTips,
3855
+ getPrimaryHotTipSuggestion,
3856
+ getInlineGhostCompletionSuffix,
3857
+ buildContextualHelpPanelLines,
3858
+ buildContextualPromptStatusLine,
3859
+ buildSlashSuggestionLines,
3860
+ removePastedReferenceFromLine,
3861
+ isShiftTabShortcut,
3862
+ isPlainTabShortcut,
3863
+ isShiftEnterSequence,
3864
+ isShiftEnterResidualSequence,
3865
+ countResidualModifiedEnterSequences,
3866
+ countRawModifiedEnterSequences,
3867
+ shouldAutoHideShortcutHelp,
3868
+ getPromptBlockWidth,
3869
+ buildPromptRenderState,
3870
+ buildMultiLineRenderState,
3871
+ formatPromptStatusRow,
3872
+ installReadlineOutputGuard,
3873
+ processImagesInText,
3874
+ NEWLINE_MARKER,
3875
+ safeEmitKeypressEvents,
3876
+ convertNewlineMarkersToNewlines,
3877
+ readInstruction,
3878
+ leavePromptSurface,
3879
+ getActiveTextBuffer,
3880
+ getLastRenderedContentLines,
3881
+ getLastRenderedCursorRow,
3882
+ resetPromptRenderState
3883
+ };
3884
+ /**
3885
+ * @license
3886
+ * Copyright 2025 Autohand AI LLC
3887
+ * SPDX-License-Identifier: Apache-2.0
3888
+ */
3889
+ /**
3890
+ * @license
3891
+ * Copyright 2025 Autohand AI LLC
3892
+ * SPDX-License-Identifier: Apache-2.0
3893
+ *
3894
+ * Visual layout engine for TextBuffer.
3895
+ * Wraps logical lines to visual lines based on viewport width and builds
3896
+ * bidirectional mapping tables between logical and visual positions.
3897
+ */