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
@@ -1,34 +1,53 @@
1
1
  import {
2
- PermissionManager
3
- } from "./chunk-DNUOXBHL.js";
2
+ GitIgnoreParser
3
+ } from "./chunk-H4D2Q2AL.js";
4
+ import {
5
+ GoalManager
6
+ } from "./chunk-SZD45IDG.js";
4
7
  import {
5
8
  applyFormatter
6
9
  } from "./chunk-W3X6PAC7.js";
10
+ import {
11
+ PermissionManager
12
+ } from "./chunk-I2ZK3AOD.js";
13
+ import {
14
+ isToolAllowedByYolo,
15
+ normalizeYoloInput,
16
+ parseYoloPattern
17
+ } from "./chunk-4LMUDS2K.js";
18
+ import {
19
+ runCommand
20
+ } from "./chunk-TH26BQJG.js";
21
+ import {
22
+ executeStreamingShellCommand
23
+ } from "./chunk-VUGOOGHB.js";
24
+ import {
25
+ getPlanModeManager
26
+ } from "./chunk-LUMV3DB2.js";
27
+ import {
28
+ GOAL_FEATURE_DISABLED_MESSAGE,
29
+ isGoalFeatureEnabled
30
+ } from "./chunk-7MY4JHER.js";
7
31
  import {
8
32
  fetchUrl,
9
33
  formatPackageInfo,
10
34
  formatSearchResults,
11
35
  getPackageInfo,
12
36
  webSearch
13
- } from "./chunk-SAHBLB3E.js";
37
+ } from "./chunk-FP46B4X3.js";
14
38
  import {
15
39
  showInput,
16
40
  showModal
17
- } from "./chunk-YZXUDM5X.js";
41
+ } from "./chunk-NZ3ZWLUW.js";
18
42
  import {
19
43
  getTheme,
20
44
  hexToRgb,
21
45
  isThemeInitialized
22
- } from "./chunk-LIEXWM2M.js";
23
- import {
24
- AUTOHAND_PATHS
25
- } from "./chunk-EGFT4PGW.js";
26
- import {
27
- getPlanModeManager
28
- } from "./chunk-NCC6ETZS.js";
46
+ } from "./chunk-JAQO6XDB.js";
29
47
  import {
30
- GitIgnoreParser
31
- } from "./chunk-EZMINVLU.js";
48
+ AUTOHAND_PATHS,
49
+ PROJECT_DIR_NAME
50
+ } from "./chunk-SHRLFX6F.js";
32
51
 
33
52
  // src/core/actionExecutor.ts
34
53
  import chalk2 from "chalk";
@@ -596,88 +615,6 @@ async function removeDependency(cwd, name, options = {}) {
596
615
  }
597
616
  }
598
617
 
599
- // src/actions/command.ts
600
- import { spawn } from "child_process";
601
- import { join } from "path";
602
- function runCommand(cmd, args, cwd, options = {}) {
603
- if (!cmd || typeof cmd !== "string") {
604
- return Promise.reject(new Error("Command is required and must be a string"));
605
- }
606
- return new Promise((resolve, reject) => {
607
- const workDir = options.directory ? join(cwd, options.directory) : cwd;
608
- const spawnOptions = {
609
- cwd: workDir,
610
- shell: options.shell ?? false,
611
- env: {
612
- ...process.env,
613
- AUTOHAND_CLI: "1",
614
- ...options.env
615
- }
616
- };
617
- if (options.background) {
618
- spawnOptions.detached = true;
619
- spawnOptions.stdio = ["ignore", "pipe", "pipe"];
620
- }
621
- let child;
622
- try {
623
- child = spawn(cmd, args, spawnOptions);
624
- } catch (error) {
625
- const err = error;
626
- if (err.code === "ENOENT") {
627
- reject(new Error(`Command not found: ${cmd}`));
628
- } else {
629
- reject(error);
630
- }
631
- return;
632
- }
633
- if (options.background) {
634
- child.unref();
635
- resolve({
636
- stdout: "",
637
- stderr: "",
638
- code: null,
639
- backgroundPid: child.pid,
640
- signal: null
641
- });
642
- return;
643
- }
644
- let stdout = "";
645
- let stderr = "";
646
- let timeoutId;
647
- if (options.timeout && options.timeout > 0) {
648
- timeoutId = setTimeout(() => {
649
- child.kill("SIGTERM");
650
- }, options.timeout);
651
- }
652
- child.stdout?.on("data", (chunk) => {
653
- const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
654
- stdout += text;
655
- options.onStdout?.(text);
656
- });
657
- child.stderr?.on("data", (chunk) => {
658
- const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
659
- stderr += text;
660
- options.onStderr?.(text);
661
- });
662
- child.once("error", (error) => {
663
- if (timeoutId) clearTimeout(timeoutId);
664
- if (error.code === "ENOENT") {
665
- reject(new Error(`Command not found: ${cmd}`));
666
- } else {
667
- reject(error);
668
- }
669
- });
670
- child.once("close", (code, signal) => {
671
- if (timeoutId) clearTimeout(timeoutId);
672
- resolve({ stdout, stderr, code, signal });
673
- });
674
- });
675
- }
676
- var SHELL_PATTERN = /[|><;&`]|\$[({A-Za-z_]|&&|\|\||[*?](?![\w./-]*$)/;
677
- function needsShell(cmd) {
678
- return SHELL_PATTERN.test(cmd);
679
- }
680
-
681
618
  // src/actions/metadata.ts
682
619
  import crypto from "crypto";
683
620
  import fs2 from "fs-extra";
@@ -688,11 +625,6 @@ async function listDirectoryTree(root, options = {}) {
688
625
  const maxEntries = options.maxEntries ?? 200;
689
626
  const workspaceRoot = options.workspaceRoot ?? root;
690
627
  const result = [];
691
- const resolvedRoot = path3.resolve(root);
692
- const resolvedWorkspace = path3.resolve(workspaceRoot);
693
- if (!resolvedRoot.startsWith(resolvedWorkspace)) {
694
- throw new Error(`Path ${root} is outside the workspace root.`);
695
- }
696
628
  const ignoreFilter = new GitIgnoreParser(workspaceRoot);
697
629
  async function walk(current, prefix, currentDepth) {
698
630
  if (result.length >= maxEntries) {
@@ -797,6 +729,13 @@ function diffFile(cwd, file) {
797
729
  }
798
730
  return result.stdout || "No diff";
799
731
  }
732
+ function diffWorkspace(cwd) {
733
+ const result = spawnSync("git", ["diff"], { cwd, encoding: "utf8" });
734
+ if (result.status !== 0) {
735
+ throw new Error(result.stderr || "git diff failed");
736
+ }
737
+ return result.stdout || "No diff";
738
+ }
800
739
  function checkoutFile(cwd, file) {
801
740
  const result = spawnSync("git", ["checkout", "--", file], { cwd, encoding: "utf8" });
802
741
  if (result.status !== 0) {
@@ -1326,7 +1265,7 @@ function gitPush(cwd, remote, branch, options = {}) {
1326
1265
  }
1327
1266
 
1328
1267
  // src/actions/worktree.ts
1329
- import { spawnSync as spawnSync2, spawn as spawn2 } from "child_process";
1268
+ import { spawnSync as spawnSync2, spawn } from "child_process";
1330
1269
  import path4 from "path";
1331
1270
  import fs3 from "fs-extra";
1332
1271
  import os from "os";
@@ -1363,6 +1302,9 @@ var DEFAULT_TEMPLATES = [
1363
1302
  }
1364
1303
  ];
1365
1304
  var WorktreeManager = class {
1305
+ repoRoot;
1306
+ repoName;
1307
+ templates;
1366
1308
  constructor(cwd) {
1367
1309
  this.repoRoot = this.findGitRoot(cwd);
1368
1310
  this.repoName = path4.basename(this.repoRoot);
@@ -1767,7 +1709,7 @@ var WorktreeManager = class {
1767
1709
  }
1768
1710
  async runInWorktree(worktreePath, command) {
1769
1711
  return new Promise((resolve, reject) => {
1770
- const child = spawn2("sh", ["-c", command], {
1712
+ const child = spawn("sh", ["-c", command], {
1771
1713
  cwd: worktreePath,
1772
1714
  stdio: ["pipe", "pipe", "pipe"]
1773
1715
  });
@@ -1792,7 +1734,7 @@ var WorktreeManager = class {
1792
1734
  }
1793
1735
  async runInWorktreeWithTimeout(worktreePath, command, timeout) {
1794
1736
  return new Promise((resolve, reject) => {
1795
- const child = spawn2("sh", ["-c", command], {
1737
+ const child = spawn("sh", ["-c", command], {
1796
1738
  cwd: worktreePath,
1797
1739
  stdio: ["pipe", "pipe", "pipe"]
1798
1740
  });
@@ -1825,6 +1767,92 @@ var WorktreeManager = class {
1825
1767
  }
1826
1768
  };
1827
1769
 
1770
+ // src/actions/notebook.ts
1771
+ function createNotebookCell(cellType, source) {
1772
+ if (cellType === "code") {
1773
+ return {
1774
+ cell_type: "code",
1775
+ source,
1776
+ metadata: {},
1777
+ outputs: [],
1778
+ execution_count: null
1779
+ };
1780
+ }
1781
+ return {
1782
+ cell_type: "markdown",
1783
+ source,
1784
+ metadata: {}
1785
+ };
1786
+ }
1787
+ function resolveCellIndex(notebook, input) {
1788
+ if (typeof input.cell_index === "number") {
1789
+ return input.cell_index;
1790
+ }
1791
+ if (input.cell_id) {
1792
+ const index = notebook.cells.findIndex((cell) => cell.id === input.cell_id);
1793
+ if (index === -1) {
1794
+ throw new Error(`Notebook cell "${input.cell_id}" not found.`);
1795
+ }
1796
+ return index;
1797
+ }
1798
+ return -1;
1799
+ }
1800
+ function applyNotebookEdit(rawContent, input) {
1801
+ if (!input.path.endsWith(".ipynb")) {
1802
+ throw new Error("notebook_edit only supports .ipynb files.");
1803
+ }
1804
+ let notebook;
1805
+ try {
1806
+ notebook = JSON.parse(rawContent);
1807
+ } catch {
1808
+ throw new Error(`Notebook ${input.path} is not valid JSON.`);
1809
+ }
1810
+ if (!Array.isArray(notebook.cells)) {
1811
+ throw new Error(`Notebook ${input.path} does not contain a valid cells array.`);
1812
+ }
1813
+ const editMode = input.edit_mode ?? "replace";
1814
+ const index = resolveCellIndex(notebook, input);
1815
+ if (editMode === "insert") {
1816
+ if (!input.cell_type) {
1817
+ throw new Error('notebook_edit insert requires "cell_type".');
1818
+ }
1819
+ if (typeof input.new_source !== "string") {
1820
+ throw new Error('notebook_edit insert requires "new_source".');
1821
+ }
1822
+ const insertAt = index >= 0 ? index + 1 : notebook.cells.length;
1823
+ notebook.cells.splice(insertAt, 0, createNotebookCell(input.cell_type, input.new_source));
1824
+ return {
1825
+ updated: `${JSON.stringify(notebook, null, 2)}
1826
+ `,
1827
+ summary: `Inserted notebook cell at index ${insertAt} in ${input.path}.`
1828
+ };
1829
+ }
1830
+ if (index < 0 || index >= notebook.cells.length) {
1831
+ throw new Error('notebook_edit requires a valid "cell_index" or "cell_id".');
1832
+ }
1833
+ if (editMode === "delete") {
1834
+ notebook.cells.splice(index, 1);
1835
+ return {
1836
+ updated: `${JSON.stringify(notebook, null, 2)}
1837
+ `,
1838
+ summary: `Deleted notebook cell ${index} in ${input.path}.`
1839
+ };
1840
+ }
1841
+ if (typeof input.new_source !== "string") {
1842
+ throw new Error('notebook_edit replace requires "new_source".');
1843
+ }
1844
+ const target = notebook.cells[index];
1845
+ target.source = input.new_source;
1846
+ if (input.cell_type) {
1847
+ target.cell_type = input.cell_type;
1848
+ }
1849
+ return {
1850
+ updated: `${JSON.stringify(notebook, null, 2)}
1851
+ `,
1852
+ summary: `Updated notebook cell ${index} in ${input.path}.`
1853
+ };
1854
+ }
1855
+
1828
1856
  // src/core/customCommands.ts
1829
1857
  import fs4 from "fs-extra";
1830
1858
  import path5 from "path";
@@ -2308,14 +2336,139 @@ async function getUser() {
2308
2336
 
2309
2337
  // src/core/toolsRegistry.ts
2310
2338
  import fs5 from "fs-extra";
2339
+ import nodeFs from "fs/promises";
2311
2340
  import path6 from "path";
2341
+
2342
+ // src/core/metaTools/schema.ts
2343
+ import { createHash } from "crypto";
2344
+ import { z } from "zod";
2345
+ var META_TOOL_SCHEMA_VERSION = 1;
2346
+ var META_TOOL_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
2347
+ var META_TOOL_SCOPES = ["user", "project"];
2348
+ var JsonSchemaObject = z.record(z.string(), z.unknown()).refine((value) => value.type === "object", 'parameters must be a JSON Schema object with type "object"');
2349
+ var MetaToolCreateInputSchema = z.object({
2350
+ name: z.string().trim().regex(META_TOOL_NAME_PATTERN, "name must be snake_case and start with a lowercase letter"),
2351
+ description: z.string().trim().min(1).max(300),
2352
+ parameters: JsonSchemaObject,
2353
+ handler: z.string().trim().min(1).max(2e3),
2354
+ source: z.enum(["agent", "user"]).default("agent"),
2355
+ scope: z.enum(META_TOOL_SCOPES).default("user")
2356
+ });
2357
+ var MetaToolDefinitionSchema = MetaToolCreateInputSchema.extend({
2358
+ schemaVersion: z.literal(META_TOOL_SCHEMA_VERSION),
2359
+ createdAt: z.string().min(1),
2360
+ updatedAt: z.string().min(1).optional(),
2361
+ fingerprint: z.string().min(16),
2362
+ disabled: z.boolean().optional()
2363
+ });
2364
+ function canonicalize(value) {
2365
+ if (Array.isArray(value)) {
2366
+ return `[${value.map(canonicalize).join(",")}]`;
2367
+ }
2368
+ if (value && typeof value === "object") {
2369
+ const record = value;
2370
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`).join(",")}}`;
2371
+ }
2372
+ return JSON.stringify(value);
2373
+ }
2374
+ function fingerprintMetaTool(input) {
2375
+ return createHash("sha256").update(canonicalize({
2376
+ name: input.name,
2377
+ description: input.description,
2378
+ parameters: input.parameters,
2379
+ handler: input.handler
2380
+ })).digest("hex");
2381
+ }
2382
+ function normalizeMetaToolDefinition(candidate) {
2383
+ if (!candidate || typeof candidate !== "object") {
2384
+ return null;
2385
+ }
2386
+ const value = candidate;
2387
+ const source = value.source === "user" ? "user" : "agent";
2388
+ const scope = value.scope === "project" ? "project" : "user";
2389
+ const definition = {
2390
+ ...value,
2391
+ schemaVersion: value.schemaVersion ?? META_TOOL_SCHEMA_VERSION,
2392
+ source,
2393
+ scope,
2394
+ createdAt: typeof value.createdAt === "string" ? value.createdAt : (/* @__PURE__ */ new Date(0)).toISOString()
2395
+ };
2396
+ const parsedCreateInput = MetaToolCreateInputSchema.safeParse(definition);
2397
+ if (!parsedCreateInput.success) {
2398
+ return null;
2399
+ }
2400
+ const parsed = MetaToolDefinitionSchema.safeParse({
2401
+ ...definition,
2402
+ fingerprint: typeof value.fingerprint === "string" ? value.fingerprint : fingerprintMetaTool(parsedCreateInput.data)
2403
+ });
2404
+ return parsed.success ? parsed.data : null;
2405
+ }
2406
+
2407
+ // src/core/metaTools/safety.ts
2408
+ var DANGEROUS_PATTERNS = [
2409
+ { pattern: /rm\s+(-[rf]+\s+)*\/(?!\w)/i, description: "rm with root path" },
2410
+ { pattern: /rm\s+.*--no-preserve-root/i, description: "rm --no-preserve-root" },
2411
+ { pattern: /dd\s+.*(?:of|if)=\/dev\/[sh]d/i, description: "dd to disk device" },
2412
+ { pattern: /mkfs\./i, description: "filesystem format" },
2413
+ { pattern: /wipefs/i, description: "disk wipe" },
2414
+ { pattern: /\bsudo\s/i, description: "sudo command" },
2415
+ { pattern: /\bsu\s+-?\s*\w/i, description: "su command" },
2416
+ { pattern: /chmod\s+[0-7]*7[0-7]*/i, description: "world-writable chmod" },
2417
+ { pattern: /chown\s+root/i, description: "chown to root" },
2418
+ { pattern: /curl\s+.*\|\s*(ba)?sh/i, description: "curl | bash" },
2419
+ { pattern: /wget\s+.*\|\s*(ba)?sh/i, description: "wget | sh" },
2420
+ { pattern: /\beval\s+[`$]/i, description: "eval with expansion" },
2421
+ { pattern: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/i, description: "fork bomb" },
2422
+ { pattern: /while\s+true.*do.*done/i, description: "infinite loop" },
2423
+ { pattern: /nc\s+.*-e\s*\/bin/i, description: "netcat reverse shell" },
2424
+ { pattern: /ncat\s+.*-e\s*\/bin/i, description: "ncat reverse shell" },
2425
+ { pattern: /bash\s+-i\s+>&?\s*\/dev\/tcp/i, description: "bash reverse shell" },
2426
+ { pattern: /iptables\s+-F/i, description: "flush firewall rules" },
2427
+ { pattern: /gpg\s+.*--encrypt.*-r\s+\S+\s+\//i, description: "gpg encrypt root" }
2428
+ ];
2429
+ function assertSafeMetaToolHandler(handler) {
2430
+ for (const { pattern, description } of DANGEROUS_PATTERNS) {
2431
+ if (pattern.test(handler)) {
2432
+ throw new Error(`Handler contains dangerous pattern: ${description}`);
2433
+ }
2434
+ }
2435
+ }
2436
+
2437
+ // src/core/toolsRegistry.ts
2438
+ function locationKey(scope, name) {
2439
+ return `${scope}:${name}`;
2440
+ }
2441
+ function normalizeLocations(input) {
2442
+ if (Array.isArray(input)) {
2443
+ return input;
2444
+ }
2445
+ return [{ scope: "user", dir: input ?? AUTOHAND_PATHS.tools }];
2446
+ }
2447
+ function delay(ms) {
2448
+ return new Promise((resolve) => setTimeout(resolve, ms));
2449
+ }
2450
+ function createToolsRegistry(workspaceRoot, userToolsDir = AUTOHAND_PATHS.tools) {
2451
+ const locations = workspaceRoot ? [
2452
+ { scope: "project", dir: path6.join(workspaceRoot, PROJECT_DIR_NAME, "tools") },
2453
+ { scope: "user", dir: userToolsDir }
2454
+ ] : [{ scope: "user", dir: userToolsDir }];
2455
+ return new ToolsRegistry(locations);
2456
+ }
2312
2457
  var ToolsRegistry = class {
2313
- constructor(toolsDir = AUTOHAND_PATHS.tools) {
2314
- this.toolsDir = toolsDir;
2315
- this.metaToolCache = /* @__PURE__ */ new Map();
2458
+ metaToolCache = /* @__PURE__ */ new Map();
2459
+ metaToolRecords = /* @__PURE__ */ new Map();
2460
+ diagnostics = [];
2461
+ constructor(locations) {
2462
+ this.locations = normalizeLocations(locations);
2316
2463
  }
2464
+ locations;
2317
2465
  async initialize() {
2318
- await fs5.ensureDir(this.toolsDir);
2466
+ this.metaToolCache.clear();
2467
+ this.metaToolRecords.clear();
2468
+ this.diagnostics = [];
2469
+ for (const location of this.locations) {
2470
+ await fs5.ensureDir(location.dir);
2471
+ }
2319
2472
  await this.loadMetaToolDefinitions();
2320
2473
  }
2321
2474
  async listTools(builtIns) {
@@ -2341,20 +2494,41 @@ var ToolsRegistry = class {
2341
2494
  entries.push({
2342
2495
  name: tool.name,
2343
2496
  description: tool.description,
2344
- source: "meta"
2497
+ source: "meta",
2498
+ scope: tool.scope,
2499
+ disabled: tool.disabled,
2500
+ createdAt: tool.createdAt,
2501
+ schemaVersion: tool.schemaVersion,
2502
+ handlerPreview: tool.handler.length > 140 ? `${tool.handler.slice(0, 137)}...` : tool.handler,
2503
+ reuseHint: `Use ${tool.name} instead of creating another tool for: ${tool.description}`
2345
2504
  });
2346
2505
  seen.add(name);
2347
2506
  }
2348
2507
  return entries;
2349
2508
  }
2350
2509
  async saveMetaTool(definition) {
2351
- const fullDef = {
2352
- ...definition,
2353
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
2354
- };
2355
- const filePath = path6.join(this.toolsDir, `${definition.name}.json`);
2356
- await fs5.writeJson(filePath, fullDef, { spaces: 2 });
2357
- this.metaToolCache.set(definition.name, fullDef);
2510
+ const fullDef = normalizeMetaToolDefinition(definition);
2511
+ if (!fullDef) {
2512
+ throw new Error(`Invalid meta-tool definition for "${definition.name}"`);
2513
+ }
2514
+ assertSafeMetaToolHandler(fullDef.handler);
2515
+ const location = this.getLocationForScope(fullDef.scope);
2516
+ const filePath = path6.join(location.dir, `${fullDef.name}.json`);
2517
+ const release = await this.acquireLock(location.dir, fullDef.name);
2518
+ try {
2519
+ const existing = await this.readDefinition(filePath);
2520
+ if (existing) {
2521
+ if (existing.fingerprint === fullDef.fingerprint) {
2522
+ this.upsertRecord(existing, filePath);
2523
+ return existing;
2524
+ }
2525
+ throw new Error(`Meta-tool "${fullDef.name}" already exists in ${fullDef.scope} scope`);
2526
+ }
2527
+ await this.writeDefinition(filePath, fullDef);
2528
+ } finally {
2529
+ await release();
2530
+ }
2531
+ this.upsertRecord(fullDef, filePath);
2358
2532
  return fullDef;
2359
2533
  }
2360
2534
  getMetaTool(name) {
@@ -2366,6 +2540,71 @@ var ToolsRegistry = class {
2366
2540
  getAllMetaTools() {
2367
2541
  return Array.from(this.metaToolCache.values());
2368
2542
  }
2543
+ listMetaTools(options = {}) {
2544
+ if (!options.includeDisabled) {
2545
+ return this.getAllMetaTools();
2546
+ }
2547
+ return Array.from(this.metaToolRecords.values()).map((record) => record.definition);
2548
+ }
2549
+ getDiagnostics() {
2550
+ return [...this.diagnostics];
2551
+ }
2552
+ async deleteMetaTool(name, scope) {
2553
+ const record = this.findRecord(name, scope);
2554
+ if (!record) {
2555
+ throw new Error(`Meta-tool "${name}" not found`);
2556
+ }
2557
+ await fs5.remove(record.filePath);
2558
+ this.deleteRecord(record.definition);
2559
+ return record.definition;
2560
+ }
2561
+ async setMetaToolDisabled(name, disabled, scope) {
2562
+ const record = this.findRecord(name, scope);
2563
+ if (!record) {
2564
+ throw new Error(`Meta-tool "${name}" not found`);
2565
+ }
2566
+ const updated = {
2567
+ ...record.definition,
2568
+ disabled,
2569
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2570
+ };
2571
+ await this.writeDefinition(record.filePath, updated);
2572
+ this.upsertRecord(updated, record.filePath);
2573
+ this.rebuildActiveCache();
2574
+ return updated;
2575
+ }
2576
+ async renameMetaTool(name, newName, scope) {
2577
+ if (!META_TOOL_NAME_PATTERN.test(newName)) {
2578
+ throw new Error("new name must be snake_case and start with a lowercase letter");
2579
+ }
2580
+ const record = this.findRecord(name, scope);
2581
+ if (!record) {
2582
+ throw new Error(`Meta-tool "${name}" not found`);
2583
+ }
2584
+ if (this.findRecord(newName)) {
2585
+ throw new Error(`Meta-tool "${newName}" already exists`);
2586
+ }
2587
+ const renamed = {
2588
+ ...record.definition,
2589
+ name: newName,
2590
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2591
+ };
2592
+ const normalized = normalizeMetaToolDefinition({
2593
+ ...renamed,
2594
+ fingerprint: fingerprintMetaTool(renamed)
2595
+ });
2596
+ if (!normalized) {
2597
+ throw new Error(`Invalid meta-tool definition for "${newName}"`);
2598
+ }
2599
+ const location = this.getLocationForScope(normalized.scope);
2600
+ const nextFilePath = path6.join(location.dir, `${newName}.json`);
2601
+ await this.writeDefinition(nextFilePath, normalized);
2602
+ await fs5.remove(record.filePath);
2603
+ this.deleteRecord(record.definition);
2604
+ this.upsertRecord(normalized, nextFilePath);
2605
+ this.rebuildActiveCache();
2606
+ return normalized;
2607
+ }
2369
2608
  toToolDefinitions() {
2370
2609
  return this.getAllMetaTools().map((tool) => {
2371
2610
  const params = tool.parameters;
@@ -2381,163 +2620,375 @@ var ToolsRegistry = class {
2381
2620
  });
2382
2621
  }
2383
2622
  async loadMetaToolDefinitions() {
2384
- try {
2385
- const exists = await fs5.pathExists(this.toolsDir);
2386
- if (!exists) {
2387
- return;
2388
- }
2389
- const files = await fs5.readdir(this.toolsDir);
2390
- for (const file of files) {
2391
- if (!file.endsWith(".json")) {
2623
+ for (const location of this.locations) {
2624
+ try {
2625
+ const exists = await fs5.pathExists(location.dir);
2626
+ if (!exists) {
2392
2627
  continue;
2393
2628
  }
2394
- const fullPath = path6.join(this.toolsDir, file);
2395
- try {
2396
- const data = await fs5.readJson(fullPath);
2397
- if (this.isValidMetaTool(data)) {
2398
- this.metaToolCache.set(data.name, data);
2629
+ const files = await fs5.readdir(location.dir);
2630
+ for (const file of files) {
2631
+ if (!file.endsWith(".json")) {
2632
+ continue;
2633
+ }
2634
+ const fullPath = path6.join(location.dir, file);
2635
+ try {
2636
+ const data = normalizeMetaToolDefinition({
2637
+ ...await fs5.readJson(fullPath),
2638
+ scope: location.scope
2639
+ });
2640
+ if (data) {
2641
+ assertSafeMetaToolHandler(data.handler);
2642
+ this.metaToolRecords.set(locationKey(data.scope, data.name), { definition: data, filePath: fullPath });
2643
+ } else {
2644
+ this.diagnostics.push({ file: fullPath, reason: "invalid meta-tool definition" });
2645
+ }
2646
+ } catch (error) {
2647
+ const reason = error instanceof Error ? error.message : "invalid meta-tool file";
2648
+ this.diagnostics.push({ file: fullPath, reason });
2399
2649
  }
2400
- } catch {
2401
2650
  }
2651
+ } catch (error) {
2652
+ const reason = error instanceof Error ? error.message : "tools directory could not be read";
2653
+ this.diagnostics.push({ file: location.dir, reason });
2402
2654
  }
2403
- } catch {
2404
2655
  }
2656
+ this.rebuildActiveCache();
2405
2657
  }
2406
- isValidMetaTool(candidate) {
2407
- if (!candidate || typeof candidate !== "object") {
2408
- return false;
2658
+ getLocationForScope(scope) {
2659
+ const location = this.locations.find((candidate) => candidate.scope === scope);
2660
+ if (!location) {
2661
+ throw new Error(`No tools directory configured for ${scope} scope`);
2662
+ }
2663
+ return location;
2664
+ }
2665
+ async readDefinition(filePath) {
2666
+ if (!await fs5.pathExists(filePath)) {
2667
+ return null;
2668
+ }
2669
+ return normalizeMetaToolDefinition(await fs5.readJson(filePath));
2670
+ }
2671
+ async writeDefinition(filePath, definition) {
2672
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
2673
+ try {
2674
+ await fs5.ensureDir(path6.dirname(filePath));
2675
+ await fs5.outputFile(tempPath, `${JSON.stringify(definition, null, 2)}
2676
+ `, { mode: 384 });
2677
+ const handle = await nodeFs.open(tempPath, "r");
2678
+ try {
2679
+ await handle.sync();
2680
+ } finally {
2681
+ await handle.close();
2682
+ }
2683
+ await nodeFs.rename(tempPath, filePath);
2684
+ } catch (error) {
2685
+ await fs5.remove(tempPath).catch(() => {
2686
+ });
2687
+ throw error;
2688
+ }
2689
+ }
2690
+ async acquireLock(dir, name) {
2691
+ await fs5.ensureDir(dir);
2692
+ const lockPath = path6.join(dir, `${name}.lock`);
2693
+ for (let attempt = 0; attempt < 40; attempt++) {
2694
+ try {
2695
+ const handle = await nodeFs.open(lockPath, "wx", 384);
2696
+ await handle.close();
2697
+ return async () => {
2698
+ await fs5.remove(lockPath).catch(() => {
2699
+ });
2700
+ };
2701
+ } catch (error) {
2702
+ const code = typeof error === "object" && error && "code" in error ? error.code : void 0;
2703
+ if (code === "EEXIST") {
2704
+ await delay(25);
2705
+ continue;
2706
+ }
2707
+ throw error;
2708
+ }
2709
+ }
2710
+ throw new Error(`Timed out waiting for meta-tool lock "${name}"`);
2711
+ }
2712
+ upsertRecord(definition, filePath) {
2713
+ this.metaToolRecords.set(locationKey(definition.scope, definition.name), { definition, filePath });
2714
+ this.rebuildActiveCache();
2715
+ }
2716
+ deleteRecord(definition) {
2717
+ this.metaToolRecords.delete(locationKey(definition.scope, definition.name));
2718
+ this.rebuildActiveCache();
2719
+ }
2720
+ findRecord(name, scope) {
2721
+ if (scope) {
2722
+ return this.metaToolRecords.get(locationKey(scope, name));
2723
+ }
2724
+ for (const location of this.locations) {
2725
+ const record = this.metaToolRecords.get(locationKey(location.scope, name));
2726
+ if (record) {
2727
+ return record;
2728
+ }
2729
+ }
2730
+ return void 0;
2731
+ }
2732
+ rebuildActiveCache() {
2733
+ this.metaToolCache.clear();
2734
+ for (const location of this.locations) {
2735
+ for (const record of this.metaToolRecords.values()) {
2736
+ if (record.definition.scope !== location.scope || record.definition.disabled) {
2737
+ continue;
2738
+ }
2739
+ if (!this.metaToolCache.has(record.definition.name)) {
2740
+ this.metaToolCache.set(record.definition.name, record.definition);
2741
+ }
2742
+ }
2409
2743
  }
2410
- const value = candidate;
2411
- return typeof value.name === "string" && typeof value.description === "string" && typeof value.handler === "string" && typeof value.parameters === "object";
2412
2744
  }
2413
2745
  };
2414
2746
 
2415
- // src/core/SecurityScanner.ts
2416
- var SecurityScanner = class {
2417
- constructor() {
2418
- /**
2419
- * Built-in secret detection patterns
2420
- */
2421
- this.patterns = [
2422
- // AWS
2423
- {
2424
- name: "AWS Access Key",
2425
- regex: /AKIA[0-9A-Z]{16}/,
2426
- severity: "high",
2427
- description: "AWS Access Key ID"
2428
- },
2429
- // GitHub
2430
- {
2431
- name: "GitHub Token",
2432
- regex: /ghp_[a-zA-Z0-9]{36}/,
2433
- severity: "high",
2434
- description: "GitHub Personal Access Token"
2435
- },
2436
- {
2437
- name: "GitHub OAuth",
2438
- regex: /gho_[a-zA-Z0-9]{36}/,
2439
- severity: "high",
2440
- description: "GitHub OAuth Token"
2441
- },
2442
- {
2443
- name: "GitHub App Token",
2444
- regex: /ghu_[a-zA-Z0-9]{36}/,
2445
- severity: "high",
2446
- description: "GitHub App User Token"
2447
- },
2448
- // OpenAI / Anthropic
2449
- {
2450
- name: "OpenAI Key",
2451
- regex: /sk-proj-[a-zA-Z0-9]{32,}/,
2452
- severity: "high",
2453
- description: "OpenAI Project API Key"
2454
- },
2455
- {
2456
- name: "Anthropic Key",
2457
- regex: /sk-ant-api[a-zA-Z0-9-]{32,}/,
2458
- severity: "high",
2459
- description: "Anthropic API Key"
2460
- },
2461
- // Google
2462
- {
2463
- name: "Google API Key",
2464
- regex: /AIzaSy[0-9A-Za-z-_]{33}/,
2465
- severity: "high",
2466
- description: "Google API Key"
2467
- },
2468
- // Stripe
2469
- {
2470
- name: "Stripe Live Key",
2471
- regex: /sk_live_[0-9a-zA-Z]{24,}/,
2472
- severity: "high",
2473
- description: "Stripe Live Secret Key"
2474
- },
2475
- {
2476
- name: "Stripe Test Key",
2477
- regex: /sk_test_[0-9a-zA-Z]{24,}/,
2478
- severity: "low",
2479
- description: "Stripe Test Secret Key"
2480
- },
2481
- // Private Keys
2482
- {
2483
- name: "Private Key",
2484
- regex: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
2485
- severity: "high",
2486
- description: "Private Key File"
2487
- },
2488
- // Database URLs with credentials
2489
- {
2490
- name: "Database URL",
2491
- regex: /(postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
2492
- severity: "high",
2493
- description: "Database URL with credentials"
2494
- },
2495
- // JWT Tokens
2496
- {
2497
- name: "JWT Token",
2498
- regex: /eyJ[a-zA-Z0-9]{10,}\.eyJ[a-zA-Z0-9]{10,}\.[a-zA-Z0-9_-]{10,}/,
2499
- severity: "medium",
2500
- description: "JSON Web Token"
2501
- },
2502
- // Generic patterns (lower priority)
2503
- {
2504
- name: "Generic API Key",
2505
- regex: /[aA][pP][iI][-_]?[kK][eE][yY]\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/,
2506
- severity: "medium",
2507
- description: "Generic API Key Assignment"
2508
- },
2509
- {
2510
- name: "Generic Secret",
2511
- regex: /[sS][eE][cC][rR][eE][tT]\s*[:=]\s*['"][^'"]{8,}['"]/,
2512
- severity: "medium",
2513
- description: "Generic Secret Assignment"
2514
- },
2515
- {
2516
- name: "Password Assignment",
2517
- regex: /[pP][aA][sS][sS][wW][oO][rR][dD]\s*[:=]\s*['"][^'"]{4,}['"]/,
2518
- severity: "medium",
2519
- description: "Password Assignment"
2747
+ // src/core/metaTools/MetaToolService.ts
2748
+ var STOP_WORDS = /* @__PURE__ */ new Set([
2749
+ "a",
2750
+ "an",
2751
+ "and",
2752
+ "by",
2753
+ "for",
2754
+ "from",
2755
+ "in",
2756
+ "of",
2757
+ "on",
2758
+ "the",
2759
+ "to",
2760
+ "with",
2761
+ "find",
2762
+ "search",
2763
+ "list",
2764
+ "get",
2765
+ "show",
2766
+ "analyze",
2767
+ "count",
2768
+ "run",
2769
+ "quick",
2770
+ "tool",
2771
+ "tools",
2772
+ "file",
2773
+ "files",
2774
+ "codebase",
2775
+ "workspace",
2776
+ "source",
2777
+ "across"
2778
+ ]);
2779
+ function normalizeHandler(handler) {
2780
+ return handler.trim().replace(/\s+/g, " ");
2781
+ }
2782
+ function tokenize2(value) {
2783
+ const tokens = value.toLowerCase().split(/[^a-z0-9]+/).map((token) => token.endsWith("s") ? token.slice(0, -1) : token).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
2784
+ return new Set(tokens);
2785
+ }
2786
+ function overlapRatio(left, right) {
2787
+ if (left.size === 0 || right.size === 0) {
2788
+ return 0;
2789
+ }
2790
+ let intersection = 0;
2791
+ for (const token of left) {
2792
+ if (right.has(token)) {
2793
+ intersection++;
2794
+ }
2795
+ }
2796
+ return intersection / Math.min(left.size, right.size);
2797
+ }
2798
+ function isSimilarTool(candidate, existing) {
2799
+ const candidateNameTokens = tokenize2(candidate.name);
2800
+ const existingNameTokens = tokenize2(existing.name);
2801
+ if (overlapRatio(candidateNameTokens, existingNameTokens) >= 0.75) {
2802
+ return true;
2803
+ }
2804
+ const candidateDescriptionTokens = tokenize2(candidate.description);
2805
+ const existingDescriptionTokens = tokenize2(existing.description ?? "");
2806
+ return overlapRatio(candidateDescriptionTokens, existingDescriptionTokens) >= 0.75;
2807
+ }
2808
+ var MetaToolService = class {
2809
+ constructor(registry) {
2810
+ this.registry = registry;
2811
+ }
2812
+ registry;
2813
+ async createMetaTool(input, registeredTools) {
2814
+ const parsed = MetaToolCreateInputSchema.safeParse(input);
2815
+ if (!parsed.success) {
2816
+ throw new Error(`Invalid meta-tool definition: ${parsed.error.issues[0]?.message ?? "unknown validation error"}`);
2817
+ }
2818
+ const definitionInput = parsed.data;
2819
+ assertSafeMetaToolHandler(definitionInput.handler);
2820
+ const fingerprint = fingerprintMetaTool(definitionInput);
2821
+ const existingByName = this.registry.getMetaTool(definitionInput.name);
2822
+ if (existingByName) {
2823
+ if (existingByName.fingerprint === fingerprint) {
2824
+ return {
2825
+ status: "existing",
2826
+ definition: existingByName,
2827
+ message: `Meta-tool "${definitionInput.name}" already exists with the same definition.`
2828
+ };
2520
2829
  }
2830
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": already exists with a different definition`);
2831
+ }
2832
+ const registeredNameConflict = registeredTools.find((tool) => tool.name === definitionInput.name);
2833
+ if (registeredNameConflict) {
2834
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": conflicts with existing tool`);
2835
+ }
2836
+ for (const existing of this.registry.getAllMetaTools()) {
2837
+ if (normalizeHandler(existing.handler) === normalizeHandler(definitionInput.handler)) {
2838
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": same handler already exists as "${existing.name}"`);
2839
+ }
2840
+ }
2841
+ const existingTools = [
2842
+ ...registeredTools,
2843
+ ...this.registry.getAllMetaTools().map((tool) => ({
2844
+ name: tool.name,
2845
+ description: tool.description
2846
+ }))
2521
2847
  ];
2522
- /**
2523
- * Placeholder patterns to ignore (false positives)
2524
- * These must be exact matches or specific patterns, not substrings
2525
- */
2526
- this.placeholderPatterns = [
2527
- /your[-_]?api[-_]?key/i,
2528
- /your[-_]?secret/i,
2529
- /\*\*\*/,
2530
- /\.\.\.$/,
2531
- /-example/i,
2532
- /placeholder/i,
2533
- /sk-xxx$/i,
2534
- /sk-your/i,
2535
- /<your[-_]?key[-_]?here>/i,
2536
- /\$\{[A-Z_]+\}/,
2537
- // Environment variable placeholders
2538
- /process\.env\./
2539
- ];
2848
+ const similar = existingTools.find((tool) => tool.name !== definitionInput.name && isSimilarTool(definitionInput, tool));
2849
+ if (similar) {
2850
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": similar existing tool "${similar.name}" should be reused`);
2851
+ }
2852
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2853
+ const saved = await this.registry.saveMetaTool({
2854
+ ...definitionInput,
2855
+ schemaVersion: 1,
2856
+ createdAt: now,
2857
+ updatedAt: now,
2858
+ fingerprint
2859
+ });
2860
+ return {
2861
+ status: "created",
2862
+ definition: saved,
2863
+ message: `Created meta-tool "${saved.name}" - available in this and future sessions`
2864
+ };
2540
2865
  }
2866
+ };
2867
+
2868
+ // src/core/SecurityScanner.ts
2869
+ var SecurityScanner = class {
2870
+ /**
2871
+ * Built-in secret detection patterns
2872
+ */
2873
+ patterns = [
2874
+ // AWS
2875
+ {
2876
+ name: "AWS Access Key",
2877
+ regex: /AKIA[0-9A-Z]{16}/,
2878
+ severity: "high",
2879
+ description: "AWS Access Key ID"
2880
+ },
2881
+ // GitHub
2882
+ {
2883
+ name: "GitHub Token",
2884
+ regex: /ghp_[a-zA-Z0-9]{36}/,
2885
+ severity: "high",
2886
+ description: "GitHub Personal Access Token"
2887
+ },
2888
+ {
2889
+ name: "GitHub OAuth",
2890
+ regex: /gho_[a-zA-Z0-9]{36}/,
2891
+ severity: "high",
2892
+ description: "GitHub OAuth Token"
2893
+ },
2894
+ {
2895
+ name: "GitHub App Token",
2896
+ regex: /ghu_[a-zA-Z0-9]{36}/,
2897
+ severity: "high",
2898
+ description: "GitHub App User Token"
2899
+ },
2900
+ // OpenAI / Anthropic
2901
+ {
2902
+ name: "OpenAI Key",
2903
+ regex: /sk-proj-[a-zA-Z0-9]{32,}/,
2904
+ severity: "high",
2905
+ description: "OpenAI Project API Key"
2906
+ },
2907
+ {
2908
+ name: "Anthropic Key",
2909
+ regex: /sk-ant-api[a-zA-Z0-9-]{32,}/,
2910
+ severity: "high",
2911
+ description: "Anthropic API Key"
2912
+ },
2913
+ // Google
2914
+ {
2915
+ name: "Google API Key",
2916
+ regex: /AIzaSy[0-9A-Za-z-_]{33}/,
2917
+ severity: "high",
2918
+ description: "Google API Key"
2919
+ },
2920
+ // Stripe
2921
+ {
2922
+ name: "Stripe Live Key",
2923
+ regex: /sk_live_[0-9a-zA-Z]{24,}/,
2924
+ severity: "high",
2925
+ description: "Stripe Live Secret Key"
2926
+ },
2927
+ {
2928
+ name: "Stripe Test Key",
2929
+ regex: /sk_test_[0-9a-zA-Z]{24,}/,
2930
+ severity: "low",
2931
+ description: "Stripe Test Secret Key"
2932
+ },
2933
+ // Private Keys
2934
+ {
2935
+ name: "Private Key",
2936
+ regex: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
2937
+ severity: "high",
2938
+ description: "Private Key File"
2939
+ },
2940
+ // Database URLs with credentials
2941
+ {
2942
+ name: "Database URL",
2943
+ regex: /(postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
2944
+ severity: "high",
2945
+ description: "Database URL with credentials"
2946
+ },
2947
+ // JWT Tokens
2948
+ {
2949
+ name: "JWT Token",
2950
+ regex: /eyJ[a-zA-Z0-9]{10,}\.eyJ[a-zA-Z0-9]{10,}\.[a-zA-Z0-9_-]{10,}/,
2951
+ severity: "medium",
2952
+ description: "JSON Web Token"
2953
+ },
2954
+ // Generic patterns (lower priority)
2955
+ {
2956
+ name: "Generic API Key",
2957
+ regex: /[aA][pP][iI][-_]?[kK][eE][yY]\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/,
2958
+ severity: "medium",
2959
+ description: "Generic API Key Assignment"
2960
+ },
2961
+ {
2962
+ name: "Generic Secret",
2963
+ regex: /[sS][eE][cC][rR][eE][tT]\s*[:=]\s*['"][^'"]{8,}['"]/,
2964
+ severity: "medium",
2965
+ description: "Generic Secret Assignment"
2966
+ },
2967
+ {
2968
+ name: "Password Assignment",
2969
+ regex: /[pP][aA][sS][sS][wW][oO][rR][dD]\s*[:=]\s*['"][^'"]{4,}['"]/,
2970
+ severity: "medium",
2971
+ description: "Password Assignment"
2972
+ }
2973
+ ];
2974
+ /**
2975
+ * Placeholder patterns to ignore (false positives)
2976
+ * These must be exact matches or specific patterns, not substrings
2977
+ */
2978
+ placeholderPatterns = [
2979
+ /your[-_]?api[-_]?key/i,
2980
+ /your[-_]?secret/i,
2981
+ /\*\*\*/,
2982
+ /\.\.\.$/,
2983
+ /-example/i,
2984
+ /placeholder/i,
2985
+ /sk-xxx$/i,
2986
+ /sk-your/i,
2987
+ /<your[-_]?key[-_]?here>/i,
2988
+ /\$\{[A-Z_]+\}/,
2989
+ // Environment variable placeholders
2990
+ /process\.env\./
2991
+ ];
2541
2992
  /**
2542
2993
  * Scan git diff for secrets
2543
2994
  * @param diff - Git diff output
@@ -2697,7 +3148,9 @@ var SecurityScanner = class {
2697
3148
  lines.push("");
2698
3149
  }
2699
3150
  if (result.blockedCount > 0) {
2700
- lines.push(`[BLOCKED] ${result.blockedCount} high-severity secrets found`);
3151
+ lines.push(
3152
+ `[BLOCKED] ${result.blockedCount} high-severity secrets found`
3153
+ );
2701
3154
  lines.push(" Remove secrets before committing.");
2702
3155
  lines.push(" Consider using environment variables instead.");
2703
3156
  } else if (result.warningCount > 0) {
@@ -2725,6 +3178,7 @@ import { execSync } from "child_process";
2725
3178
  import fs6 from "fs-extra";
2726
3179
  import path7 from "path";
2727
3180
  var PlanFileStorage = class {
3181
+ plansDir;
2728
3182
  constructor() {
2729
3183
  this.plansDir = AUTOHAND_PATHS.plans;
2730
3184
  }
@@ -2859,10 +3313,22 @@ var PlanFileStorage = class {
2859
3313
 
2860
3314
  // src/core/actionExecutor.ts
2861
3315
  import { randomUUID } from "crypto";
3316
+ var GOAL_TOOL_TYPES = /* @__PURE__ */ new Set([
3317
+ "get_goal",
3318
+ "create_goal",
3319
+ "create_goal_from_template",
3320
+ "update_goal",
3321
+ "clear_goal",
3322
+ "list_goal_templates",
3323
+ "enqueue_goal",
3324
+ "list_goal_queue",
3325
+ "start_queued_goal",
3326
+ "dequeue_goal",
3327
+ "remove_queued_goal"
3328
+ ]);
2862
3329
  var ActionExecutor = class _ActionExecutor {
2863
3330
  constructor(deps) {
2864
3331
  this.deps = deps;
2865
- this.searchCache = /* @__PURE__ */ new Map();
2866
3332
  this.runtime = deps.runtime;
2867
3333
  this.files = deps.files;
2868
3334
  this.resolveWorkspacePath = deps.resolveWorkspacePath;
@@ -2870,7 +3336,8 @@ var ActionExecutor = class _ActionExecutor {
2870
3336
  this.projectManager = deps.projectManager;
2871
3337
  this.sessionId = deps.sessionId;
2872
3338
  this.logExploration = deps.onExploration;
2873
- this.toolsRegistry = deps.toolsRegistry ?? new ToolsRegistry();
3339
+ this.toolsRegistry = deps.toolsRegistry ?? createToolsRegistry(deps.runtime.workspaceRoot);
3340
+ this.metaToolService = deps.metaToolService ?? new MetaToolService(this.toolsRegistry);
2874
3341
  this.getRegisteredTools = deps.getRegisteredTools ?? (() => []);
2875
3342
  this.permissionManager = deps.permissionManager ?? new PermissionManager(deps.runtime.config.permissions);
2876
3343
  this.memoryManager = deps.memoryManager;
@@ -2879,8 +3346,84 @@ var ActionExecutor = class _ActionExecutor {
2879
3346
  this.onAskFollowup = deps.onAskFollowup;
2880
3347
  this.onPlanCreated = deps.onPlanCreated;
2881
3348
  this.onPermissionRequest = deps.onPermissionRequest;
3349
+ this.onReviewHook = deps.onReviewHook;
3350
+ this.onModalPause = deps.onModalPause;
3351
+ this.onRequestDirectoryAccess = deps.onRequestDirectoryAccess;
3352
+ this.onLiveCommandStart = deps.onLiveCommandStart;
3353
+ this.onLiveCommandOutput = deps.onLiveCommandOutput;
3354
+ this.onLiveCommandRemove = deps.onLiveCommandRemove;
3355
+ this.onMetaToolCreated = deps.onMetaToolCreated;
2882
3356
  this.securityScanner = new SecurityScanner();
2883
3357
  }
3358
+ deps;
3359
+ runtime;
3360
+ files;
3361
+ resolveWorkspacePath;
3362
+ confirmDangerousAction;
3363
+ projectManager;
3364
+ sessionId;
3365
+ logExploration;
3366
+ toolsRegistry;
3367
+ metaToolService;
3368
+ getRegisteredTools;
3369
+ permissionManager;
3370
+ memoryManager;
3371
+ onToolOutput;
3372
+ onFileModified;
3373
+ onAskFollowup;
3374
+ onPlanCreated;
3375
+ onPermissionRequest;
3376
+ onReviewHook;
3377
+ onModalPause;
3378
+ onRequestDirectoryAccess;
3379
+ onLiveCommandStart;
3380
+ onLiveCommandOutput;
3381
+ onLiveCommandRemove;
3382
+ onMetaToolCreated;
3383
+ securityScanner;
3384
+ searchCache = /* @__PURE__ */ new Map();
3385
+ fffSearchProviderPromise = null;
3386
+ fffSearchWorkspaceRoot = null;
3387
+ fffSearchIdleTimer = null;
3388
+ static FFF_SEARCH_IDLE_TTL_MS = 6e4;
3389
+ shouldDisplayToolOutput() {
3390
+ return this.runtime.config.ui?.silentToolOutput !== true;
3391
+ }
3392
+ async getFFFSearchProvider() {
3393
+ if (this.fffSearchIdleTimer) {
3394
+ clearTimeout(this.fffSearchIdleTimer);
3395
+ this.fffSearchIdleTimer = null;
3396
+ }
3397
+ const workspaceRoot = this.runtime.workspaceRoot;
3398
+ if (this.fffSearchProviderPromise && this.fffSearchWorkspaceRoot === workspaceRoot) {
3399
+ return this.fffSearchProviderPromise;
3400
+ }
3401
+ if (this.fffSearchProviderPromise) {
3402
+ this.fffSearchProviderPromise.then((provider) => provider.destroy()).catch(() => {
3403
+ });
3404
+ }
3405
+ const { FFFSearchProvider } = await import("./fffSearchProvider-2YCNKOYD.js");
3406
+ this.fffSearchWorkspaceRoot = workspaceRoot;
3407
+ this.fffSearchProviderPromise = FFFSearchProvider.create(workspaceRoot);
3408
+ return this.fffSearchProviderPromise;
3409
+ }
3410
+ scheduleFFFSearchProviderCleanup() {
3411
+ if (!this.fffSearchProviderPromise) {
3412
+ return;
3413
+ }
3414
+ if (this.fffSearchIdleTimer) {
3415
+ clearTimeout(this.fffSearchIdleTimer);
3416
+ }
3417
+ this.fffSearchIdleTimer = setTimeout(() => {
3418
+ const providerPromise = this.fffSearchProviderPromise;
3419
+ this.fffSearchProviderPromise = null;
3420
+ this.fffSearchWorkspaceRoot = null;
3421
+ this.fffSearchIdleTimer = null;
3422
+ providerPromise?.then((provider) => provider.destroy()).catch(() => {
3423
+ });
3424
+ }, _ActionExecutor.FFF_SEARCH_IDLE_TTL_MS);
3425
+ this.fffSearchIdleTimer.unref?.();
3426
+ }
2884
3427
  /**
2885
3428
  * Check permission hooks before prompting user.
2886
3429
  * Returns true if allowed, false if denied/blocked, undefined if should ask user.
@@ -2906,7 +3449,10 @@ var ActionExecutor = class _ActionExecutor {
2906
3449
  }
2907
3450
  }
2908
3451
  async execute(action, context) {
2909
- if (this.runtime.options.dryRun && action.type !== "search" && action.type !== "plan") {
3452
+ if (GOAL_TOOL_TYPES.has(action.type) && !isGoalFeatureEnabled(this.runtime.config)) {
3453
+ return GOAL_FEATURE_DISABLED_MESSAGE;
3454
+ }
3455
+ if (this.runtime.options.dryRun && !["fff_grep", "fff_find", "find", "search", "search_with_context", "semantic_search", "glob", "plan"].includes(action.type)) {
2910
3456
  return "Dry-run mode: skipped mutation";
2911
3457
  }
2912
3458
  switch (action.type) {
@@ -3101,11 +3647,15 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
3101
3647
  const receivedKeys = Object.keys(action).filter((k) => k !== "type").join(", ") || "none";
3102
3648
  throw new Error(`write_file requires a "path" argument. Received arguments: [${receivedKeys}]`);
3103
3649
  }
3650
+ if (action.contents === void 0 && action.content === void 0) {
3651
+ return 'Error: write_file requires "contents" argument.';
3652
+ }
3104
3653
  const filePath = this.resolveWorkspacePath(action.path);
3105
3654
  const fs7 = await import("fs-extra");
3106
3655
  const exists = this.files.root && await fs7.pathExists(filePath);
3107
3656
  const oldContent = exists ? await this.files.readFile(action.path) : "";
3108
3657
  const newContent = this.pickText(action.contents, action.content) ?? "";
3658
+ let resultOutput = null;
3109
3659
  if (!exists) {
3110
3660
  const permContext = {
3111
3661
  tool: "write_file",
@@ -3151,16 +3701,18 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
3151
3701
  }
3152
3702
  }
3153
3703
  }
3704
+ resultOutput = this.formatDiffPreview("", newContent, action.path);
3154
3705
  } else if (oldContent === newContent) {
3155
3706
  return `No changes needed for ${action.path} (content identical)`;
3156
3707
  } else {
3157
3708
  console.log(chalk2.cyan(`
3158
3709
  \u{1F4DD} ${action.path}:`));
3159
3710
  this.showDiff(oldContent, newContent, action.path);
3711
+ resultOutput = this.formatDiffPreview(oldContent, newContent, action.path);
3160
3712
  }
3161
3713
  await this.files.writeFile(action.path, newContent);
3162
- this.onFileModified?.(action.path);
3163
- return exists ? `Updated ${action.path}` : `Created ${action.path}`;
3714
+ this.onFileModified?.(action.path, exists ? "modify" : "create");
3715
+ return resultOutput ?? (exists ? `Updated ${action.path}` : `Created ${action.path}`);
3164
3716
  }
3165
3717
  case "append_file": {
3166
3718
  if (!action.path) {
@@ -3173,17 +3725,17 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
3173
3725
  \u{1F4DD} ${action.path}:`));
3174
3726
  this.showDiff(oldContent, newContent, action.path);
3175
3727
  await this.files.appendFile(action.path, addition);
3176
- this.onFileModified?.(action.path);
3177
- return `Appended to ${action.path}`;
3728
+ this.onFileModified?.(action.path, "modify");
3729
+ return this.formatDiffPreview(oldContent, newContent, action.path);
3178
3730
  }
3179
3731
  case "apply_patch": {
3180
3732
  if (!action.path) {
3181
- throw new Error('apply_patch requires a "path" argument.');
3733
+ return 'Error: apply_patch requires a "path" argument.';
3182
3734
  }
3183
3735
  const oldContent = await this.files.readFile(action.path).catch(() => "");
3184
3736
  const patch = this.pickText(action.patch, action.diff);
3185
3737
  if (!patch) {
3186
- throw new Error("apply_patch requires patch or diff content.");
3738
+ return 'Error: apply_patch requires a "patch" argument.';
3187
3739
  }
3188
3740
  console.log(chalk2.cyan(`
3189
3741
  \u{1F527} ${action.path}:`));
@@ -3191,58 +3743,148 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
3191
3743
  await this.files.applyPatch(action.path, patch);
3192
3744
  const newContent = await this.files.readFile(action.path);
3193
3745
  this.showDiff(oldContent, newContent, action.path);
3194
- this.onFileModified?.(action.path);
3195
- return `Patched ${action.path}`;
3746
+ this.onFileModified?.(action.path, "modify");
3747
+ return this.formatDiffPreview(oldContent, newContent, action.path);
3748
+ }
3749
+ case "notebook_edit": {
3750
+ if (!action.path) {
3751
+ throw new Error('notebook_edit requires a "path" argument.');
3752
+ }
3753
+ const current = await this.files.readFile(action.path);
3754
+ const { updated, summary } = applyNotebookEdit(current, action);
3755
+ await this.files.writeFile(action.path, updated);
3756
+ this.onFileModified?.(action.path, "modify");
3757
+ return summary;
3196
3758
  }
3197
3759
  case "tools_registry": {
3198
3760
  const tools = await this.toolsRegistry.listTools(this.getRegisteredTools());
3199
3761
  return JSON.stringify(tools, null, 2);
3200
3762
  }
3201
- case "search": {
3202
- const cacheKey = `search:${action.query}:${action.path || ""}`;
3203
- if (this.searchCache.has(cacheKey)) {
3204
- return `[Cached] ${this.searchCache.get(cacheKey)}`;
3205
- }
3206
- const hits = this.files.search(action.query, action.path);
3207
- this.recordExploration("search", action.query);
3208
- const result = hits.slice(0, 10).map((hit) => `${hit.file}:${hit.line}: ${hit.text}`).join("\n");
3209
- this.searchCache.set(cacheKey, result);
3210
- return result;
3763
+ case "get_goal": {
3764
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3765
+ return JSON.stringify(await manager.getSnapshot(), null, 2);
3211
3766
  }
3212
- case "search_with_context": {
3213
- const cacheKey = `search_ctx:${action.query}:${action.path || ""}:${action.limit || ""}:${action.context || ""}`;
3214
- if (this.searchCache.has(cacheKey)) {
3215
- return `[Cached] ${this.searchCache.get(cacheKey)}`;
3216
- }
3217
- this.recordExploration("search", action.query);
3218
- const result = this.files.searchWithContext(action.query, {
3219
- limit: action.limit,
3220
- context: action.context,
3221
- relativePath: action.path
3767
+ case "list_goal_templates": {
3768
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3769
+ return JSON.stringify(await manager.listTemplates(), null, 2);
3770
+ }
3771
+ case "create_goal": {
3772
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3773
+ const created = await manager.createGoal({
3774
+ objective: action.objective,
3775
+ tokenBudget: action.token_budget,
3776
+ timeBudgetSeconds: action.time_budget_seconds,
3777
+ minTokensBeforeWrapUp: action.min_tokens_before_wrap_up,
3778
+ minTimeSecondsBeforeWrapUp: action.min_time_seconds_before_wrap_up
3222
3779
  });
3223
- this.searchCache.set(cacheKey, result);
3224
- return result;
3780
+ return formatGoalToolResult(created);
3225
3781
  }
3226
- case "semantic_search": {
3227
- const cacheKey = `semantic:${action.query}:${action.path || ""}:${action.limit || ""}:${action.window || ""}`;
3228
- if (this.searchCache.has(cacheKey)) {
3229
- return `[Cached] ${this.searchCache.get(cacheKey)}`;
3782
+ case "create_goal_from_template": {
3783
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3784
+ const resolution = await import("./templates-UGVZV3KJ.js").then((mod) => mod.resolveGoalTemplateByName(
3785
+ this.runtime.workspaceRoot,
3786
+ action.template,
3787
+ action.flags ?? {},
3788
+ action.args ?? ""
3789
+ ));
3790
+ if (!resolution.ok) {
3791
+ return `Error: ${"notTemplate" in resolution ? `Unknown goal template '${action.template}'.` : resolution.error}`;
3230
3792
  }
3231
- const results = this.files.semanticSearch(action.query, {
3232
- limit: action.limit,
3233
- window: action.window,
3234
- relativePath: action.path
3793
+ const created = await manager.createGoal({
3794
+ objective: resolution.template.objective,
3795
+ tokenBudget: action.token_budget,
3796
+ timeBudgetSeconds: action.time_budget_seconds,
3797
+ minTokensBeforeWrapUp: action.min_tokens_before_wrap_up,
3798
+ minTimeSecondsBeforeWrapUp: action.min_time_seconds_before_wrap_up
3799
+ }, { replace: true });
3800
+ return formatGoalToolResult(created);
3801
+ }
3802
+ case "update_goal": {
3803
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3804
+ const updated = await manager.updateGoal({
3805
+ objective: action.objective,
3806
+ status: parseGoalStatus(action.status),
3807
+ tokenBudget: action.token_budget,
3808
+ timeBudgetSeconds: action.time_budget_seconds,
3809
+ minTokensBeforeWrapUp: action.min_tokens_before_wrap_up,
3810
+ minTimeSecondsBeforeWrapUp: action.min_time_seconds_before_wrap_up
3235
3811
  });
3236
- if (!results.length) {
3237
- this.searchCache.set(cacheKey, "No matches found.");
3238
- return "No matches found.";
3812
+ return formatGoalToolResult(updated);
3813
+ }
3814
+ case "clear_goal": {
3815
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3816
+ return formatGoalToolResult(await manager.clearGoal());
3817
+ }
3818
+ case "enqueue_goal": {
3819
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3820
+ return formatGoalToolResult(await manager.enqueueGoal({
3821
+ objective: action.objective,
3822
+ source: "tool",
3823
+ tokenBudget: action.token_budget,
3824
+ timeBudgetSeconds: action.time_budget_seconds,
3825
+ minTokensBeforeWrapUp: action.min_tokens_before_wrap_up,
3826
+ minTimeSecondsBeforeWrapUp: action.min_time_seconds_before_wrap_up
3827
+ }));
3828
+ }
3829
+ case "list_goal_queue": {
3830
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3831
+ const snapshot = await manager.getSnapshot();
3832
+ return JSON.stringify({ goal: snapshot.goal, queue: snapshot.queue }, null, 2);
3833
+ }
3834
+ case "start_queued_goal": {
3835
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3836
+ return formatGoalToolResult(await manager.startQueuedGoal());
3837
+ }
3838
+ case "dequeue_goal": {
3839
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3840
+ return formatGoalToolResult(await manager.dequeueGoal({
3841
+ rationale: action.rationale,
3842
+ authority: action.authority
3843
+ }));
3844
+ }
3845
+ case "remove_queued_goal": {
3846
+ const manager = new GoalManager(this.runtime.workspaceRoot);
3847
+ const queueId = action.queueId ?? action.queue_id;
3848
+ if (!queueId) return "Error: remove_queued_goal requires queueId.";
3849
+ return formatGoalToolResult(await manager.removeQueuedGoal(queueId));
3850
+ }
3851
+ case "tool_search": {
3852
+ const query = action.query?.trim();
3853
+ if (!query) {
3854
+ throw new Error('tool_search requires a non-empty "query" argument.');
3239
3855
  }
3240
- const result = results.map((hit) => `${chalk2.cyan(hit.file)}
3241
- ${hit.snippet}`).join("\n\n");
3242
- this.searchCache.set(cacheKey, result);
3243
- return result;
3856
+ const limit = Math.max(1, action.limit ?? 10);
3857
+ const tools = await this.toolsRegistry.listTools(this.getRegisteredTools());
3858
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
3859
+ const scored = tools.map((tool) => {
3860
+ const haystack = `${tool.name} ${tool.description}`.toLowerCase();
3861
+ let score = 0;
3862
+ for (const term of terms) {
3863
+ if (tool.name.toLowerCase() === term) {
3864
+ score += 10;
3865
+ } else if (tool.name.toLowerCase().includes(term)) {
3866
+ score += 6;
3867
+ }
3868
+ if (haystack.includes(term)) {
3869
+ score += 2;
3870
+ }
3871
+ }
3872
+ return { tool, score };
3873
+ }).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name)).slice(0, limit).map((entry) => entry.tool);
3874
+ return JSON.stringify(scored, null, 2);
3244
3875
  }
3876
+ case "find":
3877
+ return this.executeFind(action);
3878
+ case "glob":
3879
+ return this.executeGlob(action);
3880
+ case "fff_grep":
3881
+ return this.executeFFFGrep(action);
3882
+ case "fff_find":
3883
+ return this.executeFFFFind(action);
3245
3884
  case "create_directory": {
3885
+ if (!action.path) {
3886
+ return 'Error: create_directory requires a "path" argument.';
3887
+ }
3246
3888
  await this.files.createDirectory(action.path);
3247
3889
  return `Created directory ${action.path}`;
3248
3890
  }
@@ -3257,14 +3899,24 @@ ${hit.snippet}`).join("\n\n");
3257
3899
  if (!confirmed) {
3258
3900
  return `Skipped deleting ${action.path}`;
3259
3901
  }
3902
+ const oldDeleteContent = await this.files.readFile(action.path).catch(() => null);
3260
3903
  await this.files.deletePath(action.path);
3261
- return `Deleted ${action.path}`;
3904
+ if (oldDeleteContent !== null) {
3905
+ console.log(chalk2.cyan(`
3906
+ \u{1F5D1}\uFE0F ${action.path}:`));
3907
+ this.showDiff(oldDeleteContent, "", action.path);
3908
+ this.onFileModified?.(action.path, "delete");
3909
+ return this.formatDiffPreview(oldDeleteContent, "", action.path);
3910
+ }
3911
+ this.onFileModified?.(action.path, "delete");
3912
+ return `Deleted directory ${action.path}`;
3262
3913
  }
3263
3914
  case "rename_path": {
3264
3915
  if (!action.from || !action.to) {
3265
3916
  throw new Error('rename_path requires "from" and "to" arguments.');
3266
3917
  }
3267
3918
  await this.files.renamePath(action.from, action.to);
3919
+ this.onFileModified?.(action.to, "create");
3268
3920
  return `Renamed ${action.from} -> ${action.to}`;
3269
3921
  }
3270
3922
  case "copy_path": {
@@ -3272,9 +3924,16 @@ ${hit.snippet}`).join("\n\n");
3272
3924
  throw new Error('copy_path requires "from" and "to" arguments.');
3273
3925
  }
3274
3926
  await this.files.copyPath(action.from, action.to);
3927
+ this.onFileModified?.(action.to, "create");
3275
3928
  return `Copied ${action.from} -> ${action.to}`;
3276
3929
  }
3277
3930
  case "search_replace": {
3931
+ if (!action.path) {
3932
+ return 'Error: search_replace requires a "path" argument.';
3933
+ }
3934
+ if (!action.blocks) {
3935
+ return 'Error: search_replace requires a "blocks" argument.';
3936
+ }
3278
3937
  const content = await this.files.readFile(action.path);
3279
3938
  const result = this.applySearchReplaceBlocks(content, action.blocks);
3280
3939
  if (content !== result) {
@@ -3282,16 +3941,26 @@ ${hit.snippet}`).join("\n\n");
3282
3941
  \u{1F504} ${action.path}:`));
3283
3942
  this.showDiff(content, result, action.path);
3284
3943
  await this.files.writeFile(action.path, result);
3285
- this.onFileModified?.(action.path);
3944
+ this.onFileModified?.(action.path, "modify");
3945
+ return this.formatDiffPreview(content, result, action.path);
3286
3946
  }
3287
- return `Updated ${action.path}`;
3947
+ return `No changes needed for ${action.path} (content identical)`;
3288
3948
  }
3289
3949
  case "format_file": {
3290
3950
  if (!action.path) {
3291
3951
  throw new Error('format_file requires a "path" argument.');
3292
3952
  }
3953
+ const oldFormatContent = await this.files.readFile(action.path).catch(() => "");
3293
3954
  await this.files.formatFile(action.path, (contents, file) => applyFormatter(action.formatter, contents, file));
3294
- return `Formatted ${action.path} (${action.formatter})`;
3955
+ const newFormatContent = await this.files.readFile(action.path).catch(() => "");
3956
+ if (oldFormatContent !== newFormatContent) {
3957
+ console.log(chalk2.cyan(`
3958
+ \u{1F3A8} ${action.path}:`));
3959
+ this.showDiff(oldFormatContent, newFormatContent, action.path);
3960
+ this.onFileModified?.(action.path, "modify");
3961
+ return this.formatDiffPreview(oldFormatContent, newFormatContent, action.path);
3962
+ }
3963
+ return `No changes needed (already formatted): ${action.path}`;
3295
3964
  }
3296
3965
  case "run_command": {
3297
3966
  if (!action.command || typeof action.command !== "string") {
@@ -3312,24 +3981,76 @@ ${hit.snippet}`).join("\n\n");
3312
3981
  });
3313
3982
  };
3314
3983
  const cmdStr = `${action.command} ${(action.args ?? []).join(" ")}`.trim();
3984
+ if (action.interactive) {
3985
+ const onModalPause = this.onModalPause;
3986
+ if (onModalPause) {
3987
+ return await onModalPause(async () => {
3988
+ let result2;
3989
+ try {
3990
+ result2 = await runCommand(
3991
+ cmdStr,
3992
+ [],
3993
+ this.runtime.workspaceRoot,
3994
+ {
3995
+ directory: action.directory,
3996
+ shell: true,
3997
+ interactive: true
3998
+ }
3999
+ );
4000
+ } catch (err) {
4001
+ const error = err;
4002
+ if (error.code === "ENOENT" || error.message.includes("Command not found")) {
4003
+ return `Error: Command not found: "${action.command}". Make sure it is installed and available on your PATH.`;
4004
+ }
4005
+ return `Error running "${cmdStr}": ${error.message}`;
4006
+ }
4007
+ const header2 = action.description ? `$ ${action.description}
4008
+ > ${cmdStr}` : `$ ${cmdStr}`;
4009
+ const dirInfo2 = action.directory ? `[dir: ${action.directory}]` : "";
4010
+ const parts2 = [dirInfo2 ? `${header2} ${dirInfo2}` : header2];
4011
+ if (result2.code !== 0) {
4012
+ parts2.push(`(exit code: ${result2.code})`);
4013
+ }
4014
+ return parts2.join("\n");
4015
+ });
4016
+ }
4017
+ }
3315
4018
  let result;
3316
- const useShell = needsShell(action.command);
3317
- const shellCmd = useShell ? `${action.command} ${(action.args ?? []).join(" ")}`.trim() : action.command;
3318
- const shellArgs = useShell ? [] : action.args ?? [];
4019
+ const shellCmd = cmdStr;
4020
+ const liveCommandId = !action.background && this.shouldDisplayToolOutput() ? this.onLiveCommandStart?.(cmdStr) : void 0;
4021
+ const hasLiveDisplay = Boolean(liveCommandId);
4022
+ const emitLiveOutput = (stream, data) => {
4023
+ if (!hasLiveDisplay || !liveCommandId) {
4024
+ return;
4025
+ }
4026
+ this.onLiveCommandOutput?.(liveCommandId, stream, data);
4027
+ };
3319
4028
  try {
3320
4029
  result = await runCommand(
3321
4030
  shellCmd,
3322
- shellArgs,
4031
+ [],
3323
4032
  this.runtime.workspaceRoot,
3324
4033
  {
3325
4034
  directory: action.directory,
3326
4035
  background: action.background,
3327
- shell: useShell,
3328
- onStdout: (chunk) => emitOutput("stdout", chunk),
3329
- onStderr: (chunk) => emitOutput("stderr", chunk)
4036
+ shell: true,
4037
+ onStdout: (chunk) => {
4038
+ emitOutput("stdout", chunk);
4039
+ emitLiveOutput("stdout", chunk);
4040
+ },
4041
+ onStderr: (chunk) => {
4042
+ emitOutput("stderr", chunk);
4043
+ emitLiveOutput("stderr", chunk);
4044
+ }
3330
4045
  }
3331
4046
  );
4047
+ if (liveCommandId) {
4048
+ this.onLiveCommandRemove?.(liveCommandId);
4049
+ }
3332
4050
  } catch (err) {
4051
+ if (liveCommandId) {
4052
+ this.onLiveCommandRemove?.(liveCommandId);
4053
+ }
3333
4054
  const error = err;
3334
4055
  if (error.code === "ENOENT" || error.message.includes("Command not found")) {
3335
4056
  return `Error: Command not found: "${action.command}". Make sure it is installed and available on your PATH.`;
@@ -3349,12 +4070,100 @@ ${hit.snippet}`).join("\n\n");
3349
4070
  }
3350
4071
  return parts.join("\n");
3351
4072
  }
4073
+ case "shell": {
4074
+ if (!action.command || typeof action.command !== "string") {
4075
+ return 'Error: shell requires a "command" argument (string)';
4076
+ }
4077
+ const cmdStr = `${action.command} ${(action.args ?? []).join(" ")}`.trim();
4078
+ const commandId = this.shouldDisplayToolOutput() ? this.onLiveCommandStart?.(cmdStr) : void 0;
4079
+ const hasLiveDisplay = Boolean(commandId);
4080
+ if (hasLiveDisplay) {
4081
+ const liveId = commandId;
4082
+ try {
4083
+ const result2 = await executeStreamingShellCommand(
4084
+ cmdStr,
4085
+ this.runtime.workspaceRoot,
4086
+ {
4087
+ onStdout: (chunk) => this.onLiveCommandOutput(liveId, "stdout", chunk),
4088
+ onStderr: (chunk) => this.onLiveCommandOutput(liveId, "stderr", chunk),
4089
+ preferPty: process.stdin.isTTY && process.stdout.isTTY,
4090
+ columns: process.stdout.columns,
4091
+ rows: process.stdout.rows,
4092
+ background: action.background
4093
+ }
4094
+ );
4095
+ this.onLiveCommandRemove(liveId);
4096
+ const header2 = action.description ? `$ ${action.description}
4097
+ > ${cmdStr}` : `$ ${cmdStr}`;
4098
+ const dirInfo2 = action.directory ? `[dir: ${action.directory}]` : "";
4099
+ const parts2 = [dirInfo2 ? `${header2} ${dirInfo2}` : header2];
4100
+ if (result2.output) parts2.push(result2.output);
4101
+ if (result2.error) parts2.push(result2.error);
4102
+ if (result2.backgroundPid) parts2.push(`[Background PID: ${result2.backgroundPid}]`);
4103
+ return parts2.join("\n");
4104
+ } catch (err) {
4105
+ this.onLiveCommandRemove(liveId);
4106
+ const error = err;
4107
+ return `Error running "${cmdStr}": ${error.message}`;
4108
+ }
4109
+ }
4110
+ let result;
4111
+ try {
4112
+ result = await runCommand(
4113
+ cmdStr,
4114
+ [],
4115
+ this.runtime.workspaceRoot,
4116
+ {
4117
+ directory: action.directory,
4118
+ shell: true,
4119
+ background: action.background
4120
+ }
4121
+ );
4122
+ } catch (err) {
4123
+ const error = err;
4124
+ if (error.code === "ENOENT" || error.message.includes("Command not found")) {
4125
+ return `Error: Command not found: "${action.command}". Make sure it is installed and available on your PATH.`;
4126
+ }
4127
+ return `Error running "${cmdStr}": ${error.message}`;
4128
+ }
4129
+ const header = action.description ? `$ ${action.description}
4130
+ > ${cmdStr}` : `$ ${cmdStr}`;
4131
+ const dirInfo = action.directory ? `[dir: ${action.directory}]` : "";
4132
+ const parts = [
4133
+ dirInfo ? `${header} ${dirInfo}` : header,
4134
+ result.stdout,
4135
+ result.stderr
4136
+ ].filter(Boolean);
4137
+ return parts.join("\n");
4138
+ }
3352
4139
  case "add_dependency": {
4140
+ const fseAdd = (await import("fs-extra")).default;
4141
+ const pkgPathAdd = `${this.runtime.workspaceRoot}/package.json`;
4142
+ const oldPkgAdd = await fseAdd.readFile(pkgPathAdd, "utf-8").catch(() => "");
3353
4143
  await addDependency(this.runtime.workspaceRoot, action.name, action.version, { dev: action.dev });
4144
+ const newPkgAdd = await fseAdd.readFile(pkgPathAdd, "utf-8").catch(() => "");
4145
+ if (oldPkgAdd !== newPkgAdd) {
4146
+ console.log(chalk2.cyan(`
4147
+ \u{1F4E6} package.json:`));
4148
+ this.showDiff(oldPkgAdd, newPkgAdd, "package.json");
4149
+ this.onFileModified?.("package.json", "modify");
4150
+ return this.formatDiffPreview(oldPkgAdd, newPkgAdd, "package.json");
4151
+ }
3354
4152
  return `Added dependency ${action.name}@${action.version}${action.dev ? " (dev)" : ""}`;
3355
4153
  }
3356
4154
  case "remove_dependency": {
4155
+ const fseRm = (await import("fs-extra")).default;
4156
+ const pkgPathRm = `${this.runtime.workspaceRoot}/package.json`;
4157
+ const oldPkgRm = await fseRm.readFile(pkgPathRm, "utf-8").catch(() => "");
3357
4158
  await removeDependency(this.runtime.workspaceRoot, action.name, { dev: action.dev });
4159
+ const newPkgRm = await fseRm.readFile(pkgPathRm, "utf-8").catch(() => "");
4160
+ if (oldPkgRm !== newPkgRm) {
4161
+ console.log(chalk2.cyan(`
4162
+ \u{1F4E6} package.json:`));
4163
+ this.showDiff(oldPkgRm, newPkgRm, "package.json");
4164
+ this.onFileModified?.("package.json", "modify");
4165
+ return this.formatDiffPreview(oldPkgRm, newPkgRm, "package.json");
4166
+ }
3358
4167
  return `Removed dependency ${action.name}${action.dev ? " (dev)" : ""}`;
3359
4168
  }
3360
4169
  case "list_tree": {
@@ -3383,11 +4192,7 @@ ${hit.snippet}`).join("\n\n");
3383
4192
  return `${action.algorithm ?? "sha256"} ${action.path}: ${sum}`;
3384
4193
  }
3385
4194
  case "git_diff": {
3386
- if (!action.path) {
3387
- throw new Error('git_diff requires a "path" argument.');
3388
- }
3389
- this.resolveWorkspacePath(action.path);
3390
- const rawDiff = diffFile(this.runtime.workspaceRoot, action.path);
4195
+ const rawDiff = action.path ? (this.resolveWorkspacePath(action.path), diffFile(this.runtime.workspaceRoot, action.path)) : diffWorkspace(this.runtime.workspaceRoot);
3391
4196
  return this.colorizeGitDiff(rawDiff);
3392
4197
  }
3393
4198
  case "git_checkout": {
@@ -3395,8 +4200,17 @@ ${hit.snippet}`).join("\n\n");
3395
4200
  throw new Error('git_checkout requires a "path" argument.');
3396
4201
  }
3397
4202
  this.resolveWorkspacePath(action.path);
4203
+ const oldCheckoutContent = await this.files.readFile(action.path).catch(() => "");
3398
4204
  checkoutFile(this.runtime.workspaceRoot, action.path);
3399
- return `Restored ${action.path} from git.`;
4205
+ const newCheckoutContent = await this.files.readFile(action.path).catch(() => "");
4206
+ if (oldCheckoutContent !== newCheckoutContent) {
4207
+ console.log(chalk2.cyan(`
4208
+ \u21A9\uFE0F ${action.path}:`));
4209
+ this.showDiff(oldCheckoutContent, newCheckoutContent, action.path);
4210
+ this.onFileModified?.(action.path, "modify");
4211
+ return this.formatDiffPreview(oldCheckoutContent, newCheckoutContent, action.path);
4212
+ }
4213
+ return `Restored ${action.path} from git (no changes).`;
3400
4214
  }
3401
4215
  case "git_status":
3402
4216
  return gitStatus(this.runtime.workspaceRoot);
@@ -3666,8 +4480,10 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3666
4480
  console.log(chalk2.cyan("Suggested commit message:"));
3667
4481
  console.log(chalk2.white(` ${commitMessage}`));
3668
4482
  console.log();
4483
+ const normalizedYolo = normalizeYoloInput(this.runtime.options.yolo);
4484
+ const yoloAllowsCommit = normalizedYolo && isToolAllowedByYolo("auto_commit", parseYoloPattern(normalizedYolo));
3669
4485
  const autoApproveCommit = Boolean(
3670
- this.runtime.options.yes || process.env.CI === "1" || process.env.AUTOHAND_NON_INTERACTIVE === "1"
4486
+ this.runtime.options.unrestricted || this.runtime.options.yes || yoloAllowsCommit || process.env.CI === "1" || process.env.AUTOHAND_NON_INTERACTIVE === "1"
3671
4487
  );
3672
4488
  if (autoApproveCommit) {
3673
4489
  console.log(chalk2.gray("Auto-commit approval enabled; committing without prompt."));
@@ -3682,26 +4498,36 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3682
4498
  return result2.message;
3683
4499
  }
3684
4500
  const options = [
3685
- { label: "Yes - commit with this message", value: "y" },
4501
+ { label: `Yes - commit with this message`, value: "y" },
3686
4502
  { label: "Edit - modify the message", value: "e" },
3687
4503
  { label: "No - cancel commit", value: "n" }
3688
4504
  ];
3689
- const modalResult = await showModal({
3690
- title: "Commit with this message?",
3691
- options
3692
- });
3693
- if (!modalResult || modalResult.value === "n") {
4505
+ const runModal = async () => {
4506
+ const modalResult = await showModal({
4507
+ title: `Commit with this message?
4508
+
4509
+ "${commitMessage}"`,
4510
+ options
4511
+ });
4512
+ if (!modalResult || modalResult.value === "n") {
4513
+ return { cancelled: true, editedMessage: null };
4514
+ }
4515
+ if (modalResult.value === "e") {
4516
+ const editedMessage = await showInput({
4517
+ title: "Enter commit message:",
4518
+ defaultValue: commitMessage
4519
+ });
4520
+ return { cancelled: false, editedMessage };
4521
+ }
4522
+ return { cancelled: false, editedMessage: null };
4523
+ };
4524
+ const modalOutcome = this.onModalPause ? await this.onModalPause(runModal) : await runModal();
4525
+ if (modalOutcome.cancelled) {
3694
4526
  console.log(chalk2.yellow("Commit cancelled."));
3695
4527
  return "Commit cancelled by user";
3696
4528
  }
3697
- if (modalResult.value === "e") {
3698
- const editedMessage = await showInput({
3699
- title: "Enter commit message:",
3700
- defaultValue: commitMessage
3701
- });
3702
- if (editedMessage) {
3703
- commitMessage = editedMessage;
3704
- }
4529
+ if (modalOutcome.editedMessage) {
4530
+ commitMessage = modalOutcome.editedMessage;
3705
4531
  }
3706
4532
  const result = executeAutoCommit(this.runtime.workspaceRoot, commitMessage, action.stage_all !== false);
3707
4533
  if (result.success) {
@@ -3735,6 +4561,12 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3735
4561
  case "custom_command":
3736
4562
  return this.executeCustomCommand(action);
3737
4563
  case "multi_file_edit": {
4564
+ if (!action.file_path) {
4565
+ return 'Error: multi_file_edit requires a "file_path" argument.';
4566
+ }
4567
+ if (!action.edits || !Array.isArray(action.edits)) {
4568
+ return 'Error: multi_file_edit requires an "edits" argument (array).';
4569
+ }
3738
4570
  const oldContent = await this.files.readFile(action.file_path);
3739
4571
  let newContent = oldContent;
3740
4572
  console.log(chalk2.cyan(`
@@ -3775,14 +4607,20 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3775
4607
  }
3776
4608
  }
3777
4609
  if (firstIndex === -1) {
3778
- console.log(chalk2.red(` \u2717 Edit ${i + 1}: Could not find text to replace`));
3779
- console.log(chalk2.gray(` Looking for (${edit.old_string.length} chars):`));
3780
- console.log(chalk2.gray(` "${edit.old_string.substring(0, 80)}${edit.old_string.length > 80 ? "..." : ""}"`));
3781
4610
  const similar = this.findSimilarText(newContent, edit.old_string);
3782
4611
  if (similar) {
3783
- console.log(chalk2.yellow(` Did you mean:`));
3784
- console.log(chalk2.yellow(` "${similar.substring(0, 80)}${similar.length > 80 ? "..." : ""}"`));
4612
+ const similarIndex = newContent.indexOf(similar);
4613
+ if (similarIndex !== -1) {
4614
+ newContent = newContent.substring(0, similarIndex) + edit.new_string + newContent.substring(similarIndex + similar.length);
4615
+ console.log(chalk2.yellow(` \u26A0 Edit ${i + 1}: Applied with fuzzy match (whitespace/indentation differed)`));
4616
+ console.log(chalk2.gray(` Original search: "${edit.old_string.substring(0, 60)}${edit.old_string.length > 60 ? "..." : ""}"`));
4617
+ console.log(chalk2.gray(` Matched: "${similar.substring(0, 60)}${similar.length > 60 ? "..." : ""}"`));
4618
+ continue;
4619
+ }
3785
4620
  }
4621
+ console.log(chalk2.red(` \u2717 Edit ${i + 1}: Could not find text to replace`));
4622
+ console.log(chalk2.gray(` Looking for (${edit.old_string.length} chars):`));
4623
+ console.log(chalk2.gray(` "${edit.old_string.substring(0, 80)}${edit.old_string.length > 80 ? "..." : ""}"`));
3786
4624
  if (edit.old_string.length < 100) {
3787
4625
  const nonAscii = edit.old_string.match(/[^\x20-\x7E\n\r\t]/g);
3788
4626
  if (nonAscii && nonAscii.length > 0) {
@@ -3798,9 +4636,10 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3798
4636
  if (oldContent !== newContent) {
3799
4637
  this.showDiff(oldContent, newContent, action.file_path);
3800
4638
  await this.files.writeFile(action.file_path, newContent);
3801
- this.onFileModified?.(action.file_path);
4639
+ this.onFileModified?.(action.file_path, "modify");
4640
+ return this.formatDiffPreview(oldContent, newContent, action.file_path);
3802
4641
  }
3803
- return `Applied ${action.edits.length} edit(s) to ${action.file_path}`;
4642
+ return `No changes needed for ${action.file_path} (content identical)`;
3804
4643
  }
3805
4644
  case "todo_write": {
3806
4645
  const todoPath = ".autohand/agents/tasks/todos.json";
@@ -3810,17 +4649,17 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3810
4649
  }
3811
4650
  const validTasks = action.tasks.filter((task) => {
3812
4651
  if (!task) return false;
3813
- const hasId = !!task.id;
3814
4652
  const hasContent = !!(task.content || task.title);
3815
- return hasId && hasContent;
4653
+ return hasContent;
3816
4654
  });
3817
- const normalizedTasks = validTasks.map((task) => {
4655
+ const normalizedTasks = validTasks.map((task, index) => {
3818
4656
  const content = task.content || task.title || "";
3819
4657
  const title = content;
3820
4658
  return {
3821
4659
  ...task,
3822
4660
  // Preserve extra properties like priority, tags, etc.
3823
- id: task.id,
4661
+ id: task.id || `task-${Date.now()}-${index}`,
4662
+ // Auto-generate id if missing
3824
4663
  title,
3825
4664
  content,
3826
4665
  // Keep original content field
@@ -3831,24 +4670,51 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3831
4670
  });
3832
4671
  const allTodos = normalizedTasks;
3833
4672
  await this.files.writeFile(todoPath, JSON.stringify(allTodos, null, 2));
3834
- console.log(chalk2.cyan("\n\u{1F4CB} Task Progress:"));
4673
+ this.onFileModified?.(todoPath, "modify");
3835
4674
  const total = allTodos.length;
3836
- const completed = allTodos.filter((t) => t.status === "completed").length;
4675
+ if (total === 0) {
4676
+ console.log(chalk2.dim("\n\u{1F4CB} Task list cleared"));
4677
+ console.log();
4678
+ return "Task list cleared (0 tasks)";
4679
+ }
4680
+ const completedTasks = allTodos.filter((t) => t.status === "completed");
3837
4681
  const inProgress = allTodos.filter((t) => t.status === "in_progress");
3838
- const pending = allTodos.filter((t) => t.status === "pending").length;
3839
- const percent = total > 0 ? Math.round(completed / total * 100) : 0;
4682
+ const pendingTasks = allTodos.filter((t) => t.status === "pending");
4683
+ const completed = completedTasks.length;
4684
+ const pending = pendingTasks.length;
4685
+ const percent = Math.round(completed / total * 100);
3840
4686
  const barWidth = 20;
3841
4687
  const filled = Math.round(barWidth * percent / 100);
3842
4688
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
3843
- console.log(` ${chalk2.green(bar)} ${percent}%`);
3844
- console.log(chalk2.gray(` ${completed} done \xB7 ${inProgress.length} in progress \xB7 ${pending} pending`));
4689
+ const titleOf = (task) => {
4690
+ const title = task.title ?? task.content;
4691
+ return typeof title === "string" && title.trim().length > 0 ? title : "Untitled task";
4692
+ };
4693
+ const outputLines = [
4694
+ chalk2.cyan("\n\u{1F4CB} Task Progress:"),
4695
+ ` ${chalk2.green(bar)} ${percent}%`,
4696
+ chalk2.gray(` ${completed} done \xB7 ${inProgress.length} in progress \xB7 ${pending} pending`)
4697
+ ];
4698
+ if (completedTasks.length > 0) {
4699
+ outputLines.push("", chalk2.green(" \u2705 Completed Tasks:"));
4700
+ for (const task of completedTasks) {
4701
+ outputLines.push(chalk2.green(` \u2713 ${titleOf(task)}`));
4702
+ }
4703
+ }
3845
4704
  if (inProgress.length > 0) {
3846
- console.log(chalk2.yellow("\n \u{1F504} Active Tasks:"));
4705
+ outputLines.push("", chalk2.yellow(" \u{1F504} Active Tasks:"));
3847
4706
  for (const task of inProgress) {
3848
- console.log(` \u2022 ${task.title || task.content}`);
4707
+ outputLines.push(chalk2.yellow(` \u2022 ${titleOf(task)}`));
3849
4708
  }
3850
4709
  }
3851
- console.log();
4710
+ if (pendingTasks.length > 0) {
4711
+ outputLines.push("", chalk2.cyan(" \u23F3 Pending Tasks:"));
4712
+ for (const task of pendingTasks) {
4713
+ outputLines.push(chalk2.dim(` \u25CB ${titleOf(task)}`));
4714
+ }
4715
+ }
4716
+ console.log(`${outputLines.join("\n")}
4717
+ `);
3852
4718
  return `Updated task list: ${percent}% complete (${completed}/${total})`;
3853
4719
  }
3854
4720
  case "save_memory": {
@@ -3876,58 +4742,21 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3876
4742
  return formatted;
3877
4743
  }
3878
4744
  case "create_meta_tool": {
3879
- if (!action.name || !action.description || !action.handler) {
3880
- throw new Error("create_meta_tool requires name, description, and handler");
3881
- }
3882
- const builtInNames = this.getRegisteredTools().map((t) => t.name);
3883
- if (builtInNames.includes(action.name)) {
3884
- throw new Error(`Cannot create meta-tool "${action.name}": conflicts with built-in tool`);
3885
- }
3886
- const dangerousPatterns = [
3887
- // Destructive file operations
3888
- { pattern: /rm\s+(-[rf]+\s+)*\/(?!\w)/i, description: "rm with root path" },
3889
- { pattern: /rm\s+.*--no-preserve-root/i, description: "rm --no-preserve-root" },
3890
- { pattern: /dd\s+.*(?:of|if)=\/dev\/[sh]d/i, description: "dd to disk device" },
3891
- { pattern: /mkfs\./i, description: "filesystem format" },
3892
- { pattern: /wipefs/i, description: "disk wipe" },
3893
- // Privilege escalation
3894
- { pattern: /\bsudo\s/i, description: "sudo command" },
3895
- { pattern: /\bsu\s+-?\s*\w/i, description: "su command" },
3896
- { pattern: /chmod\s+[0-7]*7[0-7]*/i, description: "world-writable chmod" },
3897
- { pattern: /chown\s+root/i, description: "chown to root" },
3898
- // Remote code execution
3899
- { pattern: /curl\s+.*\|\s*(ba)?sh/i, description: "curl | bash" },
3900
- { pattern: /wget\s+.*\|\s*(ba)?sh/i, description: "wget | sh" },
3901
- { pattern: /\beval\s+[`$]/i, description: "eval with expansion" },
3902
- // Fork bomb and resource exhaustion
3903
- { pattern: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/i, description: "fork bomb" },
3904
- { pattern: /while\s+true.*do.*done/i, description: "infinite loop" },
3905
- // Reverse shell indicators
3906
- { pattern: /nc\s+.*-e\s*\/bin/i, description: "netcat reverse shell" },
3907
- { pattern: /ncat\s+.*-e\s*\/bin/i, description: "ncat reverse shell" },
3908
- { pattern: /bash\s+-i\s+>&?\s*\/dev\/tcp/i, description: "bash reverse shell" },
3909
- // Dangerous network operations
3910
- { pattern: /iptables\s+-F/i, description: "flush firewall rules" },
3911
- // Crypto operations that could lock out user
3912
- { pattern: /gpg\s+.*--encrypt.*-r\s+\S+\s+\//i, description: "gpg encrypt root" }
3913
- ];
3914
- for (const { pattern, description } of dangerousPatterns) {
3915
- if (pattern.test(action.handler)) {
3916
- throw new Error(`Handler contains dangerous pattern: ${description}`);
3917
- }
3918
- }
3919
- await this.toolsRegistry.saveMetaTool({
4745
+ const result = await this.metaToolService.createMetaTool({
3920
4746
  name: action.name,
3921
4747
  description: action.description,
3922
4748
  parameters: action.parameters ?? { type: "object", properties: {} },
3923
4749
  handler: action.handler,
3924
- source: "agent"
3925
- });
4750
+ source: "agent",
4751
+ scope: action.scope ?? "user"
4752
+ }, this.getRegisteredTools());
4753
+ const metaTool = result.definition;
4754
+ this.onMetaToolCreated?.(metaTool);
3926
4755
  console.log(chalk2.green(`
3927
- \u{1F527} Created meta-tool: ${action.name}`));
4756
+ \u{1F527} ${result.status === "created" ? "Created" : "Reused"} meta-tool: ${metaTool.name}`));
3928
4757
  console.log(chalk2.gray(` ${action.description}`));
3929
4758
  console.log(chalk2.gray(` Handler: ${action.handler}`));
3930
- return `Created meta-tool "${action.name}" - available in this and future sessions`;
4759
+ return result.message;
3931
4760
  }
3932
4761
  // Web Search Operations
3933
4762
  case "web_search": {
@@ -4023,7 +4852,7 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
4023
4852
  const query = action.query ?? "";
4024
4853
  console.log(chalk2.cyan(`
4025
4854
  Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...`));
4026
- const { searchCommunitySkills } = await import("./skills-ZZCIAS7C.js");
4855
+ const { searchCommunitySkills } = await import("./skills-OB6RDW7D.js");
4027
4856
  const result = await searchCommunitySkills(query, {
4028
4857
  category: action.category,
4029
4858
  limit: action.limit
@@ -4086,6 +4915,32 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4086
4915
  return `<answer>${finalAnswer}</answer>`;
4087
4916
  }
4088
4917
  }
4918
+ // Code review tool
4919
+ // Directory access tool
4920
+ case "request_directory_access": {
4921
+ return this.executeRequestDirectoryAccess(action);
4922
+ }
4923
+ case "code_review": {
4924
+ return this.executeCodeReview(action);
4925
+ }
4926
+ // Browser tools — forwarded to Chrome extension via RPC
4927
+ case "browser_screenshot":
4928
+ case "browser_click":
4929
+ case "browser_type":
4930
+ case "browser_navigate":
4931
+ case "browser_scroll":
4932
+ case "browser_find_element":
4933
+ case "browser_press_key":
4934
+ case "browser_get_page_context":
4935
+ case "browser_get_element":
4936
+ case "browser_wait_for_element":
4937
+ case "browser_read_network":
4938
+ case "browser_read_console":
4939
+ case "browser_get_tabs":
4940
+ case "browser_get_tab_groups":
4941
+ case "browser_execute_js": {
4942
+ return this.executeBrowserTool(action);
4943
+ }
4089
4944
  default: {
4090
4945
  const actionType = action.type;
4091
4946
  const metaTool = this.toolsRegistry.getMetaTool(actionType);
@@ -4096,6 +4951,145 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4096
4951
  }
4097
4952
  }
4098
4953
  }
4954
+ async executeBrowserTool(action) {
4955
+ const { type, ...params } = action;
4956
+ const toolName = type;
4957
+ const { invokeBrowserTool } = await import("./browserToolBridge-F5N66PE7.js");
4958
+ return invokeBrowserTool(toolName, params);
4959
+ }
4960
+ async executeRequestDirectoryAccess(action) {
4961
+ const path8 = await import("path");
4962
+ const fs7 = (await import("fs-extra")).default;
4963
+ const { checkWorkspaceSafety } = await import("./workspaceSafety-XOUMUBVB.js");
4964
+ const resolvedPath = path8.resolve(action.path);
4965
+ if (!await fs7.pathExists(resolvedPath)) {
4966
+ return `Error: Directory does not exist: ${resolvedPath}`;
4967
+ }
4968
+ const stats = await fs7.stat(resolvedPath);
4969
+ if (!stats.isDirectory()) {
4970
+ return `Error: Path is not a directory: ${resolvedPath}`;
4971
+ }
4972
+ const safetyResult = checkWorkspaceSafety(resolvedPath);
4973
+ if (!safetyResult.safe) {
4974
+ return `Error: Unsafe directory: ${resolvedPath}. ${safetyResult.reason}`;
4975
+ }
4976
+ const workspaceRoot = this.runtime.workspaceRoot;
4977
+ const additionalDirs = this.files.getAllowedDirectories();
4978
+ if (resolvedPath === workspaceRoot || additionalDirs.includes(resolvedPath)) {
4979
+ return `Directory is already accessible: ${resolvedPath}`;
4980
+ }
4981
+ const normalizedResolved = resolvedPath.endsWith(path8.sep) ? resolvedPath.slice(0, -1) : resolvedPath;
4982
+ const normalizedWorkspace = workspaceRoot.endsWith(path8.sep) ? workspaceRoot.slice(0, -1) : workspaceRoot;
4983
+ if (normalizedResolved.startsWith(normalizedWorkspace + path8.sep)) {
4984
+ return `Directory is already within workspace: ${resolvedPath}`;
4985
+ }
4986
+ for (const dir of additionalDirs) {
4987
+ const normalizedDir = dir.endsWith(path8.sep) ? dir.slice(0, -1) : dir;
4988
+ if (normalizedResolved.startsWith(normalizedDir + path8.sep) || normalizedResolved === normalizedDir) {
4989
+ return `Directory is already accessible: ${resolvedPath}`;
4990
+ }
4991
+ }
4992
+ if (this.onRequestDirectoryAccess) {
4993
+ const result = await this.onRequestDirectoryAccess(resolvedPath, action.reason);
4994
+ if (result) {
4995
+ this.files.addAdditionalDirectory(resolvedPath);
4996
+ return `Access granted to directory: ${resolvedPath}
4997
+
4998
+ You can now use file tools (read_file, write_file, glob, find, etc.) to work with files in this directory.`;
4999
+ } else {
5000
+ return `Access denied to directory: ${resolvedPath}`;
5001
+ }
5002
+ }
5003
+ const normalizedYolo = normalizeYoloInput(this.runtime.options.yolo);
5004
+ if (normalizedYolo) {
5005
+ this.files.addAdditionalDirectory(resolvedPath);
5006
+ return `Access auto-granted (yolo mode) to directory: ${resolvedPath}
5007
+
5008
+ You can now use file tools (read_file, write_file, glob, find, etc.) to work with files in this directory.`;
5009
+ }
5010
+ if (this.runtime.options.unrestricted || this.runtime.options.yes) {
5011
+ this.files.addAdditionalDirectory(resolvedPath);
5012
+ return `Access auto-granted to directory: ${resolvedPath}
5013
+
5014
+ You can now use file tools (read_file, write_file, glob, find, etc.) to work with files in this directory.`;
5015
+ }
5016
+ return `Directory access required: ${resolvedPath}
5017
+
5018
+ To grant access, use:
5019
+ /add-dir ${resolvedPath}
5020
+
5021
+ Or restart with:
5022
+ --add-dir ${resolvedPath}`;
5023
+ }
5024
+ async executeCodeReview(action) {
5025
+ const targetPath = action.path ? this.resolveWorkspacePath(action.path) : this.runtime.workspaceRoot;
5026
+ const scope = action.scope || "full";
5027
+ await this.onReviewHook?.("review:start", {
5028
+ reviewPath: targetPath,
5029
+ reviewScope: scope,
5030
+ reviewInstructions: action.instructions
5031
+ });
5032
+ try {
5033
+ let context = "";
5034
+ if (scope === "diff") {
5035
+ const { execFile: execFile2 } = await import("child_process");
5036
+ const { promisify } = await import("util");
5037
+ const execFileAsync = promisify(execFile2);
5038
+ const result2 = await execFileAsync("git", ["diff", "--stat"], {
5039
+ cwd: this.runtime.workspaceRoot,
5040
+ encoding: "utf8"
5041
+ }).catch(() => null);
5042
+ context = result2?.stdout || "No uncommitted changes found.";
5043
+ } else if (scope === "file" && action.path) {
5044
+ const fse = (await import("fs-extra")).default;
5045
+ context = await fse.readFile(targetPath, "utf-8").catch(() => `Could not read ${targetPath}`);
5046
+ } else {
5047
+ const { execFile: execFile2 } = await import("child_process");
5048
+ const { promisify } = await import("util");
5049
+ const execFileAsync = promisify(execFile2);
5050
+ const tree = await execFileAsync("find", [
5051
+ targetPath,
5052
+ "-maxdepth",
5053
+ "3",
5054
+ "-type",
5055
+ "f",
5056
+ "-not",
5057
+ "-path",
5058
+ "*/node_modules/*",
5059
+ "-not",
5060
+ "-path",
5061
+ "*/.git/*"
5062
+ ], {
5063
+ cwd: this.runtime.workspaceRoot,
5064
+ encoding: "utf8"
5065
+ }).catch(() => null);
5066
+ context = tree?.stdout || "";
5067
+ }
5068
+ const result = [
5069
+ `Code review initiated for: ${targetPath}`,
5070
+ `Scope: ${scope}`,
5071
+ action.instructions ? `Focus: ${action.instructions}` : "",
5072
+ "",
5073
+ "Project structure:",
5074
+ context.slice(0, 5e3)
5075
+ ].filter(Boolean).join("\n");
5076
+ await this.onReviewHook?.("review:completed", {
5077
+ reviewPath: targetPath,
5078
+ reviewScope: scope,
5079
+ reviewInstructions: action.instructions
5080
+ });
5081
+ return result;
5082
+ } catch (error) {
5083
+ const message = error instanceof Error ? error.message : String(error);
5084
+ await this.onReviewHook?.("review:failed", {
5085
+ reviewPath: targetPath,
5086
+ reviewScope: scope,
5087
+ reviewInstructions: action.instructions,
5088
+ reviewError: message
5089
+ });
5090
+ return `Review failed: ${message}`;
5091
+ }
5092
+ }
4099
5093
  pickText(...values) {
4100
5094
  for (const value of values) {
4101
5095
  if (typeof value === "string") {
@@ -4192,6 +5186,122 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4192
5186
  }
4193
5187
  return result.length > 0 ? result.join("\n") : "No structure detected";
4194
5188
  }
5189
+ executeFind(action) {
5190
+ console.warn(chalk2.yellow("[DEPRECATED] The `find` tool is deprecated. Use `fff_grep` instead. Will be removed in v0.9.0."));
5191
+ const mode = action.mode ?? (action.context && action.context > 0 ? "context" : "exact");
5192
+ const cacheKey = `find:${mode}:${action.query}:${action.path || ""}:${action.limit || ""}:${action.context || ""}:${action.window || ""}`;
5193
+ if (this.searchCache.has(cacheKey)) {
5194
+ return `[Cached] ${this.searchCache.get(cacheKey)}`;
5195
+ }
5196
+ this.recordExploration("search", action.query);
5197
+ if (mode === "semantic") {
5198
+ const results = this.files.semanticSearch(action.query, {
5199
+ limit: action.limit,
5200
+ window: action.window,
5201
+ relativePath: action.path
5202
+ });
5203
+ if (!results.length) {
5204
+ this.searchCache.set(cacheKey, "No matches found.");
5205
+ return "No matches found.";
5206
+ }
5207
+ const result2 = results.map((hit) => `${chalk2.cyan(hit.file)}
5208
+ ${hit.snippet}`).join("\n\n");
5209
+ this.searchCache.set(cacheKey, result2);
5210
+ return result2;
5211
+ }
5212
+ if (mode === "context") {
5213
+ const result2 = this.files.searchWithContext(action.query, {
5214
+ limit: action.limit,
5215
+ context: action.context,
5216
+ relativePath: action.path
5217
+ });
5218
+ this.searchCache.set(cacheKey, result2);
5219
+ return result2;
5220
+ }
5221
+ const hits = this.files.search(action.query, action.path);
5222
+ const result = hits.slice(0, action.limit ?? 10).map((hit) => `${hit.file}:${hit.line}: ${hit.text}`).join("\n");
5223
+ this.searchCache.set(cacheKey, result);
5224
+ return result;
5225
+ }
5226
+ async executeGlob(action) {
5227
+ console.warn(chalk2.yellow("[DEPRECATED] The `glob` tool is deprecated. Use `fff_find` instead. Will be removed in v0.9.0."));
5228
+ const { resolveRipgrepCommand } = await import("./ripgrep-VHJQQ55W.js");
5229
+ const rgPath = resolveRipgrepCommand();
5230
+ const searchPath = action.path ? this.resolveWorkspacePath(action.path) : this.runtime.workspaceRoot;
5231
+ const limit = action.limit ?? 100;
5232
+ const args = ["--files"];
5233
+ const patterns = action.patterns ?? (action.pattern ? [action.pattern] : ["**/*"]);
5234
+ for (const p of patterns) {
5235
+ args.push("--glob", p);
5236
+ }
5237
+ args.push(searchPath);
5238
+ const { execFile: execFile2 } = await import("child_process");
5239
+ const { promisify } = await import("util");
5240
+ const execFileAsync = promisify(execFile2);
5241
+ try {
5242
+ const result = await execFileAsync(rgPath, args, {
5243
+ cwd: this.runtime.workspaceRoot,
5244
+ encoding: "utf8",
5245
+ maxBuffer: 10 * 1024 * 1024
5246
+ });
5247
+ const files = result.stdout.trim().split("\n").filter(Boolean);
5248
+ if (files.length === 0) {
5249
+ return "No files found matching the pattern.";
5250
+ }
5251
+ const fse = (await import("fs-extra")).default;
5252
+ const withStats = await Promise.all(
5253
+ files.map(async (f) => {
5254
+ try {
5255
+ const stat = await fse.stat(f);
5256
+ return { file: f, mtime: stat.mtimeMs };
5257
+ } catch {
5258
+ return { file: f, mtime: 0 };
5259
+ }
5260
+ })
5261
+ );
5262
+ withStats.sort((a, b) => b.mtime - a.mtime);
5263
+ const sorted = withStats.map((s) => s.file);
5264
+ const limited = sorted.slice(0, limit);
5265
+ const header = `Found ${files.length} file${files.length === 1 ? "" : "s"}${files.length > limit ? ` (showing first ${limit})` : ""}`;
5266
+ this.recordExploration("list", action.pattern ?? action.patterns?.join(", ") ?? "*");
5267
+ return `${header}
5268
+ ${limited.join("\n")}`;
5269
+ } catch (error) {
5270
+ const exitCode = error?.code;
5271
+ if (exitCode === 1 || exitCode === "1") {
5272
+ return "No files found matching the pattern.";
5273
+ }
5274
+ throw error;
5275
+ }
5276
+ }
5277
+ async executeFFFGrep(action) {
5278
+ const provider = await this.getFFFSearchProvider();
5279
+ try {
5280
+ return await provider.grep({
5281
+ query: action.query,
5282
+ path: action.path,
5283
+ exclude: action.exclude,
5284
+ caseSensitive: action.caseSensitive,
5285
+ beforeContext: action.beforeContext,
5286
+ afterContext: action.afterContext,
5287
+ classifyDefinitions: action.classifyDefinitions,
5288
+ limit: action.limit
5289
+ });
5290
+ } finally {
5291
+ this.scheduleFFFSearchProviderCleanup();
5292
+ }
5293
+ }
5294
+ async executeFFFFind(action) {
5295
+ const provider = await this.getFFFSearchProvider();
5296
+ try {
5297
+ return await provider.fileSearch({
5298
+ query: action.query,
5299
+ limit: action.limit
5300
+ });
5301
+ } finally {
5302
+ this.scheduleFFFSearchProviderCleanup();
5303
+ }
5304
+ }
4195
5305
  recordExploration(kind, target) {
4196
5306
  if (!target) {
4197
5307
  return;
@@ -4233,12 +5343,6 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4233
5343
  const lowered = command.toLowerCase();
4234
5344
  return lowered.includes("rm ") || lowered.includes("sudo ") || lowered.includes("dd ");
4235
5345
  }
4236
- static {
4237
- /**
4238
- * Shell metacharacters that could enable command injection
4239
- */
4240
- this.SHELL_METACHARACTERS = /[|;&$`><(){}[\]!#*?~'"\\]/;
4241
- }
4242
5346
  /**
4243
5347
  * Safely escape a value for shell interpolation
4244
5348
  * Uses single quotes which prevent all shell expansion except for single quotes themselves
@@ -4259,21 +5363,62 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4259
5363
  if (value === void 0 || value === null) {
4260
5364
  throw new Error(`Missing required parameter "${paramName}" for meta-tool "${metaTool.name}"`);
4261
5365
  }
4262
- const stringValue = String(value);
4263
- let safeValue;
4264
- if (_ActionExecutor.SHELL_METACHARACTERS.test(stringValue)) {
4265
- safeValue = this.shellEscape(stringValue);
4266
- console.log(chalk2.yellow(` \u26A0 Parameter "${paramName}" contains shell metacharacters, escaped for safety`));
4267
- } else {
4268
- safeValue = stringValue;
4269
- }
5366
+ const safeValue = this.shellEscape(String(value));
5367
+ command = command.replace(new RegExp(`(["'])\\{\\{${paramName}\\}\\}\\1`, "g"), safeValue);
4270
5368
  command = command.replace(new RegExp(`\\{\\{${paramName}\\}\\}`, "g"), safeValue);
4271
5369
  }
4272
5370
  console.log(chalk2.cyan(`
4273
5371
  \u{1F527} Running meta-tool: ${metaTool.name}`));
4274
5372
  console.log(chalk2.gray(` $ ${command}`));
4275
- const result = await runCommand(command, [], this.runtime.workspaceRoot, { shell: true });
4276
- return [`$ ${command}`, result.stdout, result.stderr].filter(Boolean).join("\n");
5373
+ const permissionContext = {
5374
+ tool: "run_command",
5375
+ command,
5376
+ description: `Meta-tool ${metaTool.name}: ${metaTool.description}`
5377
+ };
5378
+ const decision = this.permissionManager.checkPermission(permissionContext);
5379
+ if (decision.reason === "blacklisted" || decision.reason === "mode_restricted" || decision.reason === "pattern_denied" || decision.reason === "not_in_available" || decision.reason === "excluded" || decision.reason === "deny_list" || decision.reason === "session_deny_list" || decision.reason === "project_deny_list" || decision.reason === "user_deny_list") {
5380
+ return `Blocked: Cannot run meta-tool ${metaTool.name} (${decision.reason})`;
5381
+ }
5382
+ if (!decision.allowed) {
5383
+ const hookResult = await this.checkPermissionHook({
5384
+ tool: "run_command",
5385
+ command,
5386
+ args
5387
+ });
5388
+ if (hookResult.blocked) {
5389
+ return `Blocked: ${hookResult.reason}`;
5390
+ }
5391
+ if (hookResult.allowed !== void 0) {
5392
+ await this.permissionManager.recordDecision(permissionContext, hookResult.allowed);
5393
+ if (!hookResult.allowed) {
5394
+ return `Denied: ${hookResult.reason ?? `meta-tool ${metaTool.name}`}`;
5395
+ }
5396
+ } else {
5397
+ const confirmed = await this.confirmDangerousAction(
5398
+ `Run meta-tool ${metaTool.name}?`,
5399
+ { tool: "run_command", command }
5400
+ );
5401
+ await this.permissionManager.recordDecision(permissionContext, confirmed);
5402
+ if (!confirmed) {
5403
+ return `Skipped running meta-tool ${metaTool.name}`;
5404
+ }
5405
+ }
5406
+ }
5407
+ const result = await runCommand(command, [], this.runtime.workspaceRoot, {
5408
+ shell: true,
5409
+ timeout: 12e4
5410
+ });
5411
+ const stdout = this.truncateMetaToolOutput(result.stdout);
5412
+ const stderr = this.truncateMetaToolOutput(result.stderr);
5413
+ return [`$ ${command}`, stdout, stderr].filter(Boolean).join("\n");
5414
+ }
5415
+ truncateMetaToolOutput(output) {
5416
+ const limit = 2e5;
5417
+ if (output.length <= limit) {
5418
+ return output;
5419
+ }
5420
+ return `${output.slice(0, limit)}
5421
+ [meta-tool output truncated at ${limit} characters]`;
4277
5422
  }
4278
5423
  applySearchReplaceBlocks(content, blocks) {
4279
5424
  let result = content;
@@ -4368,10 +5513,10 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4368
5513
  score += 2;
4369
5514
  }
4370
5515
  if (score > 0 && (!bestMatch || score > bestMatch.score)) {
4371
- bestMatch = { line: line.trim(), score };
5516
+ bestMatch = { line: line.trim(), originalLine: line, score };
4372
5517
  }
4373
5518
  }
4374
- return bestMatch && bestMatch.score >= 2 ? bestMatch.line : null;
5519
+ return bestMatch && bestMatch.score >= 2 ? bestMatch.originalLine : null;
4375
5520
  }
4376
5521
  /**
4377
5522
  * Colorize raw git diff output with green for additions and red for removals
@@ -4457,6 +5602,10 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4457
5602
  }
4458
5603
  }
4459
5604
  showDiff(oldContent, newContent, filePath) {
5605
+ console.log(this.formatDiffPreview(oldContent, newContent, filePath));
5606
+ console.log();
5607
+ }
5608
+ formatDiffPreview(oldContent, newContent, filePath) {
4460
5609
  const diff = diffLines(oldContent, newContent);
4461
5610
  const contextLines = 3;
4462
5611
  const lang = filePath ? detectLanguage(filePath) : "text";
@@ -4473,10 +5622,11 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4473
5622
  const termWidth = process.stdout.columns || 100;
4474
5623
  const addText = additions === 1 ? "1 line" : `${additions} lines`;
4475
5624
  const delText = deletions === 1 ? "1 line" : `${deletions} lines`;
5625
+ const outputLines = [];
4476
5626
  if (theme) {
4477
- console.log(theme.fg("muted", ` Added ${theme.fg("diffAdded", addText)}, removed ${theme.fg("diffRemoved", delText)}`));
5627
+ outputLines.push(theme.fg("muted", ` Added ${theme.fg("diffAdded", addText)}, removed ${theme.fg("diffRemoved", delText)}`));
4478
5628
  } else {
4479
- console.log(chalk2.gray(` Added ${chalk2.green(addText)}, removed ${chalk2.red(delText)}`));
5629
+ outputLines.push(chalk2.gray(` Added ${chalk2.green(addText)}, removed ${chalk2.red(delText)}`));
4480
5630
  }
4481
5631
  const hunks = [];
4482
5632
  let currentHunk = null;
@@ -4565,7 +5715,7 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4565
5715
  const bgB = addedRgb ? Math.floor(addedRgb.b * 0.15) : 30;
4566
5716
  const prefix = chalk2.bgHex(addedColor).black(` ${lineNumStr} + `);
4567
5717
  const content = chalk2.bgRgb(bgR, bgG, bgB)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4568
- console.log(prefix + content);
5718
+ outputLines.push(prefix + content);
4569
5719
  } else if (change.type === "remove") {
4570
5720
  const removedRgb = hexToRgb(removedColor);
4571
5721
  const bgR = removedRgb ? Math.floor(removedRgb.r * 0.25) : 60;
@@ -4573,32 +5723,53 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4573
5723
  const bgB = removedRgb ? Math.floor(removedRgb.b * 0.15) : 30;
4574
5724
  const prefix = chalk2.bgHex(removedColor).white(` ${lineNumStr} - `);
4575
5725
  const content = chalk2.bgRgb(bgR, bgG, bgB)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4576
- console.log(prefix + content);
5726
+ outputLines.push(prefix + content);
4577
5727
  } else {
4578
- console.log(chalk2.hex(contextColor)(` ${lineNumStr} `) + ` ${highlighted}`);
5728
+ outputLines.push(chalk2.hex(contextColor)(` ${lineNumStr} `) + ` ${highlighted}`);
4579
5729
  }
4580
5730
  } else {
4581
5731
  if (change.type === "add") {
4582
5732
  const prefix = chalk2.bgGreen.black(` ${lineNumStr} + `);
4583
5733
  const content = chalk2.bgRgb(30, 50, 30)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4584
- console.log(prefix + content);
5734
+ outputLines.push(prefix + content);
4585
5735
  } else if (change.type === "remove") {
4586
5736
  const prefix = chalk2.bgRed.white(` ${lineNumStr} - `);
4587
5737
  const content = chalk2.bgRgb(60, 30, 30)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4588
- console.log(prefix + content);
5738
+ outputLines.push(prefix + content);
4589
5739
  } else {
4590
- console.log(chalk2.gray(` ${lineNumStr} `) + ` ${highlighted}`);
5740
+ outputLines.push(chalk2.gray(` ${lineNumStr} `) + ` ${highlighted}`);
4591
5741
  }
4592
5742
  }
4593
5743
  }
4594
5744
  }
4595
- console.log();
5745
+ return outputLines.join("\n");
4596
5746
  }
4597
5747
  };
5748
+ function parseGoalStatus(value) {
5749
+ if (!value) return void 0;
5750
+ if (value === "active" || value === "paused" || value === "complete" || value === "budgetLimited") {
5751
+ return value;
5752
+ }
5753
+ return void 0;
5754
+ }
5755
+ function formatGoalToolResult(result) {
5756
+ return JSON.stringify({
5757
+ ok: result.ok,
5758
+ message: result.message,
5759
+ goal: result.goal,
5760
+ queue: result.queue,
5761
+ queued: result.queued,
5762
+ started: result.started,
5763
+ dequeued: result.dequeued,
5764
+ removed: result.removed,
5765
+ telemetry: result.telemetry
5766
+ }, null, 2);
5767
+ }
4598
5768
 
4599
5769
  export {
4600
5770
  getAutoCommitInfo,
4601
- ToolsRegistry,
5771
+ WorktreeManager,
5772
+ createToolsRegistry,
4602
5773
  ActionExecutor
4603
5774
  };
4604
5775
  /**