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.
- package/README.md +173 -74
- package/dist/AgentRegistry-EGBDIUAK.cjs +10 -0
- package/dist/{AgentRegistry-7LDL5HJH.js → AgentRegistry-XPWSVO3Q.js} +3 -3
- package/dist/{AutomodeManager-MWLKGPZK.js → AutomodeManager-APLLPEYJ.js} +32 -18
- package/dist/{AutomodeManager-NYIZNODK.cjs → AutomodeManager-MPSXT2RH.cjs} +38 -24
- package/dist/CommunitySkillsCache-2OIUV227.cjs +8 -0
- package/dist/CommunitySkillsCache-ZEQWP6YM.js +8 -0
- package/dist/GitHubRegistryFetcher-CYJLF2XL.cjs +7 -0
- package/dist/{GitHubRegistryFetcher-6JQ5JEDZ.js → GitHubRegistryFetcher-J2BWPXJF.js} +2 -2
- package/dist/HookManager-CWLTFKWO.js +7 -0
- package/dist/HookManager-TMAJQU4S.cjs +7 -0
- package/dist/{ImportWizard-35YBJ4AM.cjs → ImportWizard-MQXEED2U.cjs} +41 -17
- package/dist/{ImportWizard-XH7CINCH.js → ImportWizard-QBKQEXDW.js} +35 -11
- package/dist/LearnAdvisor-46FG2XIP.js +9 -0
- package/dist/LearnAdvisor-XBRDNAGH.cjs +9 -0
- package/dist/McpClientManager-BNSKLHIN.cjs +8 -0
- package/dist/{McpClientManager-7RM6YT35.js → McpClientManager-SL35BR24.js} +2 -2
- package/dist/MemoryManager-34L4YOKA.cjs +8 -0
- package/dist/MemoryManager-WJMLPWGU.js +8 -0
- package/dist/NVIDIAProvider-2HR737UE.js +14 -0
- package/dist/NVIDIAProvider-MVTL62PR.cjs +14 -0
- package/dist/PermissionManager-5OOJ7FAT.cjs +11 -0
- package/dist/{PermissionManager-ATUV34LQ.js → PermissionManager-KMYILJ4Z.js} +4 -4
- package/dist/{ProjectProfiler-ZDWR2ODG.cjs → ProjectProfiler-CDAE7ECW.cjs} +2 -1
- package/dist/{ProjectProfiler-UMJJSOCE.js → ProjectProfiler-NZTJDRHD.js} +2 -1
- package/dist/ProviderFactory-C3YPXTDD.cjs +11 -0
- package/dist/ProviderFactory-HFPRVQ25.js +11 -0
- package/dist/SessionManager-PNBTJJTQ.js +10 -0
- package/dist/SessionManager-WAPTFMDO.cjs +10 -0
- package/dist/SkillsRegistry-4RRD5GMX.js +9 -0
- package/dist/SkillsRegistry-7AJP74GJ.cjs +9 -0
- package/dist/SubAgent-FDIH3DXB.js +11 -0
- package/dist/SubAgent-HVL2ICVZ.cjs +11 -0
- package/dist/{SyncApiClient-LVIO4C2S.js → SyncApiClient-LRPFNCKJ.js} +2 -2
- package/dist/SyncApiClient-VMBOLQ6H.cjs +11 -0
- package/dist/about-BW3PDZUU.js +14 -0
- package/dist/about-FCAX37YC.cjs +14 -0
- package/dist/acp-EOETGAHC.cjs +1517 -0
- package/dist/acp-VHEL7BOW.js +1517 -0
- package/dist/actionExecutor-33I47NZS.js +24 -0
- package/dist/actionExecutor-4YBMR3YH.cjs +24 -0
- package/dist/add-dir-7SS6KSH5.cjs +11 -0
- package/dist/add-dir-VFX7QT4E.js +11 -0
- package/dist/agent-DMRUFU4M.cjs +116 -0
- package/dist/agent-WCJEYQJT.js +116 -0
- package/dist/agents/builtin/code-cleaner.md +1 -1
- package/dist/agents/builtin/docs-writer.md +1 -1
- package/dist/agents/builtin/researcher.md +2 -2
- package/dist/agents/builtin/reviewer.md +1 -1
- package/dist/agents/builtin/tester.md +1 -1
- package/dist/agents/builtin/todo-resolver.md +1 -1
- package/dist/agents-CHROO3VU.cjs +17 -0
- package/dist/agents-UVDUL65K.js +17 -0
- package/dist/agents-new-AUBWSOXP.js +17 -0
- package/dist/agents-new-Y562WC47.cjs +17 -0
- package/dist/{autoSkill-6TGBTEQD.js → autoSkill-EFMK6WU6.js} +3 -3
- package/dist/autoSkill-YOLLFTP2.cjs +20 -0
- package/dist/automode-BMYSRM34.js +10 -0
- package/dist/automode-DBLKTTKD.cjs +10 -0
- package/dist/browserToolBridge-BXRQB4B4.cjs +58 -0
- package/dist/browserToolBridge-F5N66PE7.js +58 -0
- package/dist/{cc-7LEIJ3KF.js → cc-AYEP2CA3.js} +1 -1
- package/dist/{cc-Q5MM4AWC.cjs → cc-S2QLQFBR.cjs} +1 -1
- package/dist/chrome-ATC4OO5I.cjs +20 -0
- package/dist/chrome-NHJ44WUN.js +50 -0
- package/dist/chrome-O62WXV7F.js +20 -0
- package/dist/chrome-XHJLCO4M.cjs +50 -0
- package/dist/chromeSkill-53TH55PM.js +105 -0
- package/dist/chromeSkill-THW7N4IY.cjs +105 -0
- package/dist/{chunk-HOAHWIQ5.cjs → chunk-2CGE7ZV3.cjs} +26 -26
- package/dist/chunk-2H5O745H.js +63 -0
- package/dist/chunk-2HOLOHVK.js +2219 -0
- package/dist/{chunk-3PDTTAKJ.js → chunk-2QL6MNXG.js} +15 -8
- package/dist/{chunk-PGRH5Q77.cjs → chunk-2VHB43IX.cjs} +35 -6
- package/dist/{chunk-VG34MG2U.js → chunk-37M5UM6I.js} +9 -6
- package/dist/{chunk-6HYLHBQG.cjs → chunk-3DDL2E55.cjs} +20 -16
- package/dist/chunk-3EDDDZS2.cjs +5 -0
- package/dist/{chunk-6OYHF6MF.js → chunk-3GUEUCZK.js} +28 -4
- package/dist/{chunk-N254NRHT.cjs → chunk-3HT76LNN.cjs} +9 -2
- package/dist/{chunk-DVUHHH3B.cjs → chunk-3KLSNNRW.cjs} +4 -4
- package/dist/chunk-3LJJG5YY.cjs +387 -0
- package/dist/chunk-3OF56EMA.cjs +197 -0
- package/dist/chunk-3PXKRVCW.js +108 -0
- package/dist/{chunk-IKGWDOGU.cjs → chunk-3S563FNF.cjs} +160 -79
- package/dist/chunk-3UT7R3XV.js +663 -0
- package/dist/chunk-4256YRCO.cjs +80 -0
- package/dist/chunk-46C73ZKK.cjs +663 -0
- package/dist/chunk-46MTALKD.js +44 -0
- package/dist/{chunk-AEJH23FO.cjs → chunk-47PHDKNW.cjs} +6 -6
- package/dist/chunk-4LMUDS2K.js +124 -0
- package/dist/{chunk-HLHTG5ZU.cjs → chunk-5CMYEM3R.cjs} +14 -12
- package/dist/{chunk-MBBY4ZIK.js → chunk-5F6ZKSHO.js} +4 -1
- package/dist/chunk-5JFTY3VU.js +74 -0
- package/dist/chunk-5JTTM5SC.js +59 -0
- package/dist/{chunk-IETRBBMP.cjs → chunk-5K3CDSWZ.cjs} +108 -31
- package/dist/{chunk-GJH7XMSK.js → chunk-5VHP6HDQ.js} +8 -6
- package/dist/{chunk-6ZCULLCA.js → chunk-62RTC3XX.js} +1 -1
- package/dist/{chunk-MSED7RH2.cjs → chunk-6GUODJKM.cjs} +112 -41
- package/dist/{chunk-47CKWKEX.cjs → chunk-6HH236FV.cjs} +9 -4
- package/dist/{chunk-WHE2SWHU.js → chunk-6NVAK6CK.js} +2 -2
- package/dist/chunk-6R25D2H5.js +121 -0
- package/dist/{chunk-3PCTTUNW.cjs → chunk-6XVVIY54.cjs} +55 -25
- package/dist/{chunk-3L53OA4E.cjs → chunk-75EDROHL.cjs} +10 -10
- package/dist/{chunk-G4CAKI3V.js → chunk-7HPWMALN.js} +7 -2
- package/dist/chunk-7JLT2VNW.cjs +18963 -0
- package/dist/chunk-7LWQCE4Y.cjs +987 -0
- package/dist/{chunk-OLSBBZW6.cjs → chunk-7MU7LWF3.cjs} +5 -5
- package/dist/{chunk-HLQV64Y5.js → chunk-7RFJB75Y.js} +140 -59
- package/dist/{chunk-X2MSVKDV.js → chunk-A7HHCIDQ.js} +2 -2
- package/dist/{chunk-SKYG33B2.cjs → chunk-AC7DZ6SK.cjs} +3 -3
- package/dist/{chunk-XX2ZO7DS.js → chunk-ACDQGA4Z.js} +90 -16
- package/dist/{chunk-CZXGCVTR.cjs → chunk-AOKCI722.cjs} +2 -2
- package/dist/chunk-ARBEHFCG.js +715 -0
- package/dist/{chunk-OOKY3HPZ.js → chunk-AT7OWLY5.js} +50 -9
- package/dist/chunk-AU6JAGZJ.cjs +231 -0
- package/dist/chunk-AUFNACED.js +18963 -0
- package/dist/{chunk-CDBPBM2K.cjs → chunk-AUYSIEVV.cjs} +3 -3
- package/dist/chunk-B4HSNOIH.cjs +354 -0
- package/dist/{chunk-EGMZDTSL.js → chunk-B5CGDNMR.js} +10 -2
- package/dist/{chunk-34M3HWLR.js → chunk-BFQDQRDE.js} +2 -2
- package/dist/{chunk-2AA5MFES.js → chunk-BL2UC7HC.js} +8 -5
- package/dist/{chunk-LNMYK2F5.cjs → chunk-BQ22N3TX.cjs} +56 -41
- package/dist/chunk-BQU3HAE7.js +21 -0
- package/dist/chunk-BWF4VDYE.js +3897 -0
- package/dist/chunk-BYE7RQFZ.cjs +121 -0
- package/dist/{chunk-APIXPPMT.js → chunk-CBFH2J3O.js} +855 -33
- package/dist/chunk-CCVMREXI.js +420 -0
- package/dist/chunk-CFAWTLSC.js +13 -0
- package/dist/{chunk-G27PQQFD.js → chunk-CFFE4VA3.js} +1 -1
- package/dist/{chunk-ZYQMLKOK.cjs → chunk-CH2J4PVE.cjs} +212 -70
- package/dist/{chunk-7BTSG4ME.cjs → chunk-CHI52KFR.cjs} +855 -33
- package/dist/{chunk-DJDE4DTT.cjs → chunk-CWINVFRI.cjs} +25 -19
- package/dist/{chunk-HXGBSJL5.cjs → chunk-D2OSPLYC.cjs} +2 -2
- package/dist/{chunk-GBHDROGL.js → chunk-D6GZBSOX.js} +16 -4
- package/dist/{chunk-CNBKZEX5.cjs → chunk-DA7NBAJK.cjs} +49 -17
- package/dist/chunk-DEQVRSV5.cjs +866 -0
- package/dist/{chunk-RGR6ME5J.cjs → chunk-DI57A4BX.cjs} +25 -28
- package/dist/{chunk-WM5PAOTQ.cjs → chunk-DIZTWFVR.cjs} +1649 -484
- package/dist/{chunk-YZXUDM5X.js → chunk-DLP436GI.js} +204 -62
- package/dist/{chunk-OHUZKDGX.js → chunk-DMRXF5DU.js} +3 -3
- package/dist/{chunk-U46VYPLR.cjs → chunk-DPSIGY6L.cjs} +9 -9
- package/dist/{chunk-DN573ME7.cjs → chunk-DRWDEHE5.cjs} +4 -4
- package/dist/{chunk-SEKD5FH3.cjs → chunk-E26KKI46.cjs} +5 -2
- package/dist/{chunk-J4Q7XR3G.js → chunk-E46DJH5S.js} +3 -3
- package/dist/{chunk-Q7XSCYND.cjs → chunk-E6GFD7VR.cjs} +49 -26
- package/dist/chunk-ES2HJQ4N.js +37 -0
- package/dist/chunk-EZJHLOWP.js +80 -0
- package/dist/chunk-FFBDRUO5.cjs +59 -0
- package/dist/{chunk-AYSFIUFW.js → chunk-FGT6KK6T.js} +38 -15
- package/dist/{chunk-SAHBLB3E.js → chunk-FP46B4X3.js} +208 -3
- package/dist/{chunk-DSPQEHDT.js → chunk-FQAVGSPW.js} +2 -2
- package/dist/chunk-FQVG6ZHF.js +197 -0
- package/dist/chunk-GBMDFWJX.cjs +152 -0
- package/dist/{chunk-DRE2RXBZ.js → chunk-GGLKUENP.js} +1605 -440
- package/dist/{chunk-S52YW5ZQ.js → chunk-GNBBIAMD.js} +16 -19
- package/dist/{chunk-MYISNQH4.js → chunk-GWO66KBI.js} +1 -1
- package/dist/chunk-GWY26SUD.cjs +63 -0
- package/dist/{chunk-YGN4CQIP.js → chunk-GZ6DV2UG.js} +1 -1
- package/dist/{chunk-EZMINVLU.js → chunk-H4D2Q2AL.js} +5 -2
- package/dist/chunk-HDK2EHVH.cjs +570 -0
- package/dist/chunk-HJIXWGQZ.js +139 -0
- package/dist/{chunk-6RF7UKUS.js → chunk-HKYZSUQ5.js} +48 -47
- package/dist/{chunk-LQGVEP3E.js → chunk-HQUSEWT4.js} +45 -13
- package/dist/{chunk-HTLINWX6.cjs → chunk-HQWQZML5.cjs} +16 -13
- package/dist/{chunk-DVZOENQ7.cjs → chunk-HS4USDND.cjs} +9 -4
- package/dist/chunk-HSCUPEA4.cjs +3897 -0
- package/dist/{chunk-JCLYQ2JC.js → chunk-HXJEGMGB.js} +12 -6
- package/dist/{chunk-LJFUXC56.cjs → chunk-HXQREVTA.cjs} +9 -6
- package/dist/{chunk-SCXX4LW5.js → chunk-I24CWKKK.js} +15 -8
- package/dist/chunk-IAGCRVJ3.js +355 -0
- package/dist/chunk-IEXKKKOF.cjs +92 -0
- package/dist/{chunk-5IXII4HX.cjs → chunk-IGMPWJ2I.cjs} +19 -12
- package/dist/chunk-IKUMVBCW.cjs +33 -0
- package/dist/{chunk-XTB6VJVQ.cjs → chunk-J2K57QM7.cjs} +6 -6
- package/dist/{chunk-O4IF4NJT.cjs → chunk-J5ADZN6V.cjs} +57 -56
- package/dist/chunk-J5HE6CUM.cjs +124 -0
- package/dist/{chunk-JS7IPR7P.js → chunk-JAQO6XDB.js} +31 -2
- package/dist/chunk-JIMSII7R.js +987 -0
- package/dist/chunk-JJ4KA7HK.cjs +2219 -0
- package/dist/chunk-JMN3GZU6.js +570 -0
- package/dist/{chunk-X5VSP65C.cjs → chunk-JP3YHTQ2.cjs} +4 -4
- package/dist/{chunk-SKV2F3NM.js → chunk-JYKPWK5O.js} +1 -1
- package/dist/{chunk-NNPAM4HC.cjs → chunk-KNTUI4TZ.cjs} +12 -6
- package/dist/{chunk-HIVRCQS2.js → chunk-L4F7SUYL.js} +56 -25
- package/dist/{chunk-C5IJIM2V.cjs → chunk-LDJQ5QHG.cjs} +68 -37
- package/dist/{chunk-ULQ6MDSJ.cjs → chunk-LFDPTJYF.cjs} +43 -35
- package/dist/chunk-LJD7KR3L.cjs +44 -0
- package/dist/chunk-LN7D3EJZ.js +387 -0
- package/dist/{chunk-643VRA5S.cjs → chunk-LQBONA55.cjs} +32 -8
- package/dist/{chunk-NCC6ETZS.js → chunk-LUMV3DB2.js} +41 -33
- package/dist/chunk-M4KNC5BQ.js +78 -0
- package/dist/chunk-MO4KP6XS.cjs +229 -0
- package/dist/{chunk-BVKXEQVG.cjs → chunk-MOJ7ADW4.cjs} +22 -10
- package/dist/chunk-MPULXVC4.cjs +715 -0
- package/dist/{chunk-X5YJ34FZ.cjs → chunk-MQESBFBZ.cjs} +46 -23
- package/dist/chunk-MUNUUFU7.cjs +21 -0
- package/dist/{chunk-X3WS5LDG.js → chunk-MX75JYIY.js} +7 -5
- package/dist/chunk-MXVIDIC6.cjs +139 -0
- package/dist/{chunk-3K2ESU53.cjs → chunk-N6O3XUZ2.cjs} +2 -2
- package/dist/{chunk-IVM5F2AE.js → chunk-NBGOIFKP.js} +265 -18
- package/dist/{chunk-SLQAYV3W.js → chunk-NMQ47RCG.js} +8 -2
- package/dist/{chunk-JYTDYJVW.js → chunk-O64I2GYI.js} +1 -1
- package/dist/{chunk-IFFXSTOM.cjs → chunk-O6TQP7U7.cjs} +3 -3
- package/dist/{chunk-AYS2ASM7.js → chunk-OHWMWKJQ.js} +1 -1
- package/dist/{chunk-N23UAW4I.js → chunk-OM24WXEE.js} +7 -2
- package/dist/{chunk-MAKMSQMQ.cjs → chunk-OPNI6O7F.cjs} +8 -6
- package/dist/chunk-OR67YXQK.cjs +13 -0
- package/dist/{chunk-QOXPOR5D.js → chunk-OV3PVUYN.js} +115 -44
- package/dist/{chunk-TNZRZQ7Q.js → chunk-PB7W7R72.js} +3 -78
- package/dist/chunk-PCM3N3CL.cjs +37 -0
- package/dist/{chunk-A4IJHHV7.cjs → chunk-PDHT7LQS.cjs} +52 -11
- package/dist/{chunk-LA7H35XM.cjs → chunk-PHJO5YAL.cjs} +9 -9
- package/dist/{chunk-KPELYZ6L.js → chunk-PQLKJCIE.js} +2 -2
- package/dist/chunk-PUD76XQT.cjs +78 -0
- package/dist/chunk-Q2IL4DDI.cjs +355 -0
- package/dist/{chunk-WPVWQSL7.cjs → chunk-Q6AC3PHY.cjs} +16 -13
- package/dist/{chunk-XRZEUWKF.js → chunk-QCHIDZBA.js} +1 -1
- package/dist/{chunk-IQ5RXU6O.js → chunk-QEGOB6QV.js} +1 -1
- package/dist/{chunk-JSBRDJBE.js → chunk-QGM4M3NI.js} +8 -1
- package/dist/chunk-QKP772OZ.js +4291 -0
- package/dist/chunk-QQ7PANOP.js +59 -0
- package/dist/{chunk-YHD6TUIR.cjs → chunk-QW2RW2GY.cjs} +2 -0
- package/dist/chunk-RAKO7UN7.js +114 -0
- package/dist/chunk-RBHANOYY.js +33 -0
- package/dist/chunk-RBZ7AFX7.cjs +29 -0
- package/dist/{chunk-TXSDBGKX.cjs → chunk-REMGR23V.cjs} +46 -49
- package/dist/chunk-ROLA6EFO.cjs +85 -0
- package/dist/{chunk-TBEGGJNC.cjs → chunk-SOLBYSLY.cjs} +33 -16
- package/dist/{chunk-FKSDEWDH.js → chunk-SWCQM52V.js} +68 -25
- package/dist/{chunk-H5SWOLG6.js → chunk-TBOLJDUG.js} +39 -24
- package/dist/chunk-TH26BQJG.js +101 -0
- package/dist/{chunk-245KJE5Y.cjs → chunk-TJNBASFD.cjs} +14 -6
- package/dist/chunk-TKKN34TV.js +229 -0
- package/dist/chunk-TQ33WBY5.cjs +74 -0
- package/dist/{chunk-EGFT4PGW.js → chunk-TWQDAUZU.js} +5 -2
- package/dist/{chunk-OGV4WJ5L.cjs → chunk-TXPSTCG7.cjs} +74 -59
- package/dist/{chunk-CAMZTXV6.js → chunk-U4C3HW6P.js} +67 -52
- package/dist/{chunk-5P2NXKP3.js → chunk-UEIXJG45.js} +104 -76
- package/dist/{chunk-22D2CNTP.cjs → chunk-UGFVM77J.cjs} +5 -2
- package/dist/chunk-UOQECODR.js +34 -0
- package/dist/chunk-UR27UDTB.js +354 -0
- package/dist/chunk-UWFG2R2I.cjs +420 -0
- package/dist/{chunk-Y72HH2TF.cjs → chunk-UXLW45ZE.cjs} +102 -28
- package/dist/{chunk-FPHU2ES6.cjs → chunk-VBOFPU5M.cjs} +6 -0
- package/dist/{chunk-JX3DFKBI.js → chunk-VDWJMWDF.js} +54 -24
- package/dist/{chunk-LENHP55G.cjs → chunk-VGEV44V2.cjs} +1076 -158
- package/dist/{chunk-33RSHBDH.js → chunk-VKBIE6I6.js} +38 -7
- package/dist/{chunk-CWMZKFTT.js → chunk-VRMZO6TB.js} +31 -8
- package/dist/chunk-VUGOOGHB.js +400 -0
- package/dist/chunk-WBU4Q4GS.cjs +400 -0
- package/dist/chunk-WGLBC5AY.cjs +59 -0
- package/dist/{chunk-KWXVKLQ5.cjs → chunk-WGNMOVMT.cjs} +7 -82
- package/dist/{chunk-RKJTGGMU.cjs → chunk-WKRDBCP2.cjs} +221 -16
- package/dist/chunk-WRTXCQ3V.cjs +4291 -0
- package/dist/chunk-WTB7AFL6.cjs +101 -0
- package/dist/{chunk-NDMIPTV4.js → chunk-WZV6UVPY.js} +8 -3
- package/dist/chunk-XF2WIKHR.cjs +34 -0
- package/dist/{chunk-L3WAH3EM.cjs → chunk-XGMI6IYB.cjs} +46 -15
- package/dist/{chunk-FW774QXH.js → chunk-XIVFAD2L.js} +1048 -130
- package/dist/chunk-XV2HXRHX.js +85 -0
- package/dist/{chunk-72FKPBT5.js → chunk-Y3M2DABP.js} +16 -12
- package/dist/{chunk-QNGEW5TC.js → chunk-YV63VW4K.js} +1 -1
- package/dist/{chunk-UJ2JSM6H.js → chunk-YVQI26H4.js} +2 -0
- package/dist/{chunk-RD5XAJR2.cjs → chunk-YWTIXHU6.cjs} +266 -19
- package/dist/chunk-YZWE7XSM.js +5 -0
- package/dist/{chunk-I5IW3T2Y.js → chunk-Z3XSSF7B.js} +32 -15
- package/dist/{chunk-6UJMCWRY.js → chunk-Z4LIPMPM.js} +6 -0
- package/dist/chunk-ZASDSY7P.cjs +114 -0
- package/dist/{chunk-R33VKSH5.cjs → chunk-ZHJ7JGME.cjs} +11 -11
- package/dist/chunk-ZPD2AO3U.js +866 -0
- package/dist/{chunk-7UOUW76C.js → chunk-ZQAT5VT5.js} +101 -24
- package/dist/chunk-ZR46OJNZ.js +152 -0
- package/dist/{chunk-G3V4SFET.cjs → chunk-ZZ63NW7A.cjs} +78 -35
- package/dist/clear-33TWQ2ES.cjs +12 -0
- package/dist/clear-OFLFQ3MR.js +12 -0
- package/dist/{communityInstaller-6KCFN7YZ.js → communityInstaller-COB2KTOW.js} +9 -6
- package/dist/communityInstaller-TWMGPSYM.cjs +22 -0
- package/dist/completion-IIZMHXYP.cjs +17 -0
- package/dist/completion-PT4VM2H2.js +17 -0
- package/dist/config-KL6WU7R2.cjs +20 -0
- package/dist/{config-ZN66VXPS.js → config-R4O7GBTY.js} +7 -5
- package/dist/{constants-UFM5B232.js → constants-HVCHVQAF.js} +2 -2
- package/dist/constants-RLMJ5D5P.cjs +21 -0
- package/dist/{defaultHooks-RCXPHF4M.cjs → defaultHooks-2V2CQL63.cjs} +26 -11
- package/dist/{defaultHooks-RDRMER3Z.js → defaultHooks-TMHDU3FS.js} +26 -11
- package/dist/export-3PK3VFCE.js +15 -0
- package/dist/export-WLGNWEMJ.cjs +15 -0
- package/dist/{extractSessionMemories-V7K42ZHW.js → extractSessionMemories-A2JX5WJ2.js} +1 -1
- package/dist/{extractSessionMemories-SDW2MVBQ.cjs → extractSessionMemories-XL3MS37F.cjs} +1 -1
- package/dist/feedback-G635NCLJ.js +18 -0
- package/dist/feedback-LLMK3TZH.cjs +18 -0
- package/dist/fffSearchProvider-2YCNKOYD.js +412 -0
- package/dist/fffSearchProvider-W6627E2V.cjs +412 -0
- package/dist/filesystem-L6DQKGWK.js +11 -0
- package/dist/filesystem-PGUPCMVK.cjs +11 -0
- package/dist/{formatters-6K7QVWQL.cjs → formatters-53XTCNGQ.cjs} +1 -1
- package/dist/{formatters-DQHO5I36.js → formatters-T6KV4BPP.js} +1 -1
- package/dist/help-BAXLP2M2.cjs +12 -0
- package/dist/{help-2BLR7L43.js → help-X3OAZ3CY.js} +3 -3
- package/dist/history-6HOJSEMT.cjs +14 -0
- package/dist/{history-5FZ3M2AK.js → history-YKFPHBJN.js} +3 -3
- package/dist/hooks-7TA4PIIB.js +18 -0
- package/dist/hooks-XVFU67T2.cjs +18 -0
- package/dist/{i18n-K7QOWIBH.js → i18n-2KBUU7XL.js} +2 -2
- package/dist/i18n-MUJMKTFM.cjs +33 -0
- package/dist/ide-CCQ33PGC.cjs +15 -0
- package/dist/ide-VLVFBZ5F.js +15 -0
- package/dist/immediateCommandRouter-MTEHZXQX.js +15 -0
- package/dist/immediateCommandRouter-ROXU3MWT.cjs +15 -0
- package/dist/{import-UXM3VK7B.js → import-4CHYLS4K.js} +4 -4
- package/dist/{import-QEME3E4T.cjs → import-I7T4ZHYL.cjs} +4 -4
- package/dist/import-J46F54JY.cjs +10 -0
- package/dist/import-JWPYKXCZ.js +10 -0
- package/dist/index.cjs +747 -22356
- package/dist/index.d.cts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +786 -22395
- package/dist/init-262MWZV4.js +10 -0
- package/dist/init-EE5Y7RBL.cjs +10 -0
- package/dist/inkMode-VUE6ZDLD.cjs +7 -0
- package/dist/inkMode-WBNFOSAT.js +7 -0
- package/dist/inputPrompt-IIFKCX5Q.cjs +90 -0
- package/dist/inputPrompt-YGBHDUEP.js +90 -0
- package/dist/language-CVLPB7OV.js +21 -0
- package/dist/language-ZTWFHUSV.cjs +21 -0
- package/dist/learn-PON7I5QS.js +23 -0
- package/dist/learn-XLRSVNA3.cjs +23 -0
- package/dist/{lint-D5UOJWIK.cjs → lint-4NDGCSCL.cjs} +1 -1
- package/dist/{lint-NJPZWVN2.js → lint-FZ7ZJUB3.js} +1 -1
- package/dist/login-223QTGBG.cjs +26 -0
- package/dist/login-6IKTBUBY.js +26 -0
- package/dist/logout-RN2AG6SI.js +23 -0
- package/dist/logout-U3M4FFX7.cjs +23 -0
- package/dist/mcp-5O6PUL4G.js +20 -0
- package/dist/mcp-NUQ76QQB.cjs +20 -0
- package/dist/{mcp-install-VESN42PI.js → mcp-install-6XWXLFVY.js} +18 -11
- package/dist/{mcp-install-G27HSS3Z.cjs → mcp-install-P6DHES7V.cjs} +22 -15
- package/dist/memory-CYMDQ2YC.cjs +10 -0
- package/dist/memory-Q54CESNM.js +10 -0
- package/dist/{message-ZJ5AYAMT.cjs → message-4OKO775J.cjs} +1 -1
- package/dist/{message-JUBOK2VU.js → message-X3LOAAM7.js} +1 -1
- package/dist/model-6AJ77PJG.js +10 -0
- package/dist/model-IDRCKDML.cjs +10 -0
- package/dist/new-7LEWOUF2.cjs +12 -0
- package/dist/new-ZXHEWC2S.js +12 -0
- package/dist/onboarding-FXX7YHSJ.cjs +35 -0
- package/dist/onboarding-JYNMK6NI.js +35 -0
- package/dist/{patch-MOD7QC3D.cjs → patch-DJ2GPFST.cjs} +1 -1
- package/dist/{patch-5F6VBIT3.js → patch-NIJWIRHS.js} +1 -1
- package/dist/permissions-YBNSANIA.cjs +10 -0
- package/dist/permissions-YHJMVA6L.js +10 -0
- package/dist/plan-XEJMOT55.cjs +13 -0
- package/dist/plan-YYUAXPTL.js +13 -0
- package/dist/pr-review-CW6J7P62.cjs +9 -0
- package/dist/pr-review-YZSBQVT2.js +9 -0
- package/dist/quit-B43SJ6E4.cjs +10 -0
- package/dist/quit-GWTNHQSP.js +10 -0
- package/dist/rawMode-6W5AXAKI.cjs +7 -0
- package/dist/rawMode-GFNLXQPU.js +7 -0
- package/dist/{registry-KWZGYJC2.js → registry-34GL6BNJ.js} +30 -45
- package/dist/{registry-YN4FQPOO.cjs → registry-V24W7YK6.cjs} +66 -81
- package/dist/repeat-P4FAPE3Y.cjs +17 -0
- package/dist/repeat-RALE6AUO.js +17 -0
- package/dist/resume-DYVOQN5L.cjs +16 -0
- package/dist/resume-M25UQKOX.js +16 -0
- package/dist/review-QHP2KP4Q.js +9 -0
- package/dist/review-UWHWQHCB.cjs +9 -0
- package/dist/ripgrep-67SCU2BA.cjs +9 -0
- package/dist/ripgrep-VHJQQ55W.js +9 -0
- package/dist/rpc-NPS3PU4O.cjs +3730 -0
- package/dist/rpc-S3DGW6KV.js +3730 -0
- package/dist/search-CPX4PWHP.js +20 -0
- package/dist/search-VUF3M4N3.cjs +20 -0
- package/dist/{session-BSU2L5UI.cjs → session-LXKC4X5Q.cjs} +1 -1
- package/dist/{session-SZMRN5KG.js → session-NMMO4QOL.js} +1 -1
- package/dist/sessions-IYAXMK23.js +10 -0
- package/dist/sessions-JODKER5D.cjs +10 -0
- package/dist/settings-55BNW6BF.js +33 -0
- package/dist/settings-BKTS7OMC.cjs +33 -0
- package/dist/setup-IRC5HQG2.js +29 -0
- package/dist/setup-MMQ7TWOP.cjs +29 -0
- package/dist/share-6JRQECAH.cjs +17 -0
- package/dist/share-YM5MJKMO.js +17 -0
- package/dist/{skills-FYY6F2WV.cjs → skills-GGMZOVIE.cjs} +14 -11
- package/dist/skills-LWSOKOSH.cjs +29 -0
- package/dist/{skills-6OL4OSGA.js → skills-MVIZNHT4.js} +13 -10
- package/dist/skills-VWOMV3CR.js +29 -0
- package/dist/{skills-install-6CSWC24P.js → skills-install-QBA5RCV6.js} +22 -71
- package/dist/{skills-install-O3LZ2ETC.cjs → skills-install-VTAMCNNY.cjs} +35 -84
- package/dist/skills-new-GPL46YV2.js +18 -0
- package/dist/skills-new-L5BEZN5V.cjs +18 -0
- package/dist/slashCommands-FBPCIWM5.cjs +95 -0
- package/dist/slashCommands-FCWY5DXW.js +95 -0
- package/dist/status-3B5SDZPL.js +17 -0
- package/dist/status-TTGRF6NC.cjs +17 -0
- package/dist/summarizer-DGPHE5IQ.js +17 -0
- package/dist/summarizer-JNXLUAQG.cjs +17 -0
- package/dist/sync-77CXVVTK.cjs +21 -0
- package/dist/{sync-KWX67OUN.js → sync-7BILIQHP.js} +4 -4
- package/dist/sync-7OKF6636.js +21 -0
- package/dist/sync-EH4K5U3N.cjs +40 -0
- package/dist/{tasks-5FPBIFLC.js → tasks-R5MBGAQ6.js} +1 -1
- package/dist/{tasks-TXGKGNH6.cjs → tasks-V4WB3MFR.cjs} +1 -1
- package/dist/{team-5YXP3JGR.js → team-JESCHGWB.js} +1 -1
- package/dist/{team-IIWEZKNR.cjs → team-VQQKWGZB.cjs} +1 -1
- package/dist/{teammate-L6EZQ3I2.js → teammate-GZQSCIMS.js} +29 -8
- package/dist/{teammate-2KMKJXAM.cjs → teammate-J6PHGL23.cjs} +30 -9
- package/dist/theme-64BYGJ57.cjs +21 -0
- package/dist/theme-YJE6HEON.js +21 -0
- package/dist/tools-3PPTTKFV.js +9 -0
- package/dist/tools-THIQA7WC.cjs +9 -0
- package/dist/ui/questionModal.cjs +9 -6
- package/dist/ui/questionModal.js +8 -5
- package/dist/undo-PRTEGL2J.cjs +10 -0
- package/dist/undo-SZEHLX7X.js +10 -0
- package/dist/{update-TVAJMMBC.js → update-53WMKYVS.js} +1 -1
- package/dist/{update-Z6BIIQDC.cjs → update-NV6QRYY7.cjs} +1 -1
- package/dist/web-3BA2WV37.cjs +37 -0
- package/dist/web-6FYGBX5K.js +37 -0
- package/dist/workspaceSafety-MDJGHK6D.cjs +9 -0
- package/dist/workspaceSafety-XOUMUBVB.js +9 -0
- package/dist/yolo-GF2YD7ZI.js +9 -0
- package/dist/yolo-OGDA7HNC.cjs +9 -0
- package/dist/yoloMode-3DJDA75U.cjs +17 -0
- package/dist/yoloMode-4JOOSU26.js +17 -0
- package/package.json +46 -49
- package/dist/AgentRegistry-NQCLWABO.cjs +0 -10
- package/dist/CommunitySkillsCache-6QPRMTJO.js +0 -8
- package/dist/CommunitySkillsCache-GTQMOCCO.cjs +0 -8
- package/dist/GitHubRegistryFetcher-S7QFUEKV.cjs +0 -7
- package/dist/HookManager-Q2KYMCP4.cjs +0 -7
- package/dist/HookManager-TTP4Y6DC.js +0 -7
- package/dist/LearnAdvisor-A4Q5PPBI.js +0 -9
- package/dist/LearnAdvisor-GASQD7HT.cjs +0 -9
- package/dist/McpClientManager-RKD7C6OY.cjs +0 -8
- package/dist/MemoryManager-GUNLRP5S.js +0 -8
- package/dist/MemoryManager-TNSGKDKX.cjs +0 -8
- package/dist/PermissionManager-KMN53FJP.cjs +0 -11
- package/dist/ProviderFactory-MR5B23QJ.js +0 -9
- package/dist/ProviderFactory-VFGCJJX6.cjs +0 -9
- package/dist/SessionManager-FEUAU3ZJ.cjs +0 -10
- package/dist/SessionManager-IKWAK2PI.js +0 -10
- package/dist/SkillsRegistry-KPQFTRIT.cjs +0 -9
- package/dist/SkillsRegistry-XJSKPDF2.js +0 -9
- package/dist/SubAgent-NYH6GWQ3.js +0 -11
- package/dist/SubAgent-PZKBDUBA.cjs +0 -11
- package/dist/SyncApiClient-ZNYMT36M.cjs +0 -11
- package/dist/about-HHTF2YFL.js +0 -12
- package/dist/about-JGRVNNQC.cjs +0 -12
- package/dist/actionExecutor-U6IBN2TU.cjs +0 -19
- package/dist/actionExecutor-XT5FW3W6.js +0 -19
- package/dist/add-dir-247K3XRY.js +0 -10
- package/dist/add-dir-GS4DXKKH.cjs +0 -10
- package/dist/agents-R6ZEFTVR.cjs +0 -12
- package/dist/agents-WJPQWQF2.js +0 -12
- package/dist/agents-new-HKVEIBDJ.js +0 -14
- package/dist/agents-new-X6GTHIO6.cjs +0 -14
- package/dist/autoSkill-H4T6VVDA.cjs +0 -20
- package/dist/automode-BC6NVECO.js +0 -10
- package/dist/automode-WN2RSOGW.cjs +0 -10
- package/dist/chunk-33A755XB.cjs +0 -168
- package/dist/chunk-3OTU3RS3.cjs +0 -1607
- package/dist/chunk-4PKF7WPD.js +0 -100
- package/dist/chunk-ALYU6VTM.js +0 -105
- package/dist/chunk-AS6RTLN7.cjs +0 -203
- package/dist/chunk-BWN2CLLM.cjs +0 -298
- package/dist/chunk-HQ7YZKXE.js +0 -168
- package/dist/chunk-HVKOZ2VP.cjs +0 -115
- package/dist/chunk-J6QET7EF.cjs +0 -454
- package/dist/chunk-LWUJFGOZ.js +0 -115
- package/dist/chunk-P47WPOXN.js +0 -298
- package/dist/chunk-PRRCJFU3.cjs +0 -85
- package/dist/chunk-RO6WYEWH.js +0 -454
- package/dist/chunk-SYVYLZZF.cjs +0 -24
- package/dist/chunk-T73IDKDF.js +0 -111
- package/dist/chunk-YRLYSQEQ.cjs +0 -105
- package/dist/chunk-ZQE72E6W.cjs +0 -100
- package/dist/chunk-ZVY2XD6T.js +0 -1607
- package/dist/clear-UO4MNWZW.cjs +0 -12
- package/dist/clear-ZJ5NYP6E.js +0 -12
- package/dist/communityInstaller-PVSOFDZD.cjs +0 -19
- package/dist/completion-MMF2PN2H.js +0 -14
- package/dist/completion-UI5WKHXI.cjs +0 -14
- package/dist/config-E7RINK4R.cjs +0 -18
- package/dist/constants-6CPCJ3DY.cjs +0 -21
- package/dist/export-N4XIVDSL.cjs +0 -12
- package/dist/export-W22L4D5C.js +0 -12
- package/dist/feedback-DR6ADSNE.cjs +0 -15
- package/dist/feedback-FEEAP4QW.js +0 -15
- package/dist/filesystem-3SGCW2BF.js +0 -10
- package/dist/filesystem-MCFXJQ6R.cjs +0 -10
- package/dist/help-AQHGTS7P.cjs +0 -12
- package/dist/history-NIUDRMKA.cjs +0 -14
- package/dist/hooks-2EY4IPKV.js +0 -13
- package/dist/hooks-LJVORRIG.cjs +0 -13
- package/dist/i18n-ARDG2SMC.cjs +0 -33
- package/dist/ide-GFW6IJHD.js +0 -12
- package/dist/ide-N2ZNSSB3.cjs +0 -12
- package/dist/import-DFVN3KNZ.js +0 -10
- package/dist/import-ZS6DPGU5.cjs +0 -10
- package/dist/init-PY75HA3S.cjs +0 -10
- package/dist/init-QNMWLAVY.js +0 -10
- package/dist/language-5UE4G2BT.cjs +0 -18
- package/dist/language-UXMHEZUJ.js +0 -18
- package/dist/learn-HJ3FLNZC.cjs +0 -20
- package/dist/learn-MVYS3RU5.js +0 -20
- package/dist/localProjectPermissions-N77HA3XK.js +0 -18
- package/dist/localProjectPermissions-UFSMNTBJ.cjs +0 -18
- package/dist/login-DSE7H63A.js +0 -20
- package/dist/login-V3MEWPKN.cjs +0 -20
- package/dist/logout-BMVCLKKW.js +0 -18
- package/dist/logout-XEG7FHOZ.cjs +0 -18
- package/dist/mcp-PYUR4PHO.js +0 -18
- package/dist/mcp-SG6JFLGC.cjs +0 -18
- package/dist/memory-4ZMMEZ2Z.js +0 -10
- package/dist/memory-QSGMVVGH.cjs +0 -10
- package/dist/model-NANLBZ4Z.cjs +0 -10
- package/dist/model-ZXNV4AF7.js +0 -10
- package/dist/new-5QJY5JP2.js +0 -12
- package/dist/new-PMMG55UX.cjs +0 -12
- package/dist/permissions-LECTCJ4H.cjs +0 -13
- package/dist/permissions-VP5VGIBL.js +0 -13
- package/dist/plan-G5CEKJI4.js +0 -11
- package/dist/plan-QKOHE3LH.cjs +0 -11
- package/dist/quit-BKOOPHU5.cjs +0 -10
- package/dist/quit-FVFNYACP.js +0 -10
- package/dist/resume-EXFQSQPH.js +0 -13
- package/dist/resume-PP2IQM5S.cjs +0 -13
- package/dist/search-C56FBN67.cjs +0 -17
- package/dist/search-XGZDYBF4.js +0 -17
- package/dist/sessions-54KI3F2Q.js +0 -10
- package/dist/sessions-DDTSPNVW.cjs +0 -10
- package/dist/settings-BDO37TTO.cjs +0 -30
- package/dist/settings-FHRDFPLK.js +0 -30
- package/dist/share-IERCTBGN.cjs +0 -14
- package/dist/share-TGROUE6R.js +0 -14
- package/dist/skills-OM4IGBAA.cjs +0 -26
- package/dist/skills-S3GRN323.js +0 -26
- package/dist/skills-new-ALD2PTHN.js +0 -15
- package/dist/skills-new-PWLKK7GW.cjs +0 -15
- package/dist/slashCommands-L4ZD33LJ.js +0 -75
- package/dist/slashCommands-YY2VUUDF.cjs +0 -75
- package/dist/status-3PC5XWSS.cjs +0 -11
- package/dist/status-KCLVOYPD.js +0 -11
- package/dist/sync-6SDWG5RK.js +0 -18
- package/dist/sync-7JMZVEQD.cjs +0 -40
- package/dist/sync-WHURZL3U.cjs +0 -18
- package/dist/theme-BE5A4FPN.cjs +0 -18
- package/dist/theme-YMFCQP7J.js +0 -18
- package/dist/undo-KZHUUZTD.cjs +0 -10
- package/dist/undo-NEIEHQVX.js +0 -10
|
@@ -1,34 +1,46 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
30
|
+
} from "./chunk-FP46B4X3.js";
|
|
14
31
|
import {
|
|
15
32
|
showInput,
|
|
16
33
|
showModal
|
|
17
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-DLP436GI.js";
|
|
18
35
|
import {
|
|
19
36
|
getTheme,
|
|
20
37
|
hexToRgb,
|
|
21
38
|
isThemeInitialized
|
|
22
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-JAQO6XDB.js";
|
|
23
40
|
import {
|
|
24
|
-
AUTOHAND_PATHS
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2237
|
-
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
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
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
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
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
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
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
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/
|
|
2300
|
-
var
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
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
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
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(
|
|
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 ??
|
|
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 &&
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 "
|
|
3086
|
-
const
|
|
3087
|
-
if (
|
|
3088
|
-
|
|
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
|
-
|
|
3102
|
-
const
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
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 (
|
|
3569
|
-
|
|
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
|
-
|
|
3655
|
-
|
|
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 `
|
|
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
|
|
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
|
-
|
|
4539
|
+
this.onFileModified?.(todoPath, "modify");
|
|
3706
4540
|
const total = allTodos.length;
|
|
3707
|
-
|
|
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
|
|
3710
|
-
const
|
|
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
|
-
|
|
3715
|
-
|
|
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
|
-
|
|
4571
|
+
outputLines.push("", chalk2.yellow(" \u{1F504} Active Tasks:"));
|
|
3718
4572
|
for (const task of inProgress) {
|
|
3719
|
-
|
|
4573
|
+
outputLines.push(chalk2.yellow(` \u2022 ${titleOf(task)}`));
|
|
3720
4574
|
}
|
|
3721
4575
|
}
|
|
3722
|
-
|
|
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
|
-
|
|
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: ${
|
|
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
|
|
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-
|
|
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
|
|
4122
|
-
|
|
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
|
|
4135
|
-
|
|
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.
|
|
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
|
-
|
|
5493
|
+
outputLines.push(theme.fg("muted", ` Added ${theme.fg("diffAdded", addText)}, removed ${theme.fg("diffRemoved", delText)}`));
|
|
4337
5494
|
} else {
|
|
4338
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5592
|
+
outputLines.push(prefix + content);
|
|
4436
5593
|
} else {
|
|
4437
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5604
|
+
outputLines.push(prefix + content);
|
|
4448
5605
|
} else {
|
|
4449
|
-
|
|
5606
|
+
outputLines.push(chalk2.gray(` ${lineNumStr} `) + ` ${highlighted}`);
|
|
4450
5607
|
}
|
|
4451
5608
|
}
|
|
4452
5609
|
}
|
|
4453
5610
|
}
|
|
4454
|
-
|
|
5611
|
+
return outputLines.join("\n");
|
|
4455
5612
|
}
|
|
4456
5613
|
};
|
|
4457
5614
|
|
|
4458
5615
|
export {
|
|
4459
5616
|
getAutoCommitInfo,
|
|
4460
|
-
|
|
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
|