autohand-cli 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (551) hide show
  1. package/README.md +173 -74
  2. package/dist/AgentRegistry-EGBDIUAK.cjs +10 -0
  3. package/dist/{AgentRegistry-7LDL5HJH.js → AgentRegistry-XPWSVO3Q.js} +3 -3
  4. package/dist/{AutomodeManager-MWLKGPZK.js → AutomodeManager-APLLPEYJ.js} +32 -18
  5. package/dist/{AutomodeManager-NYIZNODK.cjs → AutomodeManager-MPSXT2RH.cjs} +38 -24
  6. package/dist/CommunitySkillsCache-2OIUV227.cjs +8 -0
  7. package/dist/CommunitySkillsCache-ZEQWP6YM.js +8 -0
  8. package/dist/GitHubRegistryFetcher-CYJLF2XL.cjs +7 -0
  9. package/dist/{GitHubRegistryFetcher-6JQ5JEDZ.js → GitHubRegistryFetcher-J2BWPXJF.js} +2 -2
  10. package/dist/HookManager-CWLTFKWO.js +7 -0
  11. package/dist/HookManager-TMAJQU4S.cjs +7 -0
  12. package/dist/{ImportWizard-35YBJ4AM.cjs → ImportWizard-MQXEED2U.cjs} +41 -17
  13. package/dist/{ImportWizard-XH7CINCH.js → ImportWizard-QBKQEXDW.js} +35 -11
  14. package/dist/LearnAdvisor-46FG2XIP.js +9 -0
  15. package/dist/LearnAdvisor-XBRDNAGH.cjs +9 -0
  16. package/dist/McpClientManager-BNSKLHIN.cjs +8 -0
  17. package/dist/{McpClientManager-7RM6YT35.js → McpClientManager-SL35BR24.js} +2 -2
  18. package/dist/MemoryManager-34L4YOKA.cjs +8 -0
  19. package/dist/MemoryManager-WJMLPWGU.js +8 -0
  20. package/dist/NVIDIAProvider-2HR737UE.js +14 -0
  21. package/dist/NVIDIAProvider-MVTL62PR.cjs +14 -0
  22. package/dist/PermissionManager-5OOJ7FAT.cjs +11 -0
  23. package/dist/{PermissionManager-ATUV34LQ.js → PermissionManager-KMYILJ4Z.js} +4 -4
  24. package/dist/{ProjectProfiler-ZDWR2ODG.cjs → ProjectProfiler-CDAE7ECW.cjs} +2 -1
  25. package/dist/{ProjectProfiler-UMJJSOCE.js → ProjectProfiler-NZTJDRHD.js} +2 -1
  26. package/dist/ProviderFactory-C3YPXTDD.cjs +11 -0
  27. package/dist/ProviderFactory-HFPRVQ25.js +11 -0
  28. package/dist/SessionManager-PNBTJJTQ.js +10 -0
  29. package/dist/SessionManager-WAPTFMDO.cjs +10 -0
  30. package/dist/SkillsRegistry-4RRD5GMX.js +9 -0
  31. package/dist/SkillsRegistry-7AJP74GJ.cjs +9 -0
  32. package/dist/SubAgent-FDIH3DXB.js +11 -0
  33. package/dist/SubAgent-HVL2ICVZ.cjs +11 -0
  34. package/dist/{SyncApiClient-LVIO4C2S.js → SyncApiClient-LRPFNCKJ.js} +2 -2
  35. package/dist/SyncApiClient-VMBOLQ6H.cjs +11 -0
  36. package/dist/about-BW3PDZUU.js +14 -0
  37. package/dist/about-FCAX37YC.cjs +14 -0
  38. package/dist/acp-EOETGAHC.cjs +1517 -0
  39. package/dist/acp-VHEL7BOW.js +1517 -0
  40. package/dist/actionExecutor-33I47NZS.js +24 -0
  41. package/dist/actionExecutor-4YBMR3YH.cjs +24 -0
  42. package/dist/add-dir-7SS6KSH5.cjs +11 -0
  43. package/dist/add-dir-VFX7QT4E.js +11 -0
  44. package/dist/agent-DMRUFU4M.cjs +116 -0
  45. package/dist/agent-WCJEYQJT.js +116 -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-CHROO3VU.cjs +17 -0
  53. package/dist/agents-UVDUL65K.js +17 -0
  54. package/dist/agents-new-AUBWSOXP.js +17 -0
  55. package/dist/agents-new-Y562WC47.cjs +17 -0
  56. package/dist/{autoSkill-6TGBTEQD.js → autoSkill-EFMK6WU6.js} +3 -3
  57. package/dist/autoSkill-YOLLFTP2.cjs +20 -0
  58. package/dist/automode-BMYSRM34.js +10 -0
  59. package/dist/automode-DBLKTTKD.cjs +10 -0
  60. package/dist/browserToolBridge-BXRQB4B4.cjs +58 -0
  61. package/dist/browserToolBridge-F5N66PE7.js +58 -0
  62. package/dist/{cc-7LEIJ3KF.js → cc-AYEP2CA3.js} +1 -1
  63. package/dist/{cc-Q5MM4AWC.cjs → cc-S2QLQFBR.cjs} +1 -1
  64. package/dist/chrome-ATC4OO5I.cjs +20 -0
  65. package/dist/chrome-NHJ44WUN.js +50 -0
  66. package/dist/chrome-O62WXV7F.js +20 -0
  67. package/dist/chrome-XHJLCO4M.cjs +50 -0
  68. package/dist/chromeSkill-53TH55PM.js +105 -0
  69. package/dist/chromeSkill-THW7N4IY.cjs +105 -0
  70. package/dist/{chunk-HOAHWIQ5.cjs → chunk-2CGE7ZV3.cjs} +26 -26
  71. package/dist/chunk-2H5O745H.js +63 -0
  72. package/dist/chunk-2HOLOHVK.js +2219 -0
  73. package/dist/{chunk-3PDTTAKJ.js → chunk-2QL6MNXG.js} +15 -8
  74. package/dist/{chunk-PGRH5Q77.cjs → chunk-2VHB43IX.cjs} +35 -6
  75. package/dist/{chunk-VG34MG2U.js → chunk-37M5UM6I.js} +9 -6
  76. package/dist/{chunk-6HYLHBQG.cjs → chunk-3DDL2E55.cjs} +20 -16
  77. package/dist/chunk-3EDDDZS2.cjs +5 -0
  78. package/dist/{chunk-6OYHF6MF.js → chunk-3GUEUCZK.js} +28 -4
  79. package/dist/{chunk-N254NRHT.cjs → chunk-3HT76LNN.cjs} +9 -2
  80. package/dist/{chunk-DVUHHH3B.cjs → chunk-3KLSNNRW.cjs} +4 -4
  81. package/dist/chunk-3LJJG5YY.cjs +387 -0
  82. package/dist/chunk-3OF56EMA.cjs +197 -0
  83. package/dist/chunk-3PXKRVCW.js +108 -0
  84. package/dist/{chunk-IKGWDOGU.cjs → chunk-3S563FNF.cjs} +160 -79
  85. package/dist/chunk-3UT7R3XV.js +663 -0
  86. package/dist/chunk-4256YRCO.cjs +80 -0
  87. package/dist/chunk-46C73ZKK.cjs +663 -0
  88. package/dist/chunk-46MTALKD.js +44 -0
  89. package/dist/{chunk-AEJH23FO.cjs → chunk-47PHDKNW.cjs} +6 -6
  90. package/dist/chunk-4LMUDS2K.js +124 -0
  91. package/dist/{chunk-HLHTG5ZU.cjs → chunk-5CMYEM3R.cjs} +14 -12
  92. package/dist/{chunk-MBBY4ZIK.js → chunk-5F6ZKSHO.js} +4 -1
  93. package/dist/chunk-5JFTY3VU.js +74 -0
  94. package/dist/chunk-5JTTM5SC.js +59 -0
  95. package/dist/{chunk-IETRBBMP.cjs → chunk-5K3CDSWZ.cjs} +108 -31
  96. package/dist/{chunk-GJH7XMSK.js → chunk-5VHP6HDQ.js} +8 -6
  97. package/dist/{chunk-6ZCULLCA.js → chunk-62RTC3XX.js} +1 -1
  98. package/dist/{chunk-MSED7RH2.cjs → chunk-6GUODJKM.cjs} +112 -41
  99. package/dist/{chunk-47CKWKEX.cjs → chunk-6HH236FV.cjs} +9 -4
  100. package/dist/{chunk-WHE2SWHU.js → chunk-6NVAK6CK.js} +2 -2
  101. package/dist/chunk-6R25D2H5.js +121 -0
  102. package/dist/{chunk-3PCTTUNW.cjs → chunk-6XVVIY54.cjs} +55 -25
  103. package/dist/{chunk-3L53OA4E.cjs → chunk-75EDROHL.cjs} +10 -10
  104. package/dist/{chunk-G4CAKI3V.js → chunk-7HPWMALN.js} +7 -2
  105. package/dist/chunk-7JLT2VNW.cjs +18963 -0
  106. package/dist/chunk-7LWQCE4Y.cjs +987 -0
  107. package/dist/{chunk-OLSBBZW6.cjs → chunk-7MU7LWF3.cjs} +5 -5
  108. package/dist/{chunk-HLQV64Y5.js → chunk-7RFJB75Y.js} +140 -59
  109. package/dist/{chunk-X2MSVKDV.js → chunk-A7HHCIDQ.js} +2 -2
  110. package/dist/{chunk-SKYG33B2.cjs → chunk-AC7DZ6SK.cjs} +3 -3
  111. package/dist/{chunk-XX2ZO7DS.js → chunk-ACDQGA4Z.js} +90 -16
  112. package/dist/{chunk-CZXGCVTR.cjs → chunk-AOKCI722.cjs} +2 -2
  113. package/dist/chunk-ARBEHFCG.js +715 -0
  114. package/dist/{chunk-OOKY3HPZ.js → chunk-AT7OWLY5.js} +50 -9
  115. package/dist/chunk-AU6JAGZJ.cjs +231 -0
  116. package/dist/chunk-AUFNACED.js +18963 -0
  117. package/dist/{chunk-CDBPBM2K.cjs → chunk-AUYSIEVV.cjs} +3 -3
  118. package/dist/chunk-B4HSNOIH.cjs +354 -0
  119. package/dist/{chunk-EGMZDTSL.js → chunk-B5CGDNMR.js} +10 -2
  120. package/dist/{chunk-34M3HWLR.js → chunk-BFQDQRDE.js} +2 -2
  121. package/dist/{chunk-2AA5MFES.js → chunk-BL2UC7HC.js} +8 -5
  122. package/dist/{chunk-LNMYK2F5.cjs → chunk-BQ22N3TX.cjs} +56 -41
  123. package/dist/chunk-BQU3HAE7.js +21 -0
  124. package/dist/chunk-BWF4VDYE.js +3897 -0
  125. package/dist/chunk-BYE7RQFZ.cjs +121 -0
  126. package/dist/{chunk-APIXPPMT.js → chunk-CBFH2J3O.js} +855 -33
  127. package/dist/chunk-CCVMREXI.js +420 -0
  128. package/dist/chunk-CFAWTLSC.js +13 -0
  129. package/dist/{chunk-G27PQQFD.js → chunk-CFFE4VA3.js} +1 -1
  130. package/dist/{chunk-ZYQMLKOK.cjs → chunk-CH2J4PVE.cjs} +212 -70
  131. package/dist/{chunk-7BTSG4ME.cjs → chunk-CHI52KFR.cjs} +855 -33
  132. package/dist/{chunk-DJDE4DTT.cjs → chunk-CWINVFRI.cjs} +25 -19
  133. package/dist/{chunk-HXGBSJL5.cjs → chunk-D2OSPLYC.cjs} +2 -2
  134. package/dist/{chunk-GBHDROGL.js → chunk-D6GZBSOX.js} +16 -4
  135. package/dist/{chunk-CNBKZEX5.cjs → chunk-DA7NBAJK.cjs} +49 -17
  136. package/dist/chunk-DEQVRSV5.cjs +866 -0
  137. package/dist/{chunk-RGR6ME5J.cjs → chunk-DI57A4BX.cjs} +25 -28
  138. package/dist/{chunk-WM5PAOTQ.cjs → chunk-DIZTWFVR.cjs} +1649 -484
  139. package/dist/{chunk-YZXUDM5X.js → chunk-DLP436GI.js} +204 -62
  140. package/dist/{chunk-OHUZKDGX.js → chunk-DMRXF5DU.js} +3 -3
  141. package/dist/{chunk-U46VYPLR.cjs → chunk-DPSIGY6L.cjs} +9 -9
  142. package/dist/{chunk-DN573ME7.cjs → chunk-DRWDEHE5.cjs} +4 -4
  143. package/dist/{chunk-SEKD5FH3.cjs → chunk-E26KKI46.cjs} +5 -2
  144. package/dist/{chunk-J4Q7XR3G.js → chunk-E46DJH5S.js} +3 -3
  145. package/dist/{chunk-Q7XSCYND.cjs → chunk-E6GFD7VR.cjs} +49 -26
  146. package/dist/chunk-ES2HJQ4N.js +37 -0
  147. package/dist/chunk-EZJHLOWP.js +80 -0
  148. package/dist/chunk-FFBDRUO5.cjs +59 -0
  149. package/dist/{chunk-AYSFIUFW.js → chunk-FGT6KK6T.js} +38 -15
  150. package/dist/{chunk-SAHBLB3E.js → chunk-FP46B4X3.js} +208 -3
  151. package/dist/{chunk-DSPQEHDT.js → chunk-FQAVGSPW.js} +2 -2
  152. package/dist/chunk-FQVG6ZHF.js +197 -0
  153. package/dist/chunk-GBMDFWJX.cjs +152 -0
  154. package/dist/{chunk-DRE2RXBZ.js → chunk-GGLKUENP.js} +1605 -440
  155. package/dist/{chunk-S52YW5ZQ.js → chunk-GNBBIAMD.js} +16 -19
  156. package/dist/{chunk-MYISNQH4.js → chunk-GWO66KBI.js} +1 -1
  157. package/dist/chunk-GWY26SUD.cjs +63 -0
  158. package/dist/{chunk-YGN4CQIP.js → chunk-GZ6DV2UG.js} +1 -1
  159. package/dist/{chunk-EZMINVLU.js → chunk-H4D2Q2AL.js} +5 -2
  160. package/dist/chunk-HDK2EHVH.cjs +570 -0
  161. package/dist/chunk-HJIXWGQZ.js +139 -0
  162. package/dist/{chunk-6RF7UKUS.js → chunk-HKYZSUQ5.js} +48 -47
  163. package/dist/{chunk-LQGVEP3E.js → chunk-HQUSEWT4.js} +45 -13
  164. package/dist/{chunk-HTLINWX6.cjs → chunk-HQWQZML5.cjs} +16 -13
  165. package/dist/{chunk-DVZOENQ7.cjs → chunk-HS4USDND.cjs} +9 -4
  166. package/dist/chunk-HSCUPEA4.cjs +3897 -0
  167. package/dist/{chunk-JCLYQ2JC.js → chunk-HXJEGMGB.js} +12 -6
  168. package/dist/{chunk-LJFUXC56.cjs → chunk-HXQREVTA.cjs} +9 -6
  169. package/dist/{chunk-SCXX4LW5.js → chunk-I24CWKKK.js} +15 -8
  170. package/dist/chunk-IAGCRVJ3.js +355 -0
  171. package/dist/chunk-IEXKKKOF.cjs +92 -0
  172. package/dist/{chunk-5IXII4HX.cjs → chunk-IGMPWJ2I.cjs} +19 -12
  173. package/dist/chunk-IKUMVBCW.cjs +33 -0
  174. package/dist/{chunk-XTB6VJVQ.cjs → chunk-J2K57QM7.cjs} +6 -6
  175. package/dist/{chunk-O4IF4NJT.cjs → chunk-J5ADZN6V.cjs} +57 -56
  176. package/dist/chunk-J5HE6CUM.cjs +124 -0
  177. package/dist/{chunk-JS7IPR7P.js → chunk-JAQO6XDB.js} +31 -2
  178. package/dist/chunk-JIMSII7R.js +987 -0
  179. package/dist/chunk-JJ4KA7HK.cjs +2219 -0
  180. package/dist/chunk-JMN3GZU6.js +570 -0
  181. package/dist/{chunk-X5VSP65C.cjs → chunk-JP3YHTQ2.cjs} +4 -4
  182. package/dist/{chunk-SKV2F3NM.js → chunk-JYKPWK5O.js} +1 -1
  183. package/dist/{chunk-NNPAM4HC.cjs → chunk-KNTUI4TZ.cjs} +12 -6
  184. package/dist/{chunk-HIVRCQS2.js → chunk-L4F7SUYL.js} +56 -25
  185. package/dist/{chunk-C5IJIM2V.cjs → chunk-LDJQ5QHG.cjs} +68 -37
  186. package/dist/{chunk-ULQ6MDSJ.cjs → chunk-LFDPTJYF.cjs} +43 -35
  187. package/dist/chunk-LJD7KR3L.cjs +44 -0
  188. package/dist/chunk-LN7D3EJZ.js +387 -0
  189. package/dist/{chunk-643VRA5S.cjs → chunk-LQBONA55.cjs} +32 -8
  190. package/dist/{chunk-NCC6ETZS.js → chunk-LUMV3DB2.js} +41 -33
  191. package/dist/chunk-M4KNC5BQ.js +78 -0
  192. package/dist/chunk-MO4KP6XS.cjs +229 -0
  193. package/dist/{chunk-BVKXEQVG.cjs → chunk-MOJ7ADW4.cjs} +22 -10
  194. package/dist/chunk-MPULXVC4.cjs +715 -0
  195. package/dist/{chunk-X5YJ34FZ.cjs → chunk-MQESBFBZ.cjs} +46 -23
  196. package/dist/chunk-MUNUUFU7.cjs +21 -0
  197. package/dist/{chunk-X3WS5LDG.js → chunk-MX75JYIY.js} +7 -5
  198. package/dist/chunk-MXVIDIC6.cjs +139 -0
  199. package/dist/{chunk-3K2ESU53.cjs → chunk-N6O3XUZ2.cjs} +2 -2
  200. package/dist/{chunk-IVM5F2AE.js → chunk-NBGOIFKP.js} +265 -18
  201. package/dist/{chunk-SLQAYV3W.js → chunk-NMQ47RCG.js} +8 -2
  202. package/dist/{chunk-JYTDYJVW.js → chunk-O64I2GYI.js} +1 -1
  203. package/dist/{chunk-IFFXSTOM.cjs → chunk-O6TQP7U7.cjs} +3 -3
  204. package/dist/{chunk-AYS2ASM7.js → chunk-OHWMWKJQ.js} +1 -1
  205. package/dist/{chunk-N23UAW4I.js → chunk-OM24WXEE.js} +7 -2
  206. package/dist/{chunk-MAKMSQMQ.cjs → chunk-OPNI6O7F.cjs} +8 -6
  207. package/dist/chunk-OR67YXQK.cjs +13 -0
  208. package/dist/{chunk-QOXPOR5D.js → chunk-OV3PVUYN.js} +115 -44
  209. package/dist/{chunk-TNZRZQ7Q.js → chunk-PB7W7R72.js} +3 -78
  210. package/dist/chunk-PCM3N3CL.cjs +37 -0
  211. package/dist/{chunk-A4IJHHV7.cjs → chunk-PDHT7LQS.cjs} +52 -11
  212. package/dist/{chunk-LA7H35XM.cjs → chunk-PHJO5YAL.cjs} +9 -9
  213. package/dist/{chunk-KPELYZ6L.js → chunk-PQLKJCIE.js} +2 -2
  214. package/dist/chunk-PUD76XQT.cjs +78 -0
  215. package/dist/chunk-Q2IL4DDI.cjs +355 -0
  216. package/dist/{chunk-WPVWQSL7.cjs → chunk-Q6AC3PHY.cjs} +16 -13
  217. package/dist/{chunk-XRZEUWKF.js → chunk-QCHIDZBA.js} +1 -1
  218. package/dist/{chunk-IQ5RXU6O.js → chunk-QEGOB6QV.js} +1 -1
  219. package/dist/{chunk-JSBRDJBE.js → chunk-QGM4M3NI.js} +8 -1
  220. package/dist/chunk-QKP772OZ.js +4291 -0
  221. package/dist/chunk-QQ7PANOP.js +59 -0
  222. package/dist/{chunk-YHD6TUIR.cjs → chunk-QW2RW2GY.cjs} +2 -0
  223. package/dist/chunk-RAKO7UN7.js +114 -0
  224. package/dist/chunk-RBHANOYY.js +33 -0
  225. package/dist/chunk-RBZ7AFX7.cjs +29 -0
  226. package/dist/{chunk-TXSDBGKX.cjs → chunk-REMGR23V.cjs} +46 -49
  227. package/dist/chunk-ROLA6EFO.cjs +85 -0
  228. package/dist/{chunk-TBEGGJNC.cjs → chunk-SOLBYSLY.cjs} +33 -16
  229. package/dist/{chunk-FKSDEWDH.js → chunk-SWCQM52V.js} +68 -25
  230. package/dist/{chunk-H5SWOLG6.js → chunk-TBOLJDUG.js} +39 -24
  231. package/dist/chunk-TH26BQJG.js +101 -0
  232. package/dist/{chunk-245KJE5Y.cjs → chunk-TJNBASFD.cjs} +14 -6
  233. package/dist/chunk-TKKN34TV.js +229 -0
  234. package/dist/chunk-TQ33WBY5.cjs +74 -0
  235. package/dist/{chunk-EGFT4PGW.js → chunk-TWQDAUZU.js} +5 -2
  236. package/dist/{chunk-OGV4WJ5L.cjs → chunk-TXPSTCG7.cjs} +74 -59
  237. package/dist/{chunk-CAMZTXV6.js → chunk-U4C3HW6P.js} +67 -52
  238. package/dist/{chunk-5P2NXKP3.js → chunk-UEIXJG45.js} +104 -76
  239. package/dist/{chunk-22D2CNTP.cjs → chunk-UGFVM77J.cjs} +5 -2
  240. package/dist/chunk-UOQECODR.js +34 -0
  241. package/dist/chunk-UR27UDTB.js +354 -0
  242. package/dist/chunk-UWFG2R2I.cjs +420 -0
  243. package/dist/{chunk-Y72HH2TF.cjs → chunk-UXLW45ZE.cjs} +102 -28
  244. package/dist/{chunk-FPHU2ES6.cjs → chunk-VBOFPU5M.cjs} +6 -0
  245. package/dist/{chunk-JX3DFKBI.js → chunk-VDWJMWDF.js} +54 -24
  246. package/dist/{chunk-LENHP55G.cjs → chunk-VGEV44V2.cjs} +1076 -158
  247. package/dist/{chunk-33RSHBDH.js → chunk-VKBIE6I6.js} +38 -7
  248. package/dist/{chunk-CWMZKFTT.js → chunk-VRMZO6TB.js} +31 -8
  249. package/dist/chunk-VUGOOGHB.js +400 -0
  250. package/dist/chunk-WBU4Q4GS.cjs +400 -0
  251. package/dist/chunk-WGLBC5AY.cjs +59 -0
  252. package/dist/{chunk-KWXVKLQ5.cjs → chunk-WGNMOVMT.cjs} +7 -82
  253. package/dist/{chunk-RKJTGGMU.cjs → chunk-WKRDBCP2.cjs} +221 -16
  254. package/dist/chunk-WRTXCQ3V.cjs +4291 -0
  255. package/dist/chunk-WTB7AFL6.cjs +101 -0
  256. package/dist/{chunk-NDMIPTV4.js → chunk-WZV6UVPY.js} +8 -3
  257. package/dist/chunk-XF2WIKHR.cjs +34 -0
  258. package/dist/{chunk-L3WAH3EM.cjs → chunk-XGMI6IYB.cjs} +46 -15
  259. package/dist/{chunk-FW774QXH.js → chunk-XIVFAD2L.js} +1048 -130
  260. package/dist/chunk-XV2HXRHX.js +85 -0
  261. package/dist/{chunk-72FKPBT5.js → chunk-Y3M2DABP.js} +16 -12
  262. package/dist/{chunk-QNGEW5TC.js → chunk-YV63VW4K.js} +1 -1
  263. package/dist/{chunk-UJ2JSM6H.js → chunk-YVQI26H4.js} +2 -0
  264. package/dist/{chunk-RD5XAJR2.cjs → chunk-YWTIXHU6.cjs} +266 -19
  265. package/dist/chunk-YZWE7XSM.js +5 -0
  266. package/dist/{chunk-I5IW3T2Y.js → chunk-Z3XSSF7B.js} +32 -15
  267. package/dist/{chunk-6UJMCWRY.js → chunk-Z4LIPMPM.js} +6 -0
  268. package/dist/chunk-ZASDSY7P.cjs +114 -0
  269. package/dist/{chunk-R33VKSH5.cjs → chunk-ZHJ7JGME.cjs} +11 -11
  270. package/dist/chunk-ZPD2AO3U.js +866 -0
  271. package/dist/{chunk-7UOUW76C.js → chunk-ZQAT5VT5.js} +101 -24
  272. package/dist/chunk-ZR46OJNZ.js +152 -0
  273. package/dist/{chunk-G3V4SFET.cjs → chunk-ZZ63NW7A.cjs} +78 -35
  274. package/dist/clear-33TWQ2ES.cjs +12 -0
  275. package/dist/clear-OFLFQ3MR.js +12 -0
  276. package/dist/{communityInstaller-6KCFN7YZ.js → communityInstaller-COB2KTOW.js} +9 -6
  277. package/dist/communityInstaller-TWMGPSYM.cjs +22 -0
  278. package/dist/completion-IIZMHXYP.cjs +17 -0
  279. package/dist/completion-PT4VM2H2.js +17 -0
  280. package/dist/config-KL6WU7R2.cjs +20 -0
  281. package/dist/{config-ZN66VXPS.js → config-R4O7GBTY.js} +7 -5
  282. package/dist/{constants-UFM5B232.js → constants-HVCHVQAF.js} +2 -2
  283. package/dist/constants-RLMJ5D5P.cjs +21 -0
  284. package/dist/{defaultHooks-RCXPHF4M.cjs → defaultHooks-2V2CQL63.cjs} +26 -11
  285. package/dist/{defaultHooks-RDRMER3Z.js → defaultHooks-TMHDU3FS.js} +26 -11
  286. package/dist/export-3PK3VFCE.js +15 -0
  287. package/dist/export-WLGNWEMJ.cjs +15 -0
  288. package/dist/{extractSessionMemories-V7K42ZHW.js → extractSessionMemories-A2JX5WJ2.js} +1 -1
  289. package/dist/{extractSessionMemories-SDW2MVBQ.cjs → extractSessionMemories-XL3MS37F.cjs} +1 -1
  290. package/dist/feedback-G635NCLJ.js +18 -0
  291. package/dist/feedback-LLMK3TZH.cjs +18 -0
  292. package/dist/fffSearchProvider-2YCNKOYD.js +412 -0
  293. package/dist/fffSearchProvider-W6627E2V.cjs +412 -0
  294. package/dist/filesystem-L6DQKGWK.js +11 -0
  295. package/dist/filesystem-PGUPCMVK.cjs +11 -0
  296. package/dist/{formatters-6K7QVWQL.cjs → formatters-53XTCNGQ.cjs} +1 -1
  297. package/dist/{formatters-DQHO5I36.js → formatters-T6KV4BPP.js} +1 -1
  298. package/dist/help-BAXLP2M2.cjs +12 -0
  299. package/dist/{help-2BLR7L43.js → help-X3OAZ3CY.js} +3 -3
  300. package/dist/history-6HOJSEMT.cjs +14 -0
  301. package/dist/{history-5FZ3M2AK.js → history-YKFPHBJN.js} +3 -3
  302. package/dist/hooks-7TA4PIIB.js +18 -0
  303. package/dist/hooks-XVFU67T2.cjs +18 -0
  304. package/dist/{i18n-K7QOWIBH.js → i18n-2KBUU7XL.js} +2 -2
  305. package/dist/i18n-MUJMKTFM.cjs +33 -0
  306. package/dist/ide-CCQ33PGC.cjs +15 -0
  307. package/dist/ide-VLVFBZ5F.js +15 -0
  308. package/dist/immediateCommandRouter-MTEHZXQX.js +15 -0
  309. package/dist/immediateCommandRouter-ROXU3MWT.cjs +15 -0
  310. package/dist/{import-UXM3VK7B.js → import-4CHYLS4K.js} +4 -4
  311. package/dist/{import-QEME3E4T.cjs → import-I7T4ZHYL.cjs} +4 -4
  312. package/dist/import-J46F54JY.cjs +10 -0
  313. package/dist/import-JWPYKXCZ.js +10 -0
  314. package/dist/index.cjs +747 -22356
  315. package/dist/index.d.cts +93 -0
  316. package/dist/index.d.ts +93 -0
  317. package/dist/index.js +786 -22395
  318. package/dist/init-262MWZV4.js +10 -0
  319. package/dist/init-EE5Y7RBL.cjs +10 -0
  320. package/dist/inkMode-VUE6ZDLD.cjs +7 -0
  321. package/dist/inkMode-WBNFOSAT.js +7 -0
  322. package/dist/inputPrompt-IIFKCX5Q.cjs +90 -0
  323. package/dist/inputPrompt-YGBHDUEP.js +90 -0
  324. package/dist/language-CVLPB7OV.js +21 -0
  325. package/dist/language-ZTWFHUSV.cjs +21 -0
  326. package/dist/learn-PON7I5QS.js +23 -0
  327. package/dist/learn-XLRSVNA3.cjs +23 -0
  328. package/dist/{lint-D5UOJWIK.cjs → lint-4NDGCSCL.cjs} +1 -1
  329. package/dist/{lint-NJPZWVN2.js → lint-FZ7ZJUB3.js} +1 -1
  330. package/dist/login-223QTGBG.cjs +26 -0
  331. package/dist/login-6IKTBUBY.js +26 -0
  332. package/dist/logout-RN2AG6SI.js +23 -0
  333. package/dist/logout-U3M4FFX7.cjs +23 -0
  334. package/dist/mcp-5O6PUL4G.js +20 -0
  335. package/dist/mcp-NUQ76QQB.cjs +20 -0
  336. package/dist/{mcp-install-VESN42PI.js → mcp-install-6XWXLFVY.js} +18 -11
  337. package/dist/{mcp-install-G27HSS3Z.cjs → mcp-install-P6DHES7V.cjs} +22 -15
  338. package/dist/memory-CYMDQ2YC.cjs +10 -0
  339. package/dist/memory-Q54CESNM.js +10 -0
  340. package/dist/{message-ZJ5AYAMT.cjs → message-4OKO775J.cjs} +1 -1
  341. package/dist/{message-JUBOK2VU.js → message-X3LOAAM7.js} +1 -1
  342. package/dist/model-6AJ77PJG.js +10 -0
  343. package/dist/model-IDRCKDML.cjs +10 -0
  344. package/dist/new-7LEWOUF2.cjs +12 -0
  345. package/dist/new-ZXHEWC2S.js +12 -0
  346. package/dist/onboarding-FXX7YHSJ.cjs +35 -0
  347. package/dist/onboarding-JYNMK6NI.js +35 -0
  348. package/dist/{patch-MOD7QC3D.cjs → patch-DJ2GPFST.cjs} +1 -1
  349. package/dist/{patch-5F6VBIT3.js → patch-NIJWIRHS.js} +1 -1
  350. package/dist/permissions-YBNSANIA.cjs +10 -0
  351. package/dist/permissions-YHJMVA6L.js +10 -0
  352. package/dist/plan-XEJMOT55.cjs +13 -0
  353. package/dist/plan-YYUAXPTL.js +13 -0
  354. package/dist/pr-review-CW6J7P62.cjs +9 -0
  355. package/dist/pr-review-YZSBQVT2.js +9 -0
  356. package/dist/quit-B43SJ6E4.cjs +10 -0
  357. package/dist/quit-GWTNHQSP.js +10 -0
  358. package/dist/rawMode-6W5AXAKI.cjs +7 -0
  359. package/dist/rawMode-GFNLXQPU.js +7 -0
  360. package/dist/{registry-KWZGYJC2.js → registry-34GL6BNJ.js} +30 -45
  361. package/dist/{registry-YN4FQPOO.cjs → registry-V24W7YK6.cjs} +66 -81
  362. package/dist/repeat-P4FAPE3Y.cjs +17 -0
  363. package/dist/repeat-RALE6AUO.js +17 -0
  364. package/dist/resume-DYVOQN5L.cjs +16 -0
  365. package/dist/resume-M25UQKOX.js +16 -0
  366. package/dist/review-QHP2KP4Q.js +9 -0
  367. package/dist/review-UWHWQHCB.cjs +9 -0
  368. package/dist/ripgrep-67SCU2BA.cjs +9 -0
  369. package/dist/ripgrep-VHJQQ55W.js +9 -0
  370. package/dist/rpc-NPS3PU4O.cjs +3730 -0
  371. package/dist/rpc-S3DGW6KV.js +3730 -0
  372. package/dist/search-CPX4PWHP.js +20 -0
  373. package/dist/search-VUF3M4N3.cjs +20 -0
  374. package/dist/{session-BSU2L5UI.cjs → session-LXKC4X5Q.cjs} +1 -1
  375. package/dist/{session-SZMRN5KG.js → session-NMMO4QOL.js} +1 -1
  376. package/dist/sessions-IYAXMK23.js +10 -0
  377. package/dist/sessions-JODKER5D.cjs +10 -0
  378. package/dist/settings-55BNW6BF.js +33 -0
  379. package/dist/settings-BKTS7OMC.cjs +33 -0
  380. package/dist/setup-IRC5HQG2.js +29 -0
  381. package/dist/setup-MMQ7TWOP.cjs +29 -0
  382. package/dist/share-6JRQECAH.cjs +17 -0
  383. package/dist/share-YM5MJKMO.js +17 -0
  384. package/dist/{skills-FYY6F2WV.cjs → skills-GGMZOVIE.cjs} +14 -11
  385. package/dist/skills-LWSOKOSH.cjs +29 -0
  386. package/dist/{skills-6OL4OSGA.js → skills-MVIZNHT4.js} +13 -10
  387. package/dist/skills-VWOMV3CR.js +29 -0
  388. package/dist/{skills-install-6CSWC24P.js → skills-install-QBA5RCV6.js} +22 -71
  389. package/dist/{skills-install-O3LZ2ETC.cjs → skills-install-VTAMCNNY.cjs} +35 -84
  390. package/dist/skills-new-GPL46YV2.js +18 -0
  391. package/dist/skills-new-L5BEZN5V.cjs +18 -0
  392. package/dist/slashCommands-FBPCIWM5.cjs +95 -0
  393. package/dist/slashCommands-FCWY5DXW.js +95 -0
  394. package/dist/status-3B5SDZPL.js +17 -0
  395. package/dist/status-TTGRF6NC.cjs +17 -0
  396. package/dist/summarizer-DGPHE5IQ.js +17 -0
  397. package/dist/summarizer-JNXLUAQG.cjs +17 -0
  398. package/dist/sync-77CXVVTK.cjs +21 -0
  399. package/dist/{sync-KWX67OUN.js → sync-7BILIQHP.js} +4 -4
  400. package/dist/sync-7OKF6636.js +21 -0
  401. package/dist/sync-EH4K5U3N.cjs +40 -0
  402. package/dist/{tasks-5FPBIFLC.js → tasks-R5MBGAQ6.js} +1 -1
  403. package/dist/{tasks-TXGKGNH6.cjs → tasks-V4WB3MFR.cjs} +1 -1
  404. package/dist/{team-5YXP3JGR.js → team-JESCHGWB.js} +1 -1
  405. package/dist/{team-IIWEZKNR.cjs → team-VQQKWGZB.cjs} +1 -1
  406. package/dist/{teammate-L6EZQ3I2.js → teammate-GZQSCIMS.js} +29 -8
  407. package/dist/{teammate-2KMKJXAM.cjs → teammate-J6PHGL23.cjs} +30 -9
  408. package/dist/theme-64BYGJ57.cjs +21 -0
  409. package/dist/theme-YJE6HEON.js +21 -0
  410. package/dist/tools-3PPTTKFV.js +9 -0
  411. package/dist/tools-THIQA7WC.cjs +9 -0
  412. package/dist/ui/questionModal.cjs +9 -6
  413. package/dist/ui/questionModal.js +8 -5
  414. package/dist/undo-PRTEGL2J.cjs +10 -0
  415. package/dist/undo-SZEHLX7X.js +10 -0
  416. package/dist/{update-TVAJMMBC.js → update-53WMKYVS.js} +1 -1
  417. package/dist/{update-Z6BIIQDC.cjs → update-NV6QRYY7.cjs} +1 -1
  418. package/dist/web-3BA2WV37.cjs +37 -0
  419. package/dist/web-6FYGBX5K.js +37 -0
  420. package/dist/workspaceSafety-MDJGHK6D.cjs +9 -0
  421. package/dist/workspaceSafety-XOUMUBVB.js +9 -0
  422. package/dist/yolo-GF2YD7ZI.js +9 -0
  423. package/dist/yolo-OGDA7HNC.cjs +9 -0
  424. package/dist/yoloMode-3DJDA75U.cjs +17 -0
  425. package/dist/yoloMode-4JOOSU26.js +17 -0
  426. package/package.json +46 -49
  427. package/dist/AgentRegistry-NQCLWABO.cjs +0 -10
  428. package/dist/CommunitySkillsCache-6QPRMTJO.js +0 -8
  429. package/dist/CommunitySkillsCache-GTQMOCCO.cjs +0 -8
  430. package/dist/GitHubRegistryFetcher-S7QFUEKV.cjs +0 -7
  431. package/dist/HookManager-Q2KYMCP4.cjs +0 -7
  432. package/dist/HookManager-TTP4Y6DC.js +0 -7
  433. package/dist/LearnAdvisor-A4Q5PPBI.js +0 -9
  434. package/dist/LearnAdvisor-GASQD7HT.cjs +0 -9
  435. package/dist/McpClientManager-RKD7C6OY.cjs +0 -8
  436. package/dist/MemoryManager-GUNLRP5S.js +0 -8
  437. package/dist/MemoryManager-TNSGKDKX.cjs +0 -8
  438. package/dist/PermissionManager-KMN53FJP.cjs +0 -11
  439. package/dist/ProviderFactory-MR5B23QJ.js +0 -9
  440. package/dist/ProviderFactory-VFGCJJX6.cjs +0 -9
  441. package/dist/SessionManager-FEUAU3ZJ.cjs +0 -10
  442. package/dist/SessionManager-IKWAK2PI.js +0 -10
  443. package/dist/SkillsRegistry-KPQFTRIT.cjs +0 -9
  444. package/dist/SkillsRegistry-XJSKPDF2.js +0 -9
  445. package/dist/SubAgent-NYH6GWQ3.js +0 -11
  446. package/dist/SubAgent-PZKBDUBA.cjs +0 -11
  447. package/dist/SyncApiClient-ZNYMT36M.cjs +0 -11
  448. package/dist/about-HHTF2YFL.js +0 -12
  449. package/dist/about-JGRVNNQC.cjs +0 -12
  450. package/dist/actionExecutor-U6IBN2TU.cjs +0 -19
  451. package/dist/actionExecutor-XT5FW3W6.js +0 -19
  452. package/dist/add-dir-247K3XRY.js +0 -10
  453. package/dist/add-dir-GS4DXKKH.cjs +0 -10
  454. package/dist/agents-R6ZEFTVR.cjs +0 -12
  455. package/dist/agents-WJPQWQF2.js +0 -12
  456. package/dist/agents-new-HKVEIBDJ.js +0 -14
  457. package/dist/agents-new-X6GTHIO6.cjs +0 -14
  458. package/dist/autoSkill-H4T6VVDA.cjs +0 -20
  459. package/dist/automode-BC6NVECO.js +0 -10
  460. package/dist/automode-WN2RSOGW.cjs +0 -10
  461. package/dist/chunk-33A755XB.cjs +0 -168
  462. package/dist/chunk-3OTU3RS3.cjs +0 -1607
  463. package/dist/chunk-4PKF7WPD.js +0 -100
  464. package/dist/chunk-ALYU6VTM.js +0 -105
  465. package/dist/chunk-AS6RTLN7.cjs +0 -203
  466. package/dist/chunk-BWN2CLLM.cjs +0 -298
  467. package/dist/chunk-HQ7YZKXE.js +0 -168
  468. package/dist/chunk-HVKOZ2VP.cjs +0 -115
  469. package/dist/chunk-J6QET7EF.cjs +0 -454
  470. package/dist/chunk-LWUJFGOZ.js +0 -115
  471. package/dist/chunk-P47WPOXN.js +0 -298
  472. package/dist/chunk-PRRCJFU3.cjs +0 -85
  473. package/dist/chunk-RO6WYEWH.js +0 -454
  474. package/dist/chunk-SYVYLZZF.cjs +0 -24
  475. package/dist/chunk-T73IDKDF.js +0 -111
  476. package/dist/chunk-YRLYSQEQ.cjs +0 -105
  477. package/dist/chunk-ZQE72E6W.cjs +0 -100
  478. package/dist/chunk-ZVY2XD6T.js +0 -1607
  479. package/dist/clear-UO4MNWZW.cjs +0 -12
  480. package/dist/clear-ZJ5NYP6E.js +0 -12
  481. package/dist/communityInstaller-PVSOFDZD.cjs +0 -19
  482. package/dist/completion-MMF2PN2H.js +0 -14
  483. package/dist/completion-UI5WKHXI.cjs +0 -14
  484. package/dist/config-E7RINK4R.cjs +0 -18
  485. package/dist/constants-6CPCJ3DY.cjs +0 -21
  486. package/dist/export-N4XIVDSL.cjs +0 -12
  487. package/dist/export-W22L4D5C.js +0 -12
  488. package/dist/feedback-DR6ADSNE.cjs +0 -15
  489. package/dist/feedback-FEEAP4QW.js +0 -15
  490. package/dist/filesystem-3SGCW2BF.js +0 -10
  491. package/dist/filesystem-MCFXJQ6R.cjs +0 -10
  492. package/dist/help-AQHGTS7P.cjs +0 -12
  493. package/dist/history-NIUDRMKA.cjs +0 -14
  494. package/dist/hooks-2EY4IPKV.js +0 -13
  495. package/dist/hooks-LJVORRIG.cjs +0 -13
  496. package/dist/i18n-ARDG2SMC.cjs +0 -33
  497. package/dist/ide-GFW6IJHD.js +0 -12
  498. package/dist/ide-N2ZNSSB3.cjs +0 -12
  499. package/dist/import-DFVN3KNZ.js +0 -10
  500. package/dist/import-ZS6DPGU5.cjs +0 -10
  501. package/dist/init-PY75HA3S.cjs +0 -10
  502. package/dist/init-QNMWLAVY.js +0 -10
  503. package/dist/language-5UE4G2BT.cjs +0 -18
  504. package/dist/language-UXMHEZUJ.js +0 -18
  505. package/dist/learn-HJ3FLNZC.cjs +0 -20
  506. package/dist/learn-MVYS3RU5.js +0 -20
  507. package/dist/localProjectPermissions-N77HA3XK.js +0 -18
  508. package/dist/localProjectPermissions-UFSMNTBJ.cjs +0 -18
  509. package/dist/login-DSE7H63A.js +0 -20
  510. package/dist/login-V3MEWPKN.cjs +0 -20
  511. package/dist/logout-BMVCLKKW.js +0 -18
  512. package/dist/logout-XEG7FHOZ.cjs +0 -18
  513. package/dist/mcp-PYUR4PHO.js +0 -18
  514. package/dist/mcp-SG6JFLGC.cjs +0 -18
  515. package/dist/memory-4ZMMEZ2Z.js +0 -10
  516. package/dist/memory-QSGMVVGH.cjs +0 -10
  517. package/dist/model-NANLBZ4Z.cjs +0 -10
  518. package/dist/model-ZXNV4AF7.js +0 -10
  519. package/dist/new-5QJY5JP2.js +0 -12
  520. package/dist/new-PMMG55UX.cjs +0 -12
  521. package/dist/permissions-LECTCJ4H.cjs +0 -13
  522. package/dist/permissions-VP5VGIBL.js +0 -13
  523. package/dist/plan-G5CEKJI4.js +0 -11
  524. package/dist/plan-QKOHE3LH.cjs +0 -11
  525. package/dist/quit-BKOOPHU5.cjs +0 -10
  526. package/dist/quit-FVFNYACP.js +0 -10
  527. package/dist/resume-EXFQSQPH.js +0 -13
  528. package/dist/resume-PP2IQM5S.cjs +0 -13
  529. package/dist/search-C56FBN67.cjs +0 -17
  530. package/dist/search-XGZDYBF4.js +0 -17
  531. package/dist/sessions-54KI3F2Q.js +0 -10
  532. package/dist/sessions-DDTSPNVW.cjs +0 -10
  533. package/dist/settings-BDO37TTO.cjs +0 -30
  534. package/dist/settings-FHRDFPLK.js +0 -30
  535. package/dist/share-IERCTBGN.cjs +0 -14
  536. package/dist/share-TGROUE6R.js +0 -14
  537. package/dist/skills-OM4IGBAA.cjs +0 -26
  538. package/dist/skills-S3GRN323.js +0 -26
  539. package/dist/skills-new-ALD2PTHN.js +0 -15
  540. package/dist/skills-new-PWLKK7GW.cjs +0 -15
  541. package/dist/slashCommands-L4ZD33LJ.js +0 -75
  542. package/dist/slashCommands-YY2VUUDF.cjs +0 -75
  543. package/dist/status-3PC5XWSS.cjs +0 -11
  544. package/dist/status-KCLVOYPD.js +0 -11
  545. package/dist/sync-6SDWG5RK.js +0 -18
  546. package/dist/sync-7JMZVEQD.cjs +0 -40
  547. package/dist/sync-WHURZL3U.cjs +0 -18
  548. package/dist/theme-BE5A4FPN.cjs +0 -18
  549. package/dist/theme-YMFCQP7J.js +0 -18
  550. package/dist/undo-KZHUUZTD.cjs +0 -10
  551. package/dist/undo-NEIEHQVX.js +0 -10
@@ -1,34 +1,46 @@
1
1
  import {
2
- PermissionManager
3
- } from "./chunk-RO6WYEWH.js";
2
+ GitIgnoreParser
3
+ } from "./chunk-H4D2Q2AL.js";
4
4
  import {
5
5
  applyFormatter
6
6
  } from "./chunk-W3X6PAC7.js";
7
+ import {
8
+ PermissionManager
9
+ } from "./chunk-ZPD2AO3U.js";
10
+ import {
11
+ isToolAllowedByYolo,
12
+ normalizeYoloInput,
13
+ parseYoloPattern
14
+ } from "./chunk-4LMUDS2K.js";
15
+ import {
16
+ runCommand
17
+ } from "./chunk-TH26BQJG.js";
18
+ import {
19
+ executeStreamingShellCommand
20
+ } from "./chunk-VUGOOGHB.js";
21
+ import {
22
+ getPlanModeManager
23
+ } from "./chunk-LUMV3DB2.js";
7
24
  import {
8
25
  fetchUrl,
9
26
  formatPackageInfo,
10
27
  formatSearchResults,
11
28
  getPackageInfo,
12
29
  webSearch
13
- } from "./chunk-SAHBLB3E.js";
30
+ } from "./chunk-FP46B4X3.js";
14
31
  import {
15
32
  showInput,
16
33
  showModal
17
- } from "./chunk-YZXUDM5X.js";
34
+ } from "./chunk-DLP436GI.js";
18
35
  import {
19
36
  getTheme,
20
37
  hexToRgb,
21
38
  isThemeInitialized
22
- } from "./chunk-JS7IPR7P.js";
39
+ } from "./chunk-JAQO6XDB.js";
23
40
  import {
24
- AUTOHAND_PATHS
25
- } from "./chunk-EGFT4PGW.js";
26
- import {
27
- getPlanModeManager
28
- } from "./chunk-NCC6ETZS.js";
29
- import {
30
- GitIgnoreParser
31
- } from "./chunk-EZMINVLU.js";
41
+ AUTOHAND_PATHS,
42
+ PROJECT_DIR_NAME
43
+ } from "./chunk-TWQDAUZU.js";
32
44
 
33
45
  // src/core/actionExecutor.ts
34
46
  import chalk2 from "chalk";
@@ -596,84 +608,6 @@ async function removeDependency(cwd, name, options = {}) {
596
608
  }
597
609
  }
598
610
 
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
-
677
611
  // src/actions/metadata.ts
678
612
  import crypto from "crypto";
679
613
  import fs2 from "fs-extra";
@@ -684,11 +618,6 @@ async function listDirectoryTree(root, options = {}) {
684
618
  const maxEntries = options.maxEntries ?? 200;
685
619
  const workspaceRoot = options.workspaceRoot ?? root;
686
620
  const result = [];
687
- const resolvedRoot = path3.resolve(root);
688
- const resolvedWorkspace = path3.resolve(workspaceRoot);
689
- if (!resolvedRoot.startsWith(resolvedWorkspace)) {
690
- throw new Error(`Path ${root} is outside the workspace root.`);
691
- }
692
621
  const ignoreFilter = new GitIgnoreParser(workspaceRoot);
693
622
  async function walk(current, prefix, currentDepth) {
694
623
  if (result.length >= maxEntries) {
@@ -793,6 +722,13 @@ function diffFile(cwd, file) {
793
722
  }
794
723
  return result.stdout || "No diff";
795
724
  }
725
+ function diffWorkspace(cwd) {
726
+ const result = spawnSync("git", ["diff"], { cwd, encoding: "utf8" });
727
+ if (result.status !== 0) {
728
+ throw new Error(result.stderr || "git diff failed");
729
+ }
730
+ return result.stdout || "No diff";
731
+ }
796
732
  function checkoutFile(cwd, file) {
797
733
  const result = spawnSync("git", ["checkout", "--", file], { cwd, encoding: "utf8" });
798
734
  if (result.status !== 0) {
@@ -802,14 +738,22 @@ function checkoutFile(cwd, file) {
802
738
  function gitStatus(cwd) {
803
739
  const result = spawnSync("git", ["status", "-sb"], { cwd, encoding: "utf8" });
804
740
  if (result.status !== 0) {
805
- throw new Error(result.stderr || "git status failed");
741
+ const stderr = (result.stderr || "").trim();
742
+ if (stderr.includes("not a git repository")) {
743
+ return "This directory is not a git repository. You should call run_command with `git init` to initialize one, then retry.";
744
+ }
745
+ throw new Error(stderr || "git status failed");
806
746
  }
807
747
  return result.stdout || "clean";
808
748
  }
809
749
  function gitListUntracked(cwd) {
810
750
  const result = spawnSync("git", ["ls-files", "--others", "--exclude-standard"], { cwd, encoding: "utf8" });
811
751
  if (result.status !== 0) {
812
- throw new Error(result.stderr || "git ls-files failed");
752
+ const stderr = (result.stderr || "").trim();
753
+ if (stderr.includes("not a git repository")) {
754
+ return "This directory is not a git repository. You should call run_command with `git init` to initialize one, then retry.";
755
+ }
756
+ throw new Error(stderr || "git ls-files failed");
813
757
  }
814
758
  return result.stdout || "";
815
759
  }
@@ -1314,7 +1258,7 @@ function gitPush(cwd, remote, branch, options = {}) {
1314
1258
  }
1315
1259
 
1316
1260
  // src/actions/worktree.ts
1317
- import { spawnSync as spawnSync2, spawn as spawn2 } from "child_process";
1261
+ import { spawnSync as spawnSync2, spawn } from "child_process";
1318
1262
  import path4 from "path";
1319
1263
  import fs3 from "fs-extra";
1320
1264
  import os from "os";
@@ -1351,6 +1295,9 @@ var DEFAULT_TEMPLATES = [
1351
1295
  }
1352
1296
  ];
1353
1297
  var WorktreeManager = class {
1298
+ repoRoot;
1299
+ repoName;
1300
+ templates;
1354
1301
  constructor(cwd) {
1355
1302
  this.repoRoot = this.findGitRoot(cwd);
1356
1303
  this.repoName = path4.basename(this.repoRoot);
@@ -1755,7 +1702,7 @@ var WorktreeManager = class {
1755
1702
  }
1756
1703
  async runInWorktree(worktreePath, command) {
1757
1704
  return new Promise((resolve, reject) => {
1758
- const child = spawn2("sh", ["-c", command], {
1705
+ const child = spawn("sh", ["-c", command], {
1759
1706
  cwd: worktreePath,
1760
1707
  stdio: ["pipe", "pipe", "pipe"]
1761
1708
  });
@@ -1780,7 +1727,7 @@ var WorktreeManager = class {
1780
1727
  }
1781
1728
  async runInWorktreeWithTimeout(worktreePath, command, timeout) {
1782
1729
  return new Promise((resolve, reject) => {
1783
- const child = spawn2("sh", ["-c", command], {
1730
+ const child = spawn("sh", ["-c", command], {
1784
1731
  cwd: worktreePath,
1785
1732
  stdio: ["pipe", "pipe", "pipe"]
1786
1733
  });
@@ -1813,6 +1760,92 @@ var WorktreeManager = class {
1813
1760
  }
1814
1761
  };
1815
1762
 
1763
+ // src/actions/notebook.ts
1764
+ function createNotebookCell(cellType, source) {
1765
+ if (cellType === "code") {
1766
+ return {
1767
+ cell_type: "code",
1768
+ source,
1769
+ metadata: {},
1770
+ outputs: [],
1771
+ execution_count: null
1772
+ };
1773
+ }
1774
+ return {
1775
+ cell_type: "markdown",
1776
+ source,
1777
+ metadata: {}
1778
+ };
1779
+ }
1780
+ function resolveCellIndex(notebook, input) {
1781
+ if (typeof input.cell_index === "number") {
1782
+ return input.cell_index;
1783
+ }
1784
+ if (input.cell_id) {
1785
+ const index = notebook.cells.findIndex((cell) => cell.id === input.cell_id);
1786
+ if (index === -1) {
1787
+ throw new Error(`Notebook cell "${input.cell_id}" not found.`);
1788
+ }
1789
+ return index;
1790
+ }
1791
+ return -1;
1792
+ }
1793
+ function applyNotebookEdit(rawContent, input) {
1794
+ if (!input.path.endsWith(".ipynb")) {
1795
+ throw new Error("notebook_edit only supports .ipynb files.");
1796
+ }
1797
+ let notebook;
1798
+ try {
1799
+ notebook = JSON.parse(rawContent);
1800
+ } catch {
1801
+ throw new Error(`Notebook ${input.path} is not valid JSON.`);
1802
+ }
1803
+ if (!Array.isArray(notebook.cells)) {
1804
+ throw new Error(`Notebook ${input.path} does not contain a valid cells array.`);
1805
+ }
1806
+ const editMode = input.edit_mode ?? "replace";
1807
+ const index = resolveCellIndex(notebook, input);
1808
+ if (editMode === "insert") {
1809
+ if (!input.cell_type) {
1810
+ throw new Error('notebook_edit insert requires "cell_type".');
1811
+ }
1812
+ if (typeof input.new_source !== "string") {
1813
+ throw new Error('notebook_edit insert requires "new_source".');
1814
+ }
1815
+ const insertAt = index >= 0 ? index + 1 : notebook.cells.length;
1816
+ notebook.cells.splice(insertAt, 0, createNotebookCell(input.cell_type, input.new_source));
1817
+ return {
1818
+ updated: `${JSON.stringify(notebook, null, 2)}
1819
+ `,
1820
+ summary: `Inserted notebook cell at index ${insertAt} in ${input.path}.`
1821
+ };
1822
+ }
1823
+ if (index < 0 || index >= notebook.cells.length) {
1824
+ throw new Error('notebook_edit requires a valid "cell_index" or "cell_id".');
1825
+ }
1826
+ if (editMode === "delete") {
1827
+ notebook.cells.splice(index, 1);
1828
+ return {
1829
+ updated: `${JSON.stringify(notebook, null, 2)}
1830
+ `,
1831
+ summary: `Deleted notebook cell ${index} in ${input.path}.`
1832
+ };
1833
+ }
1834
+ if (typeof input.new_source !== "string") {
1835
+ throw new Error('notebook_edit replace requires "new_source".');
1836
+ }
1837
+ const target = notebook.cells[index];
1838
+ target.source = input.new_source;
1839
+ if (input.cell_type) {
1840
+ target.cell_type = input.cell_type;
1841
+ }
1842
+ return {
1843
+ updated: `${JSON.stringify(notebook, null, 2)}
1844
+ `,
1845
+ summary: `Updated notebook cell ${index} in ${input.path}.`
1846
+ };
1847
+ }
1848
+
1816
1849
  // src/core/customCommands.ts
1817
1850
  import fs4 from "fs-extra";
1818
1851
  import path5 from "path";
@@ -2190,16 +2223,245 @@ async function webRepo(options) {
2190
2223
  }
2191
2224
  }
2192
2225
 
2226
+ // src/actions/projectTracker.ts
2227
+ import { execFile } from "child_process";
2228
+ var ISSUE_LIST_FIELDS = "number,title,state,assignees,labels,createdAt,url";
2229
+ var ISSUE_VIEW_FIELDS = "number,title,state,body,assignees,labels,comments,createdAt,milestone,author,url";
2230
+ var PR_LIST_FIELDS = "number,title,state,author,baseRefName,headRefName,labels,createdAt,isDraft,url";
2231
+ var PR_VIEW_FIELDS = "number,title,state,body,author,baseRefName,headRefName,labels,comments,latestReviews,statusCheckRollup,mergeable,additions,deletions,createdAt,isDraft,url";
2232
+ async function runGh(args) {
2233
+ return new Promise((resolve, reject) => {
2234
+ execFile("gh", args, {
2235
+ timeout: 3e4,
2236
+ maxBuffer: 5 * 1024 * 1024
2237
+ // 5MB
2238
+ }, (err, stdout, stderr) => {
2239
+ if (!err) {
2240
+ resolve(stdout);
2241
+ return;
2242
+ }
2243
+ const error = err;
2244
+ if (error.code === "ENOENT" || error.message?.includes("command not found")) {
2245
+ reject(new Error("gh CLI is not installed. Install it from https://cli.github.com"));
2246
+ return;
2247
+ }
2248
+ const errMsg = stderr || error.message || "Unknown error";
2249
+ if (errMsg.includes("auth login") || errMsg.includes("not logged")) {
2250
+ reject(new Error("gh CLI is not authenticated. Run 'gh auth login' first."));
2251
+ return;
2252
+ }
2253
+ reject(new Error(`gh command failed: ${errMsg.trim()}`));
2254
+ });
2255
+ });
2256
+ }
2257
+ async function projectTracker(action) {
2258
+ if (action.action === "get_issue" || action.action === "get_pr") {
2259
+ if (action.number == null) {
2260
+ return `Error: The 'number' parameter is required for ${action.action}`;
2261
+ }
2262
+ if (!Number.isInteger(action.number) || action.number <= 0) {
2263
+ return `Error: The 'number' parameter must be a positive integer`;
2264
+ }
2265
+ }
2266
+ if (action.state === "merged" && action.action === "list_issues") {
2267
+ return `Error: The 'merged' state is only valid for list_prs`;
2268
+ }
2269
+ try {
2270
+ switch (action.action) {
2271
+ case "list_issues":
2272
+ return await listIssues(action);
2273
+ case "get_issue":
2274
+ return await getIssue(action);
2275
+ case "list_prs":
2276
+ return await listPrs(action);
2277
+ case "get_pr":
2278
+ return await getPr(action);
2279
+ case "get_user":
2280
+ return await getUser();
2281
+ default:
2282
+ return `Error: Unknown action: ${action.action}. Valid actions: list_issues, get_issue, list_prs, get_pr, get_user`;
2283
+ }
2284
+ } catch (err) {
2285
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
2286
+ }
2287
+ }
2288
+ async function listIssues(action) {
2289
+ const args = ["issue", "list", "--json", ISSUE_LIST_FIELDS];
2290
+ args.push("--limit", String(action.limit ?? 20));
2291
+ if (action.state) args.push("--state", action.state);
2292
+ if (action.assignee) args.push("--assignee", action.assignee);
2293
+ if (action.labels) args.push("--label", action.labels);
2294
+ if (action.repo) args.push("-R", action.repo);
2295
+ return runGh(args);
2296
+ }
2297
+ async function getIssue(action) {
2298
+ const args = ["issue", "view", String(action.number), "--json", ISSUE_VIEW_FIELDS];
2299
+ if (action.repo) args.push("-R", action.repo);
2300
+ return runGh(args);
2301
+ }
2302
+ async function listPrs(action) {
2303
+ const args = ["pr", "list", "--json", PR_LIST_FIELDS];
2304
+ args.push("--limit", String(action.limit ?? 20));
2305
+ if (action.state) args.push("--state", action.state);
2306
+ if (action.author) args.push("--author", action.author);
2307
+ if (action.base) args.push("--base", action.base);
2308
+ if (action.labels) args.push("--label", action.labels);
2309
+ if (action.repo) args.push("-R", action.repo);
2310
+ return runGh(args);
2311
+ }
2312
+ async function getPr(action) {
2313
+ const args = ["pr", "view", String(action.number), "--json", PR_VIEW_FIELDS];
2314
+ if (action.repo) args.push("-R", action.repo);
2315
+ return runGh(args);
2316
+ }
2317
+ async function getUser() {
2318
+ try {
2319
+ const stdout = await runGh(["api", "user", "--jq", ".login"]);
2320
+ return `Authenticated as: ${stdout.trim()}`;
2321
+ } catch (err) {
2322
+ const msg = err instanceof Error ? err.message : String(err);
2323
+ if (msg.includes("not installed") || msg.includes("not authenticated")) {
2324
+ throw err;
2325
+ }
2326
+ throw new Error("Failed to get GitHub user. Ensure gh is authenticated: run 'gh auth status'");
2327
+ }
2328
+ }
2329
+
2193
2330
  // src/core/toolsRegistry.ts
2194
2331
  import fs5 from "fs-extra";
2332
+ import nodeFs from "fs/promises";
2195
2333
  import path6 from "path";
2334
+
2335
+ // src/core/metaTools/schema.ts
2336
+ import { createHash } from "crypto";
2337
+ import { z } from "zod";
2338
+ var META_TOOL_SCHEMA_VERSION = 1;
2339
+ var META_TOOL_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
2340
+ var META_TOOL_SCOPES = ["user", "project"];
2341
+ var JsonSchemaObject = z.record(z.string(), z.unknown()).refine((value) => value.type === "object", 'parameters must be a JSON Schema object with type "object"');
2342
+ var MetaToolCreateInputSchema = z.object({
2343
+ name: z.string().trim().regex(META_TOOL_NAME_PATTERN, "name must be snake_case and start with a lowercase letter"),
2344
+ description: z.string().trim().min(1).max(300),
2345
+ parameters: JsonSchemaObject,
2346
+ handler: z.string().trim().min(1).max(2e3),
2347
+ source: z.enum(["agent", "user"]).default("agent"),
2348
+ scope: z.enum(META_TOOL_SCOPES).default("user")
2349
+ });
2350
+ var MetaToolDefinitionSchema = MetaToolCreateInputSchema.extend({
2351
+ schemaVersion: z.literal(META_TOOL_SCHEMA_VERSION),
2352
+ createdAt: z.string().min(1),
2353
+ updatedAt: z.string().min(1).optional(),
2354
+ fingerprint: z.string().min(16),
2355
+ disabled: z.boolean().optional()
2356
+ });
2357
+ function canonicalize(value) {
2358
+ if (Array.isArray(value)) {
2359
+ return `[${value.map(canonicalize).join(",")}]`;
2360
+ }
2361
+ if (value && typeof value === "object") {
2362
+ const record = value;
2363
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`).join(",")}}`;
2364
+ }
2365
+ return JSON.stringify(value);
2366
+ }
2367
+ function fingerprintMetaTool(input) {
2368
+ return createHash("sha256").update(canonicalize({
2369
+ name: input.name,
2370
+ description: input.description,
2371
+ parameters: input.parameters,
2372
+ handler: input.handler
2373
+ })).digest("hex");
2374
+ }
2375
+ function normalizeMetaToolDefinition(candidate) {
2376
+ if (!candidate || typeof candidate !== "object") {
2377
+ return null;
2378
+ }
2379
+ const value = candidate;
2380
+ const source = value.source === "user" ? "user" : "agent";
2381
+ const scope = value.scope === "project" ? "project" : "user";
2382
+ const definition = {
2383
+ ...value,
2384
+ schemaVersion: value.schemaVersion ?? META_TOOL_SCHEMA_VERSION,
2385
+ source,
2386
+ scope,
2387
+ createdAt: typeof value.createdAt === "string" ? value.createdAt : (/* @__PURE__ */ new Date(0)).toISOString()
2388
+ };
2389
+ const parsedCreateInput = MetaToolCreateInputSchema.safeParse(definition);
2390
+ if (!parsedCreateInput.success) {
2391
+ return null;
2392
+ }
2393
+ const parsed = MetaToolDefinitionSchema.safeParse({
2394
+ ...definition,
2395
+ fingerprint: typeof value.fingerprint === "string" ? value.fingerprint : fingerprintMetaTool(parsedCreateInput.data)
2396
+ });
2397
+ return parsed.success ? parsed.data : null;
2398
+ }
2399
+
2400
+ // src/core/metaTools/safety.ts
2401
+ var DANGEROUS_PATTERNS = [
2402
+ { pattern: /rm\s+(-[rf]+\s+)*\/(?!\w)/i, description: "rm with root path" },
2403
+ { pattern: /rm\s+.*--no-preserve-root/i, description: "rm --no-preserve-root" },
2404
+ { pattern: /dd\s+.*(?:of|if)=\/dev\/[sh]d/i, description: "dd to disk device" },
2405
+ { pattern: /mkfs\./i, description: "filesystem format" },
2406
+ { pattern: /wipefs/i, description: "disk wipe" },
2407
+ { pattern: /\bsudo\s/i, description: "sudo command" },
2408
+ { pattern: /\bsu\s+-?\s*\w/i, description: "su command" },
2409
+ { pattern: /chmod\s+[0-7]*7[0-7]*/i, description: "world-writable chmod" },
2410
+ { pattern: /chown\s+root/i, description: "chown to root" },
2411
+ { pattern: /curl\s+.*\|\s*(ba)?sh/i, description: "curl | bash" },
2412
+ { pattern: /wget\s+.*\|\s*(ba)?sh/i, description: "wget | sh" },
2413
+ { pattern: /\beval\s+[`$]/i, description: "eval with expansion" },
2414
+ { pattern: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/i, description: "fork bomb" },
2415
+ { pattern: /while\s+true.*do.*done/i, description: "infinite loop" },
2416
+ { pattern: /nc\s+.*-e\s*\/bin/i, description: "netcat reverse shell" },
2417
+ { pattern: /ncat\s+.*-e\s*\/bin/i, description: "ncat reverse shell" },
2418
+ { pattern: /bash\s+-i\s+>&?\s*\/dev\/tcp/i, description: "bash reverse shell" },
2419
+ { pattern: /iptables\s+-F/i, description: "flush firewall rules" },
2420
+ { pattern: /gpg\s+.*--encrypt.*-r\s+\S+\s+\//i, description: "gpg encrypt root" }
2421
+ ];
2422
+ function assertSafeMetaToolHandler(handler) {
2423
+ for (const { pattern, description } of DANGEROUS_PATTERNS) {
2424
+ if (pattern.test(handler)) {
2425
+ throw new Error(`Handler contains dangerous pattern: ${description}`);
2426
+ }
2427
+ }
2428
+ }
2429
+
2430
+ // src/core/toolsRegistry.ts
2431
+ function locationKey(scope, name) {
2432
+ return `${scope}:${name}`;
2433
+ }
2434
+ function normalizeLocations(input) {
2435
+ if (Array.isArray(input)) {
2436
+ return input;
2437
+ }
2438
+ return [{ scope: "user", dir: input ?? AUTOHAND_PATHS.tools }];
2439
+ }
2440
+ function delay(ms) {
2441
+ return new Promise((resolve) => setTimeout(resolve, ms));
2442
+ }
2443
+ function createToolsRegistry(workspaceRoot, userToolsDir = AUTOHAND_PATHS.tools) {
2444
+ const locations = workspaceRoot ? [
2445
+ { scope: "project", dir: path6.join(workspaceRoot, PROJECT_DIR_NAME, "tools") },
2446
+ { scope: "user", dir: userToolsDir }
2447
+ ] : [{ scope: "user", dir: userToolsDir }];
2448
+ return new ToolsRegistry(locations);
2449
+ }
2196
2450
  var ToolsRegistry = class {
2197
- constructor(toolsDir = AUTOHAND_PATHS.tools) {
2198
- this.toolsDir = toolsDir;
2199
- this.metaToolCache = /* @__PURE__ */ new Map();
2451
+ metaToolCache = /* @__PURE__ */ new Map();
2452
+ metaToolRecords = /* @__PURE__ */ new Map();
2453
+ diagnostics = [];
2454
+ constructor(locations) {
2455
+ this.locations = normalizeLocations(locations);
2200
2456
  }
2457
+ locations;
2201
2458
  async initialize() {
2202
- await fs5.ensureDir(this.toolsDir);
2459
+ this.metaToolCache.clear();
2460
+ this.metaToolRecords.clear();
2461
+ this.diagnostics = [];
2462
+ for (const location of this.locations) {
2463
+ await fs5.ensureDir(location.dir);
2464
+ }
2203
2465
  await this.loadMetaToolDefinitions();
2204
2466
  }
2205
2467
  async listTools(builtIns) {
@@ -2225,20 +2487,41 @@ var ToolsRegistry = class {
2225
2487
  entries.push({
2226
2488
  name: tool.name,
2227
2489
  description: tool.description,
2228
- source: "meta"
2490
+ source: "meta",
2491
+ scope: tool.scope,
2492
+ disabled: tool.disabled,
2493
+ createdAt: tool.createdAt,
2494
+ schemaVersion: tool.schemaVersion,
2495
+ handlerPreview: tool.handler.length > 140 ? `${tool.handler.slice(0, 137)}...` : tool.handler,
2496
+ reuseHint: `Use ${tool.name} instead of creating another tool for: ${tool.description}`
2229
2497
  });
2230
2498
  seen.add(name);
2231
2499
  }
2232
2500
  return entries;
2233
2501
  }
2234
2502
  async saveMetaTool(definition) {
2235
- const fullDef = {
2236
- ...definition,
2237
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
2238
- };
2239
- const filePath = path6.join(this.toolsDir, `${definition.name}.json`);
2240
- await fs5.writeJson(filePath, fullDef, { spaces: 2 });
2241
- this.metaToolCache.set(definition.name, fullDef);
2503
+ const fullDef = normalizeMetaToolDefinition(definition);
2504
+ if (!fullDef) {
2505
+ throw new Error(`Invalid meta-tool definition for "${definition.name}"`);
2506
+ }
2507
+ assertSafeMetaToolHandler(fullDef.handler);
2508
+ const location = this.getLocationForScope(fullDef.scope);
2509
+ const filePath = path6.join(location.dir, `${fullDef.name}.json`);
2510
+ const release = await this.acquireLock(location.dir, fullDef.name);
2511
+ try {
2512
+ const existing = await this.readDefinition(filePath);
2513
+ if (existing) {
2514
+ if (existing.fingerprint === fullDef.fingerprint) {
2515
+ this.upsertRecord(existing, filePath);
2516
+ return existing;
2517
+ }
2518
+ throw new Error(`Meta-tool "${fullDef.name}" already exists in ${fullDef.scope} scope`);
2519
+ }
2520
+ await this.writeDefinition(filePath, fullDef);
2521
+ } finally {
2522
+ await release();
2523
+ }
2524
+ this.upsertRecord(fullDef, filePath);
2242
2525
  return fullDef;
2243
2526
  }
2244
2527
  getMetaTool(name) {
@@ -2250,6 +2533,71 @@ var ToolsRegistry = class {
2250
2533
  getAllMetaTools() {
2251
2534
  return Array.from(this.metaToolCache.values());
2252
2535
  }
2536
+ listMetaTools(options = {}) {
2537
+ if (!options.includeDisabled) {
2538
+ return this.getAllMetaTools();
2539
+ }
2540
+ return Array.from(this.metaToolRecords.values()).map((record) => record.definition);
2541
+ }
2542
+ getDiagnostics() {
2543
+ return [...this.diagnostics];
2544
+ }
2545
+ async deleteMetaTool(name, scope) {
2546
+ const record = this.findRecord(name, scope);
2547
+ if (!record) {
2548
+ throw new Error(`Meta-tool "${name}" not found`);
2549
+ }
2550
+ await fs5.remove(record.filePath);
2551
+ this.deleteRecord(record.definition);
2552
+ return record.definition;
2553
+ }
2554
+ async setMetaToolDisabled(name, disabled, scope) {
2555
+ const record = this.findRecord(name, scope);
2556
+ if (!record) {
2557
+ throw new Error(`Meta-tool "${name}" not found`);
2558
+ }
2559
+ const updated = {
2560
+ ...record.definition,
2561
+ disabled,
2562
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2563
+ };
2564
+ await this.writeDefinition(record.filePath, updated);
2565
+ this.upsertRecord(updated, record.filePath);
2566
+ this.rebuildActiveCache();
2567
+ return updated;
2568
+ }
2569
+ async renameMetaTool(name, newName, scope) {
2570
+ if (!META_TOOL_NAME_PATTERN.test(newName)) {
2571
+ throw new Error("new name must be snake_case and start with a lowercase letter");
2572
+ }
2573
+ const record = this.findRecord(name, scope);
2574
+ if (!record) {
2575
+ throw new Error(`Meta-tool "${name}" not found`);
2576
+ }
2577
+ if (this.findRecord(newName)) {
2578
+ throw new Error(`Meta-tool "${newName}" already exists`);
2579
+ }
2580
+ const renamed = {
2581
+ ...record.definition,
2582
+ name: newName,
2583
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2584
+ };
2585
+ const normalized = normalizeMetaToolDefinition({
2586
+ ...renamed,
2587
+ fingerprint: fingerprintMetaTool(renamed)
2588
+ });
2589
+ if (!normalized) {
2590
+ throw new Error(`Invalid meta-tool definition for "${newName}"`);
2591
+ }
2592
+ const location = this.getLocationForScope(normalized.scope);
2593
+ const nextFilePath = path6.join(location.dir, `${newName}.json`);
2594
+ await this.writeDefinition(nextFilePath, normalized);
2595
+ await fs5.remove(record.filePath);
2596
+ this.deleteRecord(record.definition);
2597
+ this.upsertRecord(normalized, nextFilePath);
2598
+ this.rebuildActiveCache();
2599
+ return normalized;
2600
+ }
2253
2601
  toToolDefinitions() {
2254
2602
  return this.getAllMetaTools().map((tool) => {
2255
2603
  const params = tool.parameters;
@@ -2265,163 +2613,375 @@ var ToolsRegistry = class {
2265
2613
  });
2266
2614
  }
2267
2615
  async loadMetaToolDefinitions() {
2268
- try {
2269
- const exists = await fs5.pathExists(this.toolsDir);
2270
- if (!exists) {
2271
- return;
2272
- }
2273
- const files = await fs5.readdir(this.toolsDir);
2274
- for (const file of files) {
2275
- if (!file.endsWith(".json")) {
2616
+ for (const location of this.locations) {
2617
+ try {
2618
+ const exists = await fs5.pathExists(location.dir);
2619
+ if (!exists) {
2276
2620
  continue;
2277
2621
  }
2278
- const fullPath = path6.join(this.toolsDir, file);
2279
- try {
2280
- const data = await fs5.readJson(fullPath);
2281
- if (this.isValidMetaTool(data)) {
2282
- this.metaToolCache.set(data.name, data);
2622
+ const files = await fs5.readdir(location.dir);
2623
+ for (const file of files) {
2624
+ if (!file.endsWith(".json")) {
2625
+ continue;
2626
+ }
2627
+ const fullPath = path6.join(location.dir, file);
2628
+ try {
2629
+ const data = normalizeMetaToolDefinition({
2630
+ ...await fs5.readJson(fullPath),
2631
+ scope: location.scope
2632
+ });
2633
+ if (data) {
2634
+ assertSafeMetaToolHandler(data.handler);
2635
+ this.metaToolRecords.set(locationKey(data.scope, data.name), { definition: data, filePath: fullPath });
2636
+ } else {
2637
+ this.diagnostics.push({ file: fullPath, reason: "invalid meta-tool definition" });
2638
+ }
2639
+ } catch (error) {
2640
+ const reason = error instanceof Error ? error.message : "invalid meta-tool file";
2641
+ this.diagnostics.push({ file: fullPath, reason });
2283
2642
  }
2284
- } catch {
2285
2643
  }
2644
+ } catch (error) {
2645
+ const reason = error instanceof Error ? error.message : "tools directory could not be read";
2646
+ this.diagnostics.push({ file: location.dir, reason });
2286
2647
  }
2287
- } catch {
2288
2648
  }
2649
+ this.rebuildActiveCache();
2289
2650
  }
2290
- isValidMetaTool(candidate) {
2291
- if (!candidate || typeof candidate !== "object") {
2292
- return false;
2651
+ getLocationForScope(scope) {
2652
+ const location = this.locations.find((candidate) => candidate.scope === scope);
2653
+ if (!location) {
2654
+ throw new Error(`No tools directory configured for ${scope} scope`);
2655
+ }
2656
+ return location;
2657
+ }
2658
+ async readDefinition(filePath) {
2659
+ if (!await fs5.pathExists(filePath)) {
2660
+ return null;
2661
+ }
2662
+ return normalizeMetaToolDefinition(await fs5.readJson(filePath));
2663
+ }
2664
+ async writeDefinition(filePath, definition) {
2665
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
2666
+ try {
2667
+ await fs5.ensureDir(path6.dirname(filePath));
2668
+ await fs5.outputFile(tempPath, `${JSON.stringify(definition, null, 2)}
2669
+ `, { mode: 384 });
2670
+ const handle = await nodeFs.open(tempPath, "r");
2671
+ try {
2672
+ await handle.sync();
2673
+ } finally {
2674
+ await handle.close();
2675
+ }
2676
+ await nodeFs.rename(tempPath, filePath);
2677
+ } catch (error) {
2678
+ await fs5.remove(tempPath).catch(() => {
2679
+ });
2680
+ throw error;
2681
+ }
2682
+ }
2683
+ async acquireLock(dir, name) {
2684
+ await fs5.ensureDir(dir);
2685
+ const lockPath = path6.join(dir, `${name}.lock`);
2686
+ for (let attempt = 0; attempt < 40; attempt++) {
2687
+ try {
2688
+ const handle = await nodeFs.open(lockPath, "wx", 384);
2689
+ await handle.close();
2690
+ return async () => {
2691
+ await fs5.remove(lockPath).catch(() => {
2692
+ });
2693
+ };
2694
+ } catch (error) {
2695
+ const code = typeof error === "object" && error && "code" in error ? error.code : void 0;
2696
+ if (code === "EEXIST") {
2697
+ await delay(25);
2698
+ continue;
2699
+ }
2700
+ throw error;
2701
+ }
2702
+ }
2703
+ throw new Error(`Timed out waiting for meta-tool lock "${name}"`);
2704
+ }
2705
+ upsertRecord(definition, filePath) {
2706
+ this.metaToolRecords.set(locationKey(definition.scope, definition.name), { definition, filePath });
2707
+ this.rebuildActiveCache();
2708
+ }
2709
+ deleteRecord(definition) {
2710
+ this.metaToolRecords.delete(locationKey(definition.scope, definition.name));
2711
+ this.rebuildActiveCache();
2712
+ }
2713
+ findRecord(name, scope) {
2714
+ if (scope) {
2715
+ return this.metaToolRecords.get(locationKey(scope, name));
2716
+ }
2717
+ for (const location of this.locations) {
2718
+ const record = this.metaToolRecords.get(locationKey(location.scope, name));
2719
+ if (record) {
2720
+ return record;
2721
+ }
2722
+ }
2723
+ return void 0;
2724
+ }
2725
+ rebuildActiveCache() {
2726
+ this.metaToolCache.clear();
2727
+ for (const location of this.locations) {
2728
+ for (const record of this.metaToolRecords.values()) {
2729
+ if (record.definition.scope !== location.scope || record.definition.disabled) {
2730
+ continue;
2731
+ }
2732
+ if (!this.metaToolCache.has(record.definition.name)) {
2733
+ this.metaToolCache.set(record.definition.name, record.definition);
2734
+ }
2735
+ }
2293
2736
  }
2294
- const value = candidate;
2295
- return typeof value.name === "string" && typeof value.description === "string" && typeof value.handler === "string" && typeof value.parameters === "object";
2296
2737
  }
2297
2738
  };
2298
2739
 
2299
- // src/core/SecurityScanner.ts
2300
- var SecurityScanner = class {
2301
- constructor() {
2302
- /**
2303
- * Built-in secret detection patterns
2304
- */
2305
- this.patterns = [
2306
- // AWS
2307
- {
2308
- name: "AWS Access Key",
2309
- regex: /AKIA[0-9A-Z]{16}/,
2310
- severity: "high",
2311
- description: "AWS Access Key ID"
2312
- },
2313
- // GitHub
2314
- {
2315
- name: "GitHub Token",
2316
- regex: /ghp_[a-zA-Z0-9]{36}/,
2317
- severity: "high",
2318
- description: "GitHub Personal Access Token"
2319
- },
2320
- {
2321
- name: "GitHub OAuth",
2322
- regex: /gho_[a-zA-Z0-9]{36}/,
2323
- severity: "high",
2324
- description: "GitHub OAuth Token"
2325
- },
2326
- {
2327
- name: "GitHub App Token",
2328
- regex: /ghu_[a-zA-Z0-9]{36}/,
2329
- severity: "high",
2330
- description: "GitHub App User Token"
2331
- },
2332
- // OpenAI / Anthropic
2333
- {
2334
- name: "OpenAI Key",
2335
- regex: /sk-proj-[a-zA-Z0-9]{32,}/,
2336
- severity: "high",
2337
- description: "OpenAI Project API Key"
2338
- },
2339
- {
2340
- name: "Anthropic Key",
2341
- regex: /sk-ant-api[a-zA-Z0-9-]{32,}/,
2342
- severity: "high",
2343
- description: "Anthropic API Key"
2344
- },
2345
- // Google
2346
- {
2347
- name: "Google API Key",
2348
- regex: /AIzaSy[0-9A-Za-z-_]{33}/,
2349
- severity: "high",
2350
- description: "Google API Key"
2351
- },
2352
- // Stripe
2353
- {
2354
- name: "Stripe Live Key",
2355
- regex: /sk_live_[0-9a-zA-Z]{24,}/,
2356
- severity: "high",
2357
- description: "Stripe Live Secret Key"
2358
- },
2359
- {
2360
- name: "Stripe Test Key",
2361
- regex: /sk_test_[0-9a-zA-Z]{24,}/,
2362
- severity: "low",
2363
- description: "Stripe Test Secret Key"
2364
- },
2365
- // Private Keys
2366
- {
2367
- name: "Private Key",
2368
- regex: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
2369
- severity: "high",
2370
- description: "Private Key File"
2371
- },
2372
- // Database URLs with credentials
2373
- {
2374
- name: "Database URL",
2375
- regex: /(postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
2376
- severity: "high",
2377
- description: "Database URL with credentials"
2378
- },
2379
- // JWT Tokens
2380
- {
2381
- name: "JWT Token",
2382
- regex: /eyJ[a-zA-Z0-9]{10,}\.eyJ[a-zA-Z0-9]{10,}\.[a-zA-Z0-9_-]{10,}/,
2383
- severity: "medium",
2384
- description: "JSON Web Token"
2385
- },
2386
- // Generic patterns (lower priority)
2387
- {
2388
- name: "Generic API Key",
2389
- regex: /[aA][pP][iI][-_]?[kK][eE][yY]\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/,
2390
- severity: "medium",
2391
- description: "Generic API Key Assignment"
2392
- },
2393
- {
2394
- name: "Generic Secret",
2395
- regex: /[sS][eE][cC][rR][eE][tT]\s*[:=]\s*['"][^'"]{8,}['"]/,
2396
- severity: "medium",
2397
- description: "Generic Secret Assignment"
2398
- },
2399
- {
2400
- name: "Password Assignment",
2401
- regex: /[pP][aA][sS][sS][wW][oO][rR][dD]\s*[:=]\s*['"][^'"]{4,}['"]/,
2402
- severity: "medium",
2403
- description: "Password Assignment"
2740
+ // src/core/metaTools/MetaToolService.ts
2741
+ var STOP_WORDS = /* @__PURE__ */ new Set([
2742
+ "a",
2743
+ "an",
2744
+ "and",
2745
+ "by",
2746
+ "for",
2747
+ "from",
2748
+ "in",
2749
+ "of",
2750
+ "on",
2751
+ "the",
2752
+ "to",
2753
+ "with",
2754
+ "find",
2755
+ "search",
2756
+ "list",
2757
+ "get",
2758
+ "show",
2759
+ "analyze",
2760
+ "count",
2761
+ "run",
2762
+ "quick",
2763
+ "tool",
2764
+ "tools",
2765
+ "file",
2766
+ "files",
2767
+ "codebase",
2768
+ "workspace",
2769
+ "source",
2770
+ "across"
2771
+ ]);
2772
+ function normalizeHandler(handler) {
2773
+ return handler.trim().replace(/\s+/g, " ");
2774
+ }
2775
+ function tokenize2(value) {
2776
+ 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));
2777
+ return new Set(tokens);
2778
+ }
2779
+ function overlapRatio(left, right) {
2780
+ if (left.size === 0 || right.size === 0) {
2781
+ return 0;
2782
+ }
2783
+ let intersection = 0;
2784
+ for (const token of left) {
2785
+ if (right.has(token)) {
2786
+ intersection++;
2787
+ }
2788
+ }
2789
+ return intersection / Math.min(left.size, right.size);
2790
+ }
2791
+ function isSimilarTool(candidate, existing) {
2792
+ const candidateNameTokens = tokenize2(candidate.name);
2793
+ const existingNameTokens = tokenize2(existing.name);
2794
+ if (overlapRatio(candidateNameTokens, existingNameTokens) >= 0.75) {
2795
+ return true;
2796
+ }
2797
+ const candidateDescriptionTokens = tokenize2(candidate.description);
2798
+ const existingDescriptionTokens = tokenize2(existing.description ?? "");
2799
+ return overlapRatio(candidateDescriptionTokens, existingDescriptionTokens) >= 0.75;
2800
+ }
2801
+ var MetaToolService = class {
2802
+ constructor(registry) {
2803
+ this.registry = registry;
2804
+ }
2805
+ registry;
2806
+ async createMetaTool(input, registeredTools) {
2807
+ const parsed = MetaToolCreateInputSchema.safeParse(input);
2808
+ if (!parsed.success) {
2809
+ throw new Error(`Invalid meta-tool definition: ${parsed.error.issues[0]?.message ?? "unknown validation error"}`);
2810
+ }
2811
+ const definitionInput = parsed.data;
2812
+ assertSafeMetaToolHandler(definitionInput.handler);
2813
+ const fingerprint = fingerprintMetaTool(definitionInput);
2814
+ const existingByName = this.registry.getMetaTool(definitionInput.name);
2815
+ if (existingByName) {
2816
+ if (existingByName.fingerprint === fingerprint) {
2817
+ return {
2818
+ status: "existing",
2819
+ definition: existingByName,
2820
+ message: `Meta-tool "${definitionInput.name}" already exists with the same definition.`
2821
+ };
2404
2822
  }
2823
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": already exists with a different definition`);
2824
+ }
2825
+ const registeredNameConflict = registeredTools.find((tool) => tool.name === definitionInput.name);
2826
+ if (registeredNameConflict) {
2827
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": conflicts with existing tool`);
2828
+ }
2829
+ for (const existing of this.registry.getAllMetaTools()) {
2830
+ if (normalizeHandler(existing.handler) === normalizeHandler(definitionInput.handler)) {
2831
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": same handler already exists as "${existing.name}"`);
2832
+ }
2833
+ }
2834
+ const existingTools = [
2835
+ ...registeredTools,
2836
+ ...this.registry.getAllMetaTools().map((tool) => ({
2837
+ name: tool.name,
2838
+ description: tool.description
2839
+ }))
2405
2840
  ];
2406
- /**
2407
- * Placeholder patterns to ignore (false positives)
2408
- * These must be exact matches or specific patterns, not substrings
2409
- */
2410
- this.placeholderPatterns = [
2411
- /your[-_]?api[-_]?key/i,
2412
- /your[-_]?secret/i,
2413
- /\*\*\*/,
2414
- /\.\.\.$/,
2415
- /-example/i,
2416
- /placeholder/i,
2417
- /sk-xxx$/i,
2418
- /sk-your/i,
2419
- /<your[-_]?key[-_]?here>/i,
2420
- /\$\{[A-Z_]+\}/,
2421
- // Environment variable placeholders
2422
- /process\.env\./
2423
- ];
2841
+ const similar = existingTools.find((tool) => tool.name !== definitionInput.name && isSimilarTool(definitionInput, tool));
2842
+ if (similar) {
2843
+ throw new Error(`Cannot create meta-tool "${definitionInput.name}": similar existing tool "${similar.name}" should be reused`);
2844
+ }
2845
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2846
+ const saved = await this.registry.saveMetaTool({
2847
+ ...definitionInput,
2848
+ schemaVersion: 1,
2849
+ createdAt: now,
2850
+ updatedAt: now,
2851
+ fingerprint
2852
+ });
2853
+ return {
2854
+ status: "created",
2855
+ definition: saved,
2856
+ message: `Created meta-tool "${saved.name}" - available in this and future sessions`
2857
+ };
2424
2858
  }
2859
+ };
2860
+
2861
+ // src/core/SecurityScanner.ts
2862
+ var SecurityScanner = class {
2863
+ /**
2864
+ * Built-in secret detection patterns
2865
+ */
2866
+ patterns = [
2867
+ // AWS
2868
+ {
2869
+ name: "AWS Access Key",
2870
+ regex: /AKIA[0-9A-Z]{16}/,
2871
+ severity: "high",
2872
+ description: "AWS Access Key ID"
2873
+ },
2874
+ // GitHub
2875
+ {
2876
+ name: "GitHub Token",
2877
+ regex: /ghp_[a-zA-Z0-9]{36}/,
2878
+ severity: "high",
2879
+ description: "GitHub Personal Access Token"
2880
+ },
2881
+ {
2882
+ name: "GitHub OAuth",
2883
+ regex: /gho_[a-zA-Z0-9]{36}/,
2884
+ severity: "high",
2885
+ description: "GitHub OAuth Token"
2886
+ },
2887
+ {
2888
+ name: "GitHub App Token",
2889
+ regex: /ghu_[a-zA-Z0-9]{36}/,
2890
+ severity: "high",
2891
+ description: "GitHub App User Token"
2892
+ },
2893
+ // OpenAI / Anthropic
2894
+ {
2895
+ name: "OpenAI Key",
2896
+ regex: /sk-proj-[a-zA-Z0-9]{32,}/,
2897
+ severity: "high",
2898
+ description: "OpenAI Project API Key"
2899
+ },
2900
+ {
2901
+ name: "Anthropic Key",
2902
+ regex: /sk-ant-api[a-zA-Z0-9-]{32,}/,
2903
+ severity: "high",
2904
+ description: "Anthropic API Key"
2905
+ },
2906
+ // Google
2907
+ {
2908
+ name: "Google API Key",
2909
+ regex: /AIzaSy[0-9A-Za-z-_]{33}/,
2910
+ severity: "high",
2911
+ description: "Google API Key"
2912
+ },
2913
+ // Stripe
2914
+ {
2915
+ name: "Stripe Live Key",
2916
+ regex: /sk_live_[0-9a-zA-Z]{24,}/,
2917
+ severity: "high",
2918
+ description: "Stripe Live Secret Key"
2919
+ },
2920
+ {
2921
+ name: "Stripe Test Key",
2922
+ regex: /sk_test_[0-9a-zA-Z]{24,}/,
2923
+ severity: "low",
2924
+ description: "Stripe Test Secret Key"
2925
+ },
2926
+ // Private Keys
2927
+ {
2928
+ name: "Private Key",
2929
+ regex: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
2930
+ severity: "high",
2931
+ description: "Private Key File"
2932
+ },
2933
+ // Database URLs with credentials
2934
+ {
2935
+ name: "Database URL",
2936
+ regex: /(postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
2937
+ severity: "high",
2938
+ description: "Database URL with credentials"
2939
+ },
2940
+ // JWT Tokens
2941
+ {
2942
+ name: "JWT Token",
2943
+ regex: /eyJ[a-zA-Z0-9]{10,}\.eyJ[a-zA-Z0-9]{10,}\.[a-zA-Z0-9_-]{10,}/,
2944
+ severity: "medium",
2945
+ description: "JSON Web Token"
2946
+ },
2947
+ // Generic patterns (lower priority)
2948
+ {
2949
+ name: "Generic API Key",
2950
+ regex: /[aA][pP][iI][-_]?[kK][eE][yY]\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/,
2951
+ severity: "medium",
2952
+ description: "Generic API Key Assignment"
2953
+ },
2954
+ {
2955
+ name: "Generic Secret",
2956
+ regex: /[sS][eE][cC][rR][eE][tT]\s*[:=]\s*['"][^'"]{8,}['"]/,
2957
+ severity: "medium",
2958
+ description: "Generic Secret Assignment"
2959
+ },
2960
+ {
2961
+ name: "Password Assignment",
2962
+ regex: /[pP][aA][sS][sS][wW][oO][rR][dD]\s*[:=]\s*['"][^'"]{4,}['"]/,
2963
+ severity: "medium",
2964
+ description: "Password Assignment"
2965
+ }
2966
+ ];
2967
+ /**
2968
+ * Placeholder patterns to ignore (false positives)
2969
+ * These must be exact matches or specific patterns, not substrings
2970
+ */
2971
+ placeholderPatterns = [
2972
+ /your[-_]?api[-_]?key/i,
2973
+ /your[-_]?secret/i,
2974
+ /\*\*\*/,
2975
+ /\.\.\.$/,
2976
+ /-example/i,
2977
+ /placeholder/i,
2978
+ /sk-xxx$/i,
2979
+ /sk-your/i,
2980
+ /<your[-_]?key[-_]?here>/i,
2981
+ /\$\{[A-Z_]+\}/,
2982
+ // Environment variable placeholders
2983
+ /process\.env\./
2984
+ ];
2425
2985
  /**
2426
2986
  * Scan git diff for secrets
2427
2987
  * @param diff - Git diff output
@@ -2581,7 +3141,9 @@ var SecurityScanner = class {
2581
3141
  lines.push("");
2582
3142
  }
2583
3143
  if (result.blockedCount > 0) {
2584
- lines.push(`[BLOCKED] ${result.blockedCount} high-severity secrets found`);
3144
+ lines.push(
3145
+ `[BLOCKED] ${result.blockedCount} high-severity secrets found`
3146
+ );
2585
3147
  lines.push(" Remove secrets before committing.");
2586
3148
  lines.push(" Consider using environment variables instead.");
2587
3149
  } else if (result.warningCount > 0) {
@@ -2609,6 +3171,7 @@ import { execSync } from "child_process";
2609
3171
  import fs6 from "fs-extra";
2610
3172
  import path7 from "path";
2611
3173
  var PlanFileStorage = class {
3174
+ plansDir;
2612
3175
  constructor() {
2613
3176
  this.plansDir = AUTOHAND_PATHS.plans;
2614
3177
  }
@@ -2746,7 +3309,6 @@ import { randomUUID } from "crypto";
2746
3309
  var ActionExecutor = class _ActionExecutor {
2747
3310
  constructor(deps) {
2748
3311
  this.deps = deps;
2749
- this.searchCache = /* @__PURE__ */ new Map();
2750
3312
  this.runtime = deps.runtime;
2751
3313
  this.files = deps.files;
2752
3314
  this.resolveWorkspacePath = deps.resolveWorkspacePath;
@@ -2754,7 +3316,8 @@ var ActionExecutor = class _ActionExecutor {
2754
3316
  this.projectManager = deps.projectManager;
2755
3317
  this.sessionId = deps.sessionId;
2756
3318
  this.logExploration = deps.onExploration;
2757
- this.toolsRegistry = deps.toolsRegistry ?? new ToolsRegistry();
3319
+ this.toolsRegistry = deps.toolsRegistry ?? createToolsRegistry(deps.runtime.workspaceRoot);
3320
+ this.metaToolService = deps.metaToolService ?? new MetaToolService(this.toolsRegistry);
2758
3321
  this.getRegisteredTools = deps.getRegisteredTools ?? (() => []);
2759
3322
  this.permissionManager = deps.permissionManager ?? new PermissionManager(deps.runtime.config.permissions);
2760
3323
  this.memoryManager = deps.memoryManager;
@@ -2763,8 +3326,81 @@ var ActionExecutor = class _ActionExecutor {
2763
3326
  this.onAskFollowup = deps.onAskFollowup;
2764
3327
  this.onPlanCreated = deps.onPlanCreated;
2765
3328
  this.onPermissionRequest = deps.onPermissionRequest;
3329
+ this.onReviewHook = deps.onReviewHook;
3330
+ this.onModalPause = deps.onModalPause;
3331
+ this.onRequestDirectoryAccess = deps.onRequestDirectoryAccess;
3332
+ this.onLiveCommandStart = deps.onLiveCommandStart;
3333
+ this.onLiveCommandOutput = deps.onLiveCommandOutput;
3334
+ this.onLiveCommandRemove = deps.onLiveCommandRemove;
3335
+ this.onMetaToolCreated = deps.onMetaToolCreated;
2766
3336
  this.securityScanner = new SecurityScanner();
2767
3337
  }
3338
+ deps;
3339
+ runtime;
3340
+ files;
3341
+ resolveWorkspacePath;
3342
+ confirmDangerousAction;
3343
+ projectManager;
3344
+ sessionId;
3345
+ logExploration;
3346
+ toolsRegistry;
3347
+ metaToolService;
3348
+ getRegisteredTools;
3349
+ permissionManager;
3350
+ memoryManager;
3351
+ onToolOutput;
3352
+ onFileModified;
3353
+ onAskFollowup;
3354
+ onPlanCreated;
3355
+ onPermissionRequest;
3356
+ onReviewHook;
3357
+ onModalPause;
3358
+ onRequestDirectoryAccess;
3359
+ onLiveCommandStart;
3360
+ onLiveCommandOutput;
3361
+ onLiveCommandRemove;
3362
+ onMetaToolCreated;
3363
+ securityScanner;
3364
+ searchCache = /* @__PURE__ */ new Map();
3365
+ fffSearchProviderPromise = null;
3366
+ fffSearchWorkspaceRoot = null;
3367
+ fffSearchIdleTimer = null;
3368
+ static FFF_SEARCH_IDLE_TTL_MS = 6e4;
3369
+ async getFFFSearchProvider() {
3370
+ if (this.fffSearchIdleTimer) {
3371
+ clearTimeout(this.fffSearchIdleTimer);
3372
+ this.fffSearchIdleTimer = null;
3373
+ }
3374
+ const workspaceRoot = this.runtime.workspaceRoot;
3375
+ if (this.fffSearchProviderPromise && this.fffSearchWorkspaceRoot === workspaceRoot) {
3376
+ return this.fffSearchProviderPromise;
3377
+ }
3378
+ if (this.fffSearchProviderPromise) {
3379
+ this.fffSearchProviderPromise.then((provider) => provider.destroy()).catch(() => {
3380
+ });
3381
+ }
3382
+ const { FFFSearchProvider } = await import("./fffSearchProvider-2YCNKOYD.js");
3383
+ this.fffSearchWorkspaceRoot = workspaceRoot;
3384
+ this.fffSearchProviderPromise = FFFSearchProvider.create(workspaceRoot);
3385
+ return this.fffSearchProviderPromise;
3386
+ }
3387
+ scheduleFFFSearchProviderCleanup() {
3388
+ if (!this.fffSearchProviderPromise) {
3389
+ return;
3390
+ }
3391
+ if (this.fffSearchIdleTimer) {
3392
+ clearTimeout(this.fffSearchIdleTimer);
3393
+ }
3394
+ this.fffSearchIdleTimer = setTimeout(() => {
3395
+ const providerPromise = this.fffSearchProviderPromise;
3396
+ this.fffSearchProviderPromise = null;
3397
+ this.fffSearchWorkspaceRoot = null;
3398
+ this.fffSearchIdleTimer = null;
3399
+ providerPromise?.then((provider) => provider.destroy()).catch(() => {
3400
+ });
3401
+ }, _ActionExecutor.FFF_SEARCH_IDLE_TTL_MS);
3402
+ this.fffSearchIdleTimer.unref?.();
3403
+ }
2768
3404
  /**
2769
3405
  * Check permission hooks before prompting user.
2770
3406
  * Returns true if allowed, false if denied/blocked, undefined if should ask user.
@@ -2790,7 +3426,7 @@ var ActionExecutor = class _ActionExecutor {
2790
3426
  }
2791
3427
  }
2792
3428
  async execute(action, context) {
2793
- if (this.runtime.options.dryRun && action.type !== "search" && action.type !== "plan") {
3429
+ if (this.runtime.options.dryRun && !["fff_grep", "fff_find", "find", "search", "search_with_context", "semantic_search", "glob", "plan"].includes(action.type)) {
2794
3430
  return "Dry-run mode: skipped mutation";
2795
3431
  }
2796
3432
  switch (action.type) {
@@ -2985,11 +3621,15 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
2985
3621
  const receivedKeys = Object.keys(action).filter((k) => k !== "type").join(", ") || "none";
2986
3622
  throw new Error(`write_file requires a "path" argument. Received arguments: [${receivedKeys}]`);
2987
3623
  }
3624
+ if (action.contents === void 0 && action.content === void 0) {
3625
+ return 'Error: write_file requires "contents" argument.';
3626
+ }
2988
3627
  const filePath = this.resolveWorkspacePath(action.path);
2989
3628
  const fs7 = await import("fs-extra");
2990
3629
  const exists = this.files.root && await fs7.pathExists(filePath);
2991
3630
  const oldContent = exists ? await this.files.readFile(action.path) : "";
2992
3631
  const newContent = this.pickText(action.contents, action.content) ?? "";
3632
+ let resultOutput = null;
2993
3633
  if (!exists) {
2994
3634
  const permContext = {
2995
3635
  tool: "write_file",
@@ -3035,16 +3675,18 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
3035
3675
  }
3036
3676
  }
3037
3677
  }
3678
+ resultOutput = this.formatDiffPreview("", newContent, action.path);
3038
3679
  } else if (oldContent === newContent) {
3039
3680
  return `No changes needed for ${action.path} (content identical)`;
3040
3681
  } else {
3041
3682
  console.log(chalk2.cyan(`
3042
3683
  \u{1F4DD} ${action.path}:`));
3043
3684
  this.showDiff(oldContent, newContent, action.path);
3685
+ resultOutput = this.formatDiffPreview(oldContent, newContent, action.path);
3044
3686
  }
3045
3687
  await this.files.writeFile(action.path, newContent);
3046
- this.onFileModified?.(action.path);
3047
- return exists ? `Updated ${action.path}` : `Created ${action.path}`;
3688
+ this.onFileModified?.(action.path, exists ? "modify" : "create");
3689
+ return resultOutput ?? (exists ? `Updated ${action.path}` : `Created ${action.path}`);
3048
3690
  }
3049
3691
  case "append_file": {
3050
3692
  if (!action.path) {
@@ -3057,17 +3699,17 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
3057
3699
  \u{1F4DD} ${action.path}:`));
3058
3700
  this.showDiff(oldContent, newContent, action.path);
3059
3701
  await this.files.appendFile(action.path, addition);
3060
- this.onFileModified?.(action.path);
3061
- return `Appended to ${action.path}`;
3702
+ this.onFileModified?.(action.path, "modify");
3703
+ return this.formatDiffPreview(oldContent, newContent, action.path);
3062
3704
  }
3063
3705
  case "apply_patch": {
3064
3706
  if (!action.path) {
3065
- throw new Error('apply_patch requires a "path" argument.');
3707
+ return 'Error: apply_patch requires a "path" argument.';
3066
3708
  }
3067
3709
  const oldContent = await this.files.readFile(action.path).catch(() => "");
3068
3710
  const patch = this.pickText(action.patch, action.diff);
3069
3711
  if (!patch) {
3070
- throw new Error("apply_patch requires patch or diff content.");
3712
+ return 'Error: apply_patch requires a "patch" argument.';
3071
3713
  }
3072
3714
  console.log(chalk2.cyan(`
3073
3715
  \u{1F527} ${action.path}:`));
@@ -3075,58 +3717,60 @@ ${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
3075
3717
  await this.files.applyPatch(action.path, patch);
3076
3718
  const newContent = await this.files.readFile(action.path);
3077
3719
  this.showDiff(oldContent, newContent, action.path);
3078
- this.onFileModified?.(action.path);
3079
- return `Patched ${action.path}`;
3720
+ this.onFileModified?.(action.path, "modify");
3721
+ return this.formatDiffPreview(oldContent, newContent, action.path);
3722
+ }
3723
+ case "notebook_edit": {
3724
+ if (!action.path) {
3725
+ throw new Error('notebook_edit requires a "path" argument.');
3726
+ }
3727
+ const current = await this.files.readFile(action.path);
3728
+ const { updated, summary } = applyNotebookEdit(current, action);
3729
+ await this.files.writeFile(action.path, updated);
3730
+ this.onFileModified?.(action.path, "modify");
3731
+ return summary;
3080
3732
  }
3081
3733
  case "tools_registry": {
3082
3734
  const tools = await this.toolsRegistry.listTools(this.getRegisteredTools());
3083
3735
  return JSON.stringify(tools, null, 2);
3084
3736
  }
3085
- case "search": {
3086
- const cacheKey = `search:${action.query}:${action.path || ""}`;
3087
- if (this.searchCache.has(cacheKey)) {
3088
- return `[Cached] ${this.searchCache.get(cacheKey)}`;
3089
- }
3090
- const hits = this.files.search(action.query, action.path);
3091
- this.recordExploration("search", action.query);
3092
- const result = hits.slice(0, 10).map((hit) => `${hit.file}:${hit.line}: ${hit.text}`).join("\n");
3093
- this.searchCache.set(cacheKey, result);
3094
- return result;
3095
- }
3096
- case "search_with_context": {
3097
- const cacheKey = `search_ctx:${action.query}:${action.path || ""}:${action.limit || ""}:${action.context || ""}`;
3098
- if (this.searchCache.has(cacheKey)) {
3099
- return `[Cached] ${this.searchCache.get(cacheKey)}`;
3737
+ case "tool_search": {
3738
+ const query = action.query?.trim();
3739
+ if (!query) {
3740
+ throw new Error('tool_search requires a non-empty "query" argument.');
3100
3741
  }
3101
- this.recordExploration("search", action.query);
3102
- const result = this.files.searchWithContext(action.query, {
3103
- limit: action.limit,
3104
- context: action.context,
3105
- relativePath: action.path
3106
- });
3107
- this.searchCache.set(cacheKey, result);
3108
- return result;
3109
- }
3110
- case "semantic_search": {
3111
- const cacheKey = `semantic:${action.query}:${action.path || ""}:${action.limit || ""}:${action.window || ""}`;
3112
- if (this.searchCache.has(cacheKey)) {
3113
- return `[Cached] ${this.searchCache.get(cacheKey)}`;
3114
- }
3115
- const results = this.files.semanticSearch(action.query, {
3116
- limit: action.limit,
3117
- window: action.window,
3118
- relativePath: action.path
3119
- });
3120
- if (!results.length) {
3121
- this.searchCache.set(cacheKey, "No matches found.");
3122
- return "No matches found.";
3123
- }
3124
- const result = results.map((hit) => `${chalk2.cyan(hit.file)}
3125
- ${hit.snippet}`).join("\n\n");
3126
- this.searchCache.set(cacheKey, result);
3127
- return result;
3742
+ const limit = Math.max(1, action.limit ?? 10);
3743
+ const tools = await this.toolsRegistry.listTools(this.getRegisteredTools());
3744
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
3745
+ const scored = tools.map((tool) => {
3746
+ const haystack = `${tool.name} ${tool.description}`.toLowerCase();
3747
+ let score = 0;
3748
+ for (const term of terms) {
3749
+ if (tool.name.toLowerCase() === term) {
3750
+ score += 10;
3751
+ } else if (tool.name.toLowerCase().includes(term)) {
3752
+ score += 6;
3753
+ }
3754
+ if (haystack.includes(term)) {
3755
+ score += 2;
3756
+ }
3757
+ }
3758
+ return { tool, score };
3759
+ }).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);
3760
+ return JSON.stringify(scored, null, 2);
3128
3761
  }
3762
+ case "find":
3763
+ return this.executeFind(action);
3764
+ case "glob":
3765
+ return this.executeGlob(action);
3766
+ case "fff_grep":
3767
+ return this.executeFFFGrep(action);
3768
+ case "fff_find":
3769
+ return this.executeFFFFind(action);
3129
3770
  case "create_directory": {
3771
+ if (!action.path) {
3772
+ return 'Error: create_directory requires a "path" argument.';
3773
+ }
3130
3774
  await this.files.createDirectory(action.path);
3131
3775
  return `Created directory ${action.path}`;
3132
3776
  }
@@ -3141,14 +3785,24 @@ ${hit.snippet}`).join("\n\n");
3141
3785
  if (!confirmed) {
3142
3786
  return `Skipped deleting ${action.path}`;
3143
3787
  }
3788
+ const oldDeleteContent = await this.files.readFile(action.path).catch(() => null);
3144
3789
  await this.files.deletePath(action.path);
3145
- return `Deleted ${action.path}`;
3790
+ if (oldDeleteContent !== null) {
3791
+ console.log(chalk2.cyan(`
3792
+ \u{1F5D1}\uFE0F ${action.path}:`));
3793
+ this.showDiff(oldDeleteContent, "", action.path);
3794
+ this.onFileModified?.(action.path, "delete");
3795
+ return this.formatDiffPreview(oldDeleteContent, "", action.path);
3796
+ }
3797
+ this.onFileModified?.(action.path, "delete");
3798
+ return `Deleted directory ${action.path}`;
3146
3799
  }
3147
3800
  case "rename_path": {
3148
3801
  if (!action.from || !action.to) {
3149
3802
  throw new Error('rename_path requires "from" and "to" arguments.');
3150
3803
  }
3151
3804
  await this.files.renamePath(action.from, action.to);
3805
+ this.onFileModified?.(action.to, "create");
3152
3806
  return `Renamed ${action.from} -> ${action.to}`;
3153
3807
  }
3154
3808
  case "copy_path": {
@@ -3156,9 +3810,16 @@ ${hit.snippet}`).join("\n\n");
3156
3810
  throw new Error('copy_path requires "from" and "to" arguments.');
3157
3811
  }
3158
3812
  await this.files.copyPath(action.from, action.to);
3813
+ this.onFileModified?.(action.to, "create");
3159
3814
  return `Copied ${action.from} -> ${action.to}`;
3160
3815
  }
3161
3816
  case "search_replace": {
3817
+ if (!action.path) {
3818
+ return 'Error: search_replace requires a "path" argument.';
3819
+ }
3820
+ if (!action.blocks) {
3821
+ return 'Error: search_replace requires a "blocks" argument.';
3822
+ }
3162
3823
  const content = await this.files.readFile(action.path);
3163
3824
  const result = this.applySearchReplaceBlocks(content, action.blocks);
3164
3825
  if (content !== result) {
@@ -3166,16 +3827,26 @@ ${hit.snippet}`).join("\n\n");
3166
3827
  \u{1F504} ${action.path}:`));
3167
3828
  this.showDiff(content, result, action.path);
3168
3829
  await this.files.writeFile(action.path, result);
3169
- this.onFileModified?.(action.path);
3830
+ this.onFileModified?.(action.path, "modify");
3831
+ return this.formatDiffPreview(content, result, action.path);
3170
3832
  }
3171
- return `Updated ${action.path}`;
3833
+ return `No changes needed for ${action.path} (content identical)`;
3172
3834
  }
3173
3835
  case "format_file": {
3174
3836
  if (!action.path) {
3175
3837
  throw new Error('format_file requires a "path" argument.');
3176
3838
  }
3839
+ const oldFormatContent = await this.files.readFile(action.path).catch(() => "");
3177
3840
  await this.files.formatFile(action.path, (contents, file) => applyFormatter(action.formatter, contents, file));
3178
- return `Formatted ${action.path} (${action.formatter})`;
3841
+ const newFormatContent = await this.files.readFile(action.path).catch(() => "");
3842
+ if (oldFormatContent !== newFormatContent) {
3843
+ console.log(chalk2.cyan(`
3844
+ \u{1F3A8} ${action.path}:`));
3845
+ this.showDiff(oldFormatContent, newFormatContent, action.path);
3846
+ this.onFileModified?.(action.path, "modify");
3847
+ return this.formatDiffPreview(oldFormatContent, newFormatContent, action.path);
3848
+ }
3849
+ return `No changes needed (already formatted): ${action.path}`;
3179
3850
  }
3180
3851
  case "run_command": {
3181
3852
  if (!action.command || typeof action.command !== "string") {
@@ -3195,18 +3866,63 @@ ${hit.snippet}`).join("\n\n");
3195
3866
  data
3196
3867
  });
3197
3868
  };
3198
- const result = await runCommand(
3199
- action.command,
3200
- action.args ?? [],
3201
- this.runtime.workspaceRoot,
3202
- {
3203
- directory: action.directory,
3204
- background: action.background,
3205
- onStdout: (chunk) => emitOutput("stdout", chunk),
3206
- onStderr: (chunk) => emitOutput("stderr", chunk)
3207
- }
3208
- );
3209
3869
  const cmdStr = `${action.command} ${(action.args ?? []).join(" ")}`.trim();
3870
+ if (action.interactive) {
3871
+ const onModalPause = this.onModalPause;
3872
+ if (onModalPause) {
3873
+ return await onModalPause(async () => {
3874
+ let result2;
3875
+ try {
3876
+ result2 = await runCommand(
3877
+ cmdStr,
3878
+ [],
3879
+ this.runtime.workspaceRoot,
3880
+ {
3881
+ directory: action.directory,
3882
+ shell: true,
3883
+ interactive: true
3884
+ }
3885
+ );
3886
+ } catch (err) {
3887
+ const error = err;
3888
+ if (error.code === "ENOENT" || error.message.includes("Command not found")) {
3889
+ return `Error: Command not found: "${action.command}". Make sure it is installed and available on your PATH.`;
3890
+ }
3891
+ return `Error running "${cmdStr}": ${error.message}`;
3892
+ }
3893
+ const header2 = action.description ? `$ ${action.description}
3894
+ > ${cmdStr}` : `$ ${cmdStr}`;
3895
+ const dirInfo2 = action.directory ? `[dir: ${action.directory}]` : "";
3896
+ const parts2 = [dirInfo2 ? `${header2} ${dirInfo2}` : header2];
3897
+ if (result2.code !== 0) {
3898
+ parts2.push(`(exit code: ${result2.code})`);
3899
+ }
3900
+ return parts2.join("\n");
3901
+ });
3902
+ }
3903
+ }
3904
+ let result;
3905
+ const shellCmd = cmdStr;
3906
+ try {
3907
+ result = await runCommand(
3908
+ shellCmd,
3909
+ [],
3910
+ this.runtime.workspaceRoot,
3911
+ {
3912
+ directory: action.directory,
3913
+ background: action.background,
3914
+ shell: true,
3915
+ onStdout: (chunk) => emitOutput("stdout", chunk),
3916
+ onStderr: (chunk) => emitOutput("stderr", chunk)
3917
+ }
3918
+ );
3919
+ } catch (err) {
3920
+ const error = err;
3921
+ if (error.code === "ENOENT" || error.message.includes("Command not found")) {
3922
+ return `Error: Command not found: "${action.command}". Make sure it is installed and available on your PATH.`;
3923
+ }
3924
+ return `Error running "${cmdStr}": ${error.message}`;
3925
+ }
3210
3926
  const header = action.description ? `$ ${action.description}
3211
3927
  > ${cmdStr}` : `$ ${cmdStr}`;
3212
3928
  const dirInfo = action.directory ? `[dir: ${action.directory}]` : "";
@@ -3220,12 +3936,100 @@ ${hit.snippet}`).join("\n\n");
3220
3936
  }
3221
3937
  return parts.join("\n");
3222
3938
  }
3939
+ case "shell": {
3940
+ if (!action.command || typeof action.command !== "string") {
3941
+ return 'Error: shell requires a "command" argument (string)';
3942
+ }
3943
+ const cmdStr = `${action.command} ${(action.args ?? []).join(" ")}`.trim();
3944
+ const commandId = this.onLiveCommandStart?.(cmdStr);
3945
+ const hasLiveDisplay = Boolean(commandId);
3946
+ if (hasLiveDisplay) {
3947
+ const liveId = commandId;
3948
+ try {
3949
+ const result2 = await executeStreamingShellCommand(
3950
+ cmdStr,
3951
+ this.runtime.workspaceRoot,
3952
+ {
3953
+ onStdout: (chunk) => this.onLiveCommandOutput(liveId, "stdout", chunk),
3954
+ onStderr: (chunk) => this.onLiveCommandOutput(liveId, "stderr", chunk),
3955
+ preferPty: process.stdin.isTTY && process.stdout.isTTY,
3956
+ columns: process.stdout.columns,
3957
+ rows: process.stdout.rows,
3958
+ background: action.background
3959
+ }
3960
+ );
3961
+ this.onLiveCommandRemove(liveId);
3962
+ const header2 = action.description ? `$ ${action.description}
3963
+ > ${cmdStr}` : `$ ${cmdStr}`;
3964
+ const dirInfo2 = action.directory ? `[dir: ${action.directory}]` : "";
3965
+ const parts2 = [dirInfo2 ? `${header2} ${dirInfo2}` : header2];
3966
+ if (result2.output) parts2.push(result2.output);
3967
+ if (result2.error) parts2.push(result2.error);
3968
+ if (result2.backgroundPid) parts2.push(`[Background PID: ${result2.backgroundPid}]`);
3969
+ return parts2.join("\n");
3970
+ } catch (err) {
3971
+ this.onLiveCommandRemove(liveId);
3972
+ const error = err;
3973
+ return `Error running "${cmdStr}": ${error.message}`;
3974
+ }
3975
+ }
3976
+ let result;
3977
+ try {
3978
+ result = await runCommand(
3979
+ cmdStr,
3980
+ [],
3981
+ this.runtime.workspaceRoot,
3982
+ {
3983
+ directory: action.directory,
3984
+ shell: true,
3985
+ background: action.background
3986
+ }
3987
+ );
3988
+ } catch (err) {
3989
+ const error = err;
3990
+ if (error.code === "ENOENT" || error.message.includes("Command not found")) {
3991
+ return `Error: Command not found: "${action.command}". Make sure it is installed and available on your PATH.`;
3992
+ }
3993
+ return `Error running "${cmdStr}": ${error.message}`;
3994
+ }
3995
+ const header = action.description ? `$ ${action.description}
3996
+ > ${cmdStr}` : `$ ${cmdStr}`;
3997
+ const dirInfo = action.directory ? `[dir: ${action.directory}]` : "";
3998
+ const parts = [
3999
+ dirInfo ? `${header} ${dirInfo}` : header,
4000
+ result.stdout,
4001
+ result.stderr
4002
+ ].filter(Boolean);
4003
+ return parts.join("\n");
4004
+ }
3223
4005
  case "add_dependency": {
4006
+ const fseAdd = (await import("fs-extra")).default;
4007
+ const pkgPathAdd = `${this.runtime.workspaceRoot}/package.json`;
4008
+ const oldPkgAdd = await fseAdd.readFile(pkgPathAdd, "utf-8").catch(() => "");
3224
4009
  await addDependency(this.runtime.workspaceRoot, action.name, action.version, { dev: action.dev });
4010
+ const newPkgAdd = await fseAdd.readFile(pkgPathAdd, "utf-8").catch(() => "");
4011
+ if (oldPkgAdd !== newPkgAdd) {
4012
+ console.log(chalk2.cyan(`
4013
+ \u{1F4E6} package.json:`));
4014
+ this.showDiff(oldPkgAdd, newPkgAdd, "package.json");
4015
+ this.onFileModified?.("package.json", "modify");
4016
+ return this.formatDiffPreview(oldPkgAdd, newPkgAdd, "package.json");
4017
+ }
3225
4018
  return `Added dependency ${action.name}@${action.version}${action.dev ? " (dev)" : ""}`;
3226
4019
  }
3227
4020
  case "remove_dependency": {
4021
+ const fseRm = (await import("fs-extra")).default;
4022
+ const pkgPathRm = `${this.runtime.workspaceRoot}/package.json`;
4023
+ const oldPkgRm = await fseRm.readFile(pkgPathRm, "utf-8").catch(() => "");
3228
4024
  await removeDependency(this.runtime.workspaceRoot, action.name, { dev: action.dev });
4025
+ const newPkgRm = await fseRm.readFile(pkgPathRm, "utf-8").catch(() => "");
4026
+ if (oldPkgRm !== newPkgRm) {
4027
+ console.log(chalk2.cyan(`
4028
+ \u{1F4E6} package.json:`));
4029
+ this.showDiff(oldPkgRm, newPkgRm, "package.json");
4030
+ this.onFileModified?.("package.json", "modify");
4031
+ return this.formatDiffPreview(oldPkgRm, newPkgRm, "package.json");
4032
+ }
3229
4033
  return `Removed dependency ${action.name}${action.dev ? " (dev)" : ""}`;
3230
4034
  }
3231
4035
  case "list_tree": {
@@ -3254,11 +4058,7 @@ ${hit.snippet}`).join("\n\n");
3254
4058
  return `${action.algorithm ?? "sha256"} ${action.path}: ${sum}`;
3255
4059
  }
3256
4060
  case "git_diff": {
3257
- if (!action.path) {
3258
- throw new Error('git_diff requires a "path" argument.');
3259
- }
3260
- this.resolveWorkspacePath(action.path);
3261
- const rawDiff = diffFile(this.runtime.workspaceRoot, action.path);
4061
+ const rawDiff = action.path ? (this.resolveWorkspacePath(action.path), diffFile(this.runtime.workspaceRoot, action.path)) : diffWorkspace(this.runtime.workspaceRoot);
3262
4062
  return this.colorizeGitDiff(rawDiff);
3263
4063
  }
3264
4064
  case "git_checkout": {
@@ -3266,8 +4066,17 @@ ${hit.snippet}`).join("\n\n");
3266
4066
  throw new Error('git_checkout requires a "path" argument.');
3267
4067
  }
3268
4068
  this.resolveWorkspacePath(action.path);
4069
+ const oldCheckoutContent = await this.files.readFile(action.path).catch(() => "");
3269
4070
  checkoutFile(this.runtime.workspaceRoot, action.path);
3270
- return `Restored ${action.path} from git.`;
4071
+ const newCheckoutContent = await this.files.readFile(action.path).catch(() => "");
4072
+ if (oldCheckoutContent !== newCheckoutContent) {
4073
+ console.log(chalk2.cyan(`
4074
+ \u21A9\uFE0F ${action.path}:`));
4075
+ this.showDiff(oldCheckoutContent, newCheckoutContent, action.path);
4076
+ this.onFileModified?.(action.path, "modify");
4077
+ return this.formatDiffPreview(oldCheckoutContent, newCheckoutContent, action.path);
4078
+ }
4079
+ return `Restored ${action.path} from git (no changes).`;
3271
4080
  }
3272
4081
  case "git_status":
3273
4082
  return gitStatus(this.runtime.workspaceRoot);
@@ -3537,8 +4346,10 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3537
4346
  console.log(chalk2.cyan("Suggested commit message:"));
3538
4347
  console.log(chalk2.white(` ${commitMessage}`));
3539
4348
  console.log();
4349
+ const normalizedYolo = normalizeYoloInput(this.runtime.options.yolo);
4350
+ const yoloAllowsCommit = normalizedYolo && isToolAllowedByYolo("auto_commit", parseYoloPattern(normalizedYolo));
3540
4351
  const autoApproveCommit = Boolean(
3541
- this.runtime.options.yes || process.env.CI === "1" || process.env.AUTOHAND_NON_INTERACTIVE === "1"
4352
+ this.runtime.options.unrestricted || this.runtime.options.yes || yoloAllowsCommit || process.env.CI === "1" || process.env.AUTOHAND_NON_INTERACTIVE === "1"
3542
4353
  );
3543
4354
  if (autoApproveCommit) {
3544
4355
  console.log(chalk2.gray("Auto-commit approval enabled; committing without prompt."));
@@ -3553,26 +4364,36 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3553
4364
  return result2.message;
3554
4365
  }
3555
4366
  const options = [
3556
- { label: "Yes - commit with this message", value: "y" },
4367
+ { label: `Yes - commit with this message`, value: "y" },
3557
4368
  { label: "Edit - modify the message", value: "e" },
3558
4369
  { label: "No - cancel commit", value: "n" }
3559
4370
  ];
3560
- const modalResult = await showModal({
3561
- title: "Commit with this message?",
3562
- options
3563
- });
3564
- if (!modalResult || modalResult.value === "n") {
4371
+ const runModal = async () => {
4372
+ const modalResult = await showModal({
4373
+ title: `Commit with this message?
4374
+
4375
+ "${commitMessage}"`,
4376
+ options
4377
+ });
4378
+ if (!modalResult || modalResult.value === "n") {
4379
+ return { cancelled: true, editedMessage: null };
4380
+ }
4381
+ if (modalResult.value === "e") {
4382
+ const editedMessage = await showInput({
4383
+ title: "Enter commit message:",
4384
+ defaultValue: commitMessage
4385
+ });
4386
+ return { cancelled: false, editedMessage };
4387
+ }
4388
+ return { cancelled: false, editedMessage: null };
4389
+ };
4390
+ const modalOutcome = this.onModalPause ? await this.onModalPause(runModal) : await runModal();
4391
+ if (modalOutcome.cancelled) {
3565
4392
  console.log(chalk2.yellow("Commit cancelled."));
3566
4393
  return "Commit cancelled by user";
3567
4394
  }
3568
- if (modalResult.value === "e") {
3569
- const editedMessage = await showInput({
3570
- title: "Enter commit message:",
3571
- defaultValue: commitMessage
3572
- });
3573
- if (editedMessage) {
3574
- commitMessage = editedMessage;
3575
- }
4395
+ if (modalOutcome.editedMessage) {
4396
+ commitMessage = modalOutcome.editedMessage;
3576
4397
  }
3577
4398
  const result = executeAutoCommit(this.runtime.workspaceRoot, commitMessage, action.stage_all !== false);
3578
4399
  if (result.success) {
@@ -3606,6 +4427,12 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3606
4427
  case "custom_command":
3607
4428
  return this.executeCustomCommand(action);
3608
4429
  case "multi_file_edit": {
4430
+ if (!action.file_path) {
4431
+ return 'Error: multi_file_edit requires a "file_path" argument.';
4432
+ }
4433
+ if (!action.edits || !Array.isArray(action.edits)) {
4434
+ return 'Error: multi_file_edit requires an "edits" argument (array).';
4435
+ }
3609
4436
  const oldContent = await this.files.readFile(action.file_path);
3610
4437
  let newContent = oldContent;
3611
4438
  console.log(chalk2.cyan(`
@@ -3646,14 +4473,20 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3646
4473
  }
3647
4474
  }
3648
4475
  if (firstIndex === -1) {
3649
- console.log(chalk2.red(` \u2717 Edit ${i + 1}: Could not find text to replace`));
3650
- console.log(chalk2.gray(` Looking for (${edit.old_string.length} chars):`));
3651
- console.log(chalk2.gray(` "${edit.old_string.substring(0, 80)}${edit.old_string.length > 80 ? "..." : ""}"`));
3652
4476
  const similar = this.findSimilarText(newContent, edit.old_string);
3653
4477
  if (similar) {
3654
- console.log(chalk2.yellow(` Did you mean:`));
3655
- console.log(chalk2.yellow(` "${similar.substring(0, 80)}${similar.length > 80 ? "..." : ""}"`));
4478
+ const similarIndex = newContent.indexOf(similar);
4479
+ if (similarIndex !== -1) {
4480
+ newContent = newContent.substring(0, similarIndex) + edit.new_string + newContent.substring(similarIndex + similar.length);
4481
+ console.log(chalk2.yellow(` \u26A0 Edit ${i + 1}: Applied with fuzzy match (whitespace/indentation differed)`));
4482
+ console.log(chalk2.gray(` Original search: "${edit.old_string.substring(0, 60)}${edit.old_string.length > 60 ? "..." : ""}"`));
4483
+ console.log(chalk2.gray(` Matched: "${similar.substring(0, 60)}${similar.length > 60 ? "..." : ""}"`));
4484
+ continue;
4485
+ }
3656
4486
  }
4487
+ console.log(chalk2.red(` \u2717 Edit ${i + 1}: Could not find text to replace`));
4488
+ console.log(chalk2.gray(` Looking for (${edit.old_string.length} chars):`));
4489
+ console.log(chalk2.gray(` "${edit.old_string.substring(0, 80)}${edit.old_string.length > 80 ? "..." : ""}"`));
3657
4490
  if (edit.old_string.length < 100) {
3658
4491
  const nonAscii = edit.old_string.match(/[^\x20-\x7E\n\r\t]/g);
3659
4492
  if (nonAscii && nonAscii.length > 0) {
@@ -3669,9 +4502,10 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3669
4502
  if (oldContent !== newContent) {
3670
4503
  this.showDiff(oldContent, newContent, action.file_path);
3671
4504
  await this.files.writeFile(action.file_path, newContent);
3672
- this.onFileModified?.(action.file_path);
4505
+ this.onFileModified?.(action.file_path, "modify");
4506
+ return this.formatDiffPreview(oldContent, newContent, action.file_path);
3673
4507
  }
3674
- return `Applied ${action.edits.length} edit(s) to ${action.file_path}`;
4508
+ return `No changes needed for ${action.file_path} (content identical)`;
3675
4509
  }
3676
4510
  case "todo_write": {
3677
4511
  const todoPath = ".autohand/agents/tasks/todos.json";
@@ -3681,17 +4515,17 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3681
4515
  }
3682
4516
  const validTasks = action.tasks.filter((task) => {
3683
4517
  if (!task) return false;
3684
- const hasId = !!task.id;
3685
4518
  const hasContent = !!(task.content || task.title);
3686
- return hasId && hasContent;
4519
+ return hasContent;
3687
4520
  });
3688
- const normalizedTasks = validTasks.map((task) => {
4521
+ const normalizedTasks = validTasks.map((task, index) => {
3689
4522
  const content = task.content || task.title || "";
3690
4523
  const title = content;
3691
4524
  return {
3692
4525
  ...task,
3693
4526
  // Preserve extra properties like priority, tags, etc.
3694
- id: task.id,
4527
+ id: task.id || `task-${Date.now()}-${index}`,
4528
+ // Auto-generate id if missing
3695
4529
  title,
3696
4530
  content,
3697
4531
  // Keep original content field
@@ -3702,24 +4536,51 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3702
4536
  });
3703
4537
  const allTodos = normalizedTasks;
3704
4538
  await this.files.writeFile(todoPath, JSON.stringify(allTodos, null, 2));
3705
- console.log(chalk2.cyan("\n\u{1F4CB} Task Progress:"));
4539
+ this.onFileModified?.(todoPath, "modify");
3706
4540
  const total = allTodos.length;
3707
- const completed = allTodos.filter((t) => t.status === "completed").length;
4541
+ if (total === 0) {
4542
+ console.log(chalk2.dim("\n\u{1F4CB} Task list cleared"));
4543
+ console.log();
4544
+ return "Task list cleared (0 tasks)";
4545
+ }
4546
+ const completedTasks = allTodos.filter((t) => t.status === "completed");
3708
4547
  const inProgress = allTodos.filter((t) => t.status === "in_progress");
3709
- const pending = allTodos.filter((t) => t.status === "pending").length;
3710
- const percent = total > 0 ? Math.round(completed / total * 100) : 0;
4548
+ const pendingTasks = allTodos.filter((t) => t.status === "pending");
4549
+ const completed = completedTasks.length;
4550
+ const pending = pendingTasks.length;
4551
+ const percent = Math.round(completed / total * 100);
3711
4552
  const barWidth = 20;
3712
4553
  const filled = Math.round(barWidth * percent / 100);
3713
4554
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
3714
- console.log(` ${chalk2.green(bar)} ${percent}%`);
3715
- console.log(chalk2.gray(` ${completed} done \xB7 ${inProgress.length} in progress \xB7 ${pending} pending`));
4555
+ const titleOf = (task) => {
4556
+ const title = task.title ?? task.content;
4557
+ return typeof title === "string" && title.trim().length > 0 ? title : "Untitled task";
4558
+ };
4559
+ const outputLines = [
4560
+ chalk2.cyan("\n\u{1F4CB} Task Progress:"),
4561
+ ` ${chalk2.green(bar)} ${percent}%`,
4562
+ chalk2.gray(` ${completed} done \xB7 ${inProgress.length} in progress \xB7 ${pending} pending`)
4563
+ ];
4564
+ if (completedTasks.length > 0) {
4565
+ outputLines.push("", chalk2.green(" \u2705 Completed Tasks:"));
4566
+ for (const task of completedTasks) {
4567
+ outputLines.push(chalk2.green(` \u2713 ${titleOf(task)}`));
4568
+ }
4569
+ }
3716
4570
  if (inProgress.length > 0) {
3717
- console.log(chalk2.yellow("\n \u{1F504} Active Tasks:"));
4571
+ outputLines.push("", chalk2.yellow(" \u{1F504} Active Tasks:"));
3718
4572
  for (const task of inProgress) {
3719
- console.log(` \u2022 ${task.title || task.content}`);
4573
+ outputLines.push(chalk2.yellow(` \u2022 ${titleOf(task)}`));
3720
4574
  }
3721
4575
  }
3722
- console.log();
4576
+ if (pendingTasks.length > 0) {
4577
+ outputLines.push("", chalk2.cyan(" \u23F3 Pending Tasks:"));
4578
+ for (const task of pendingTasks) {
4579
+ outputLines.push(chalk2.dim(` \u25CB ${titleOf(task)}`));
4580
+ }
4581
+ }
4582
+ console.log(`${outputLines.join("\n")}
4583
+ `);
3723
4584
  return `Updated task list: ${percent}% complete (${completed}/${total})`;
3724
4585
  }
3725
4586
  case "save_memory": {
@@ -3747,58 +4608,21 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3747
4608
  return formatted;
3748
4609
  }
3749
4610
  case "create_meta_tool": {
3750
- if (!action.name || !action.description || !action.handler) {
3751
- throw new Error("create_meta_tool requires name, description, and handler");
3752
- }
3753
- const builtInNames = this.getRegisteredTools().map((t) => t.name);
3754
- if (builtInNames.includes(action.name)) {
3755
- throw new Error(`Cannot create meta-tool "${action.name}": conflicts with built-in tool`);
3756
- }
3757
- const dangerousPatterns = [
3758
- // Destructive file operations
3759
- { pattern: /rm\s+(-[rf]+\s+)*\/(?!\w)/i, description: "rm with root path" },
3760
- { pattern: /rm\s+.*--no-preserve-root/i, description: "rm --no-preserve-root" },
3761
- { pattern: /dd\s+.*(?:of|if)=\/dev\/[sh]d/i, description: "dd to disk device" },
3762
- { pattern: /mkfs\./i, description: "filesystem format" },
3763
- { pattern: /wipefs/i, description: "disk wipe" },
3764
- // Privilege escalation
3765
- { pattern: /\bsudo\s/i, description: "sudo command" },
3766
- { pattern: /\bsu\s+-?\s*\w/i, description: "su command" },
3767
- { pattern: /chmod\s+[0-7]*7[0-7]*/i, description: "world-writable chmod" },
3768
- { pattern: /chown\s+root/i, description: "chown to root" },
3769
- // Remote code execution
3770
- { pattern: /curl\s+.*\|\s*(ba)?sh/i, description: "curl | bash" },
3771
- { pattern: /wget\s+.*\|\s*(ba)?sh/i, description: "wget | sh" },
3772
- { pattern: /\beval\s+[`$]/i, description: "eval with expansion" },
3773
- // Fork bomb and resource exhaustion
3774
- { pattern: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/i, description: "fork bomb" },
3775
- { pattern: /while\s+true.*do.*done/i, description: "infinite loop" },
3776
- // Reverse shell indicators
3777
- { pattern: /nc\s+.*-e\s*\/bin/i, description: "netcat reverse shell" },
3778
- { pattern: /ncat\s+.*-e\s*\/bin/i, description: "ncat reverse shell" },
3779
- { pattern: /bash\s+-i\s+>&?\s*\/dev\/tcp/i, description: "bash reverse shell" },
3780
- // Dangerous network operations
3781
- { pattern: /iptables\s+-F/i, description: "flush firewall rules" },
3782
- // Crypto operations that could lock out user
3783
- { pattern: /gpg\s+.*--encrypt.*-r\s+\S+\s+\//i, description: "gpg encrypt root" }
3784
- ];
3785
- for (const { pattern, description } of dangerousPatterns) {
3786
- if (pattern.test(action.handler)) {
3787
- throw new Error(`Handler contains dangerous pattern: ${description}`);
3788
- }
3789
- }
3790
- await this.toolsRegistry.saveMetaTool({
4611
+ const result = await this.metaToolService.createMetaTool({
3791
4612
  name: action.name,
3792
4613
  description: action.description,
3793
4614
  parameters: action.parameters ?? { type: "object", properties: {} },
3794
4615
  handler: action.handler,
3795
- source: "agent"
3796
- });
4616
+ source: "agent",
4617
+ scope: action.scope ?? "user"
4618
+ }, this.getRegisteredTools());
4619
+ const metaTool = result.definition;
4620
+ this.onMetaToolCreated?.(metaTool);
3797
4621
  console.log(chalk2.green(`
3798
- \u{1F527} Created meta-tool: ${action.name}`));
4622
+ \u{1F527} ${result.status === "created" ? "Created" : "Reused"} meta-tool: ${metaTool.name}`));
3799
4623
  console.log(chalk2.gray(` ${action.description}`));
3800
4624
  console.log(chalk2.gray(` Handler: ${action.handler}`));
3801
- return `Created meta-tool "${action.name}" - available in this and future sessions`;
4625
+ return result.message;
3802
4626
  }
3803
4627
  // Web Search Operations
3804
4628
  case "web_search": {
@@ -3877,12 +4701,24 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
3877
4701
  console.log(chalk2.gray(previewResult + (formattedResult.length > 500 ? "\n ... (truncated)" : "")));
3878
4702
  return formattedResult;
3879
4703
  }
4704
+ // Project Tracker
4705
+ case "project_tracker": {
4706
+ if (!action.action) {
4707
+ throw new Error('project_tracker requires an "action" parameter.');
4708
+ }
4709
+ console.log(chalk2.cyan(`
4710
+ \u{1F50D} project_tracker: ${action.action}${action.number ? ` #${action.number}` : ""}...`));
4711
+ const trackerResult = await projectTracker(action);
4712
+ const trackerPreview = trackerResult.slice(0, 500);
4713
+ console.log(chalk2.gray(trackerPreview + (trackerResult.length > 500 ? "\n ... (truncated)" : "")));
4714
+ return trackerResult;
4715
+ }
3880
4716
  // Skills Discovery
3881
4717
  case "find_agent_skills": {
3882
4718
  const query = action.query ?? "";
3883
4719
  console.log(chalk2.cyan(`
3884
4720
  Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...`));
3885
- const { searchCommunitySkills } = await import("./skills-6OL4OSGA.js");
4721
+ const { searchCommunitySkills } = await import("./skills-MVIZNHT4.js");
3886
4722
  const result = await searchCommunitySkills(query, {
3887
4723
  category: action.category,
3888
4724
  limit: action.limit
@@ -3945,6 +4781,32 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
3945
4781
  return `<answer>${finalAnswer}</answer>`;
3946
4782
  }
3947
4783
  }
4784
+ // Code review tool
4785
+ // Directory access tool
4786
+ case "request_directory_access": {
4787
+ return this.executeRequestDirectoryAccess(action);
4788
+ }
4789
+ case "code_review": {
4790
+ return this.executeCodeReview(action);
4791
+ }
4792
+ // Browser tools — forwarded to Chrome extension via RPC
4793
+ case "browser_screenshot":
4794
+ case "browser_click":
4795
+ case "browser_type":
4796
+ case "browser_navigate":
4797
+ case "browser_scroll":
4798
+ case "browser_find_element":
4799
+ case "browser_press_key":
4800
+ case "browser_get_page_context":
4801
+ case "browser_get_element":
4802
+ case "browser_wait_for_element":
4803
+ case "browser_read_network":
4804
+ case "browser_read_console":
4805
+ case "browser_get_tabs":
4806
+ case "browser_get_tab_groups":
4807
+ case "browser_execute_js": {
4808
+ return this.executeBrowserTool(action);
4809
+ }
3948
4810
  default: {
3949
4811
  const actionType = action.type;
3950
4812
  const metaTool = this.toolsRegistry.getMetaTool(actionType);
@@ -3955,6 +4817,145 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
3955
4817
  }
3956
4818
  }
3957
4819
  }
4820
+ async executeBrowserTool(action) {
4821
+ const { type, ...params } = action;
4822
+ const toolName = type;
4823
+ const { invokeBrowserTool } = await import("./browserToolBridge-F5N66PE7.js");
4824
+ return invokeBrowserTool(toolName, params);
4825
+ }
4826
+ async executeRequestDirectoryAccess(action) {
4827
+ const path8 = await import("path");
4828
+ const fs7 = (await import("fs-extra")).default;
4829
+ const { checkWorkspaceSafety } = await import("./workspaceSafety-XOUMUBVB.js");
4830
+ const resolvedPath = path8.resolve(action.path);
4831
+ if (!await fs7.pathExists(resolvedPath)) {
4832
+ return `Error: Directory does not exist: ${resolvedPath}`;
4833
+ }
4834
+ const stats = await fs7.stat(resolvedPath);
4835
+ if (!stats.isDirectory()) {
4836
+ return `Error: Path is not a directory: ${resolvedPath}`;
4837
+ }
4838
+ const safetyResult = checkWorkspaceSafety(resolvedPath);
4839
+ if (!safetyResult.safe) {
4840
+ return `Error: Unsafe directory: ${resolvedPath}. ${safetyResult.reason}`;
4841
+ }
4842
+ const workspaceRoot = this.runtime.workspaceRoot;
4843
+ const additionalDirs = this.files.getAllowedDirectories();
4844
+ if (resolvedPath === workspaceRoot || additionalDirs.includes(resolvedPath)) {
4845
+ return `Directory is already accessible: ${resolvedPath}`;
4846
+ }
4847
+ const normalizedResolved = resolvedPath.endsWith(path8.sep) ? resolvedPath.slice(0, -1) : resolvedPath;
4848
+ const normalizedWorkspace = workspaceRoot.endsWith(path8.sep) ? workspaceRoot.slice(0, -1) : workspaceRoot;
4849
+ if (normalizedResolved.startsWith(normalizedWorkspace + path8.sep)) {
4850
+ return `Directory is already within workspace: ${resolvedPath}`;
4851
+ }
4852
+ for (const dir of additionalDirs) {
4853
+ const normalizedDir = dir.endsWith(path8.sep) ? dir.slice(0, -1) : dir;
4854
+ if (normalizedResolved.startsWith(normalizedDir + path8.sep) || normalizedResolved === normalizedDir) {
4855
+ return `Directory is already accessible: ${resolvedPath}`;
4856
+ }
4857
+ }
4858
+ if (this.onRequestDirectoryAccess) {
4859
+ const result = await this.onRequestDirectoryAccess(resolvedPath, action.reason);
4860
+ if (result) {
4861
+ this.files.addAdditionalDirectory(resolvedPath);
4862
+ return `Access granted to directory: ${resolvedPath}
4863
+
4864
+ You can now use file tools (read_file, write_file, glob, find, etc.) to work with files in this directory.`;
4865
+ } else {
4866
+ return `Access denied to directory: ${resolvedPath}`;
4867
+ }
4868
+ }
4869
+ const normalizedYolo = normalizeYoloInput(this.runtime.options.yolo);
4870
+ if (normalizedYolo) {
4871
+ this.files.addAdditionalDirectory(resolvedPath);
4872
+ return `Access auto-granted (yolo mode) to directory: ${resolvedPath}
4873
+
4874
+ You can now use file tools (read_file, write_file, glob, find, etc.) to work with files in this directory.`;
4875
+ }
4876
+ if (this.runtime.options.unrestricted || this.runtime.options.yes) {
4877
+ this.files.addAdditionalDirectory(resolvedPath);
4878
+ return `Access auto-granted to directory: ${resolvedPath}
4879
+
4880
+ You can now use file tools (read_file, write_file, glob, find, etc.) to work with files in this directory.`;
4881
+ }
4882
+ return `Directory access required: ${resolvedPath}
4883
+
4884
+ To grant access, use:
4885
+ /add-dir ${resolvedPath}
4886
+
4887
+ Or restart with:
4888
+ --add-dir ${resolvedPath}`;
4889
+ }
4890
+ async executeCodeReview(action) {
4891
+ const targetPath = action.path ? this.resolveWorkspacePath(action.path) : this.runtime.workspaceRoot;
4892
+ const scope = action.scope || "full";
4893
+ await this.onReviewHook?.("review:start", {
4894
+ reviewPath: targetPath,
4895
+ reviewScope: scope,
4896
+ reviewInstructions: action.instructions
4897
+ });
4898
+ try {
4899
+ let context = "";
4900
+ if (scope === "diff") {
4901
+ const { execFile: execFile2 } = await import("child_process");
4902
+ const { promisify } = await import("util");
4903
+ const execFileAsync = promisify(execFile2);
4904
+ const result2 = await execFileAsync("git", ["diff", "--stat"], {
4905
+ cwd: this.runtime.workspaceRoot,
4906
+ encoding: "utf8"
4907
+ }).catch(() => null);
4908
+ context = result2?.stdout || "No uncommitted changes found.";
4909
+ } else if (scope === "file" && action.path) {
4910
+ const fse = (await import("fs-extra")).default;
4911
+ context = await fse.readFile(targetPath, "utf-8").catch(() => `Could not read ${targetPath}`);
4912
+ } else {
4913
+ const { execFile: execFile2 } = await import("child_process");
4914
+ const { promisify } = await import("util");
4915
+ const execFileAsync = promisify(execFile2);
4916
+ const tree = await execFileAsync("find", [
4917
+ targetPath,
4918
+ "-maxdepth",
4919
+ "3",
4920
+ "-type",
4921
+ "f",
4922
+ "-not",
4923
+ "-path",
4924
+ "*/node_modules/*",
4925
+ "-not",
4926
+ "-path",
4927
+ "*/.git/*"
4928
+ ], {
4929
+ cwd: this.runtime.workspaceRoot,
4930
+ encoding: "utf8"
4931
+ }).catch(() => null);
4932
+ context = tree?.stdout || "";
4933
+ }
4934
+ const result = [
4935
+ `Code review initiated for: ${targetPath}`,
4936
+ `Scope: ${scope}`,
4937
+ action.instructions ? `Focus: ${action.instructions}` : "",
4938
+ "",
4939
+ "Project structure:",
4940
+ context.slice(0, 5e3)
4941
+ ].filter(Boolean).join("\n");
4942
+ await this.onReviewHook?.("review:completed", {
4943
+ reviewPath: targetPath,
4944
+ reviewScope: scope,
4945
+ reviewInstructions: action.instructions
4946
+ });
4947
+ return result;
4948
+ } catch (error) {
4949
+ const message = error instanceof Error ? error.message : String(error);
4950
+ await this.onReviewHook?.("review:failed", {
4951
+ reviewPath: targetPath,
4952
+ reviewScope: scope,
4953
+ reviewInstructions: action.instructions,
4954
+ reviewError: message
4955
+ });
4956
+ return `Review failed: ${message}`;
4957
+ }
4958
+ }
3958
4959
  pickText(...values) {
3959
4960
  for (const value of values) {
3960
4961
  if (typeof value === "string") {
@@ -4051,6 +5052,122 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4051
5052
  }
4052
5053
  return result.length > 0 ? result.join("\n") : "No structure detected";
4053
5054
  }
5055
+ executeFind(action) {
5056
+ console.warn(chalk2.yellow("[DEPRECATED] The `find` tool is deprecated. Use `fff_grep` instead. Will be removed in v0.9.0."));
5057
+ const mode = action.mode ?? (action.context && action.context > 0 ? "context" : "exact");
5058
+ const cacheKey = `find:${mode}:${action.query}:${action.path || ""}:${action.limit || ""}:${action.context || ""}:${action.window || ""}`;
5059
+ if (this.searchCache.has(cacheKey)) {
5060
+ return `[Cached] ${this.searchCache.get(cacheKey)}`;
5061
+ }
5062
+ this.recordExploration("search", action.query);
5063
+ if (mode === "semantic") {
5064
+ const results = this.files.semanticSearch(action.query, {
5065
+ limit: action.limit,
5066
+ window: action.window,
5067
+ relativePath: action.path
5068
+ });
5069
+ if (!results.length) {
5070
+ this.searchCache.set(cacheKey, "No matches found.");
5071
+ return "No matches found.";
5072
+ }
5073
+ const result2 = results.map((hit) => `${chalk2.cyan(hit.file)}
5074
+ ${hit.snippet}`).join("\n\n");
5075
+ this.searchCache.set(cacheKey, result2);
5076
+ return result2;
5077
+ }
5078
+ if (mode === "context") {
5079
+ const result2 = this.files.searchWithContext(action.query, {
5080
+ limit: action.limit,
5081
+ context: action.context,
5082
+ relativePath: action.path
5083
+ });
5084
+ this.searchCache.set(cacheKey, result2);
5085
+ return result2;
5086
+ }
5087
+ const hits = this.files.search(action.query, action.path);
5088
+ const result = hits.slice(0, action.limit ?? 10).map((hit) => `${hit.file}:${hit.line}: ${hit.text}`).join("\n");
5089
+ this.searchCache.set(cacheKey, result);
5090
+ return result;
5091
+ }
5092
+ async executeGlob(action) {
5093
+ console.warn(chalk2.yellow("[DEPRECATED] The `glob` tool is deprecated. Use `fff_find` instead. Will be removed in v0.9.0."));
5094
+ const { resolveRipgrepCommand } = await import("./ripgrep-VHJQQ55W.js");
5095
+ const rgPath = resolveRipgrepCommand();
5096
+ const searchPath = action.path ? this.resolveWorkspacePath(action.path) : this.runtime.workspaceRoot;
5097
+ const limit = action.limit ?? 100;
5098
+ const args = ["--files"];
5099
+ const patterns = action.patterns ?? (action.pattern ? [action.pattern] : ["**/*"]);
5100
+ for (const p of patterns) {
5101
+ args.push("--glob", p);
5102
+ }
5103
+ args.push(searchPath);
5104
+ const { execFile: execFile2 } = await import("child_process");
5105
+ const { promisify } = await import("util");
5106
+ const execFileAsync = promisify(execFile2);
5107
+ try {
5108
+ const result = await execFileAsync(rgPath, args, {
5109
+ cwd: this.runtime.workspaceRoot,
5110
+ encoding: "utf8",
5111
+ maxBuffer: 10 * 1024 * 1024
5112
+ });
5113
+ const files = result.stdout.trim().split("\n").filter(Boolean);
5114
+ if (files.length === 0) {
5115
+ return "No files found matching the pattern.";
5116
+ }
5117
+ const fse = (await import("fs-extra")).default;
5118
+ const withStats = await Promise.all(
5119
+ files.map(async (f) => {
5120
+ try {
5121
+ const stat = await fse.stat(f);
5122
+ return { file: f, mtime: stat.mtimeMs };
5123
+ } catch {
5124
+ return { file: f, mtime: 0 };
5125
+ }
5126
+ })
5127
+ );
5128
+ withStats.sort((a, b) => b.mtime - a.mtime);
5129
+ const sorted = withStats.map((s) => s.file);
5130
+ const limited = sorted.slice(0, limit);
5131
+ const header = `Found ${files.length} file${files.length === 1 ? "" : "s"}${files.length > limit ? ` (showing first ${limit})` : ""}`;
5132
+ this.recordExploration("list", action.pattern ?? action.patterns?.join(", ") ?? "*");
5133
+ return `${header}
5134
+ ${limited.join("\n")}`;
5135
+ } catch (error) {
5136
+ const exitCode = error?.code;
5137
+ if (exitCode === 1 || exitCode === "1") {
5138
+ return "No files found matching the pattern.";
5139
+ }
5140
+ throw error;
5141
+ }
5142
+ }
5143
+ async executeFFFGrep(action) {
5144
+ const provider = await this.getFFFSearchProvider();
5145
+ try {
5146
+ return await provider.grep({
5147
+ query: action.query,
5148
+ path: action.path,
5149
+ exclude: action.exclude,
5150
+ caseSensitive: action.caseSensitive,
5151
+ beforeContext: action.beforeContext,
5152
+ afterContext: action.afterContext,
5153
+ classifyDefinitions: action.classifyDefinitions,
5154
+ limit: action.limit
5155
+ });
5156
+ } finally {
5157
+ this.scheduleFFFSearchProviderCleanup();
5158
+ }
5159
+ }
5160
+ async executeFFFFind(action) {
5161
+ const provider = await this.getFFFSearchProvider();
5162
+ try {
5163
+ return await provider.fileSearch({
5164
+ query: action.query,
5165
+ limit: action.limit
5166
+ });
5167
+ } finally {
5168
+ this.scheduleFFFSearchProviderCleanup();
5169
+ }
5170
+ }
4054
5171
  recordExploration(kind, target) {
4055
5172
  if (!target) {
4056
5173
  return;
@@ -4092,12 +5209,6 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4092
5209
  const lowered = command.toLowerCase();
4093
5210
  return lowered.includes("rm ") || lowered.includes("sudo ") || lowered.includes("dd ");
4094
5211
  }
4095
- static {
4096
- /**
4097
- * Shell metacharacters that could enable command injection
4098
- */
4099
- this.SHELL_METACHARACTERS = /[|;&$`><(){}[\]!#*?~'"\\]/;
4100
- }
4101
5212
  /**
4102
5213
  * Safely escape a value for shell interpolation
4103
5214
  * Uses single quotes which prevent all shell expansion except for single quotes themselves
@@ -4118,21 +5229,62 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4118
5229
  if (value === void 0 || value === null) {
4119
5230
  throw new Error(`Missing required parameter "${paramName}" for meta-tool "${metaTool.name}"`);
4120
5231
  }
4121
- const stringValue = String(value);
4122
- let safeValue;
4123
- if (_ActionExecutor.SHELL_METACHARACTERS.test(stringValue)) {
4124
- safeValue = this.shellEscape(stringValue);
4125
- console.log(chalk2.yellow(` \u26A0 Parameter "${paramName}" contains shell metacharacters, escaped for safety`));
4126
- } else {
4127
- safeValue = stringValue;
4128
- }
5232
+ const safeValue = this.shellEscape(String(value));
5233
+ command = command.replace(new RegExp(`(["'])\\{\\{${paramName}\\}\\}\\1`, "g"), safeValue);
4129
5234
  command = command.replace(new RegExp(`\\{\\{${paramName}\\}\\}`, "g"), safeValue);
4130
5235
  }
4131
5236
  console.log(chalk2.cyan(`
4132
5237
  \u{1F527} Running meta-tool: ${metaTool.name}`));
4133
5238
  console.log(chalk2.gray(` $ ${command}`));
4134
- const result = await runCommand(command, [], this.runtime.workspaceRoot, { shell: true });
4135
- return [`$ ${command}`, result.stdout, result.stderr].filter(Boolean).join("\n");
5239
+ const permissionContext = {
5240
+ tool: "run_command",
5241
+ command,
5242
+ description: `Meta-tool ${metaTool.name}: ${metaTool.description}`
5243
+ };
5244
+ const decision = this.permissionManager.checkPermission(permissionContext);
5245
+ 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") {
5246
+ return `Blocked: Cannot run meta-tool ${metaTool.name} (${decision.reason})`;
5247
+ }
5248
+ if (!decision.allowed) {
5249
+ const hookResult = await this.checkPermissionHook({
5250
+ tool: "run_command",
5251
+ command,
5252
+ args
5253
+ });
5254
+ if (hookResult.blocked) {
5255
+ return `Blocked: ${hookResult.reason}`;
5256
+ }
5257
+ if (hookResult.allowed !== void 0) {
5258
+ await this.permissionManager.recordDecision(permissionContext, hookResult.allowed);
5259
+ if (!hookResult.allowed) {
5260
+ return `Denied: ${hookResult.reason ?? `meta-tool ${metaTool.name}`}`;
5261
+ }
5262
+ } else {
5263
+ const confirmed = await this.confirmDangerousAction(
5264
+ `Run meta-tool ${metaTool.name}?`,
5265
+ { tool: "run_command", command }
5266
+ );
5267
+ await this.permissionManager.recordDecision(permissionContext, confirmed);
5268
+ if (!confirmed) {
5269
+ return `Skipped running meta-tool ${metaTool.name}`;
5270
+ }
5271
+ }
5272
+ }
5273
+ const result = await runCommand(command, [], this.runtime.workspaceRoot, {
5274
+ shell: true,
5275
+ timeout: 12e4
5276
+ });
5277
+ const stdout = this.truncateMetaToolOutput(result.stdout);
5278
+ const stderr = this.truncateMetaToolOutput(result.stderr);
5279
+ return [`$ ${command}`, stdout, stderr].filter(Boolean).join("\n");
5280
+ }
5281
+ truncateMetaToolOutput(output) {
5282
+ const limit = 2e5;
5283
+ if (output.length <= limit) {
5284
+ return output;
5285
+ }
5286
+ return `${output.slice(0, limit)}
5287
+ [meta-tool output truncated at ${limit} characters]`;
4136
5288
  }
4137
5289
  applySearchReplaceBlocks(content, blocks) {
4138
5290
  let result = content;
@@ -4227,10 +5379,10 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4227
5379
  score += 2;
4228
5380
  }
4229
5381
  if (score > 0 && (!bestMatch || score > bestMatch.score)) {
4230
- bestMatch = { line: line.trim(), score };
5382
+ bestMatch = { line: line.trim(), originalLine: line, score };
4231
5383
  }
4232
5384
  }
4233
- return bestMatch && bestMatch.score >= 2 ? bestMatch.line : null;
5385
+ return bestMatch && bestMatch.score >= 2 ? bestMatch.originalLine : null;
4234
5386
  }
4235
5387
  /**
4236
5388
  * Colorize raw git diff output with green for additions and red for removals
@@ -4316,6 +5468,10 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4316
5468
  }
4317
5469
  }
4318
5470
  showDiff(oldContent, newContent, filePath) {
5471
+ console.log(this.formatDiffPreview(oldContent, newContent, filePath));
5472
+ console.log();
5473
+ }
5474
+ formatDiffPreview(oldContent, newContent, filePath) {
4319
5475
  const diff = diffLines(oldContent, newContent);
4320
5476
  const contextLines = 3;
4321
5477
  const lang = filePath ? detectLanguage(filePath) : "text";
@@ -4332,10 +5488,11 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4332
5488
  const termWidth = process.stdout.columns || 100;
4333
5489
  const addText = additions === 1 ? "1 line" : `${additions} lines`;
4334
5490
  const delText = deletions === 1 ? "1 line" : `${deletions} lines`;
5491
+ const outputLines = [];
4335
5492
  if (theme) {
4336
- console.log(theme.fg("muted", ` Added ${theme.fg("diffAdded", addText)}, removed ${theme.fg("diffRemoved", delText)}`));
5493
+ outputLines.push(theme.fg("muted", ` Added ${theme.fg("diffAdded", addText)}, removed ${theme.fg("diffRemoved", delText)}`));
4337
5494
  } else {
4338
- console.log(chalk2.gray(` Added ${chalk2.green(addText)}, removed ${chalk2.red(delText)}`));
5495
+ outputLines.push(chalk2.gray(` Added ${chalk2.green(addText)}, removed ${chalk2.red(delText)}`));
4339
5496
  }
4340
5497
  const hunks = [];
4341
5498
  let currentHunk = null;
@@ -4424,7 +5581,7 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4424
5581
  const bgB = addedRgb ? Math.floor(addedRgb.b * 0.15) : 30;
4425
5582
  const prefix = chalk2.bgHex(addedColor).black(` ${lineNumStr} + `);
4426
5583
  const content = chalk2.bgRgb(bgR, bgG, bgB)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4427
- console.log(prefix + content);
5584
+ outputLines.push(prefix + content);
4428
5585
  } else if (change.type === "remove") {
4429
5586
  const removedRgb = hexToRgb(removedColor);
4430
5587
  const bgR = removedRgb ? Math.floor(removedRgb.r * 0.25) : 60;
@@ -4432,32 +5589,33 @@ Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...
4432
5589
  const bgB = removedRgb ? Math.floor(removedRgb.b * 0.15) : 30;
4433
5590
  const prefix = chalk2.bgHex(removedColor).white(` ${lineNumStr} - `);
4434
5591
  const content = chalk2.bgRgb(bgR, bgG, bgB)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4435
- console.log(prefix + content);
5592
+ outputLines.push(prefix + content);
4436
5593
  } else {
4437
- console.log(chalk2.hex(contextColor)(` ${lineNumStr} `) + ` ${highlighted}`);
5594
+ outputLines.push(chalk2.hex(contextColor)(` ${lineNumStr} `) + ` ${highlighted}`);
4438
5595
  }
4439
5596
  } else {
4440
5597
  if (change.type === "add") {
4441
5598
  const prefix = chalk2.bgGreen.black(` ${lineNumStr} + `);
4442
5599
  const content = chalk2.bgRgb(30, 50, 30)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4443
- console.log(prefix + content);
5600
+ outputLines.push(prefix + content);
4444
5601
  } else if (change.type === "remove") {
4445
5602
  const prefix = chalk2.bgRed.white(` ${lineNumStr} - `);
4446
5603
  const content = chalk2.bgRgb(60, 30, 30)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
4447
- console.log(prefix + content);
5604
+ outputLines.push(prefix + content);
4448
5605
  } else {
4449
- console.log(chalk2.gray(` ${lineNumStr} `) + ` ${highlighted}`);
5606
+ outputLines.push(chalk2.gray(` ${lineNumStr} `) + ` ${highlighted}`);
4450
5607
  }
4451
5608
  }
4452
5609
  }
4453
5610
  }
4454
- console.log();
5611
+ return outputLines.join("\n");
4455
5612
  }
4456
5613
  };
4457
5614
 
4458
5615
  export {
4459
5616
  getAutoCommitInfo,
4460
- ToolsRegistry,
5617
+ WorktreeManager,
5618
+ createToolsRegistry,
4461
5619
  ActionExecutor
4462
5620
  };
4463
5621
  /**
@@ -4488,6 +5646,13 @@ export {
4488
5646
  *
4489
5647
  * GitHub and GitLab repository browsing capabilities.
4490
5648
  */
5649
+ /**
5650
+ * @license
5651
+ * Copyright 2025 Autohand AI LLC
5652
+ * SPDX-License-Identifier: Apache-2.0
5653
+ *
5654
+ * Project tracker — queries GitHub issues and PRs via gh CLI.
5655
+ */
4491
5656
  /**
4492
5657
  * @license
4493
5658
  * Copyright 2025 Autohand AI LLC