autohand-cli 0.7.14 → 0.8.2
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 +97 -2
- package/assets/icon.png +0 -0
- package/dist/AgentRegistry-7LDL5HJH.js +10 -0
- package/dist/AgentRegistry-NQCLWABO.cjs +10 -0
- package/dist/{AutomodeManager-NGRAO2MH.js → AutomodeManager-MWLKGPZK.js} +2 -0
- package/dist/{AutomodeManager-ZKQMBM4T.cjs → AutomodeManager-NYIZNODK.cjs} +3 -1
- package/dist/CommunitySkillsCache-6QPRMTJO.js +8 -0
- package/dist/CommunitySkillsCache-GTQMOCCO.cjs +8 -0
- package/dist/{GitHubRegistryFetcher-US2JJID4.js → GitHubRegistryFetcher-6JQ5JEDZ.js} +1 -0
- package/dist/{GitHubRegistryFetcher-K744NNAJ.cjs → GitHubRegistryFetcher-S7QFUEKV.cjs} +1 -0
- package/dist/HookManager-Q2KYMCP4.cjs +7 -0
- package/dist/HookManager-TTP4Y6DC.js +7 -0
- package/dist/ImportWizard-35YBJ4AM.cjs +466 -0
- package/dist/ImportWizard-XH7CINCH.js +466 -0
- package/dist/LearnAdvisor-A4Q5PPBI.js +9 -0
- package/dist/LearnAdvisor-GASQD7HT.cjs +9 -0
- package/dist/McpClientManager-7RM6YT35.js +8 -0
- package/dist/McpClientManager-RKD7C6OY.cjs +8 -0
- package/dist/MemoryManager-GUNLRP5S.js +8 -0
- package/dist/MemoryManager-TNSGKDKX.cjs +8 -0
- package/dist/{PermissionManager-U5OMGR3L.js → PermissionManager-ATUV34LQ.js} +4 -3
- package/dist/PermissionManager-KMN53FJP.cjs +11 -0
- package/dist/ProjectProfiler-UMJJSOCE.js +194 -0
- package/dist/ProjectProfiler-ZDWR2ODG.cjs +194 -0
- package/dist/ProviderFactory-MR5B23QJ.js +9 -0
- package/dist/ProviderFactory-VFGCJJX6.cjs +9 -0
- package/dist/SessionManager-FEUAU3ZJ.cjs +10 -0
- package/dist/SessionManager-IKWAK2PI.js +10 -0
- package/dist/SkillsRegistry-KPQFTRIT.cjs +9 -0
- package/dist/SkillsRegistry-XJSKPDF2.js +9 -0
- package/dist/SubAgent-NYH6GWQ3.js +11 -0
- package/dist/SubAgent-PZKBDUBA.cjs +11 -0
- package/dist/{SyncApiClient-AYXYSOJM.js → SyncApiClient-LVIO4C2S.js} +1 -0
- package/dist/{SyncApiClient-ID3KXEMA.cjs → SyncApiClient-ZNYMT36M.cjs} +1 -0
- package/dist/about-HHTF2YFL.js +12 -0
- package/dist/about-JGRVNNQC.cjs +12 -0
- package/dist/actionExecutor-U6IBN2TU.cjs +19 -0
- package/dist/actionExecutor-XT5FW3W6.js +19 -0
- package/dist/add-dir-247K3XRY.js +10 -0
- package/dist/add-dir-GS4DXKKH.cjs +10 -0
- package/dist/agents/builtin/code-cleaner.md +14 -0
- package/dist/agents/builtin/docs-writer.md +14 -0
- package/dist/agents/builtin/researcher.md +14 -0
- package/dist/agents/builtin/reviewer.md +15 -0
- package/dist/agents/builtin/tester.md +15 -0
- package/dist/agents/builtin/todo-resolver.md +15 -0
- package/dist/agents-R6ZEFTVR.cjs +12 -0
- package/dist/agents-WJPQWQF2.js +12 -0
- package/dist/agents-new-HKVEIBDJ.js +14 -0
- package/dist/agents-new-X6GTHIO6.cjs +14 -0
- package/dist/assets/icon.png +0 -0
- package/dist/autoSkill-6TGBTEQD.js +20 -0
- package/dist/autoSkill-H4T6VVDA.cjs +20 -0
- package/dist/automode-BC6NVECO.js +10 -0
- package/dist/automode-WN2RSOGW.cjs +10 -0
- package/dist/{cc-UTTLESTY.js → cc-7LEIJ3KF.js} +1 -0
- package/dist/{cc-2W6M7J45.cjs → cc-Q5MM4AWC.cjs} +1 -0
- package/dist/{chunk-N4ZSG6JJ.cjs → chunk-22D2CNTP.cjs} +2 -2
- package/dist/chunk-245KJE5Y.cjs +55 -0
- package/dist/chunk-24QIWILL.js +51 -0
- package/dist/{chunk-MYNHJHDZ.js → chunk-2AA5MFES.js} +1 -1
- package/dist/{chunk-TSY7JHIV.cjs → chunk-33A755XB.cjs} +2 -2
- package/dist/chunk-33RSHBDH.js +131 -0
- package/dist/{chunk-QRGPAUST.js → chunk-34M3HWLR.js} +2 -2
- package/dist/{chunk-U7CZFKPL.cjs → chunk-3K2ESU53.cjs} +2 -2
- package/dist/{chunk-SIGWDEPS.cjs → chunk-3L53OA4E.cjs} +10 -10
- package/dist/chunk-3OEDGIFW.js +42 -0
- package/dist/chunk-3OTU3RS3.cjs +1607 -0
- package/dist/{chunk-MNSTWHK3.cjs → chunk-3PCTTUNW.cjs} +11 -11
- package/dist/{chunk-P5VDZ6PV.js → chunk-3PDTTAKJ.js} +1 -1
- package/dist/chunk-47CKWKEX.cjs +59 -0
- package/dist/{chunk-Z4J4W6YQ.cjs → chunk-4JNNTOGF.cjs} +2 -48
- package/dist/{chunk-GVZPIQWB.js → chunk-4PKF7WPD.js} +11 -5
- package/dist/{chunk-3S4DEIJP.cjs → chunk-5IXII4HX.cjs} +2 -2
- package/dist/{chunk-CRQKDBLD.js → chunk-5P2NXKP3.js} +98 -64
- package/dist/{chunk-DSKVMFRM.cjs → chunk-643VRA5S.cjs} +12 -4
- package/dist/{chunk-VVBBEYTH.cjs → chunk-6HYLHBQG.cjs} +10 -10
- package/dist/{chunk-L5ZFPWHY.js → chunk-6OYHF6MF.js} +12 -4
- package/dist/{chunk-BPTBKO7D.js → chunk-6RF7UKUS.js} +224 -37
- package/dist/{chunk-YHGTBPEC.js → chunk-6ZCULLCA.js} +1 -1
- package/dist/{chunk-VHBUKGRG.js → chunk-72FKPBT5.js} +4 -4
- package/dist/{chunk-2U5HFVRO.cjs → chunk-7BTSG4ME.cjs} +5165 -2194
- package/dist/chunk-7UOUW76C.js +603 -0
- package/dist/{chunk-FEVHH525.cjs → chunk-A4IJHHV7.cjs} +11 -5
- package/dist/{chunk-B6EBHCK2.cjs → chunk-AEJH23FO.cjs} +6 -6
- package/dist/{chunk-WLTVF77A.js → chunk-ALYU6VTM.js} +1 -1
- package/dist/{chunk-NMWEDN4Z.js → chunk-APIXPPMT.js} +5165 -2194
- package/dist/chunk-AS6RTLN7.cjs +203 -0
- package/dist/{chunk-BHV7CBNT.js → chunk-AYS2ASM7.js} +1 -1
- package/dist/{chunk-GRSVQ5YZ.js → chunk-AYSFIUFW.js} +44 -12
- package/dist/{chunk-NUHYCFHW.cjs → chunk-BVKXEQVG.cjs} +54 -65
- package/dist/chunk-BWN2CLLM.cjs +298 -0
- package/dist/{chunk-4HA7IHLJ.cjs → chunk-C5IJIM2V.cjs} +38 -16
- package/dist/{chunk-SRLY7K6J.js → chunk-CAMZTXV6.js} +2 -2
- package/dist/chunk-CDBPBM2K.cjs +29 -0
- package/dist/chunk-CNBKZEX5.cjs +109 -0
- package/dist/{chunk-WQUQ5JMM.js → chunk-CWMZKFTT.js} +4 -4
- package/dist/{chunk-CKN2BLHK.cjs → chunk-CZXGCVTR.cjs} +2 -2
- package/dist/{chunk-SZP4ULM5.cjs → chunk-DJDE4DTT.cjs} +17 -17
- package/dist/chunk-DN573ME7.cjs +1675 -0
- package/dist/chunk-DRE2RXBZ.js +4498 -0
- package/dist/chunk-DSPQEHDT.js +29 -0
- package/dist/{chunk-SFNT5DYE.cjs → chunk-DVUHHH3B.cjs} +4 -4
- package/dist/chunk-DVZOENQ7.cjs +58 -0
- package/dist/{chunk-PWLLLJHU.js → chunk-EGFT4PGW.js} +3 -1
- package/dist/chunk-EGMZDTSL.js +55 -0
- package/dist/chunk-EZMINVLU.js +123 -0
- package/dist/chunk-FHK7UDOJ.cjs +42 -0
- package/dist/{chunk-KWRUQRXR.js → chunk-FKSDEWDH.js} +44 -10
- package/dist/chunk-FMB3TSWP.cjs +218 -0
- package/dist/chunk-FW774QXH.js +1838 -0
- package/dist/{chunk-MY3TZER2.js → chunk-G27PQQFD.js} +1 -1
- package/dist/{chunk-Y2ZSH3YF.cjs → chunk-G3V4SFET.cjs} +57 -23
- package/dist/chunk-G4CAKI3V.js +58 -0
- package/dist/{chunk-FB6JWNJS.js → chunk-GBHDROGL.js} +54 -65
- package/dist/{chunk-DEAEO7RI.js → chunk-GJH7XMSK.js} +15 -1
- package/dist/chunk-GLBAF54O.js +218 -0
- package/dist/{chunk-S47TCZDL.js → chunk-H5SWOLG6.js} +7 -7
- package/dist/chunk-HBXAA3XB.js +83 -0
- package/dist/{chunk-63BXZQZW.js → chunk-HIVRCQS2.js} +26 -4
- package/dist/{chunk-WOGJXDBU.cjs → chunk-HLHTG5ZU.cjs} +18 -4
- package/dist/{chunk-FK2DVRPJ.js → chunk-HLQV64Y5.js} +170 -4
- package/dist/chunk-HOAHWIQ5.cjs +260 -0
- package/dist/{chunk-D2XFTCRP.js → chunk-HQ7YZKXE.js} +1 -1
- package/dist/{chunk-L42HTMMR.cjs → chunk-HTLINWX6.cjs} +2 -2
- package/dist/{chunk-PKOAXQKW.cjs → chunk-HVKOZ2VP.cjs} +11 -11
- package/dist/chunk-HXGBSJL5.cjs +27 -0
- package/dist/chunk-I5IW3T2Y.js +310 -0
- package/dist/chunk-IETRBBMP.cjs +603 -0
- package/dist/{chunk-MTALRU7R.cjs → chunk-IFFXSTOM.cjs} +3 -3
- package/dist/{chunk-V7YTCNMN.cjs → chunk-IKGWDOGU.cjs} +174 -8
- package/dist/chunk-IQ5RXU6O.js +1675 -0
- package/dist/{chunk-ZBIBLOZL.js → chunk-IVM5F2AE.js} +500 -317
- package/dist/chunk-J4Q7XR3G.js +260 -0
- package/dist/{chunk-XL77XYI2.cjs → chunk-J6QET7EF.cjs} +27 -7
- package/dist/{chunk-TQB222ZB.js → chunk-JCLYQ2JC.js} +2 -2
- package/dist/chunk-JSBRDJBE.js +30 -0
- package/dist/{chunk-XPOHYKR3.js → chunk-JX3DFKBI.js} +2 -2
- package/dist/chunk-JYTDYJVW.js +27 -0
- package/dist/{chunk-6SHHB2VD.js → chunk-KPELYZ6L.js} +2 -2
- package/dist/{chunk-ZLOTP56B.cjs → chunk-KWXVKLQ5.cjs} +5 -5
- package/dist/chunk-L3WAH3EM.cjs +131 -0
- package/dist/{chunk-ZXIQCYYV.cjs → chunk-LA7H35XM.cjs} +9 -9
- package/dist/chunk-LENHP55G.cjs +1838 -0
- package/dist/chunk-LJFUXC56.cjs +123 -0
- package/dist/{chunk-R5OO7MEB.cjs → chunk-LNMYK2F5.cjs} +22 -22
- package/dist/chunk-LQGVEP3E.js +109 -0
- package/dist/{chunk-KXAAEROY.js → chunk-LWUJFGOZ.js} +2 -2
- package/dist/chunk-MAKMSQMQ.cjs +504 -0
- package/dist/{chunk-BG4OQUKP.js → chunk-MBBY4ZIK.js} +1 -1
- package/dist/chunk-MSED7RH2.cjs +267 -0
- package/dist/{chunk-NMGF2KUN.js → chunk-MYISNQH4.js} +1 -1
- package/dist/chunk-N23UAW4I.js +59 -0
- package/dist/chunk-N254NRHT.cjs +30 -0
- package/dist/{chunk-TOTDRAWG.js → chunk-NDMIPTV4.js} +1 -1
- package/dist/{chunk-AIH6GUGB.cjs → chunk-NNPAM4HC.cjs} +5 -5
- package/dist/{chunk-HSPWX4Z2.cjs → chunk-O4IF4NJT.cjs} +231 -44
- package/dist/{chunk-DZHR34H6.cjs → chunk-OGV4WJ5L.cjs} +8 -8
- package/dist/chunk-OHUZKDGX.js +348 -0
- package/dist/{chunk-BRXIEKJ3.cjs → chunk-OLSBBZW6.cjs} +5 -5
- package/dist/{chunk-MILZEEUV.js → chunk-OOKY3HPZ.js} +9 -3
- package/dist/chunk-P47WPOXN.js +298 -0
- package/dist/{chunk-ULMPJUJW.cjs → chunk-PRRCJFU3.cjs} +23 -23
- package/dist/{chunk-SMHY3Q7B.cjs → chunk-Q7XSCYND.cjs} +54 -22
- package/dist/chunk-QCLYBIMM.cjs +51 -0
- package/dist/chunk-QMAKTSZB.cjs +48 -0
- package/dist/{chunk-DTFR3WD6.js → chunk-QNGEW5TC.js} +1 -1
- package/dist/chunk-QOXPOR5D.js +267 -0
- package/dist/chunk-R33VKSH5.cjs +348 -0
- package/dist/{chunk-RJP3SZ7Q.cjs → chunk-RD5XAJR2.cjs} +492 -309
- package/dist/chunk-RGR6ME5J.cjs +844 -0
- package/dist/{chunk-EOGKE5GD.cjs → chunk-RKJTGGMU.cjs} +221 -126
- package/dist/{chunk-GD4AFYJ3.js → chunk-RO6WYEWH.js} +24 -4
- package/dist/chunk-S52YW5ZQ.js +844 -0
- package/dist/{chunk-6DWXHBAY.js → chunk-SAHBLB3E.js} +222 -127
- package/dist/{chunk-JHOQABEF.js → chunk-SCXX4LW5.js} +5 -5
- package/dist/{chunk-GIZL57FE.cjs → chunk-SEKD5FH3.cjs} +3 -1
- package/dist/{chunk-JWPI6O5Z.js → chunk-SKV2F3NM.js} +31 -4
- package/dist/{chunk-FHUNAB2K.cjs → chunk-SKYG33B2.cjs} +33 -6
- package/dist/{chunk-BISFR6ZL.js → chunk-SLQAYV3W.js} +1 -1
- package/dist/{chunk-RFNCTE4V.cjs → chunk-SYVYLZZF.cjs} +2 -2
- package/dist/{chunk-3XJD56Z4.js → chunk-T73IDKDF.js} +10 -3
- package/dist/chunk-TBEGGJNC.cjs +310 -0
- package/dist/{chunk-RRZS5A53.js → chunk-TNZRZQ7Q.js} +1 -1
- package/dist/{chunk-CH4SPVFD.cjs → chunk-TXSDBGKX.cjs} +10 -3
- package/dist/chunk-U3WDY42C.cjs +42 -0
- package/dist/{chunk-425MT6Y5.cjs → chunk-U46VYPLR.cjs} +9 -9
- package/dist/{chunk-OLG7LZBD.js → chunk-VG34MG2U.js} +1 -1
- package/dist/{chunk-XDVG3NM4.js → chunk-W3X6PAC7.js} +2 -48
- package/dist/{chunk-LYMTYC67.js → chunk-WHE2SWHU.js} +2 -2
- package/dist/chunk-WM5PAOTQ.cjs +4498 -0
- package/dist/chunk-WNUVPKBW.js +42 -0
- package/dist/{chunk-EV53SLSB.cjs → chunk-WPVWQSL7.cjs} +4 -4
- package/dist/chunk-WQ3VJXZB.js +118 -0
- package/dist/{chunk-HMRDNRTH.js → chunk-X2MSVKDV.js} +2 -2
- package/dist/chunk-X3WS5LDG.js +504 -0
- package/dist/{chunk-43XS26AQ.cjs → chunk-X5VSP65C.cjs} +4 -4
- package/dist/{chunk-DSCQPWUB.cjs → chunk-X5YJ34FZ.cjs} +15 -15
- package/dist/chunk-XAV24VYN.js +48 -0
- package/dist/chunk-XDLH4EDL.cjs +118 -0
- package/dist/{chunk-X765A7J5.js → chunk-XRZEUWKF.js} +1 -1
- package/dist/{chunk-ZKZRFH37.cjs → chunk-XTB6VJVQ.cjs} +6 -6
- package/dist/{chunk-H3GBSPK5.js → chunk-XX2ZO7DS.js} +14 -6
- package/dist/{chunk-RUZB43HU.cjs → chunk-Y72HH2TF.cjs} +22 -14
- package/dist/chunk-YFXTE422.cjs +92 -0
- package/dist/{chunk-OSUWEUZE.js → chunk-YGN4CQIP.js} +1 -1
- package/dist/{chunk-KC5FPUOF.cjs → chunk-YRLYSQEQ.cjs} +2 -2
- package/dist/{chunk-3KBBARKO.js → chunk-YZXUDM5X.js} +85 -28
- package/dist/chunk-Z36XBUMX.cjs +83 -0
- package/dist/chunk-ZK6HOR62.js +92 -0
- package/dist/{chunk-PDKNHU5G.cjs → chunk-ZQE72E6W.cjs} +22 -16
- package/dist/chunk-ZVY2XD6T.js +1607 -0
- package/dist/{chunk-XBUMKEFN.cjs → chunk-ZYQMLKOK.cjs} +91 -34
- package/dist/clear-UO4MNWZW.cjs +12 -0
- package/dist/clear-ZJ5NYP6E.js +12 -0
- package/dist/communityInstaller-6KCFN7YZ.js +19 -0
- package/dist/communityInstaller-PVSOFDZD.cjs +19 -0
- package/dist/completion-MMF2PN2H.js +14 -0
- package/dist/completion-UI5WKHXI.cjs +14 -0
- package/dist/config-E7RINK4R.cjs +18 -0
- package/dist/config-ZN66VXPS.js +18 -0
- package/dist/constants-6CPCJ3DY.cjs +21 -0
- package/dist/{constants-V6J54N3X.js → constants-UFM5B232.js} +2 -1
- package/dist/{defaultHooks-WLMRQUXG.cjs → defaultHooks-RCXPHF4M.cjs} +3 -1
- package/dist/{defaultHooks-R56VYG7I.js → defaultHooks-RDRMER3Z.js} +2 -0
- package/dist/export-N4XIVDSL.cjs +12 -0
- package/dist/export-W22L4D5C.js +12 -0
- package/dist/extractSessionMemories-SDW2MVBQ.cjs +7 -0
- package/dist/extractSessionMemories-V7K42ZHW.js +7 -0
- package/dist/feedback-DR6ADSNE.cjs +15 -0
- package/dist/feedback-FEEAP4QW.js +15 -0
- package/dist/filesystem-3SGCW2BF.js +10 -0
- package/dist/filesystem-MCFXJQ6R.cjs +10 -0
- package/dist/formatters-6K7QVWQL.cjs +10 -0
- package/dist/formatters-DQHO5I36.js +10 -0
- package/dist/{help-LKKQU2TN.js → help-2BLR7L43.js} +3 -2
- package/dist/help-AQHGTS7P.cjs +12 -0
- package/dist/{history-AV4XBFRK.js → history-5FZ3M2AK.js} +3 -2
- package/dist/history-NIUDRMKA.cjs +14 -0
- package/dist/hooks-2EY4IPKV.js +13 -0
- package/dist/hooks-LJVORRIG.cjs +13 -0
- package/dist/i18n-ARDG2SMC.cjs +33 -0
- package/dist/{i18n-BSAPXM56.js → i18n-K7QOWIBH.js} +2 -1
- package/dist/ide-GFW6IJHD.js +12 -0
- package/dist/ide-N2ZNSSB3.cjs +12 -0
- package/dist/import-DFVN3KNZ.js +10 -0
- package/dist/import-QEME3E4T.cjs +170 -0
- package/dist/import-UXM3VK7B.js +170 -0
- package/dist/import-ZS6DPGU5.cjs +10 -0
- package/dist/index.cjs +11233 -11804
- package/dist/index.js +12594 -13165
- package/dist/init-PY75HA3S.cjs +10 -0
- package/dist/init-QNMWLAVY.js +10 -0
- package/dist/language-5UE4G2BT.cjs +18 -0
- package/dist/language-UXMHEZUJ.js +18 -0
- package/dist/learn-HJ3FLNZC.cjs +20 -0
- package/dist/learn-MVYS3RU5.js +20 -0
- package/dist/{lint-44UQJ673.cjs → lint-D5UOJWIK.cjs} +1 -0
- package/dist/{lint-TA2ZHVLM.js → lint-NJPZWVN2.js} +1 -0
- package/dist/{localProjectPermissions-WQYMGI42.js → localProjectPermissions-N77HA3XK.js} +3 -2
- package/dist/localProjectPermissions-UFSMNTBJ.cjs +18 -0
- package/dist/login-DSE7H63A.js +20 -0
- package/dist/login-V3MEWPKN.cjs +20 -0
- package/dist/logout-BMVCLKKW.js +18 -0
- package/dist/logout-XEG7FHOZ.cjs +18 -0
- package/dist/mcp-PYUR4PHO.js +18 -0
- package/dist/mcp-SG6JFLGC.cjs +18 -0
- package/dist/{mcp-install-2KVKRAMQ.cjs → mcp-install-G27HSS3Z.cjs} +26 -14
- package/dist/{mcp-install-77UXRN6R.js → mcp-install-VESN42PI.js} +21 -9
- package/dist/memory-4ZMMEZ2Z.js +10 -0
- package/dist/memory-QSGMVVGH.cjs +10 -0
- package/dist/message-JUBOK2VU.js +9 -0
- package/dist/message-ZJ5AYAMT.cjs +9 -0
- package/dist/model-NANLBZ4Z.cjs +10 -0
- package/dist/model-ZXNV4AF7.js +10 -0
- package/dist/new-5QJY5JP2.js +12 -0
- package/dist/new-PMMG55UX.cjs +12 -0
- package/dist/{patch-BAAQIYSW.js → patch-5F6VBIT3.js} +2 -0
- package/dist/{patch-J32X2QQP.cjs → patch-MOD7QC3D.cjs} +3 -1
- package/dist/permissions-LECTCJ4H.cjs +13 -0
- package/dist/permissions-VP5VGIBL.js +13 -0
- package/dist/{plan-JFGNRL2S.js → plan-G5CEKJI4.js} +1 -0
- package/dist/{plan-B3CW5DXJ.cjs → plan-QKOHE3LH.cjs} +1 -0
- package/dist/quit-BKOOPHU5.cjs +10 -0
- package/dist/quit-FVFNYACP.js +10 -0
- package/dist/registry-KWZGYJC2.js +2108 -0
- package/dist/registry-YN4FQPOO.cjs +2108 -0
- package/dist/resume-EXFQSQPH.js +13 -0
- package/dist/resume-PP2IQM5S.cjs +13 -0
- package/dist/search-C56FBN67.cjs +17 -0
- package/dist/search-XGZDYBF4.js +17 -0
- package/dist/{session-T3TAZ5ZU.cjs → session-BSU2L5UI.cjs} +1 -0
- package/dist/{session-H5QWKE5E.js → session-SZMRN5KG.js} +1 -0
- package/dist/sessions-54KI3F2Q.js +10 -0
- package/dist/sessions-DDTSPNVW.cjs +10 -0
- package/dist/settings-BDO37TTO.cjs +30 -0
- package/dist/settings-FHRDFPLK.js +30 -0
- package/dist/share-IERCTBGN.cjs +14 -0
- package/dist/share-TGROUE6R.js +14 -0
- package/dist/skills-6OL4OSGA.js +76 -0
- package/dist/skills-FYY6F2WV.cjs +76 -0
- package/dist/skills-OM4IGBAA.cjs +26 -0
- package/dist/skills-S3GRN323.js +26 -0
- package/dist/{skills-install-MQINL3EC.js → skills-install-6CSWC24P.js} +97 -26
- package/dist/{skills-install-IKJZN4G2.cjs → skills-install-O3LZ2ETC.cjs} +106 -35
- package/dist/skills-new-ALD2PTHN.js +15 -0
- package/dist/skills-new-PWLKK7GW.cjs +15 -0
- package/dist/slashCommands-L4ZD33LJ.js +75 -0
- package/dist/slashCommands-YY2VUUDF.cjs +75 -0
- package/dist/status-3PC5XWSS.cjs +11 -0
- package/dist/status-KCLVOYPD.js +11 -0
- package/dist/sync-6SDWG5RK.js +18 -0
- package/dist/sync-7JMZVEQD.cjs +40 -0
- package/dist/{sync-EXYX7HXW.js → sync-KWX67OUN.js} +3 -2
- package/dist/sync-WHURZL3U.cjs +18 -0
- package/dist/tasks-5FPBIFLC.js +9 -0
- package/dist/tasks-TXGKGNH6.cjs +9 -0
- package/dist/team-5YXP3JGR.js +9 -0
- package/dist/team-IIWEZKNR.cjs +9 -0
- package/dist/teammate-2KMKJXAM.cjs +139 -0
- package/dist/teammate-L6EZQ3I2.js +139 -0
- package/dist/theme-BE5A4FPN.cjs +18 -0
- package/dist/theme-YMFCQP7J.js +18 -0
- package/dist/ui/questionModal.cjs +7 -25
- package/dist/ui/questionModal.js +6 -24
- package/dist/undo-KZHUUZTD.cjs +10 -0
- package/dist/undo-NEIEHQVX.js +10 -0
- package/dist/update-TVAJMMBC.js +82 -0
- package/dist/update-Z6BIIQDC.cjs +82 -0
- package/package.json +10 -3
- package/dist/CommunitySkillsCache-ILWHWE5P.js +0 -7
- package/dist/CommunitySkillsCache-KHC6RUJW.cjs +0 -7
- package/dist/HookManager-X47HCM5G.cjs +0 -6
- package/dist/HookManager-ZXKHCD7U.js +0 -6
- package/dist/MemoryManager-6ZT7IDO5.cjs +0 -7
- package/dist/MemoryManager-AJGS5AKB.js +0 -7
- package/dist/PermissionManager-HG6W2DGU.cjs +0 -10
- package/dist/SessionManager-BJ2G6VV4.cjs +0 -9
- package/dist/SessionManager-ENPGYK5J.js +0 -9
- package/dist/SkillsRegistry-6ZFOCT25.cjs +0 -8
- package/dist/SkillsRegistry-C2SHOZ5D.js +0 -8
- package/dist/about-3BJTNSLK.js +0 -11
- package/dist/about-EABQNJGV.cjs +0 -11
- package/dist/add-dir-7FD4DMDA.cjs +0 -9
- package/dist/add-dir-LOYJ4YB5.js +0 -9
- package/dist/agents-2Y6ASV7C.js +0 -10
- package/dist/agents-UOSPKLQL.cjs +0 -10
- package/dist/agents-new-23NSGAM5.js +0 -13
- package/dist/agents-new-WI2EL7IJ.cjs +0 -13
- package/dist/automode-LGWTY3UX.js +0 -9
- package/dist/automode-WLBQ7MN7.cjs +0 -9
- package/dist/chunk-5UBW2BGC.js +0 -33
- package/dist/chunk-I6DBWNLN.cjs +0 -169
- package/dist/chunk-IZBCMJHJ.cjs +0 -33
- package/dist/completion-7WGMHKOR.cjs +0 -13
- package/dist/completion-KH33NSGP.js +0 -13
- package/dist/constants-RBQTR32A.cjs +0 -20
- package/dist/export-3QN3IH7A.js +0 -11
- package/dist/export-BI54X3MP.cjs +0 -11
- package/dist/feedback-CI4OIPOS.cjs +0 -14
- package/dist/feedback-GFPL5STE.js +0 -14
- package/dist/formatters-N5IJKYZY.cjs +0 -8
- package/dist/formatters-UG6VZJJ5.js +0 -8
- package/dist/help-CWMUGD3V.cjs +0 -11
- package/dist/history-73VBEMSI.cjs +0 -13
- package/dist/hooks-62UDQBGH.cjs +0 -12
- package/dist/hooks-XORDJD5X.js +0 -12
- package/dist/i18n-X2IU2EZD.cjs +0 -32
- package/dist/ide-RPKZALQV.js +0 -11
- package/dist/ide-YMNXJB6A.cjs +0 -11
- package/dist/init-J5HR4R7U.js +0 -9
- package/dist/init-JCC7RVMC.cjs +0 -9
- package/dist/language-AZISJCEZ.js +0 -16
- package/dist/language-F65RA6FZ.cjs +0 -16
- package/dist/localProjectPermissions-2EATUDZM.cjs +0 -17
- package/dist/login-5HLPMECE.js +0 -18
- package/dist/login-ISWYYBXP.cjs +0 -18
- package/dist/logout-3EKZM5J3.cjs +0 -16
- package/dist/logout-GE7TSZ24.js +0 -16
- package/dist/mcp-EW64QRFA.cjs +0 -15
- package/dist/mcp-VHS7AMF2.js +0 -15
- package/dist/memory-2I473RU3.js +0 -9
- package/dist/memory-JZ6NPSP3.cjs +0 -9
- package/dist/model-GXZLARPT.js +0 -9
- package/dist/model-Y274DBDO.cjs +0 -9
- package/dist/new-BG5VIGZ7.cjs +0 -9
- package/dist/new-YXFDQOA7.js +0 -9
- package/dist/permissions-QILEAGBP.cjs +0 -12
- package/dist/permissions-WVEOVMWO.js +0 -12
- package/dist/quit-NC32OEJG.cjs +0 -9
- package/dist/quit-WRRIGU33.js +0 -9
- package/dist/resume-GJIKIDPR.cjs +0 -12
- package/dist/resume-RMJNCAOK.js +0 -12
- package/dist/search-UIWIXB73.js +0 -14
- package/dist/search-WQNXDA2E.cjs +0 -14
- package/dist/sessions-HPFX2GDD.js +0 -9
- package/dist/sessions-SAQU6MFA.cjs +0 -9
- package/dist/share-2WH5ZVOO.cjs +0 -13
- package/dist/share-PSSWWVV5.js +0 -13
- package/dist/skills-LJZA6PVJ.js +0 -13
- package/dist/skills-YTYGART7.cjs +0 -13
- package/dist/skills-new-3WCU3CWB.js +0 -14
- package/dist/skills-new-O5LFVFZU.cjs +0 -14
- package/dist/slashCommands-7IRDOXOQ.cjs +0 -55
- package/dist/slashCommands-C6CAQA25.js +0 -55
- package/dist/status-4EDV2LSY.cjs +0 -10
- package/dist/status-NU7TJDCE.js +0 -10
- package/dist/sync-3GFSEIAZ.js +0 -16
- package/dist/sync-6M3WRKMH.cjs +0 -39
- package/dist/sync-CQNQDNTJ.cjs +0 -16
- package/dist/theme-EMJGULMI.cjs +0 -16
- package/dist/theme-FGDSXNU3.js +0 -16
- package/dist/undo-CTXQYE7C.cjs +0 -9
- package/dist/undo-HX2ZMECP.js +0 -9
|
@@ -0,0 +1,4498 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
var _chunkJ6QET7EFcjs = require('./chunk-J6QET7EF.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
var _chunk4JNNTOGFcjs = require('./chunk-4JNNTOGF.cjs');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
var _chunkRKJTGGMUcjs = require('./chunk-RKJTGGMU.cjs');
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
var _chunkZYQMLKOKcjs = require('./chunk-ZYQMLKOK.cjs');
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
var _chunkPGRH5Q77cjs = require('./chunk-PGRH5Q77.cjs');
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
var _chunkSEKD5FH3cjs = require('./chunk-SEKD5FH3.cjs');
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
var _chunkULQ6MDSJcjs = require('./chunk-ULQ6MDSJ.cjs');
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
var _chunkLJFUXC56cjs = require('./chunk-LJFUXC56.cjs');
|
|
32
|
+
|
|
33
|
+
// src/core/actionExecutor.ts
|
|
34
|
+
var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
|
|
35
|
+
var _diff = require('diff');
|
|
36
|
+
|
|
37
|
+
// src/ui/syntaxHighlight.ts
|
|
38
|
+
|
|
39
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
40
|
+
var EXTENSION_MAP = {
|
|
41
|
+
".ts": "typescript",
|
|
42
|
+
".tsx": "typescript",
|
|
43
|
+
".js": "javascript",
|
|
44
|
+
".jsx": "javascript",
|
|
45
|
+
".mjs": "javascript",
|
|
46
|
+
".cjs": "javascript",
|
|
47
|
+
".py": "python",
|
|
48
|
+
".rs": "rust",
|
|
49
|
+
".go": "go",
|
|
50
|
+
".rb": "ruby",
|
|
51
|
+
".java": "java",
|
|
52
|
+
".kt": "kotlin",
|
|
53
|
+
".swift": "swift",
|
|
54
|
+
".c": "c",
|
|
55
|
+
".cpp": "cpp",
|
|
56
|
+
".h": "c",
|
|
57
|
+
".hpp": "cpp",
|
|
58
|
+
".cs": "csharp",
|
|
59
|
+
".php": "php",
|
|
60
|
+
".sh": "bash",
|
|
61
|
+
".bash": "bash",
|
|
62
|
+
".zsh": "bash",
|
|
63
|
+
".fish": "fish",
|
|
64
|
+
".json": "json",
|
|
65
|
+
".yaml": "yaml",
|
|
66
|
+
".yml": "yaml",
|
|
67
|
+
".toml": "toml",
|
|
68
|
+
".xml": "xml",
|
|
69
|
+
".html": "html",
|
|
70
|
+
".htm": "html",
|
|
71
|
+
".css": "css",
|
|
72
|
+
".scss": "css",
|
|
73
|
+
".less": "css",
|
|
74
|
+
".sql": "sql",
|
|
75
|
+
".md": "markdown",
|
|
76
|
+
".dockerfile": "dockerfile",
|
|
77
|
+
".prisma": "prisma",
|
|
78
|
+
".graphql": "graphql",
|
|
79
|
+
".gql": "graphql"
|
|
80
|
+
};
|
|
81
|
+
var KEYWORDS = {
|
|
82
|
+
typescript: [
|
|
83
|
+
"const",
|
|
84
|
+
"let",
|
|
85
|
+
"var",
|
|
86
|
+
"function",
|
|
87
|
+
"class",
|
|
88
|
+
"interface",
|
|
89
|
+
"type",
|
|
90
|
+
"enum",
|
|
91
|
+
"import",
|
|
92
|
+
"export",
|
|
93
|
+
"from",
|
|
94
|
+
"default",
|
|
95
|
+
"async",
|
|
96
|
+
"await",
|
|
97
|
+
"return",
|
|
98
|
+
"if",
|
|
99
|
+
"else",
|
|
100
|
+
"for",
|
|
101
|
+
"while",
|
|
102
|
+
"do",
|
|
103
|
+
"switch",
|
|
104
|
+
"case",
|
|
105
|
+
"break",
|
|
106
|
+
"continue",
|
|
107
|
+
"try",
|
|
108
|
+
"catch",
|
|
109
|
+
"finally",
|
|
110
|
+
"throw",
|
|
111
|
+
"new",
|
|
112
|
+
"this",
|
|
113
|
+
"super",
|
|
114
|
+
"extends",
|
|
115
|
+
"implements",
|
|
116
|
+
"static",
|
|
117
|
+
"public",
|
|
118
|
+
"private",
|
|
119
|
+
"protected",
|
|
120
|
+
"readonly",
|
|
121
|
+
"abstract",
|
|
122
|
+
"as",
|
|
123
|
+
"is",
|
|
124
|
+
"in",
|
|
125
|
+
"of",
|
|
126
|
+
"typeof",
|
|
127
|
+
"instanceof",
|
|
128
|
+
"keyof",
|
|
129
|
+
"never",
|
|
130
|
+
"unknown",
|
|
131
|
+
"any",
|
|
132
|
+
"void",
|
|
133
|
+
"null",
|
|
134
|
+
"undefined",
|
|
135
|
+
"true",
|
|
136
|
+
"false"
|
|
137
|
+
],
|
|
138
|
+
javascript: [
|
|
139
|
+
"const",
|
|
140
|
+
"let",
|
|
141
|
+
"var",
|
|
142
|
+
"function",
|
|
143
|
+
"class",
|
|
144
|
+
"import",
|
|
145
|
+
"export",
|
|
146
|
+
"from",
|
|
147
|
+
"default",
|
|
148
|
+
"async",
|
|
149
|
+
"await",
|
|
150
|
+
"return",
|
|
151
|
+
"if",
|
|
152
|
+
"else",
|
|
153
|
+
"for",
|
|
154
|
+
"while",
|
|
155
|
+
"do",
|
|
156
|
+
"switch",
|
|
157
|
+
"case",
|
|
158
|
+
"break",
|
|
159
|
+
"continue",
|
|
160
|
+
"try",
|
|
161
|
+
"catch",
|
|
162
|
+
"finally",
|
|
163
|
+
"throw",
|
|
164
|
+
"new",
|
|
165
|
+
"this",
|
|
166
|
+
"super",
|
|
167
|
+
"extends",
|
|
168
|
+
"static",
|
|
169
|
+
"get",
|
|
170
|
+
"set",
|
|
171
|
+
"typeof",
|
|
172
|
+
"instanceof",
|
|
173
|
+
"in",
|
|
174
|
+
"of",
|
|
175
|
+
"true",
|
|
176
|
+
"false",
|
|
177
|
+
"null",
|
|
178
|
+
"undefined"
|
|
179
|
+
],
|
|
180
|
+
python: [
|
|
181
|
+
"def",
|
|
182
|
+
"class",
|
|
183
|
+
"import",
|
|
184
|
+
"from",
|
|
185
|
+
"as",
|
|
186
|
+
"return",
|
|
187
|
+
"if",
|
|
188
|
+
"elif",
|
|
189
|
+
"else",
|
|
190
|
+
"for",
|
|
191
|
+
"while",
|
|
192
|
+
"break",
|
|
193
|
+
"continue",
|
|
194
|
+
"try",
|
|
195
|
+
"except",
|
|
196
|
+
"finally",
|
|
197
|
+
"raise",
|
|
198
|
+
"with",
|
|
199
|
+
"as",
|
|
200
|
+
"pass",
|
|
201
|
+
"lambda",
|
|
202
|
+
"yield",
|
|
203
|
+
"global",
|
|
204
|
+
"nonlocal",
|
|
205
|
+
"assert",
|
|
206
|
+
"async",
|
|
207
|
+
"await",
|
|
208
|
+
"True",
|
|
209
|
+
"False",
|
|
210
|
+
"None",
|
|
211
|
+
"and",
|
|
212
|
+
"or",
|
|
213
|
+
"not",
|
|
214
|
+
"in",
|
|
215
|
+
"is"
|
|
216
|
+
],
|
|
217
|
+
rust: [
|
|
218
|
+
"fn",
|
|
219
|
+
"let",
|
|
220
|
+
"mut",
|
|
221
|
+
"const",
|
|
222
|
+
"static",
|
|
223
|
+
"struct",
|
|
224
|
+
"enum",
|
|
225
|
+
"impl",
|
|
226
|
+
"trait",
|
|
227
|
+
"type",
|
|
228
|
+
"where",
|
|
229
|
+
"for",
|
|
230
|
+
"loop",
|
|
231
|
+
"while",
|
|
232
|
+
"if",
|
|
233
|
+
"else",
|
|
234
|
+
"match",
|
|
235
|
+
"return",
|
|
236
|
+
"break",
|
|
237
|
+
"continue",
|
|
238
|
+
"pub",
|
|
239
|
+
"mod",
|
|
240
|
+
"use",
|
|
241
|
+
"crate",
|
|
242
|
+
"super",
|
|
243
|
+
"self",
|
|
244
|
+
"Self",
|
|
245
|
+
"async",
|
|
246
|
+
"await",
|
|
247
|
+
"move",
|
|
248
|
+
"ref",
|
|
249
|
+
"dyn",
|
|
250
|
+
"true",
|
|
251
|
+
"false",
|
|
252
|
+
"as",
|
|
253
|
+
"in",
|
|
254
|
+
"unsafe"
|
|
255
|
+
],
|
|
256
|
+
go: [
|
|
257
|
+
"func",
|
|
258
|
+
"var",
|
|
259
|
+
"const",
|
|
260
|
+
"type",
|
|
261
|
+
"struct",
|
|
262
|
+
"interface",
|
|
263
|
+
"map",
|
|
264
|
+
"chan",
|
|
265
|
+
"package",
|
|
266
|
+
"import",
|
|
267
|
+
"return",
|
|
268
|
+
"if",
|
|
269
|
+
"else",
|
|
270
|
+
"for",
|
|
271
|
+
"range",
|
|
272
|
+
"switch",
|
|
273
|
+
"case",
|
|
274
|
+
"default",
|
|
275
|
+
"break",
|
|
276
|
+
"continue",
|
|
277
|
+
"go",
|
|
278
|
+
"defer",
|
|
279
|
+
"select",
|
|
280
|
+
"fallthrough",
|
|
281
|
+
"true",
|
|
282
|
+
"false",
|
|
283
|
+
"nil",
|
|
284
|
+
"make",
|
|
285
|
+
"new",
|
|
286
|
+
"append",
|
|
287
|
+
"len",
|
|
288
|
+
"cap"
|
|
289
|
+
],
|
|
290
|
+
bash: [
|
|
291
|
+
"if",
|
|
292
|
+
"then",
|
|
293
|
+
"else",
|
|
294
|
+
"elif",
|
|
295
|
+
"fi",
|
|
296
|
+
"for",
|
|
297
|
+
"while",
|
|
298
|
+
"do",
|
|
299
|
+
"done",
|
|
300
|
+
"case",
|
|
301
|
+
"esac",
|
|
302
|
+
"function",
|
|
303
|
+
"return",
|
|
304
|
+
"exit",
|
|
305
|
+
"break",
|
|
306
|
+
"continue",
|
|
307
|
+
"export",
|
|
308
|
+
"local",
|
|
309
|
+
"readonly",
|
|
310
|
+
"declare",
|
|
311
|
+
"unset",
|
|
312
|
+
"source",
|
|
313
|
+
"alias",
|
|
314
|
+
"echo",
|
|
315
|
+
"printf"
|
|
316
|
+
],
|
|
317
|
+
sql: [
|
|
318
|
+
"SELECT",
|
|
319
|
+
"FROM",
|
|
320
|
+
"WHERE",
|
|
321
|
+
"AND",
|
|
322
|
+
"OR",
|
|
323
|
+
"NOT",
|
|
324
|
+
"IN",
|
|
325
|
+
"LIKE",
|
|
326
|
+
"ORDER",
|
|
327
|
+
"BY",
|
|
328
|
+
"GROUP",
|
|
329
|
+
"HAVING",
|
|
330
|
+
"JOIN",
|
|
331
|
+
"LEFT",
|
|
332
|
+
"RIGHT",
|
|
333
|
+
"INNER",
|
|
334
|
+
"OUTER",
|
|
335
|
+
"ON",
|
|
336
|
+
"AS",
|
|
337
|
+
"INSERT",
|
|
338
|
+
"INTO",
|
|
339
|
+
"VALUES",
|
|
340
|
+
"UPDATE",
|
|
341
|
+
"SET",
|
|
342
|
+
"DELETE",
|
|
343
|
+
"CREATE",
|
|
344
|
+
"TABLE",
|
|
345
|
+
"DROP",
|
|
346
|
+
"ALTER",
|
|
347
|
+
"INDEX",
|
|
348
|
+
"PRIMARY",
|
|
349
|
+
"KEY",
|
|
350
|
+
"FOREIGN",
|
|
351
|
+
"REFERENCES",
|
|
352
|
+
"NULL",
|
|
353
|
+
"DEFAULT",
|
|
354
|
+
"DISTINCT",
|
|
355
|
+
"LIMIT",
|
|
356
|
+
"OFFSET",
|
|
357
|
+
"UNION",
|
|
358
|
+
"ALL"
|
|
359
|
+
]
|
|
360
|
+
};
|
|
361
|
+
var BUILTIN_TYPES = {
|
|
362
|
+
typescript: [
|
|
363
|
+
"string",
|
|
364
|
+
"number",
|
|
365
|
+
"boolean",
|
|
366
|
+
"object",
|
|
367
|
+
"symbol",
|
|
368
|
+
"bigint",
|
|
369
|
+
"Array",
|
|
370
|
+
"Map",
|
|
371
|
+
"Set",
|
|
372
|
+
"Promise",
|
|
373
|
+
"Record",
|
|
374
|
+
"Partial",
|
|
375
|
+
"Required",
|
|
376
|
+
"Readonly",
|
|
377
|
+
"Pick",
|
|
378
|
+
"Omit",
|
|
379
|
+
"Exclude",
|
|
380
|
+
"Extract",
|
|
381
|
+
"NonNullable",
|
|
382
|
+
"ReturnType"
|
|
383
|
+
],
|
|
384
|
+
rust: [
|
|
385
|
+
"i8",
|
|
386
|
+
"i16",
|
|
387
|
+
"i32",
|
|
388
|
+
"i64",
|
|
389
|
+
"i128",
|
|
390
|
+
"isize",
|
|
391
|
+
"u8",
|
|
392
|
+
"u16",
|
|
393
|
+
"u32",
|
|
394
|
+
"u64",
|
|
395
|
+
"u128",
|
|
396
|
+
"usize",
|
|
397
|
+
"f32",
|
|
398
|
+
"f64",
|
|
399
|
+
"bool",
|
|
400
|
+
"char",
|
|
401
|
+
"str",
|
|
402
|
+
"String",
|
|
403
|
+
"Vec",
|
|
404
|
+
"Option",
|
|
405
|
+
"Result",
|
|
406
|
+
"Box",
|
|
407
|
+
"Rc",
|
|
408
|
+
"Arc",
|
|
409
|
+
"Cell",
|
|
410
|
+
"RefCell",
|
|
411
|
+
"HashMap"
|
|
412
|
+
],
|
|
413
|
+
go: [
|
|
414
|
+
"int",
|
|
415
|
+
"int8",
|
|
416
|
+
"int16",
|
|
417
|
+
"int32",
|
|
418
|
+
"int64",
|
|
419
|
+
"uint",
|
|
420
|
+
"uint8",
|
|
421
|
+
"uint16",
|
|
422
|
+
"uint32",
|
|
423
|
+
"uint64",
|
|
424
|
+
"uintptr",
|
|
425
|
+
"float32",
|
|
426
|
+
"float64",
|
|
427
|
+
"complex64",
|
|
428
|
+
"complex128",
|
|
429
|
+
"bool",
|
|
430
|
+
"byte",
|
|
431
|
+
"rune",
|
|
432
|
+
"string",
|
|
433
|
+
"error"
|
|
434
|
+
]
|
|
435
|
+
};
|
|
436
|
+
function detectLanguage(filePath, content) {
|
|
437
|
+
if (filePath) {
|
|
438
|
+
const ext = _path2.default.extname(filePath).toLowerCase();
|
|
439
|
+
if (EXTENSION_MAP[ext]) {
|
|
440
|
+
return EXTENSION_MAP[ext];
|
|
441
|
+
}
|
|
442
|
+
const basename = _path2.default.basename(filePath).toLowerCase();
|
|
443
|
+
if (basename === "dockerfile") return "dockerfile";
|
|
444
|
+
if (basename === "makefile") return "makefile";
|
|
445
|
+
if (basename.endsWith(".d.ts")) return "typescript";
|
|
446
|
+
}
|
|
447
|
+
if (content) {
|
|
448
|
+
if (content.includes("#!/bin/bash") || content.includes("#!/usr/bin/env bash")) {
|
|
449
|
+
return "bash";
|
|
450
|
+
}
|
|
451
|
+
if (content.includes("#!/usr/bin/env python") || content.includes("#!/usr/bin/python")) {
|
|
452
|
+
return "python";
|
|
453
|
+
}
|
|
454
|
+
if (content.includes("#!/usr/bin/env node")) {
|
|
455
|
+
return "javascript";
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return "text";
|
|
459
|
+
}
|
|
460
|
+
function tokenize(code, language) {
|
|
461
|
+
const tokens = [];
|
|
462
|
+
const keywords = KEYWORDS[language] || KEYWORDS.javascript || [];
|
|
463
|
+
const types = BUILTIN_TYPES[language] || [];
|
|
464
|
+
const patterns = [
|
|
465
|
+
// Comments (single line)
|
|
466
|
+
{ type: "comment", regex: /^(\/\/.*|#.*|--.*)/m },
|
|
467
|
+
// Comments (multi-line for JS/TS/C-style)
|
|
468
|
+
{ type: "comment", regex: /^\/\*[\s\S]*?\*\// },
|
|
469
|
+
// Strings (double quotes)
|
|
470
|
+
{ type: "string", regex: /^"(?:[^"\\]|\\.)*"/ },
|
|
471
|
+
// Strings (single quotes)
|
|
472
|
+
{ type: "string", regex: /^'(?:[^'\\]|\\.)*'/ },
|
|
473
|
+
// Template literals
|
|
474
|
+
{ type: "string", regex: /^`(?:[^`\\]|\\.)*`/ },
|
|
475
|
+
// Numbers
|
|
476
|
+
{ type: "number", regex: /^0x[0-9a-fA-F]+|^0b[01]+|^0o[0-7]+|^\d+\.?\d*(?:[eE][+-]?\d+)?/ },
|
|
477
|
+
// Operators
|
|
478
|
+
{ type: "operator", regex: /^(?:=>|===|!==|==|!=|<=|>=|&&|\|\||[+\-*/%<>=!&|^~?:])+/ },
|
|
479
|
+
// Punctuation
|
|
480
|
+
{ type: "punctuation", regex: /^[{}[\]();,.]/ },
|
|
481
|
+
// Words (identifiers, keywords, types)
|
|
482
|
+
{ type: "text", regex: /^[a-zA-Z_$][a-zA-Z0-9_$]*/ },
|
|
483
|
+
// Whitespace and other
|
|
484
|
+
{ type: "text", regex: /^\s+/ },
|
|
485
|
+
{ type: "text", regex: /^./ }
|
|
486
|
+
];
|
|
487
|
+
let remaining = code;
|
|
488
|
+
while (remaining.length > 0) {
|
|
489
|
+
let matched = false;
|
|
490
|
+
for (const { type, regex } of patterns) {
|
|
491
|
+
const match = remaining.match(regex);
|
|
492
|
+
if (match) {
|
|
493
|
+
let tokenType = type;
|
|
494
|
+
const value = match[0];
|
|
495
|
+
if (type === "text" && /^[a-zA-Z_$]/.test(value)) {
|
|
496
|
+
if (keywords.includes(value)) {
|
|
497
|
+
tokenType = "keyword";
|
|
498
|
+
} else if (types.includes(value)) {
|
|
499
|
+
tokenType = "type";
|
|
500
|
+
} else if (/^[A-Z]/.test(value)) {
|
|
501
|
+
tokenType = "type";
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
tokens.push({ type: tokenType, value });
|
|
505
|
+
remaining = remaining.slice(value.length);
|
|
506
|
+
matched = true;
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (!matched) {
|
|
511
|
+
tokens.push({ type: "text", value: remaining[0] });
|
|
512
|
+
remaining = remaining.slice(1);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return tokens;
|
|
516
|
+
}
|
|
517
|
+
function colorToken(token) {
|
|
518
|
+
if (_chunkPGRH5Q77cjs.isThemeInitialized.call(void 0, )) {
|
|
519
|
+
const theme = _chunkPGRH5Q77cjs.getTheme.call(void 0, );
|
|
520
|
+
switch (token.type) {
|
|
521
|
+
case "keyword":
|
|
522
|
+
return theme.fg("syntaxKeyword", token.value);
|
|
523
|
+
case "string":
|
|
524
|
+
return theme.fg("syntaxString", token.value);
|
|
525
|
+
case "number":
|
|
526
|
+
return theme.fg("syntaxNumber", token.value);
|
|
527
|
+
case "comment":
|
|
528
|
+
return theme.fg("syntaxComment", token.value);
|
|
529
|
+
case "type":
|
|
530
|
+
return theme.fg("syntaxType", token.value);
|
|
531
|
+
case "function":
|
|
532
|
+
return theme.fg("syntaxFunction", token.value);
|
|
533
|
+
case "operator":
|
|
534
|
+
return theme.fg("syntaxOperator", token.value);
|
|
535
|
+
case "punctuation":
|
|
536
|
+
return theme.fg("syntaxPunctuation", token.value);
|
|
537
|
+
default:
|
|
538
|
+
return token.value;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
switch (token.type) {
|
|
542
|
+
case "keyword":
|
|
543
|
+
return _chalk2.default.magenta(token.value);
|
|
544
|
+
case "string":
|
|
545
|
+
return _chalk2.default.green(token.value);
|
|
546
|
+
case "number":
|
|
547
|
+
return _chalk2.default.yellow(token.value);
|
|
548
|
+
case "comment":
|
|
549
|
+
return _chalk2.default.gray(token.value);
|
|
550
|
+
case "type":
|
|
551
|
+
return _chalk2.default.cyan(token.value);
|
|
552
|
+
case "function":
|
|
553
|
+
return _chalk2.default.blue(token.value);
|
|
554
|
+
case "operator":
|
|
555
|
+
return _chalk2.default.white(token.value);
|
|
556
|
+
case "punctuation":
|
|
557
|
+
return _chalk2.default.white(token.value);
|
|
558
|
+
default:
|
|
559
|
+
return token.value;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function highlightLine(line, language) {
|
|
563
|
+
const tokens = tokenize(line, language);
|
|
564
|
+
return tokens.map(colorToken).join("");
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/actions/dependencies.ts
|
|
568
|
+
var _fsextra = require('fs-extra'); var _fsextra2 = _interopRequireDefault(_fsextra);
|
|
569
|
+
|
|
570
|
+
async function readPackageManifest(cwd) {
|
|
571
|
+
const manifestPath = _path2.default.join(cwd, "package.json");
|
|
572
|
+
if (!await _fsextra2.default.pathExists(manifestPath)) {
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
return _fsextra2.default.readJson(manifestPath);
|
|
576
|
+
}
|
|
577
|
+
async function addDependency(cwd, name, version, options = {}) {
|
|
578
|
+
const manifest = await _asyncNullishCoalesce(await readPackageManifest(cwd), async () => ( {}));
|
|
579
|
+
if (options.dev) {
|
|
580
|
+
manifest.devDependencies = _nullishCoalesce(manifest.devDependencies, () => ( {}));
|
|
581
|
+
manifest.devDependencies[name] = version;
|
|
582
|
+
} else {
|
|
583
|
+
manifest.dependencies = _nullishCoalesce(manifest.dependencies, () => ( {}));
|
|
584
|
+
manifest.dependencies[name] = version;
|
|
585
|
+
}
|
|
586
|
+
const manifestPath = _path2.default.join(cwd, "package.json");
|
|
587
|
+
await _fsextra2.default.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
588
|
+
}
|
|
589
|
+
async function removeDependency(cwd, name, options = {}) {
|
|
590
|
+
const manifest = await _asyncNullishCoalesce(await readPackageManifest(cwd), async () => ( {}));
|
|
591
|
+
const targetKey = options.dev ? "devDependencies" : "dependencies";
|
|
592
|
+
if (manifest[targetKey] && manifest[targetKey][name]) {
|
|
593
|
+
delete manifest[targetKey][name];
|
|
594
|
+
const manifestPath = _path2.default.join(cwd, "package.json");
|
|
595
|
+
await _fsextra2.default.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/actions/command.ts
|
|
600
|
+
var _child_process = require('child_process');
|
|
601
|
+
|
|
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 ? _path.join.call(void 0, cwd, options.directory) : cwd;
|
|
608
|
+
const spawnOptions = {
|
|
609
|
+
cwd: workDir,
|
|
610
|
+
shell: _nullishCoalesce(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 = _child_process.spawn.call(void 0, 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
|
+
_optionalChain([child, 'access', _ => _.stdout, 'optionalAccess', _2 => _2.on, 'call', _3 => _3("data", (chunk) => {
|
|
653
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
654
|
+
stdout += text;
|
|
655
|
+
_optionalChain([options, 'access', _4 => _4.onStdout, 'optionalCall', _5 => _5(text)]);
|
|
656
|
+
})]);
|
|
657
|
+
_optionalChain([child, 'access', _6 => _6.stderr, 'optionalAccess', _7 => _7.on, 'call', _8 => _8("data", (chunk) => {
|
|
658
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
659
|
+
stderr += text;
|
|
660
|
+
_optionalChain([options, 'access', _9 => _9.onStderr, 'optionalCall', _10 => _10(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
|
+
// src/actions/metadata.ts
|
|
678
|
+
var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto);
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
var ALWAYS_SKIP = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", "__pycache__", ".cache"]);
|
|
682
|
+
async function listDirectoryTree(root, options = {}) {
|
|
683
|
+
const depth = _nullishCoalesce(options.depth, () => ( 2));
|
|
684
|
+
const maxEntries = _nullishCoalesce(options.maxEntries, () => ( 200));
|
|
685
|
+
const workspaceRoot = _nullishCoalesce(options.workspaceRoot, () => ( root));
|
|
686
|
+
const result = [];
|
|
687
|
+
const resolvedRoot = _path2.default.resolve(root);
|
|
688
|
+
const resolvedWorkspace = _path2.default.resolve(workspaceRoot);
|
|
689
|
+
if (!resolvedRoot.startsWith(resolvedWorkspace)) {
|
|
690
|
+
throw new Error(`Path ${root} is outside the workspace root.`);
|
|
691
|
+
}
|
|
692
|
+
const ignoreFilter = new (0, _chunkLJFUXC56cjs.GitIgnoreParser)(workspaceRoot);
|
|
693
|
+
async function walk(current, prefix, currentDepth) {
|
|
694
|
+
if (result.length >= maxEntries) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
let entries;
|
|
698
|
+
try {
|
|
699
|
+
entries = await _fsextra2.default.readdir(current);
|
|
700
|
+
} catch (e2) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const slice = entries.slice(0, maxEntries - result.length);
|
|
704
|
+
for (const entry of slice) {
|
|
705
|
+
if (entry.startsWith(".")) {
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
const full = _path2.default.join(current, entry);
|
|
709
|
+
const rel = _path2.default.relative(workspaceRoot, full);
|
|
710
|
+
if (ALWAYS_SKIP.has(entry) || ignoreFilter.isIgnored(rel)) {
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
try {
|
|
714
|
+
const stats = await _fsextra2.default.stat(full);
|
|
715
|
+
result.push(`${prefix}${entry}${stats.isDirectory() ? "/" : ""}`);
|
|
716
|
+
if (stats.isDirectory() && currentDepth < depth) {
|
|
717
|
+
await walk(full, `${prefix} `, currentDepth + 1);
|
|
718
|
+
}
|
|
719
|
+
} catch (e3) {
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (result.length >= maxEntries) {
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
await walk(root, "", 0);
|
|
728
|
+
return result;
|
|
729
|
+
}
|
|
730
|
+
async function fileStats(root, relativePath) {
|
|
731
|
+
const fullPath = _path2.default.join(root, relativePath);
|
|
732
|
+
if (!await _fsextra2.default.pathExists(fullPath)) {
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
const stats = await _fsextra2.default.stat(fullPath);
|
|
736
|
+
return {
|
|
737
|
+
size: stats.size,
|
|
738
|
+
mtime: stats.mtime.toISOString(),
|
|
739
|
+
isDirectory: stats.isDirectory()
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
async function checksumFile(root, relativePath, algorithm = "sha256") {
|
|
743
|
+
const fullPath = _path2.default.join(root, relativePath);
|
|
744
|
+
const exists = await _fsextra2.default.pathExists(fullPath);
|
|
745
|
+
if (!exists) {
|
|
746
|
+
throw new Error(`${relativePath} does not exist.`);
|
|
747
|
+
}
|
|
748
|
+
const hash = _crypto2.default.createHash(algorithm);
|
|
749
|
+
const stream = _fsextra2.default.createReadStream(fullPath);
|
|
750
|
+
return await new Promise((resolve, reject) => {
|
|
751
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
752
|
+
stream.on("error", reject);
|
|
753
|
+
stream.on("end", () => resolve(hash.digest("hex")));
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/actions/git.ts
|
|
758
|
+
|
|
759
|
+
var GIT_SAFETY = {
|
|
760
|
+
/** Branches where force push is blocked */
|
|
761
|
+
PROTECTED_BRANCHES: ["main", "master", "develop", "production", "staging"],
|
|
762
|
+
/** Maximum commits to push at once (to prevent accidental mass pushes) */
|
|
763
|
+
MAX_COMMITS_PER_PUSH: 50
|
|
764
|
+
};
|
|
765
|
+
function getCurrentBranch(cwd) {
|
|
766
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd, encoding: "utf8" });
|
|
767
|
+
return _optionalChain([result, 'access', _11 => _11.stdout, 'optionalAccess', _12 => _12.trim, 'call', _13 => _13()]) || "";
|
|
768
|
+
}
|
|
769
|
+
function getCommitsAhead(cwd, remote = "origin", branch) {
|
|
770
|
+
const currentBranch = branch || getCurrentBranch(cwd);
|
|
771
|
+
if (!currentBranch) return 0;
|
|
772
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["rev-list", "--count", `${remote}/${currentBranch}..HEAD`], {
|
|
773
|
+
cwd,
|
|
774
|
+
encoding: "utf8"
|
|
775
|
+
});
|
|
776
|
+
return parseInt(_optionalChain([result, 'access', _14 => _14.stdout, 'optionalAccess', _15 => _15.trim, 'call', _16 => _16()]) || "0", 10) || 0;
|
|
777
|
+
}
|
|
778
|
+
function applyGitPatch(cwd, patch) {
|
|
779
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["apply", "-"], {
|
|
780
|
+
cwd,
|
|
781
|
+
input: patch,
|
|
782
|
+
encoding: "utf8"
|
|
783
|
+
});
|
|
784
|
+
if (result.status !== 0) {
|
|
785
|
+
throw new Error(result.stderr || "git apply failed");
|
|
786
|
+
}
|
|
787
|
+
return _nullishCoalesce(result.stdout, () => ( ""));
|
|
788
|
+
}
|
|
789
|
+
function diffFile(cwd, file) {
|
|
790
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["diff", "--", file], { cwd, encoding: "utf8" });
|
|
791
|
+
if (result.status !== 0) {
|
|
792
|
+
throw new Error(result.stderr || `git diff failed for ${file}`);
|
|
793
|
+
}
|
|
794
|
+
return result.stdout || "No diff";
|
|
795
|
+
}
|
|
796
|
+
function checkoutFile(cwd, file) {
|
|
797
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["checkout", "--", file], { cwd, encoding: "utf8" });
|
|
798
|
+
if (result.status !== 0) {
|
|
799
|
+
throw new Error(result.stderr || `git checkout failed for ${file}`);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
function gitStatus(cwd) {
|
|
803
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["status", "-sb"], { cwd, encoding: "utf8" });
|
|
804
|
+
if (result.status !== 0) {
|
|
805
|
+
throw new Error(result.stderr || "git status failed");
|
|
806
|
+
}
|
|
807
|
+
return result.stdout || "clean";
|
|
808
|
+
}
|
|
809
|
+
function gitListUntracked(cwd) {
|
|
810
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["ls-files", "--others", "--exclude-standard"], { cwd, encoding: "utf8" });
|
|
811
|
+
if (result.status !== 0) {
|
|
812
|
+
throw new Error(result.stderr || "git ls-files failed");
|
|
813
|
+
}
|
|
814
|
+
return result.stdout || "";
|
|
815
|
+
}
|
|
816
|
+
function gitDiffRange(cwd, options = {}) {
|
|
817
|
+
const args = ["diff"];
|
|
818
|
+
if (options.staged) {
|
|
819
|
+
args.push("--staged");
|
|
820
|
+
}
|
|
821
|
+
if (options.range) {
|
|
822
|
+
args.push(options.range);
|
|
823
|
+
}
|
|
824
|
+
if (_optionalChain([options, 'access', _17 => _17.paths, 'optionalAccess', _18 => _18.length])) {
|
|
825
|
+
args.push("--", ...options.paths);
|
|
826
|
+
}
|
|
827
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
828
|
+
if (result.status !== 0) {
|
|
829
|
+
throw new Error(result.stderr || "git diff failed");
|
|
830
|
+
}
|
|
831
|
+
return result.stdout || "No diff output.";
|
|
832
|
+
}
|
|
833
|
+
function gitListWorktrees(cwd) {
|
|
834
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["worktree", "list", "--porcelain"], { cwd, encoding: "utf8" });
|
|
835
|
+
if (result.status !== 0) {
|
|
836
|
+
throw new Error(result.stderr || "git worktree list failed");
|
|
837
|
+
}
|
|
838
|
+
return result.stdout || "No worktrees.";
|
|
839
|
+
}
|
|
840
|
+
function gitAddWorktree(cwd, pathArg, ref) {
|
|
841
|
+
const args = ["worktree", "add", pathArg];
|
|
842
|
+
if (ref) {
|
|
843
|
+
args.push(ref);
|
|
844
|
+
}
|
|
845
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
846
|
+
if (result.status !== 0) {
|
|
847
|
+
throw new Error(result.stderr || "git worktree add failed");
|
|
848
|
+
}
|
|
849
|
+
return result.stdout || `Added worktree at ${pathArg}`;
|
|
850
|
+
}
|
|
851
|
+
function gitRemoveWorktree(cwd, pathArg, force = false) {
|
|
852
|
+
const args = ["worktree", "remove"];
|
|
853
|
+
if (force) {
|
|
854
|
+
args.push("--force");
|
|
855
|
+
}
|
|
856
|
+
args.push(pathArg);
|
|
857
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
858
|
+
if (result.status !== 0) {
|
|
859
|
+
throw new Error(result.stderr || "git worktree remove failed");
|
|
860
|
+
}
|
|
861
|
+
return result.stdout || `Removed worktree ${pathArg}`;
|
|
862
|
+
}
|
|
863
|
+
function gitStash(cwd, options = {}) {
|
|
864
|
+
const args = ["stash", "push"];
|
|
865
|
+
if (options.includeUntracked) {
|
|
866
|
+
args.push("--include-untracked");
|
|
867
|
+
}
|
|
868
|
+
if (options.keepIndex) {
|
|
869
|
+
args.push("--keep-index");
|
|
870
|
+
}
|
|
871
|
+
if (options.message) {
|
|
872
|
+
args.push("-m", options.message);
|
|
873
|
+
}
|
|
874
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
875
|
+
if (result.status !== 0) {
|
|
876
|
+
throw new Error(result.stderr || "git stash failed");
|
|
877
|
+
}
|
|
878
|
+
return result.stdout || result.stderr || "Stashed changes";
|
|
879
|
+
}
|
|
880
|
+
function gitStashList(cwd) {
|
|
881
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["stash", "list"], { cwd, encoding: "utf8" });
|
|
882
|
+
if (result.status !== 0) {
|
|
883
|
+
throw new Error(result.stderr || "git stash list failed");
|
|
884
|
+
}
|
|
885
|
+
return result.stdout || "No stashes";
|
|
886
|
+
}
|
|
887
|
+
function gitStashPop(cwd, stashRef) {
|
|
888
|
+
const args = ["stash", "pop"];
|
|
889
|
+
if (stashRef) {
|
|
890
|
+
args.push(stashRef);
|
|
891
|
+
}
|
|
892
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
893
|
+
if (result.status !== 0) {
|
|
894
|
+
throw new Error(result.stderr || "git stash pop failed");
|
|
895
|
+
}
|
|
896
|
+
return result.stdout || "Applied and dropped stash";
|
|
897
|
+
}
|
|
898
|
+
function gitStashApply(cwd, stashRef) {
|
|
899
|
+
const args = ["stash", "apply"];
|
|
900
|
+
if (stashRef) {
|
|
901
|
+
args.push(stashRef);
|
|
902
|
+
}
|
|
903
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
904
|
+
if (result.status !== 0) {
|
|
905
|
+
throw new Error(result.stderr || "git stash apply failed");
|
|
906
|
+
}
|
|
907
|
+
return result.stdout || "Applied stash";
|
|
908
|
+
}
|
|
909
|
+
function gitStashDrop(cwd, stashRef) {
|
|
910
|
+
const args = ["stash", "drop"];
|
|
911
|
+
if (stashRef) {
|
|
912
|
+
args.push(stashRef);
|
|
913
|
+
}
|
|
914
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
915
|
+
if (result.status !== 0) {
|
|
916
|
+
throw new Error(result.stderr || "git stash drop failed");
|
|
917
|
+
}
|
|
918
|
+
return result.stdout || "Dropped stash";
|
|
919
|
+
}
|
|
920
|
+
function gitBranch(cwd, branchName, options = {}) {
|
|
921
|
+
const args = ["branch"];
|
|
922
|
+
if (options.delete) {
|
|
923
|
+
args.push(options.force ? "-D" : "-d");
|
|
924
|
+
}
|
|
925
|
+
if (branchName) {
|
|
926
|
+
args.push(branchName);
|
|
927
|
+
}
|
|
928
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
929
|
+
if (result.status !== 0) {
|
|
930
|
+
throw new Error(result.stderr || "git branch failed");
|
|
931
|
+
}
|
|
932
|
+
return result.stdout || "Branch operation completed";
|
|
933
|
+
}
|
|
934
|
+
function gitSwitch(cwd, branchName, options = {}) {
|
|
935
|
+
const args = ["switch"];
|
|
936
|
+
if (options.create) {
|
|
937
|
+
args.push("-c");
|
|
938
|
+
}
|
|
939
|
+
args.push(branchName);
|
|
940
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
941
|
+
if (result.status !== 0) {
|
|
942
|
+
throw new Error(result.stderr || "git switch failed");
|
|
943
|
+
}
|
|
944
|
+
return result.stdout || `Switched to ${branchName}`;
|
|
945
|
+
}
|
|
946
|
+
function gitCherryPick(cwd, commits, options = {}) {
|
|
947
|
+
const args = ["cherry-pick"];
|
|
948
|
+
if (options.noCommit) {
|
|
949
|
+
args.push("--no-commit");
|
|
950
|
+
}
|
|
951
|
+
if (options.mainline !== void 0) {
|
|
952
|
+
args.push("-m", String(options.mainline));
|
|
953
|
+
}
|
|
954
|
+
args.push(...commits);
|
|
955
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
956
|
+
if (result.status !== 0) {
|
|
957
|
+
throw new Error(result.stderr || "git cherry-pick failed");
|
|
958
|
+
}
|
|
959
|
+
return result.stdout || `Cherry-picked ${commits.join(", ")}`;
|
|
960
|
+
}
|
|
961
|
+
function gitCherryPickAbort(cwd) {
|
|
962
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["cherry-pick", "--abort"], { cwd, encoding: "utf8" });
|
|
963
|
+
if (result.status !== 0) {
|
|
964
|
+
throw new Error(result.stderr || "git cherry-pick --abort failed");
|
|
965
|
+
}
|
|
966
|
+
return "Cherry-pick aborted";
|
|
967
|
+
}
|
|
968
|
+
function gitCherryPickContinue(cwd) {
|
|
969
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["cherry-pick", "--continue"], { cwd, encoding: "utf8" });
|
|
970
|
+
if (result.status !== 0) {
|
|
971
|
+
throw new Error(result.stderr || "git cherry-pick --continue failed");
|
|
972
|
+
}
|
|
973
|
+
return "Cherry-pick continued";
|
|
974
|
+
}
|
|
975
|
+
function gitRebase(cwd, upstream, options = {}) {
|
|
976
|
+
const currentBranch = getCurrentBranch(cwd);
|
|
977
|
+
if (GIT_SAFETY.PROTECTED_BRANCHES.includes(currentBranch)) {
|
|
978
|
+
throw new Error(
|
|
979
|
+
`Rebasing protected branch "${currentBranch}" is blocked. Protected branches should not be rebased as it rewrites history. Use merge instead, or switch to a feature branch first.`
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
const args = ["rebase"];
|
|
983
|
+
if (options.onto) {
|
|
984
|
+
args.push("--onto", options.onto);
|
|
985
|
+
}
|
|
986
|
+
if (options.autosquash) {
|
|
987
|
+
args.push("--autosquash");
|
|
988
|
+
}
|
|
989
|
+
args.push(upstream);
|
|
990
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
991
|
+
if (result.status !== 0) {
|
|
992
|
+
throw new Error(result.stderr || "git rebase failed");
|
|
993
|
+
}
|
|
994
|
+
return result.stdout || `Rebased onto ${upstream}`;
|
|
995
|
+
}
|
|
996
|
+
function gitRebaseAbort(cwd) {
|
|
997
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["rebase", "--abort"], { cwd, encoding: "utf8" });
|
|
998
|
+
if (result.status !== 0) {
|
|
999
|
+
throw new Error(result.stderr || "git rebase --abort failed");
|
|
1000
|
+
}
|
|
1001
|
+
return "Rebase aborted";
|
|
1002
|
+
}
|
|
1003
|
+
function gitRebaseContinue(cwd) {
|
|
1004
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["rebase", "--continue"], { cwd, encoding: "utf8" });
|
|
1005
|
+
if (result.status !== 0) {
|
|
1006
|
+
throw new Error(result.stderr || "git rebase --continue failed");
|
|
1007
|
+
}
|
|
1008
|
+
return "Rebase continued";
|
|
1009
|
+
}
|
|
1010
|
+
function gitRebaseSkip(cwd) {
|
|
1011
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["rebase", "--skip"], { cwd, encoding: "utf8" });
|
|
1012
|
+
if (result.status !== 0) {
|
|
1013
|
+
throw new Error(result.stderr || "git rebase --skip failed");
|
|
1014
|
+
}
|
|
1015
|
+
return "Skipped commit and continued rebase";
|
|
1016
|
+
}
|
|
1017
|
+
function gitMerge(cwd, branch, options = {}) {
|
|
1018
|
+
const localBranches = _child_process.spawnSync.call(void 0, "git", ["branch", "--list"], { cwd, encoding: "utf8" });
|
|
1019
|
+
const remoteBranches = _child_process.spawnSync.call(void 0, "git", ["branch", "-r", "--list"], { cwd, encoding: "utf8" });
|
|
1020
|
+
const allBranches = (localBranches.stdout || "") + (remoteBranches.stdout || "");
|
|
1021
|
+
const branchExists = allBranches.split("\n").some(
|
|
1022
|
+
(b) => b.trim().replace("* ", "") === branch || b.trim() === `origin/${branch}` || b.trim() === branch
|
|
1023
|
+
);
|
|
1024
|
+
if (!branchExists) {
|
|
1025
|
+
throw new Error(
|
|
1026
|
+
`Branch "${branch}" not found locally or in remotes. For security, only existing branches can be merged. Run 'git fetch' first if the branch exists remotely.`
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
const args = ["merge"];
|
|
1030
|
+
if (options.noCommit) {
|
|
1031
|
+
args.push("--no-commit");
|
|
1032
|
+
}
|
|
1033
|
+
if (options.noFastForward) {
|
|
1034
|
+
args.push("--no-ff");
|
|
1035
|
+
}
|
|
1036
|
+
if (options.squash) {
|
|
1037
|
+
args.push("--squash");
|
|
1038
|
+
}
|
|
1039
|
+
if (options.message) {
|
|
1040
|
+
args.push("-m", options.message);
|
|
1041
|
+
}
|
|
1042
|
+
args.push(branch);
|
|
1043
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1044
|
+
if (result.status !== 0) {
|
|
1045
|
+
throw new Error(result.stderr || "git merge failed");
|
|
1046
|
+
}
|
|
1047
|
+
return result.stdout || `Merged ${branch}`;
|
|
1048
|
+
}
|
|
1049
|
+
function gitMergeAbort(cwd) {
|
|
1050
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["merge", "--abort"], { cwd, encoding: "utf8" });
|
|
1051
|
+
if (result.status !== 0) {
|
|
1052
|
+
throw new Error(result.stderr || "git merge --abort failed");
|
|
1053
|
+
}
|
|
1054
|
+
return "Merge aborted";
|
|
1055
|
+
}
|
|
1056
|
+
function gitCommit(cwd, options) {
|
|
1057
|
+
const args = ["commit", "-m", options.message];
|
|
1058
|
+
if (options.amend) {
|
|
1059
|
+
args.push("--amend");
|
|
1060
|
+
}
|
|
1061
|
+
if (options.allowEmpty) {
|
|
1062
|
+
args.push("--allow-empty");
|
|
1063
|
+
}
|
|
1064
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1065
|
+
if (result.status !== 0) {
|
|
1066
|
+
throw new Error(result.stderr || "git commit failed");
|
|
1067
|
+
}
|
|
1068
|
+
return result.stdout || "Committed";
|
|
1069
|
+
}
|
|
1070
|
+
function gitAdd(cwd, paths) {
|
|
1071
|
+
const args = ["add", ...paths];
|
|
1072
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1073
|
+
if (result.status !== 0) {
|
|
1074
|
+
throw new Error(result.stderr || "git add failed");
|
|
1075
|
+
}
|
|
1076
|
+
return result.stdout || `Staged ${paths.join(", ")}`;
|
|
1077
|
+
}
|
|
1078
|
+
function gitReset(cwd, mode = "mixed", ref) {
|
|
1079
|
+
if (mode === "hard") {
|
|
1080
|
+
const currentBranch = getCurrentBranch(cwd);
|
|
1081
|
+
if (GIT_SAFETY.PROTECTED_BRANCHES.includes(currentBranch)) {
|
|
1082
|
+
throw new Error(
|
|
1083
|
+
`Hard reset on protected branch "${currentBranch}" is blocked for safety.
|
|
1084
|
+
Protected branches: ${GIT_SAFETY.PROTECTED_BRANCHES.join(", ")}
|
|
1085
|
+
Use soft or mixed reset instead, or switch to a feature branch.`
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
const args = ["reset", `--${mode}`];
|
|
1090
|
+
if (ref) {
|
|
1091
|
+
args.push(ref);
|
|
1092
|
+
}
|
|
1093
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1094
|
+
if (result.status !== 0) {
|
|
1095
|
+
throw new Error(result.stderr || "git reset failed");
|
|
1096
|
+
}
|
|
1097
|
+
return result.stdout || `Reset ${mode}${ref ? ` to ${ref}` : ""}`;
|
|
1098
|
+
}
|
|
1099
|
+
function isGitRepository(cwd) {
|
|
1100
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["rev-parse", "--is-inside-work-tree"], { cwd, encoding: "utf8" });
|
|
1101
|
+
return result.status === 0;
|
|
1102
|
+
}
|
|
1103
|
+
function getAutoCommitInfo(cwd) {
|
|
1104
|
+
if (!isGitRepository(cwd)) {
|
|
1105
|
+
return {
|
|
1106
|
+
canCommit: false,
|
|
1107
|
+
error: "Not a git repository",
|
|
1108
|
+
filesChanged: [],
|
|
1109
|
+
suggestedMessage: "",
|
|
1110
|
+
diffSummary: ""
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
const statusResult = _child_process.spawnSync.call(void 0, "git", ["status", "--porcelain"], { cwd, encoding: "utf8" });
|
|
1114
|
+
if (statusResult.status !== 0) {
|
|
1115
|
+
return {
|
|
1116
|
+
canCommit: false,
|
|
1117
|
+
error: "Failed to get git status",
|
|
1118
|
+
filesChanged: [],
|
|
1119
|
+
suggestedMessage: "",
|
|
1120
|
+
diffSummary: ""
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
const changes = _optionalChain([statusResult, 'access', _19 => _19.stdout, 'optionalAccess', _20 => _20.trim, 'call', _21 => _21(), 'access', _22 => _22.split, 'call', _23 => _23("\n"), 'access', _24 => _24.filter, 'call', _25 => _25(Boolean)]) || [];
|
|
1124
|
+
if (changes.length === 0) {
|
|
1125
|
+
return {
|
|
1126
|
+
canCommit: false,
|
|
1127
|
+
error: "No changes to commit",
|
|
1128
|
+
filesChanged: [],
|
|
1129
|
+
suggestedMessage: "",
|
|
1130
|
+
diffSummary: ""
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
const added = [];
|
|
1134
|
+
const modified = [];
|
|
1135
|
+
const deleted = [];
|
|
1136
|
+
const renamed = [];
|
|
1137
|
+
for (const change of changes) {
|
|
1138
|
+
const status = change.substring(0, 2);
|
|
1139
|
+
const file = change.substring(3).trim();
|
|
1140
|
+
if (status.includes("A") || status === "??") {
|
|
1141
|
+
added.push(file);
|
|
1142
|
+
} else if (status.includes("M")) {
|
|
1143
|
+
modified.push(file);
|
|
1144
|
+
} else if (status.includes("D")) {
|
|
1145
|
+
deleted.push(file);
|
|
1146
|
+
} else if (status.includes("R")) {
|
|
1147
|
+
renamed.push(file);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
const diffStatResult = _child_process.spawnSync.call(void 0, "git", ["diff", "--stat", "HEAD"], { cwd, encoding: "utf8" });
|
|
1151
|
+
const diffSummary = _optionalChain([diffStatResult, 'access', _26 => _26.stdout, 'optionalAccess', _27 => _27.trim, 'call', _28 => _28()]) || "";
|
|
1152
|
+
const suggestedMessage = generateCommitMessage(added, modified, deleted, renamed);
|
|
1153
|
+
return {
|
|
1154
|
+
canCommit: true,
|
|
1155
|
+
filesChanged: changes.map((c) => c.substring(3).trim()),
|
|
1156
|
+
suggestedMessage,
|
|
1157
|
+
diffSummary
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function generateCommitMessage(added, modified, deleted, renamed) {
|
|
1161
|
+
const parts = [];
|
|
1162
|
+
const totalChanges = added.length + modified.length + deleted.length + renamed.length;
|
|
1163
|
+
if (totalChanges === 1) {
|
|
1164
|
+
if (added.length === 1) {
|
|
1165
|
+
return `feat: Add ${added[0]}`;
|
|
1166
|
+
} else if (modified.length === 1) {
|
|
1167
|
+
return `update: Modify ${modified[0]}`;
|
|
1168
|
+
} else if (deleted.length === 1) {
|
|
1169
|
+
return `chore: Remove ${deleted[0]}`;
|
|
1170
|
+
} else if (renamed.length === 1) {
|
|
1171
|
+
return `refactor: Rename ${renamed[0]}`;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (added.length > 0) {
|
|
1175
|
+
parts.push(`add ${added.length} file${added.length > 1 ? "s" : ""}`);
|
|
1176
|
+
}
|
|
1177
|
+
if (modified.length > 0) {
|
|
1178
|
+
parts.push(`update ${modified.length} file${modified.length > 1 ? "s" : ""}`);
|
|
1179
|
+
}
|
|
1180
|
+
if (deleted.length > 0) {
|
|
1181
|
+
parts.push(`remove ${deleted.length} file${deleted.length > 1 ? "s" : ""}`);
|
|
1182
|
+
}
|
|
1183
|
+
if (renamed.length > 0) {
|
|
1184
|
+
parts.push(`rename ${renamed.length} file${renamed.length > 1 ? "s" : ""}`);
|
|
1185
|
+
}
|
|
1186
|
+
const allFiles = [...added, ...modified];
|
|
1187
|
+
const hasTests = allFiles.some((f) => f.includes("test") || f.includes("spec"));
|
|
1188
|
+
const hasDocs = allFiles.some((f) => f.includes(".md") || f.includes("doc"));
|
|
1189
|
+
const hasConfig = allFiles.some((f) => f.includes("config") || f.includes(".json") || f.includes(".yaml"));
|
|
1190
|
+
let prefix = "chore";
|
|
1191
|
+
if (added.length > modified.length && added.length > deleted.length) {
|
|
1192
|
+
prefix = "feat";
|
|
1193
|
+
} else if (hasTests) {
|
|
1194
|
+
prefix = "test";
|
|
1195
|
+
} else if (hasDocs) {
|
|
1196
|
+
prefix = "docs";
|
|
1197
|
+
} else if (hasConfig) {
|
|
1198
|
+
prefix = "chore";
|
|
1199
|
+
} else if (modified.length > 0) {
|
|
1200
|
+
prefix = "update";
|
|
1201
|
+
}
|
|
1202
|
+
return `${prefix}: ${parts.join(", ")}`;
|
|
1203
|
+
}
|
|
1204
|
+
function executeAutoCommit(cwd, message, stageAll = true) {
|
|
1205
|
+
const statusResult = _child_process.spawnSync.call(void 0, "git", ["status", "--porcelain"], { cwd, encoding: "utf8" });
|
|
1206
|
+
const changes = _optionalChain([statusResult, 'access', _29 => _29.stdout, 'optionalAccess', _30 => _30.trim, 'call', _31 => _31(), 'access', _32 => _32.split, 'call', _33 => _33("\n"), 'access', _34 => _34.filter, 'call', _35 => _35(Boolean)]) || [];
|
|
1207
|
+
if (stageAll) {
|
|
1208
|
+
const addResult = _child_process.spawnSync.call(void 0, "git", ["add", "-A"], { cwd, encoding: "utf8" });
|
|
1209
|
+
if (addResult.status !== 0) {
|
|
1210
|
+
return {
|
|
1211
|
+
success: false,
|
|
1212
|
+
message: `Failed to stage changes: ${addResult.stderr}`,
|
|
1213
|
+
filesChanged: 0
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
const commitResult = _child_process.spawnSync.call(void 0, "git", ["commit", "-m", message], { cwd, encoding: "utf8" });
|
|
1218
|
+
if (commitResult.status !== 0) {
|
|
1219
|
+
return {
|
|
1220
|
+
success: false,
|
|
1221
|
+
message: `Failed to commit: ${commitResult.stderr}`,
|
|
1222
|
+
filesChanged: changes.length
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
const hashResult = _child_process.spawnSync.call(void 0, "git", ["rev-parse", "--short", "HEAD"], { cwd, encoding: "utf8" });
|
|
1226
|
+
const commitHash = _optionalChain([hashResult, 'access', _36 => _36.stdout, 'optionalAccess', _37 => _37.trim, 'call', _38 => _38()]);
|
|
1227
|
+
return {
|
|
1228
|
+
success: true,
|
|
1229
|
+
message: `Committed ${changes.length} file(s): ${commitHash}`,
|
|
1230
|
+
filesChanged: changes.length,
|
|
1231
|
+
commitHash
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function gitLog(cwd, options = {}) {
|
|
1235
|
+
const args = ["log"];
|
|
1236
|
+
if (options.maxCount) {
|
|
1237
|
+
args.push(`-n${options.maxCount}`);
|
|
1238
|
+
}
|
|
1239
|
+
if (options.oneline) {
|
|
1240
|
+
args.push("--oneline");
|
|
1241
|
+
}
|
|
1242
|
+
if (options.graph) {
|
|
1243
|
+
args.push("--graph");
|
|
1244
|
+
}
|
|
1245
|
+
if (options.all) {
|
|
1246
|
+
args.push("--all");
|
|
1247
|
+
}
|
|
1248
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1249
|
+
if (result.status !== 0) {
|
|
1250
|
+
throw new Error(result.stderr || "git log failed");
|
|
1251
|
+
}
|
|
1252
|
+
return result.stdout || "No commits";
|
|
1253
|
+
}
|
|
1254
|
+
function gitFetch(cwd, remote, branch) {
|
|
1255
|
+
const args = ["fetch"];
|
|
1256
|
+
if (remote) {
|
|
1257
|
+
args.push(remote);
|
|
1258
|
+
if (branch) {
|
|
1259
|
+
args.push(branch);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1263
|
+
if (result.status !== 0) {
|
|
1264
|
+
throw new Error(result.stderr || "git fetch failed");
|
|
1265
|
+
}
|
|
1266
|
+
return result.stdout || result.stderr || "Fetched";
|
|
1267
|
+
}
|
|
1268
|
+
function gitPull(cwd, remote, branch) {
|
|
1269
|
+
const args = ["pull"];
|
|
1270
|
+
if (remote) {
|
|
1271
|
+
args.push(remote);
|
|
1272
|
+
if (branch) {
|
|
1273
|
+
args.push(branch);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1277
|
+
if (result.status !== 0) {
|
|
1278
|
+
throw new Error(result.stderr || "git pull failed");
|
|
1279
|
+
}
|
|
1280
|
+
return result.stdout || "Pulled";
|
|
1281
|
+
}
|
|
1282
|
+
function gitPush(cwd, remote, branch, options = {}) {
|
|
1283
|
+
const targetRemote = remote || "origin";
|
|
1284
|
+
const targetBranch = branch || getCurrentBranch(cwd);
|
|
1285
|
+
if (options.force && GIT_SAFETY.PROTECTED_BRANCHES.includes(targetBranch)) {
|
|
1286
|
+
throw new Error(
|
|
1287
|
+
`Force push to protected branch "${targetBranch}" is blocked. Protected branches: ${GIT_SAFETY.PROTECTED_BRANCHES.join(", ")}`
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
const commitsAhead = getCommitsAhead(cwd, targetRemote, targetBranch);
|
|
1291
|
+
if (commitsAhead > GIT_SAFETY.MAX_COMMITS_PER_PUSH) {
|
|
1292
|
+
throw new Error(
|
|
1293
|
+
`Too many commits to push (${commitsAhead}). Maximum allowed: ${GIT_SAFETY.MAX_COMMITS_PER_PUSH}. This limit exists to prevent accidental large pushes. Push manually if intentional.`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
const args = ["push"];
|
|
1297
|
+
if (options.force) {
|
|
1298
|
+
args.push("--force-with-lease");
|
|
1299
|
+
}
|
|
1300
|
+
if (options.setUpstream) {
|
|
1301
|
+
args.push("--set-upstream");
|
|
1302
|
+
}
|
|
1303
|
+
if (remote) {
|
|
1304
|
+
args.push(remote);
|
|
1305
|
+
if (branch) {
|
|
1306
|
+
args.push(branch);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, { cwd, encoding: "utf8" });
|
|
1310
|
+
if (result.status !== 0) {
|
|
1311
|
+
throw new Error(result.stderr || "git push failed");
|
|
1312
|
+
}
|
|
1313
|
+
return result.stdout || result.stderr || "Pushed";
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// src/actions/worktree.ts
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
var _os = require('os'); var _os2 = _interopRequireDefault(_os);
|
|
1321
|
+
var DEFAULT_TEMPLATES = [
|
|
1322
|
+
{
|
|
1323
|
+
name: "feature",
|
|
1324
|
+
description: "Feature branch worktree",
|
|
1325
|
+
pathPattern: "../{repo}-feature-{branch}",
|
|
1326
|
+
setupCommands: ["npm install || yarn install || pnpm install || true"]
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
name: "hotfix",
|
|
1330
|
+
description: "Hotfix branch from main/master",
|
|
1331
|
+
pathPattern: "../{repo}-hotfix-{branch}",
|
|
1332
|
+
setupCommands: ["npm install || yarn install || pnpm install || true"]
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
name: "release",
|
|
1336
|
+
description: "Release branch worktree",
|
|
1337
|
+
pathPattern: "../{repo}-release-{branch}",
|
|
1338
|
+
setupCommands: ["npm install || yarn install || pnpm install || true"]
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
name: "review",
|
|
1342
|
+
description: "PR review worktree",
|
|
1343
|
+
pathPattern: "../{repo}-review-{branch}",
|
|
1344
|
+
setupCommands: []
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
name: "experiment",
|
|
1348
|
+
description: "Experimental detached worktree",
|
|
1349
|
+
pathPattern: "../{repo}-experiment-{timestamp}",
|
|
1350
|
+
setupCommands: []
|
|
1351
|
+
}
|
|
1352
|
+
];
|
|
1353
|
+
var WorktreeManager = class {
|
|
1354
|
+
constructor(cwd) {
|
|
1355
|
+
this.repoRoot = this.findGitRoot(cwd);
|
|
1356
|
+
this.repoName = _path2.default.basename(this.repoRoot);
|
|
1357
|
+
this.templates = [...DEFAULT_TEMPLATES];
|
|
1358
|
+
}
|
|
1359
|
+
// ============ Core Operations ============
|
|
1360
|
+
/**
|
|
1361
|
+
* List all worktrees with detailed information
|
|
1362
|
+
*/
|
|
1363
|
+
list() {
|
|
1364
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["worktree", "list", "--porcelain"], {
|
|
1365
|
+
cwd: this.repoRoot,
|
|
1366
|
+
encoding: "utf8"
|
|
1367
|
+
});
|
|
1368
|
+
if (result.status !== 0) {
|
|
1369
|
+
throw new Error(result.stderr || "Failed to list worktrees");
|
|
1370
|
+
}
|
|
1371
|
+
return this.parseWorktreeList(result.stdout);
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Get comprehensive status of all worktrees
|
|
1375
|
+
*/
|
|
1376
|
+
async statusAll() {
|
|
1377
|
+
const worktrees = this.list();
|
|
1378
|
+
const statuses = [];
|
|
1379
|
+
for (const wt of worktrees) {
|
|
1380
|
+
if (wt.bare) continue;
|
|
1381
|
+
try {
|
|
1382
|
+
const status = await this.getWorktreeStatus(wt);
|
|
1383
|
+
statuses.push(status);
|
|
1384
|
+
} catch (e4) {
|
|
1385
|
+
statuses.push({
|
|
1386
|
+
worktree: wt,
|
|
1387
|
+
gitStatus: { staged: 0, modified: 0, untracked: 0, conflicts: 0, ahead: 0, behind: 0 },
|
|
1388
|
+
isClean: false,
|
|
1389
|
+
lastCommit: null
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
return statuses;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Smart worktree creation with templates
|
|
1397
|
+
*/
|
|
1398
|
+
async create(options) {
|
|
1399
|
+
const branch = options.branch || `worktree-${Date.now()}`;
|
|
1400
|
+
const template = options.template ? this.getTemplate(options.template) : null;
|
|
1401
|
+
let worktreePath;
|
|
1402
|
+
if (template) {
|
|
1403
|
+
worktreePath = this.resolvePath(template.pathPattern, branch);
|
|
1404
|
+
} else {
|
|
1405
|
+
worktreePath = _path2.default.join(_path2.default.dirname(this.repoRoot), `${this.repoName}-${branch}`);
|
|
1406
|
+
}
|
|
1407
|
+
await _fsextra2.default.ensureDir(_path2.default.dirname(worktreePath));
|
|
1408
|
+
const args = ["worktree", "add"];
|
|
1409
|
+
if (options.newBranch) {
|
|
1410
|
+
args.push("-b", branch);
|
|
1411
|
+
if (options.baseBranch) {
|
|
1412
|
+
args.push(worktreePath, options.baseBranch);
|
|
1413
|
+
} else {
|
|
1414
|
+
args.push(worktreePath);
|
|
1415
|
+
}
|
|
1416
|
+
} else if (options.detach) {
|
|
1417
|
+
args.push("--detach", worktreePath);
|
|
1418
|
+
if (options.branch) {
|
|
1419
|
+
args.push(options.branch);
|
|
1420
|
+
}
|
|
1421
|
+
} else {
|
|
1422
|
+
args.push(worktreePath);
|
|
1423
|
+
if (options.branch) {
|
|
1424
|
+
args.push(options.branch);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
if (options.force) {
|
|
1428
|
+
args.splice(2, 0, "--force");
|
|
1429
|
+
}
|
|
1430
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, {
|
|
1431
|
+
cwd: this.repoRoot,
|
|
1432
|
+
encoding: "utf8"
|
|
1433
|
+
});
|
|
1434
|
+
if (result.status !== 0) {
|
|
1435
|
+
throw new Error(result.stderr || "Failed to create worktree");
|
|
1436
|
+
}
|
|
1437
|
+
if (_optionalChain([template, 'optionalAccess', _39 => _39.setupCommands]) && options.runSetup !== false) {
|
|
1438
|
+
for (const cmd of template.setupCommands) {
|
|
1439
|
+
await this.runInWorktree(worktreePath, cmd);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return { path: worktreePath, branch };
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Remove a worktree with cleanup
|
|
1446
|
+
*/
|
|
1447
|
+
async remove(worktreePath, options = {}) {
|
|
1448
|
+
const absolutePath = _path2.default.isAbsolute(worktreePath) ? worktreePath : _path2.default.resolve(this.repoRoot, worktreePath);
|
|
1449
|
+
const worktrees = this.list();
|
|
1450
|
+
const wt = worktrees.find((w) => w.path === absolutePath);
|
|
1451
|
+
const branchToDelete = _optionalChain([wt, 'optionalAccess', _40 => _40.branch]);
|
|
1452
|
+
const args = ["worktree", "remove"];
|
|
1453
|
+
if (options.force) {
|
|
1454
|
+
args.push("--force");
|
|
1455
|
+
}
|
|
1456
|
+
args.push(absolutePath);
|
|
1457
|
+
const result = _child_process.spawnSync.call(void 0, "git", args, {
|
|
1458
|
+
cwd: this.repoRoot,
|
|
1459
|
+
encoding: "utf8"
|
|
1460
|
+
});
|
|
1461
|
+
if (result.status !== 0) {
|
|
1462
|
+
throw new Error(result.stderr || "Failed to remove worktree");
|
|
1463
|
+
}
|
|
1464
|
+
if (options.deleteBranch && branchToDelete) {
|
|
1465
|
+
const deleteResult = _child_process.spawnSync.call(void 0, "git", ["branch", "-d", branchToDelete], {
|
|
1466
|
+
cwd: this.repoRoot,
|
|
1467
|
+
encoding: "utf8"
|
|
1468
|
+
});
|
|
1469
|
+
if (deleteResult.status === 0) {
|
|
1470
|
+
return `Removed worktree and deleted branch ${branchToDelete}`;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return `Removed worktree at ${absolutePath}`;
|
|
1474
|
+
}
|
|
1475
|
+
// ============ Advanced Operations ============
|
|
1476
|
+
/**
|
|
1477
|
+
* Find and clean up stale/merged worktrees
|
|
1478
|
+
*/
|
|
1479
|
+
async cleanup(options = {}) {
|
|
1480
|
+
const worktrees = this.list();
|
|
1481
|
+
const mainBranch = this.getMainBranch();
|
|
1482
|
+
const toRemove = [];
|
|
1483
|
+
for (const wt of worktrees) {
|
|
1484
|
+
if (wt.bare || wt.path === this.repoRoot) continue;
|
|
1485
|
+
if (options.removeStale !== false && wt.prunable) {
|
|
1486
|
+
toRemove.push(wt);
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
if (options.removeMerged && wt.branch) {
|
|
1490
|
+
const isMerged = this.isBranchMerged(wt.branch, mainBranch);
|
|
1491
|
+
if (isMerged) {
|
|
1492
|
+
toRemove.push(wt);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
if (options.dryRun) {
|
|
1497
|
+
return { removed: [], wouldRemove: toRemove.map((w) => w.path) };
|
|
1498
|
+
}
|
|
1499
|
+
const removed = [];
|
|
1500
|
+
for (const wt of toRemove) {
|
|
1501
|
+
try {
|
|
1502
|
+
await this.remove(wt.path, { force: true, deleteBranch: options.removeMerged });
|
|
1503
|
+
removed.push(wt.path);
|
|
1504
|
+
} catch (e5) {
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
_child_process.spawnSync.call(void 0, "git", ["worktree", "prune"], { cwd: this.repoRoot });
|
|
1508
|
+
return { removed, wouldRemove: [] };
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Run a command in parallel across all worktrees
|
|
1512
|
+
*/
|
|
1513
|
+
async runParallel(command, options = {}) {
|
|
1514
|
+
const worktrees = this.list().filter((wt) => !wt.bare);
|
|
1515
|
+
const filtered = options.filter ? worktrees.filter(options.filter) : worktrees;
|
|
1516
|
+
const maxConcurrent = options.maxConcurrent || _os2.default.cpus().length;
|
|
1517
|
+
const timeout = options.timeout || 3e5;
|
|
1518
|
+
const results = [];
|
|
1519
|
+
const running = [];
|
|
1520
|
+
for (const wt of filtered) {
|
|
1521
|
+
const task = (async () => {
|
|
1522
|
+
const start = Date.now();
|
|
1523
|
+
try {
|
|
1524
|
+
const output = await this.runInWorktreeWithTimeout(wt.path, command, timeout);
|
|
1525
|
+
results.push({
|
|
1526
|
+
worktree: wt.path,
|
|
1527
|
+
branch: wt.branch,
|
|
1528
|
+
success: true,
|
|
1529
|
+
output,
|
|
1530
|
+
exitCode: 0,
|
|
1531
|
+
duration: Date.now() - start
|
|
1532
|
+
});
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
results.push({
|
|
1535
|
+
worktree: wt.path,
|
|
1536
|
+
branch: wt.branch,
|
|
1537
|
+
success: false,
|
|
1538
|
+
output: "",
|
|
1539
|
+
error: error.message,
|
|
1540
|
+
exitCode: error.exitCode || 1,
|
|
1541
|
+
duration: Date.now() - start
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
})();
|
|
1545
|
+
running.push(task);
|
|
1546
|
+
if (running.length >= maxConcurrent) {
|
|
1547
|
+
await Promise.race(running);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
await Promise.all(running);
|
|
1551
|
+
return results;
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Sync changes from main branch to all worktrees
|
|
1555
|
+
*/
|
|
1556
|
+
async syncAll(options = {}) {
|
|
1557
|
+
const mainBranch = options.mainBranch || this.getMainBranch();
|
|
1558
|
+
const strategy = options.strategy || "rebase";
|
|
1559
|
+
const worktrees = this.list().filter((wt) => !wt.bare && wt.branch && wt.branch !== mainBranch);
|
|
1560
|
+
_child_process.spawnSync.call(void 0, "git", ["fetch", "--all"], { cwd: this.repoRoot });
|
|
1561
|
+
const synced = [];
|
|
1562
|
+
const failed = [];
|
|
1563
|
+
const skipped = [];
|
|
1564
|
+
for (const wt of worktrees) {
|
|
1565
|
+
const status = await this.getWorktreeStatus(wt);
|
|
1566
|
+
if (!status.isClean) {
|
|
1567
|
+
skipped.push(`${wt.path} (has uncommitted changes)`);
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
if (options.dryRun) {
|
|
1571
|
+
synced.push(wt.path);
|
|
1572
|
+
continue;
|
|
1573
|
+
}
|
|
1574
|
+
try {
|
|
1575
|
+
const cmd = strategy === "rebase" ? `git rebase origin/${mainBranch}` : `git merge origin/${mainBranch}`;
|
|
1576
|
+
await this.runInWorktree(wt.path, cmd);
|
|
1577
|
+
synced.push(wt.path);
|
|
1578
|
+
} catch (error) {
|
|
1579
|
+
try {
|
|
1580
|
+
await this.runInWorktree(wt.path, `git ${strategy} --abort`);
|
|
1581
|
+
} catch (e6) {
|
|
1582
|
+
}
|
|
1583
|
+
failed.push(`${wt.path}: ${error.message}`);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return { synced, failed, skipped };
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Create a worktree for a PR review
|
|
1590
|
+
*/
|
|
1591
|
+
async createForPR(prNumber, remote = "origin") {
|
|
1592
|
+
const branch = `pr-${prNumber}`;
|
|
1593
|
+
const fetchResult = _child_process.spawnSync.call(void 0, "git", ["fetch", remote, `pull/${prNumber}/head:${branch}`], {
|
|
1594
|
+
cwd: this.repoRoot,
|
|
1595
|
+
encoding: "utf8"
|
|
1596
|
+
});
|
|
1597
|
+
if (fetchResult.status !== 0) {
|
|
1598
|
+
throw new Error(`Failed to fetch PR #${prNumber}: ${fetchResult.stderr}`);
|
|
1599
|
+
}
|
|
1600
|
+
return this.create({
|
|
1601
|
+
branch,
|
|
1602
|
+
template: "review",
|
|
1603
|
+
runSetup: true
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Switch to a worktree (opens in current terminal context)
|
|
1608
|
+
*/
|
|
1609
|
+
getWorktreePath(branchOrPath) {
|
|
1610
|
+
const worktrees = this.list();
|
|
1611
|
+
const byPath = worktrees.find((wt) => wt.path === branchOrPath || wt.path.endsWith(branchOrPath));
|
|
1612
|
+
if (byPath) return byPath.path;
|
|
1613
|
+
const byBranch = worktrees.find((wt) => wt.branch === branchOrPath);
|
|
1614
|
+
if (byBranch) return byBranch.path;
|
|
1615
|
+
return null;
|
|
1616
|
+
}
|
|
1617
|
+
// ============ Template Management ============
|
|
1618
|
+
getTemplates() {
|
|
1619
|
+
return [...this.templates];
|
|
1620
|
+
}
|
|
1621
|
+
getTemplate(name) {
|
|
1622
|
+
return this.templates.find((t) => t.name === name) || null;
|
|
1623
|
+
}
|
|
1624
|
+
addTemplate(template) {
|
|
1625
|
+
const existing = this.templates.findIndex((t) => t.name === template.name);
|
|
1626
|
+
if (existing >= 0) {
|
|
1627
|
+
this.templates[existing] = template;
|
|
1628
|
+
} else {
|
|
1629
|
+
this.templates.push(template);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
// ============ Helper Methods ============
|
|
1633
|
+
findGitRoot(cwd) {
|
|
1634
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["rev-parse", "--show-toplevel"], {
|
|
1635
|
+
cwd,
|
|
1636
|
+
encoding: "utf8"
|
|
1637
|
+
});
|
|
1638
|
+
if (result.status !== 0) {
|
|
1639
|
+
throw new Error("Not a git repository");
|
|
1640
|
+
}
|
|
1641
|
+
return result.stdout.trim();
|
|
1642
|
+
}
|
|
1643
|
+
parseWorktreeList(output) {
|
|
1644
|
+
const worktrees = [];
|
|
1645
|
+
const entries = output.trim().split("\n\n");
|
|
1646
|
+
for (const entry of entries) {
|
|
1647
|
+
if (!entry.trim()) continue;
|
|
1648
|
+
const lines = entry.split("\n");
|
|
1649
|
+
const info = {
|
|
1650
|
+
bare: false,
|
|
1651
|
+
detached: false,
|
|
1652
|
+
locked: false,
|
|
1653
|
+
prunable: false
|
|
1654
|
+
};
|
|
1655
|
+
for (const line of lines) {
|
|
1656
|
+
if (line.startsWith("worktree ")) {
|
|
1657
|
+
info.path = line.slice(9);
|
|
1658
|
+
} else if (line.startsWith("HEAD ")) {
|
|
1659
|
+
info.head = line.slice(5);
|
|
1660
|
+
} else if (line.startsWith("branch ")) {
|
|
1661
|
+
info.branch = line.slice(7).replace("refs/heads/", "");
|
|
1662
|
+
} else if (line === "bare") {
|
|
1663
|
+
info.bare = true;
|
|
1664
|
+
} else if (line === "detached") {
|
|
1665
|
+
info.detached = true;
|
|
1666
|
+
} else if (line.startsWith("locked")) {
|
|
1667
|
+
info.locked = true;
|
|
1668
|
+
} else if (line.startsWith("prunable")) {
|
|
1669
|
+
info.prunable = true;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
if (info.path && info.head) {
|
|
1673
|
+
worktrees.push(info);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return worktrees;
|
|
1677
|
+
}
|
|
1678
|
+
async getWorktreeStatus(wt) {
|
|
1679
|
+
const statusResult = _child_process.spawnSync.call(void 0, "git", ["status", "--porcelain=v1", "-b"], {
|
|
1680
|
+
cwd: wt.path,
|
|
1681
|
+
encoding: "utf8"
|
|
1682
|
+
});
|
|
1683
|
+
const lines = statusResult.stdout.trim().split("\n");
|
|
1684
|
+
const branchLine = lines[0] || "";
|
|
1685
|
+
const fileLines = lines.slice(1);
|
|
1686
|
+
let ahead = 0;
|
|
1687
|
+
let behind = 0;
|
|
1688
|
+
const aheadBehindMatch = branchLine.match(/\[ahead (\d+)(?:, behind (\d+))?\]|\[behind (\d+)\]/);
|
|
1689
|
+
if (aheadBehindMatch) {
|
|
1690
|
+
ahead = parseInt(aheadBehindMatch[1] || "0", 10);
|
|
1691
|
+
behind = parseInt(aheadBehindMatch[2] || aheadBehindMatch[3] || "0", 10);
|
|
1692
|
+
}
|
|
1693
|
+
let staged = 0, modified = 0, untracked = 0, conflicts = 0;
|
|
1694
|
+
for (const line of fileLines) {
|
|
1695
|
+
if (!line) continue;
|
|
1696
|
+
const x = line[0];
|
|
1697
|
+
const y = line[1];
|
|
1698
|
+
if (x === "U" || y === "U" || x === "D" && y === "D" || x === "A" && y === "A") {
|
|
1699
|
+
conflicts++;
|
|
1700
|
+
} else {
|
|
1701
|
+
if (x !== " " && x !== "?") staged++;
|
|
1702
|
+
if (y === "M" || y === "D") modified++;
|
|
1703
|
+
if (x === "?") untracked++;
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
const logResult = _child_process.spawnSync.call(void 0, "git", ["log", "-1", "--format=%H|%s|%an|%ai"], {
|
|
1707
|
+
cwd: wt.path,
|
|
1708
|
+
encoding: "utf8"
|
|
1709
|
+
});
|
|
1710
|
+
let lastCommit = null;
|
|
1711
|
+
if (logResult.status === 0 && logResult.stdout.trim()) {
|
|
1712
|
+
const [hash, message, author, date] = logResult.stdout.trim().split("|");
|
|
1713
|
+
lastCommit = { hash, message, author, date };
|
|
1714
|
+
}
|
|
1715
|
+
return {
|
|
1716
|
+
worktree: wt,
|
|
1717
|
+
gitStatus: { staged, modified, untracked, conflicts, ahead, behind },
|
|
1718
|
+
isClean: staged === 0 && modified === 0 && untracked === 0 && conflicts === 0,
|
|
1719
|
+
lastCommit
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
getMainBranch() {
|
|
1723
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
1724
|
+
cwd: this.repoRoot,
|
|
1725
|
+
encoding: "utf8"
|
|
1726
|
+
});
|
|
1727
|
+
if (result.status === 0) {
|
|
1728
|
+
return result.stdout.trim().replace("refs/remotes/origin/", "");
|
|
1729
|
+
}
|
|
1730
|
+
const branches = _child_process.spawnSync.call(void 0, "git", ["branch", "-l", "main", "master"], {
|
|
1731
|
+
cwd: this.repoRoot,
|
|
1732
|
+
encoding: "utf8"
|
|
1733
|
+
});
|
|
1734
|
+
if (branches.stdout.includes("main")) return "main";
|
|
1735
|
+
if (branches.stdout.includes("master")) return "master";
|
|
1736
|
+
return "main";
|
|
1737
|
+
}
|
|
1738
|
+
isBranchMerged(branch, into) {
|
|
1739
|
+
const result = _child_process.spawnSync.call(void 0, "git", ["branch", "--merged", into], {
|
|
1740
|
+
cwd: this.repoRoot,
|
|
1741
|
+
encoding: "utf8"
|
|
1742
|
+
});
|
|
1743
|
+
if (result.status !== 0) return false;
|
|
1744
|
+
return result.stdout.split("\n").some(
|
|
1745
|
+
(line) => line.trim() === branch || line.trim() === `* ${branch}`
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
resolvePath(pattern, branch) {
|
|
1749
|
+
const timestamp = Date.now().toString();
|
|
1750
|
+
const sanitizedBranch = branch.replace(/[^a-zA-Z0-9-_]/g, "-");
|
|
1751
|
+
return _path2.default.resolve(
|
|
1752
|
+
this.repoRoot,
|
|
1753
|
+
pattern.replace("{repo}", this.repoName).replace("{branch}", sanitizedBranch).replace("{timestamp}", timestamp)
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
async runInWorktree(worktreePath, command) {
|
|
1757
|
+
return new Promise((resolve, reject) => {
|
|
1758
|
+
const child = _child_process.spawn.call(void 0, "sh", ["-c", command], {
|
|
1759
|
+
cwd: worktreePath,
|
|
1760
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1761
|
+
});
|
|
1762
|
+
let stdout = "";
|
|
1763
|
+
let stderr = "";
|
|
1764
|
+
child.stdout.on("data", (data) => {
|
|
1765
|
+
stdout += data;
|
|
1766
|
+
});
|
|
1767
|
+
child.stderr.on("data", (data) => {
|
|
1768
|
+
stderr += data;
|
|
1769
|
+
});
|
|
1770
|
+
child.on("close", (code) => {
|
|
1771
|
+
if (code === 0) {
|
|
1772
|
+
resolve(stdout);
|
|
1773
|
+
} else {
|
|
1774
|
+
const error = new Error(stderr || `Command failed with code ${code}`);
|
|
1775
|
+
error.exitCode = code;
|
|
1776
|
+
reject(error);
|
|
1777
|
+
}
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
async runInWorktreeWithTimeout(worktreePath, command, timeout) {
|
|
1782
|
+
return new Promise((resolve, reject) => {
|
|
1783
|
+
const child = _child_process.spawn.call(void 0, "sh", ["-c", command], {
|
|
1784
|
+
cwd: worktreePath,
|
|
1785
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1786
|
+
});
|
|
1787
|
+
let stdout = "";
|
|
1788
|
+
let stderr = "";
|
|
1789
|
+
let killed = false;
|
|
1790
|
+
const timer = setTimeout(() => {
|
|
1791
|
+
killed = true;
|
|
1792
|
+
child.kill("SIGTERM");
|
|
1793
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1794
|
+
}, timeout);
|
|
1795
|
+
child.stdout.on("data", (data) => {
|
|
1796
|
+
stdout += data;
|
|
1797
|
+
});
|
|
1798
|
+
child.stderr.on("data", (data) => {
|
|
1799
|
+
stderr += data;
|
|
1800
|
+
});
|
|
1801
|
+
child.on("close", (code) => {
|
|
1802
|
+
clearTimeout(timer);
|
|
1803
|
+
if (killed) return;
|
|
1804
|
+
if (code === 0) {
|
|
1805
|
+
resolve(stdout);
|
|
1806
|
+
} else {
|
|
1807
|
+
const error = new Error(stderr || `Command failed with code ${code}`);
|
|
1808
|
+
error.exitCode = code;
|
|
1809
|
+
reject(error);
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
|
|
1816
|
+
// src/core/customCommands.ts
|
|
1817
|
+
|
|
1818
|
+
|
|
1819
|
+
var COMMANDS_DIR = _chunkSEKD5FH3cjs.AUTOHAND_PATHS.commands;
|
|
1820
|
+
async function loadCustomCommand(name) {
|
|
1821
|
+
const filePath = _path2.default.join(COMMANDS_DIR, `${sanitizeName(name)}.json`);
|
|
1822
|
+
if (!await _fsextra2.default.pathExists(filePath)) {
|
|
1823
|
+
return null;
|
|
1824
|
+
}
|
|
1825
|
+
return _fsextra2.default.readJson(filePath);
|
|
1826
|
+
}
|
|
1827
|
+
async function saveCustomCommand(definition) {
|
|
1828
|
+
await _fsextra2.default.ensureDir(COMMANDS_DIR);
|
|
1829
|
+
const filePath = _path2.default.join(COMMANDS_DIR, `${sanitizeName(definition.name)}.json`);
|
|
1830
|
+
await _fsextra2.default.writeJson(filePath, definition, { spaces: 2 });
|
|
1831
|
+
}
|
|
1832
|
+
function sanitizeName(name) {
|
|
1833
|
+
return name.replace(/[^a-z0-9-_]/gi, "_");
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// src/actions/webRepo.ts
|
|
1837
|
+
var _https = require('https'); var https = _interopRequireWildcard(_https);
|
|
1838
|
+
function parseRepoUrl(input) {
|
|
1839
|
+
const shorthandMatch = input.match(/^(github|gitlab):(.+)$/);
|
|
1840
|
+
if (shorthandMatch) {
|
|
1841
|
+
const platform = shorthandMatch[1];
|
|
1842
|
+
const path8 = shorthandMatch[2];
|
|
1843
|
+
const lastSlash = path8.lastIndexOf("/");
|
|
1844
|
+
if (lastSlash === -1) {
|
|
1845
|
+
throw new Error("Could not parse repo URL. Use format: owner/repo (GitHub), github:owner/repo, gitlab:group/project, or full URL.");
|
|
1846
|
+
}
|
|
1847
|
+
return {
|
|
1848
|
+
platform,
|
|
1849
|
+
owner: path8.slice(0, lastSlash),
|
|
1850
|
+
repo: path8.slice(lastSlash + 1)
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
if (!input.includes(":") && input.includes("/")) {
|
|
1854
|
+
const slashIndex = input.indexOf("/");
|
|
1855
|
+
const lastSlashIndex = input.lastIndexOf("/");
|
|
1856
|
+
if (slashIndex === lastSlashIndex && slashIndex > 0) {
|
|
1857
|
+
const owner = input.slice(0, slashIndex);
|
|
1858
|
+
const repo = input.slice(slashIndex + 1);
|
|
1859
|
+
if (owner && repo) {
|
|
1860
|
+
return {
|
|
1861
|
+
platform: "github",
|
|
1862
|
+
owner,
|
|
1863
|
+
repo
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
try {
|
|
1869
|
+
const url = new URL(input);
|
|
1870
|
+
const hostname = url.hostname.toLowerCase();
|
|
1871
|
+
const pathParts = url.pathname.replace(/\/$/, "").split("/").filter(Boolean);
|
|
1872
|
+
if (pathParts.length < 2) {
|
|
1873
|
+
throw new Error("Could not parse repo URL. Use format: owner/repo (GitHub), github:owner/repo, gitlab:group/project, or full URL.");
|
|
1874
|
+
}
|
|
1875
|
+
if (hostname === "github.com") {
|
|
1876
|
+
return {
|
|
1877
|
+
platform: "github",
|
|
1878
|
+
owner: pathParts[0],
|
|
1879
|
+
repo: pathParts[1]
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
if (hostname === "gitlab.com") {
|
|
1883
|
+
const repo = pathParts[pathParts.length - 1];
|
|
1884
|
+
const owner = pathParts.slice(0, -1).join("/");
|
|
1885
|
+
return {
|
|
1886
|
+
platform: "gitlab",
|
|
1887
|
+
owner,
|
|
1888
|
+
repo
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
throw new Error("Could not parse repo URL. Use format: github:owner/repo, gitlab:group/project, or full URL.");
|
|
1892
|
+
} catch (e) {
|
|
1893
|
+
if (e instanceof Error && e.message.includes("Could not parse")) {
|
|
1894
|
+
throw e;
|
|
1895
|
+
}
|
|
1896
|
+
throw new Error("Could not parse repo URL. Use format: github:owner/repo, gitlab:group/project, or full URL.");
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
async function fetchJson(url, headers = {}) {
|
|
1900
|
+
const TIMEOUT_MS = 15e3;
|
|
1901
|
+
return new Promise((resolve, reject) => {
|
|
1902
|
+
const req = https.get(url, {
|
|
1903
|
+
timeout: TIMEOUT_MS,
|
|
1904
|
+
headers: {
|
|
1905
|
+
"User-Agent": "Autohand-CLI/1.0",
|
|
1906
|
+
"Accept": "application/json",
|
|
1907
|
+
...headers
|
|
1908
|
+
}
|
|
1909
|
+
}, (res) => {
|
|
1910
|
+
if (res.statusCode === 403) {
|
|
1911
|
+
reject(new Error("Rate limited. Hint: Set GITHUB_TOKEN or GITLAB_TOKEN in env or ~/.autohand/config.json to increase limits."));
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (res.statusCode === 404) {
|
|
1915
|
+
reject(new Error("Repository not found. Check the URL/shorthand is correct."));
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1918
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
1919
|
+
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
let data = "";
|
|
1923
|
+
res.on("data", (chunk) => {
|
|
1924
|
+
data += chunk;
|
|
1925
|
+
});
|
|
1926
|
+
res.on("end", () => {
|
|
1927
|
+
try {
|
|
1928
|
+
const json = JSON.parse(data);
|
|
1929
|
+
resolve(json);
|
|
1930
|
+
} catch (parseError) {
|
|
1931
|
+
reject(new Error(`Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : String(parseError)}`));
|
|
1932
|
+
}
|
|
1933
|
+
});
|
|
1934
|
+
res.on("error", reject);
|
|
1935
|
+
});
|
|
1936
|
+
req.on("error", reject);
|
|
1937
|
+
req.on("timeout", () => {
|
|
1938
|
+
req.destroy();
|
|
1939
|
+
reject(new Error("Request timed out"));
|
|
1940
|
+
});
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
async function fetchGitHubInfo(parsed) {
|
|
1944
|
+
const url = `https://api.github.com/repos/${parsed.owner}/${parsed.repo}`;
|
|
1945
|
+
const token = process.env.GITHUB_TOKEN;
|
|
1946
|
+
const headers = {};
|
|
1947
|
+
if (token) {
|
|
1948
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
1949
|
+
}
|
|
1950
|
+
const data = await fetchJson(url, headers);
|
|
1951
|
+
return {
|
|
1952
|
+
platform: "github",
|
|
1953
|
+
name: data.name,
|
|
1954
|
+
fullName: data.full_name,
|
|
1955
|
+
description: data.description || "",
|
|
1956
|
+
stars: data.stargazers_count,
|
|
1957
|
+
language: data.language,
|
|
1958
|
+
defaultBranch: data.default_branch,
|
|
1959
|
+
license: _optionalChain([data, 'access', _41 => _41.license, 'optionalAccess', _42 => _42.spdx_id]) || null
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
async function fetchGitLabInfo(parsed) {
|
|
1963
|
+
const projectPath = encodeURIComponent(`${parsed.owner}/${parsed.repo}`);
|
|
1964
|
+
const url = `https://gitlab.com/api/v4/projects/${projectPath}`;
|
|
1965
|
+
const token = process.env.GITLAB_TOKEN;
|
|
1966
|
+
const headers = {};
|
|
1967
|
+
if (token) {
|
|
1968
|
+
headers["PRIVATE-TOKEN"] = token;
|
|
1969
|
+
}
|
|
1970
|
+
const data = await fetchJson(url, headers);
|
|
1971
|
+
return {
|
|
1972
|
+
platform: "gitlab",
|
|
1973
|
+
name: data.name,
|
|
1974
|
+
fullName: data.path_with_namespace,
|
|
1975
|
+
description: data.description || "",
|
|
1976
|
+
stars: data.star_count,
|
|
1977
|
+
language: null,
|
|
1978
|
+
// Would require additional API call to /languages
|
|
1979
|
+
defaultBranch: data.default_branch,
|
|
1980
|
+
license: null
|
|
1981
|
+
// Would require additional API call
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
async function fetchRepoInfo(parsed) {
|
|
1985
|
+
switch (parsed.platform) {
|
|
1986
|
+
case "github":
|
|
1987
|
+
return fetchGitHubInfo(parsed);
|
|
1988
|
+
case "gitlab":
|
|
1989
|
+
return fetchGitLabInfo(parsed);
|
|
1990
|
+
default:
|
|
1991
|
+
throw new Error(`Unsupported platform: ${parsed.platform}`);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
async function listGitHubDir(parsed, path8, branch) {
|
|
1995
|
+
const encodedPath = path8 ? encodeURIComponent(path8).replace(/%2F/g, "/") : "";
|
|
1996
|
+
let url = `https://api.github.com/repos/${parsed.owner}/${parsed.repo}/contents/${encodedPath}`;
|
|
1997
|
+
if (branch) {
|
|
1998
|
+
url += `?ref=${encodeURIComponent(branch)}`;
|
|
1999
|
+
}
|
|
2000
|
+
const token = process.env.GITHUB_TOKEN;
|
|
2001
|
+
const headers = {};
|
|
2002
|
+
if (token) {
|
|
2003
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2004
|
+
}
|
|
2005
|
+
const data = await fetchJson(url, headers);
|
|
2006
|
+
return data.map((item) => ({
|
|
2007
|
+
name: item.name,
|
|
2008
|
+
type: item.type === "dir" ? "dir" : "file",
|
|
2009
|
+
path: item.path,
|
|
2010
|
+
size: item.type === "file" ? item.size : void 0
|
|
2011
|
+
}));
|
|
2012
|
+
}
|
|
2013
|
+
async function listGitLabDir(parsed, path8, branch) {
|
|
2014
|
+
const projectPath = encodeURIComponent(`${parsed.owner}/${parsed.repo}`);
|
|
2015
|
+
let url = `https://gitlab.com/api/v4/projects/${projectPath}/repository/tree?per_page=100`;
|
|
2016
|
+
if (path8) {
|
|
2017
|
+
url += `&path=${encodeURIComponent(path8)}`;
|
|
2018
|
+
}
|
|
2019
|
+
if (branch) {
|
|
2020
|
+
url += `&ref=${encodeURIComponent(branch)}`;
|
|
2021
|
+
}
|
|
2022
|
+
const token = process.env.GITLAB_TOKEN;
|
|
2023
|
+
const headers = {};
|
|
2024
|
+
if (token) {
|
|
2025
|
+
headers["PRIVATE-TOKEN"] = token;
|
|
2026
|
+
}
|
|
2027
|
+
const data = await fetchJson(url, headers);
|
|
2028
|
+
return data.map((item) => ({
|
|
2029
|
+
name: item.name,
|
|
2030
|
+
type: item.type === "tree" ? "dir" : "file",
|
|
2031
|
+
path: item.path
|
|
2032
|
+
// GitLab tree API does not return file sizes
|
|
2033
|
+
}));
|
|
2034
|
+
}
|
|
2035
|
+
async function listRepoDir(parsed, path8, branch) {
|
|
2036
|
+
switch (parsed.platform) {
|
|
2037
|
+
case "github":
|
|
2038
|
+
return listGitHubDir(parsed, path8, branch);
|
|
2039
|
+
case "gitlab":
|
|
2040
|
+
return listGitLabDir(parsed, path8, branch);
|
|
2041
|
+
default:
|
|
2042
|
+
throw new Error(`Unsupported platform: ${parsed.platform}`);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
async function fetchText(url, headers = {}, maxRedirects = 5) {
|
|
2046
|
+
const TIMEOUT_MS = 15e3;
|
|
2047
|
+
return new Promise((resolve, reject) => {
|
|
2048
|
+
const req = https.get(url, {
|
|
2049
|
+
timeout: TIMEOUT_MS,
|
|
2050
|
+
headers: {
|
|
2051
|
+
"User-Agent": "Autohand-CLI/1.0",
|
|
2052
|
+
...headers
|
|
2053
|
+
}
|
|
2054
|
+
}, (res) => {
|
|
2055
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
2056
|
+
if (maxRedirects <= 0) {
|
|
2057
|
+
reject(new Error("Too many redirects"));
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
const redirectUrl = new URL(res.headers.location, url).toString();
|
|
2061
|
+
fetchText(redirectUrl, headers, maxRedirects - 1).then(resolve).catch(reject);
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
if (res.statusCode === 403) {
|
|
2065
|
+
reject(new Error("Rate limited. Hint: Set GITHUB_TOKEN or GITLAB_TOKEN in env or ~/.autohand/config.json to increase limits."));
|
|
2066
|
+
return;
|
|
2067
|
+
}
|
|
2068
|
+
if (res.statusCode === 404) {
|
|
2069
|
+
reject(new Error("File not found. Use operation 'list' to see available files."));
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
2073
|
+
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
let data = "";
|
|
2077
|
+
res.on("data", (chunk) => {
|
|
2078
|
+
data += chunk;
|
|
2079
|
+
});
|
|
2080
|
+
res.on("end", () => {
|
|
2081
|
+
resolve(data);
|
|
2082
|
+
});
|
|
2083
|
+
res.on("error", reject);
|
|
2084
|
+
});
|
|
2085
|
+
req.on("error", reject);
|
|
2086
|
+
req.on("timeout", () => {
|
|
2087
|
+
req.destroy();
|
|
2088
|
+
reject(new Error("Request timed out"));
|
|
2089
|
+
});
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
async function fetchGitHubFile(parsed, path8, branch = "HEAD") {
|
|
2093
|
+
const url = `https://raw.githubusercontent.com/${parsed.owner}/${parsed.repo}/${branch}/${path8}`;
|
|
2094
|
+
const token = process.env.GITHUB_TOKEN;
|
|
2095
|
+
const headers = {};
|
|
2096
|
+
if (token) {
|
|
2097
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2098
|
+
}
|
|
2099
|
+
return fetchText(url, headers);
|
|
2100
|
+
}
|
|
2101
|
+
async function fetchGitLabFile(parsed, path8, branch = "HEAD") {
|
|
2102
|
+
const projectPath = encodeURIComponent(`${parsed.owner}/${parsed.repo}`);
|
|
2103
|
+
const encodedFilePath = encodeURIComponent(path8);
|
|
2104
|
+
const url = `https://gitlab.com/api/v4/projects/${projectPath}/repository/files/${encodedFilePath}/raw?ref=${encodeURIComponent(branch)}`;
|
|
2105
|
+
const token = process.env.GITLAB_TOKEN;
|
|
2106
|
+
const headers = {};
|
|
2107
|
+
if (token) {
|
|
2108
|
+
headers["PRIVATE-TOKEN"] = token;
|
|
2109
|
+
}
|
|
2110
|
+
return fetchText(url, headers);
|
|
2111
|
+
}
|
|
2112
|
+
async function fetchRepoFile(parsed, path8, branch) {
|
|
2113
|
+
switch (parsed.platform) {
|
|
2114
|
+
case "github":
|
|
2115
|
+
return fetchGitHubFile(parsed, path8, branch);
|
|
2116
|
+
case "gitlab":
|
|
2117
|
+
return fetchGitLabFile(parsed, path8, branch);
|
|
2118
|
+
default:
|
|
2119
|
+
throw new Error(`Unsupported platform: ${parsed.platform}`);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
function formatBytes(bytes) {
|
|
2123
|
+
if (bytes < 1024) {
|
|
2124
|
+
return `${bytes} B`;
|
|
2125
|
+
}
|
|
2126
|
+
if (bytes < 1024 * 1024) {
|
|
2127
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2128
|
+
}
|
|
2129
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2130
|
+
}
|
|
2131
|
+
function formatRepoInfo(info) {
|
|
2132
|
+
const lines = [];
|
|
2133
|
+
lines.push(`**${info.fullName}** (${info.platform})`);
|
|
2134
|
+
lines.push("");
|
|
2135
|
+
if (info.description) {
|
|
2136
|
+
lines.push(info.description);
|
|
2137
|
+
lines.push("");
|
|
2138
|
+
}
|
|
2139
|
+
lines.push(`Stars: ${info.stars}`);
|
|
2140
|
+
lines.push(`Default branch: ${info.defaultBranch}`);
|
|
2141
|
+
if (info.language) {
|
|
2142
|
+
lines.push(`Language: ${info.language}`);
|
|
2143
|
+
}
|
|
2144
|
+
if (info.license) {
|
|
2145
|
+
lines.push(`License: ${info.license}`);
|
|
2146
|
+
}
|
|
2147
|
+
return lines.join("\n");
|
|
2148
|
+
}
|
|
2149
|
+
function formatRepoDir(files, path8) {
|
|
2150
|
+
const lines = [];
|
|
2151
|
+
if (path8 === "") {
|
|
2152
|
+
lines.push("Repository root:");
|
|
2153
|
+
} else {
|
|
2154
|
+
lines.push(`Contents of ${path8}/`);
|
|
2155
|
+
}
|
|
2156
|
+
lines.push("");
|
|
2157
|
+
const dirs = files.filter((f) => f.type === "dir").sort((a, b) => a.name.localeCompare(b.name));
|
|
2158
|
+
const fileItems = files.filter((f) => f.type === "file").sort((a, b) => a.name.localeCompare(b.name));
|
|
2159
|
+
for (const dir of dirs) {
|
|
2160
|
+
lines.push(`\u{1F4C1} ${dir.name}/`);
|
|
2161
|
+
}
|
|
2162
|
+
for (const file of fileItems) {
|
|
2163
|
+
if (file.size !== void 0) {
|
|
2164
|
+
lines.push(`\u{1F4C4} ${file.name} (${formatBytes(file.size)})`);
|
|
2165
|
+
} else {
|
|
2166
|
+
lines.push(`\u{1F4C4} ${file.name}`);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
return lines.join("\n");
|
|
2170
|
+
}
|
|
2171
|
+
async function webRepo(options) {
|
|
2172
|
+
const parsed = parseRepoUrl(options.repo);
|
|
2173
|
+
switch (options.operation) {
|
|
2174
|
+
case "info": {
|
|
2175
|
+
const data = await fetchRepoInfo(parsed);
|
|
2176
|
+
return { type: "info", data };
|
|
2177
|
+
}
|
|
2178
|
+
case "list": {
|
|
2179
|
+
const path8 = _nullishCoalesce(options.path, () => ( ""));
|
|
2180
|
+
const data = await listRepoDir(parsed, path8, options.branch);
|
|
2181
|
+
return { type: "list", data, path: path8 };
|
|
2182
|
+
}
|
|
2183
|
+
case "fetch": {
|
|
2184
|
+
const path8 = _nullishCoalesce(options.path, () => ( "README.md"));
|
|
2185
|
+
const data = await fetchRepoFile(parsed, path8, options.branch);
|
|
2186
|
+
return { type: "fetch", data, path: path8 };
|
|
2187
|
+
}
|
|
2188
|
+
default:
|
|
2189
|
+
throw new Error(`Invalid operation: ${options.operation}. Valid operations are: info, list, fetch`);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// src/core/toolsRegistry.ts
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
var ToolsRegistry = class {
|
|
2197
|
+
constructor(toolsDir = _chunkSEKD5FH3cjs.AUTOHAND_PATHS.tools) {
|
|
2198
|
+
this.toolsDir = toolsDir;
|
|
2199
|
+
this.metaToolCache = /* @__PURE__ */ new Map();
|
|
2200
|
+
}
|
|
2201
|
+
async initialize() {
|
|
2202
|
+
await _fsextra2.default.ensureDir(this.toolsDir);
|
|
2203
|
+
await this.loadMetaToolDefinitions();
|
|
2204
|
+
}
|
|
2205
|
+
async listTools(builtIns) {
|
|
2206
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2207
|
+
const entries = [];
|
|
2208
|
+
for (const def of builtIns) {
|
|
2209
|
+
if (seen.has(def.name)) {
|
|
2210
|
+
continue;
|
|
2211
|
+
}
|
|
2212
|
+
entries.push({
|
|
2213
|
+
name: def.name,
|
|
2214
|
+
description: def.description,
|
|
2215
|
+
requiresApproval: def.requiresApproval,
|
|
2216
|
+
approvalMessage: def.approvalMessage,
|
|
2217
|
+
source: "builtin"
|
|
2218
|
+
});
|
|
2219
|
+
seen.add(def.name);
|
|
2220
|
+
}
|
|
2221
|
+
for (const [name, tool] of this.metaToolCache) {
|
|
2222
|
+
if (seen.has(name)) {
|
|
2223
|
+
continue;
|
|
2224
|
+
}
|
|
2225
|
+
entries.push({
|
|
2226
|
+
name: tool.name,
|
|
2227
|
+
description: tool.description,
|
|
2228
|
+
source: "meta"
|
|
2229
|
+
});
|
|
2230
|
+
seen.add(name);
|
|
2231
|
+
}
|
|
2232
|
+
return entries;
|
|
2233
|
+
}
|
|
2234
|
+
async saveMetaTool(definition) {
|
|
2235
|
+
const fullDef = {
|
|
2236
|
+
...definition,
|
|
2237
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2238
|
+
};
|
|
2239
|
+
const filePath = _path2.default.join(this.toolsDir, `${definition.name}.json`);
|
|
2240
|
+
await _fsextra2.default.writeJson(filePath, fullDef, { spaces: 2 });
|
|
2241
|
+
this.metaToolCache.set(definition.name, fullDef);
|
|
2242
|
+
return fullDef;
|
|
2243
|
+
}
|
|
2244
|
+
getMetaTool(name) {
|
|
2245
|
+
return this.metaToolCache.get(name);
|
|
2246
|
+
}
|
|
2247
|
+
hasMetaTool(name) {
|
|
2248
|
+
return this.metaToolCache.has(name);
|
|
2249
|
+
}
|
|
2250
|
+
getAllMetaTools() {
|
|
2251
|
+
return Array.from(this.metaToolCache.values());
|
|
2252
|
+
}
|
|
2253
|
+
toToolDefinitions() {
|
|
2254
|
+
return this.getAllMetaTools().map((tool) => {
|
|
2255
|
+
const params = tool.parameters;
|
|
2256
|
+
return {
|
|
2257
|
+
name: tool.name,
|
|
2258
|
+
description: tool.description,
|
|
2259
|
+
parameters: params.properties ? {
|
|
2260
|
+
type: "object",
|
|
2261
|
+
properties: params.properties,
|
|
2262
|
+
required: _nullishCoalesce(params.required, () => ( []))
|
|
2263
|
+
} : void 0
|
|
2264
|
+
};
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
async loadMetaToolDefinitions() {
|
|
2268
|
+
try {
|
|
2269
|
+
const exists = await _fsextra2.default.pathExists(this.toolsDir);
|
|
2270
|
+
if (!exists) {
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2273
|
+
const files = await _fsextra2.default.readdir(this.toolsDir);
|
|
2274
|
+
for (const file of files) {
|
|
2275
|
+
if (!file.endsWith(".json")) {
|
|
2276
|
+
continue;
|
|
2277
|
+
}
|
|
2278
|
+
const fullPath = _path2.default.join(this.toolsDir, file);
|
|
2279
|
+
try {
|
|
2280
|
+
const data = await _fsextra2.default.readJson(fullPath);
|
|
2281
|
+
if (this.isValidMetaTool(data)) {
|
|
2282
|
+
this.metaToolCache.set(data.name, data);
|
|
2283
|
+
}
|
|
2284
|
+
} catch (e7) {
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
} catch (e8) {
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
isValidMetaTool(candidate) {
|
|
2291
|
+
if (!candidate || typeof candidate !== "object") {
|
|
2292
|
+
return false;
|
|
2293
|
+
}
|
|
2294
|
+
const value = candidate;
|
|
2295
|
+
return typeof value.name === "string" && typeof value.description === "string" && typeof value.handler === "string" && typeof value.parameters === "object";
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
|
|
2299
|
+
// src/core/SecurityScanner.ts
|
|
2300
|
+
var SecurityScanner = class {
|
|
2301
|
+
constructor() {
|
|
2302
|
+
/**
|
|
2303
|
+
* Built-in secret detection patterns
|
|
2304
|
+
*/
|
|
2305
|
+
this.patterns = [
|
|
2306
|
+
// AWS
|
|
2307
|
+
{
|
|
2308
|
+
name: "AWS Access Key",
|
|
2309
|
+
regex: /AKIA[0-9A-Z]{16}/,
|
|
2310
|
+
severity: "high",
|
|
2311
|
+
description: "AWS Access Key ID"
|
|
2312
|
+
},
|
|
2313
|
+
// GitHub
|
|
2314
|
+
{
|
|
2315
|
+
name: "GitHub Token",
|
|
2316
|
+
regex: /ghp_[a-zA-Z0-9]{36}/,
|
|
2317
|
+
severity: "high",
|
|
2318
|
+
description: "GitHub Personal Access Token"
|
|
2319
|
+
},
|
|
2320
|
+
{
|
|
2321
|
+
name: "GitHub OAuth",
|
|
2322
|
+
regex: /gho_[a-zA-Z0-9]{36}/,
|
|
2323
|
+
severity: "high",
|
|
2324
|
+
description: "GitHub OAuth Token"
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
name: "GitHub App Token",
|
|
2328
|
+
regex: /ghu_[a-zA-Z0-9]{36}/,
|
|
2329
|
+
severity: "high",
|
|
2330
|
+
description: "GitHub App User Token"
|
|
2331
|
+
},
|
|
2332
|
+
// OpenAI / Anthropic
|
|
2333
|
+
{
|
|
2334
|
+
name: "OpenAI Key",
|
|
2335
|
+
regex: /sk-proj-[a-zA-Z0-9]{32,}/,
|
|
2336
|
+
severity: "high",
|
|
2337
|
+
description: "OpenAI Project API Key"
|
|
2338
|
+
},
|
|
2339
|
+
{
|
|
2340
|
+
name: "Anthropic Key",
|
|
2341
|
+
regex: /sk-ant-api[a-zA-Z0-9-]{32,}/,
|
|
2342
|
+
severity: "high",
|
|
2343
|
+
description: "Anthropic API Key"
|
|
2344
|
+
},
|
|
2345
|
+
// Google
|
|
2346
|
+
{
|
|
2347
|
+
name: "Google API Key",
|
|
2348
|
+
regex: /AIzaSy[0-9A-Za-z-_]{33}/,
|
|
2349
|
+
severity: "high",
|
|
2350
|
+
description: "Google API Key"
|
|
2351
|
+
},
|
|
2352
|
+
// Stripe
|
|
2353
|
+
{
|
|
2354
|
+
name: "Stripe Live Key",
|
|
2355
|
+
regex: /sk_live_[0-9a-zA-Z]{24,}/,
|
|
2356
|
+
severity: "high",
|
|
2357
|
+
description: "Stripe Live Secret Key"
|
|
2358
|
+
},
|
|
2359
|
+
{
|
|
2360
|
+
name: "Stripe Test Key",
|
|
2361
|
+
regex: /sk_test_[0-9a-zA-Z]{24,}/,
|
|
2362
|
+
severity: "low",
|
|
2363
|
+
description: "Stripe Test Secret Key"
|
|
2364
|
+
},
|
|
2365
|
+
// Private Keys
|
|
2366
|
+
{
|
|
2367
|
+
name: "Private Key",
|
|
2368
|
+
regex: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
2369
|
+
severity: "high",
|
|
2370
|
+
description: "Private Key File"
|
|
2371
|
+
},
|
|
2372
|
+
// Database URLs with credentials
|
|
2373
|
+
{
|
|
2374
|
+
name: "Database URL",
|
|
2375
|
+
regex: /(postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
|
|
2376
|
+
severity: "high",
|
|
2377
|
+
description: "Database URL with credentials"
|
|
2378
|
+
},
|
|
2379
|
+
// JWT Tokens
|
|
2380
|
+
{
|
|
2381
|
+
name: "JWT Token",
|
|
2382
|
+
regex: /eyJ[a-zA-Z0-9]{10,}\.eyJ[a-zA-Z0-9]{10,}\.[a-zA-Z0-9_-]{10,}/,
|
|
2383
|
+
severity: "medium",
|
|
2384
|
+
description: "JSON Web Token"
|
|
2385
|
+
},
|
|
2386
|
+
// Generic patterns (lower priority)
|
|
2387
|
+
{
|
|
2388
|
+
name: "Generic API Key",
|
|
2389
|
+
regex: /[aA][pP][iI][-_]?[kK][eE][yY]\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/,
|
|
2390
|
+
severity: "medium",
|
|
2391
|
+
description: "Generic API Key Assignment"
|
|
2392
|
+
},
|
|
2393
|
+
{
|
|
2394
|
+
name: "Generic Secret",
|
|
2395
|
+
regex: /[sS][eE][cC][rR][eE][tT]\s*[:=]\s*['"][^'"]{8,}['"]/,
|
|
2396
|
+
severity: "medium",
|
|
2397
|
+
description: "Generic Secret Assignment"
|
|
2398
|
+
},
|
|
2399
|
+
{
|
|
2400
|
+
name: "Password Assignment",
|
|
2401
|
+
regex: /[pP][aA][sS][sS][wW][oO][rR][dD]\s*[:=]\s*['"][^'"]{4,}['"]/,
|
|
2402
|
+
severity: "medium",
|
|
2403
|
+
description: "Password Assignment"
|
|
2404
|
+
}
|
|
2405
|
+
];
|
|
2406
|
+
/**
|
|
2407
|
+
* Placeholder patterns to ignore (false positives)
|
|
2408
|
+
* These must be exact matches or specific patterns, not substrings
|
|
2409
|
+
*/
|
|
2410
|
+
this.placeholderPatterns = [
|
|
2411
|
+
/your[-_]?api[-_]?key/i,
|
|
2412
|
+
/your[-_]?secret/i,
|
|
2413
|
+
/\*\*\*/,
|
|
2414
|
+
/\.\.\.$/,
|
|
2415
|
+
/-example/i,
|
|
2416
|
+
/placeholder/i,
|
|
2417
|
+
/sk-xxx$/i,
|
|
2418
|
+
/sk-your/i,
|
|
2419
|
+
/<your[-_]?key[-_]?here>/i,
|
|
2420
|
+
/\$\{[A-Z_]+\}/,
|
|
2421
|
+
// Environment variable placeholders
|
|
2422
|
+
/process\.env\./
|
|
2423
|
+
];
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Scan git diff for secrets
|
|
2427
|
+
* @param diff - Git diff output
|
|
2428
|
+
* @returns Security scan result
|
|
2429
|
+
*/
|
|
2430
|
+
scanDiff(diff) {
|
|
2431
|
+
const findings = [];
|
|
2432
|
+
const lines = diff.split("\n");
|
|
2433
|
+
let currentFile;
|
|
2434
|
+
let lineNumber = 0;
|
|
2435
|
+
for (const line of lines) {
|
|
2436
|
+
if (line.startsWith("+++ b/")) {
|
|
2437
|
+
currentFile = line.slice(6);
|
|
2438
|
+
continue;
|
|
2439
|
+
}
|
|
2440
|
+
if (line.startsWith("diff --git")) {
|
|
2441
|
+
const match = line.match(/diff --git a\/.+ b\/(.+)/);
|
|
2442
|
+
if (match) {
|
|
2443
|
+
currentFile = match[1];
|
|
2444
|
+
}
|
|
2445
|
+
continue;
|
|
2446
|
+
}
|
|
2447
|
+
if (line.startsWith("@@")) {
|
|
2448
|
+
const match = line.match(/@@ -\d+(?:,\d+)? \+(\d+)/);
|
|
2449
|
+
if (match) {
|
|
2450
|
+
lineNumber = parseInt(match[1], 10) - 1;
|
|
2451
|
+
}
|
|
2452
|
+
continue;
|
|
2453
|
+
}
|
|
2454
|
+
if (line.startsWith("-") && !line.startsWith("---")) {
|
|
2455
|
+
continue;
|
|
2456
|
+
}
|
|
2457
|
+
if (line.startsWith(" ")) {
|
|
2458
|
+
lineNumber++;
|
|
2459
|
+
continue;
|
|
2460
|
+
}
|
|
2461
|
+
if (!line.startsWith("+") || line.startsWith("+++")) {
|
|
2462
|
+
continue;
|
|
2463
|
+
}
|
|
2464
|
+
lineNumber++;
|
|
2465
|
+
const content = line.slice(1);
|
|
2466
|
+
if (this.isLikelyFalsePositive(content)) {
|
|
2467
|
+
continue;
|
|
2468
|
+
}
|
|
2469
|
+
this.scanLine(content, currentFile, lineNumber, findings);
|
|
2470
|
+
}
|
|
2471
|
+
return this.buildResult(findings);
|
|
2472
|
+
}
|
|
2473
|
+
/**
|
|
2474
|
+
* Scan file content directly
|
|
2475
|
+
* @param content - File content
|
|
2476
|
+
* @param filename - Optional filename for reporting
|
|
2477
|
+
* @returns Security scan result
|
|
2478
|
+
*/
|
|
2479
|
+
scanFile(content, filename) {
|
|
2480
|
+
const findings = [];
|
|
2481
|
+
const lines = content.split("\n");
|
|
2482
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2483
|
+
const line = lines[i];
|
|
2484
|
+
if (this.isLikelyFalsePositive(line)) {
|
|
2485
|
+
continue;
|
|
2486
|
+
}
|
|
2487
|
+
this.scanLine(line, filename, i + 1, findings);
|
|
2488
|
+
}
|
|
2489
|
+
return this.buildResult(findings);
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* Scan a single line for secrets
|
|
2493
|
+
*/
|
|
2494
|
+
scanLine(line, file, lineNumber, findings) {
|
|
2495
|
+
for (const pattern of this.patterns) {
|
|
2496
|
+
const match = line.match(pattern.regex);
|
|
2497
|
+
if (match) {
|
|
2498
|
+
findings.push({
|
|
2499
|
+
type: pattern.name,
|
|
2500
|
+
severity: pattern.severity,
|
|
2501
|
+
line,
|
|
2502
|
+
lineNumber,
|
|
2503
|
+
file,
|
|
2504
|
+
match: match[0]
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
/**
|
|
2510
|
+
* Build scan result from findings
|
|
2511
|
+
*/
|
|
2512
|
+
buildResult(findings) {
|
|
2513
|
+
const blockedCount = findings.filter((f) => f.severity === "high").length;
|
|
2514
|
+
const warningCount = findings.filter((f) => f.severity !== "high").length;
|
|
2515
|
+
return {
|
|
2516
|
+
clean: blockedCount === 0,
|
|
2517
|
+
findings,
|
|
2518
|
+
blockedCount,
|
|
2519
|
+
warningCount
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
/**
|
|
2523
|
+
* Check if commit should be blocked based on scan result
|
|
2524
|
+
* @param result - Security scan result
|
|
2525
|
+
* @returns true if commit should be blocked
|
|
2526
|
+
*/
|
|
2527
|
+
shouldBlockCommit(result) {
|
|
2528
|
+
return result.blockedCount > 0;
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Check if a line is likely a false positive
|
|
2532
|
+
*/
|
|
2533
|
+
isLikelyFalsePositive(line) {
|
|
2534
|
+
if (/^\s*(\/\/|#|\/\*|\*|<!--)/.test(line)) {
|
|
2535
|
+
return true;
|
|
2536
|
+
}
|
|
2537
|
+
for (const pattern of this.placeholderPatterns) {
|
|
2538
|
+
if (pattern.test(line)) {
|
|
2539
|
+
return true;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
return false;
|
|
2543
|
+
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Add a custom secret pattern
|
|
2546
|
+
* @param pattern - Pattern to add
|
|
2547
|
+
*/
|
|
2548
|
+
addPattern(pattern) {
|
|
2549
|
+
this.patterns.push(pattern);
|
|
2550
|
+
}
|
|
2551
|
+
/**
|
|
2552
|
+
* Get all configured patterns (returns a copy)
|
|
2553
|
+
* @returns Array of secret patterns
|
|
2554
|
+
*/
|
|
2555
|
+
getPatterns() {
|
|
2556
|
+
return [...this.patterns];
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2559
|
+
* Format scan result for display
|
|
2560
|
+
* @param result - Security scan result
|
|
2561
|
+
* @returns Formatted string for terminal display
|
|
2562
|
+
*/
|
|
2563
|
+
formatDisplay(result) {
|
|
2564
|
+
const lines = ["[SECURITY] Scanning staged changes..."];
|
|
2565
|
+
if (result.findings.length === 0) {
|
|
2566
|
+
lines.push("");
|
|
2567
|
+
lines.push("[OK] No secrets detected");
|
|
2568
|
+
return lines.join("\n");
|
|
2569
|
+
}
|
|
2570
|
+
lines.push("");
|
|
2571
|
+
for (const finding of result.findings) {
|
|
2572
|
+
const severity = finding.severity === "high" ? "[HIGH]" : finding.severity === "medium" ? "[WARN]" : "[LOW]";
|
|
2573
|
+
lines.push(` ${severity} ${finding.type} detected`);
|
|
2574
|
+
if (finding.file && finding.lineNumber) {
|
|
2575
|
+
lines.push(` File: ${finding.file}:${finding.lineNumber}`);
|
|
2576
|
+
} else if (finding.file) {
|
|
2577
|
+
lines.push(` File: ${finding.file}`);
|
|
2578
|
+
}
|
|
2579
|
+
const redactedLine = this.redactSecret(finding.line, finding.match);
|
|
2580
|
+
lines.push(` Line: ${redactedLine}`);
|
|
2581
|
+
lines.push("");
|
|
2582
|
+
}
|
|
2583
|
+
if (result.blockedCount > 0) {
|
|
2584
|
+
lines.push(`[BLOCKED] ${result.blockedCount} high-severity secrets found`);
|
|
2585
|
+
lines.push(" Remove secrets before committing.");
|
|
2586
|
+
lines.push(" Consider using environment variables instead.");
|
|
2587
|
+
} else if (result.warningCount > 0) {
|
|
2588
|
+
lines.push(`[WARN] ${result.warningCount} potential secrets found`);
|
|
2589
|
+
lines.push(" Review before committing.");
|
|
2590
|
+
}
|
|
2591
|
+
return lines.join("\n");
|
|
2592
|
+
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Redact a secret in a line for display
|
|
2595
|
+
*/
|
|
2596
|
+
redactSecret(line, secret) {
|
|
2597
|
+
if (secret.length <= 8) {
|
|
2598
|
+
return line.replace(secret, "*".repeat(secret.length));
|
|
2599
|
+
}
|
|
2600
|
+
const redacted = secret.slice(0, 4) + "*".repeat(secret.length - 8) + secret.slice(-4);
|
|
2601
|
+
return line.replace(secret, redacted);
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
|
|
2605
|
+
// src/core/actionExecutor.ts
|
|
2606
|
+
|
|
2607
|
+
|
|
2608
|
+
// src/modes/planMode/PlanFileStorage.ts
|
|
2609
|
+
|
|
2610
|
+
|
|
2611
|
+
var PlanFileStorage = class {
|
|
2612
|
+
constructor() {
|
|
2613
|
+
this.plansDir = _chunkSEKD5FH3cjs.AUTOHAND_PATHS.plans;
|
|
2614
|
+
}
|
|
2615
|
+
/**
|
|
2616
|
+
* Get the plans directory path
|
|
2617
|
+
*/
|
|
2618
|
+
getPlansDirectory() {
|
|
2619
|
+
return this.plansDir;
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* Save a plan to a markdown file
|
|
2623
|
+
* Returns the file path
|
|
2624
|
+
*/
|
|
2625
|
+
async savePlan(plan) {
|
|
2626
|
+
await _fsextra2.default.ensureDir(this.plansDir);
|
|
2627
|
+
const filePath = _path2.default.join(this.plansDir, `${plan.id}.md`);
|
|
2628
|
+
const content = this.formatPlanAsMarkdown(plan);
|
|
2629
|
+
await _fsextra2.default.writeFile(filePath, content, "utf8");
|
|
2630
|
+
return filePath;
|
|
2631
|
+
}
|
|
2632
|
+
/**
|
|
2633
|
+
* Load a plan from file by ID
|
|
2634
|
+
* Returns null if not found
|
|
2635
|
+
*/
|
|
2636
|
+
async loadPlan(planId) {
|
|
2637
|
+
const filePath = _path2.default.join(this.plansDir, `${planId}.md`);
|
|
2638
|
+
if (!await _fsextra2.default.pathExists(filePath)) {
|
|
2639
|
+
return null;
|
|
2640
|
+
}
|
|
2641
|
+
const content = await _fsextra2.default.readFile(filePath, "utf8");
|
|
2642
|
+
return this.parsePlanFromMarkdown(content, planId);
|
|
2643
|
+
}
|
|
2644
|
+
/**
|
|
2645
|
+
* List all plan IDs in the plans directory
|
|
2646
|
+
*/
|
|
2647
|
+
async listPlans() {
|
|
2648
|
+
if (!await _fsextra2.default.pathExists(this.plansDir)) {
|
|
2649
|
+
return [];
|
|
2650
|
+
}
|
|
2651
|
+
const files = await _fsextra2.default.readdir(this.plansDir);
|
|
2652
|
+
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, ""));
|
|
2653
|
+
}
|
|
2654
|
+
/**
|
|
2655
|
+
* Delete a plan file by ID
|
|
2656
|
+
* Returns true if deleted, false if not found
|
|
2657
|
+
*/
|
|
2658
|
+
async deletePlan(planId) {
|
|
2659
|
+
const filePath = _path2.default.join(this.plansDir, `${planId}.md`);
|
|
2660
|
+
if (!await _fsextra2.default.pathExists(filePath)) {
|
|
2661
|
+
return false;
|
|
2662
|
+
}
|
|
2663
|
+
await _fsextra2.default.remove(filePath);
|
|
2664
|
+
return true;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* Format a plan as markdown content
|
|
2668
|
+
*/
|
|
2669
|
+
formatPlanAsMarkdown(plan) {
|
|
2670
|
+
const createdDate = new Date(plan.createdAt).toISOString();
|
|
2671
|
+
const lines = [
|
|
2672
|
+
`# Plan: ${plan.id}`,
|
|
2673
|
+
"",
|
|
2674
|
+
`Created: ${createdDate}`,
|
|
2675
|
+
"",
|
|
2676
|
+
"## Steps",
|
|
2677
|
+
""
|
|
2678
|
+
];
|
|
2679
|
+
for (const step of plan.steps) {
|
|
2680
|
+
const checkbox = this.getCheckboxForStatus(step.status);
|
|
2681
|
+
lines.push(`- ${checkbox} ${step.number}. ${step.description}`);
|
|
2682
|
+
}
|
|
2683
|
+
lines.push("");
|
|
2684
|
+
return lines.join("\n");
|
|
2685
|
+
}
|
|
2686
|
+
/**
|
|
2687
|
+
* Get markdown checkbox for step status
|
|
2688
|
+
*/
|
|
2689
|
+
getCheckboxForStatus(status) {
|
|
2690
|
+
switch (status) {
|
|
2691
|
+
case "completed":
|
|
2692
|
+
return "[x]";
|
|
2693
|
+
case "in_progress":
|
|
2694
|
+
return "[>]";
|
|
2695
|
+
case "skipped":
|
|
2696
|
+
return "[-]";
|
|
2697
|
+
default:
|
|
2698
|
+
return "[ ]";
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Parse markdown content back to a Plan object
|
|
2703
|
+
*/
|
|
2704
|
+
parsePlanFromMarkdown(content, planId) {
|
|
2705
|
+
const steps = [];
|
|
2706
|
+
const stepPattern = /^-\s+\[([ x>\-])\]\s+(\d+)\.\s+(.+)$/gm;
|
|
2707
|
+
let match;
|
|
2708
|
+
while ((match = stepPattern.exec(content)) !== null) {
|
|
2709
|
+
const checkboxChar = match[1];
|
|
2710
|
+
const number = parseInt(match[2], 10);
|
|
2711
|
+
const description = match[3].trim();
|
|
2712
|
+
steps.push({
|
|
2713
|
+
number,
|
|
2714
|
+
description,
|
|
2715
|
+
status: this.parseStatusFromCheckbox(checkboxChar)
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
const dateMatch = content.match(/Created:\s*(.+)/);
|
|
2719
|
+
const createdAt = dateMatch ? new Date(dateMatch[1]).getTime() : Date.now();
|
|
2720
|
+
return {
|
|
2721
|
+
id: planId,
|
|
2722
|
+
steps,
|
|
2723
|
+
rawText: steps.map((s) => `${s.number}. ${s.description}`).join("\n"),
|
|
2724
|
+
createdAt
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
/**
|
|
2728
|
+
* Parse step status from checkbox character
|
|
2729
|
+
*/
|
|
2730
|
+
parseStatusFromCheckbox(char) {
|
|
2731
|
+
switch (char) {
|
|
2732
|
+
case "x":
|
|
2733
|
+
return "completed";
|
|
2734
|
+
case ">":
|
|
2735
|
+
return "in_progress";
|
|
2736
|
+
case "-":
|
|
2737
|
+
return "skipped";
|
|
2738
|
+
default:
|
|
2739
|
+
return "pending";
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
|
|
2744
|
+
// src/core/actionExecutor.ts
|
|
2745
|
+
|
|
2746
|
+
var ActionExecutor = class _ActionExecutor {
|
|
2747
|
+
constructor(deps) {
|
|
2748
|
+
this.deps = deps;
|
|
2749
|
+
this.searchCache = /* @__PURE__ */ new Map();
|
|
2750
|
+
this.runtime = deps.runtime;
|
|
2751
|
+
this.files = deps.files;
|
|
2752
|
+
this.resolveWorkspacePath = deps.resolveWorkspacePath;
|
|
2753
|
+
this.confirmDangerousAction = deps.confirmDangerousAction;
|
|
2754
|
+
this.projectManager = deps.projectManager;
|
|
2755
|
+
this.sessionId = deps.sessionId;
|
|
2756
|
+
this.logExploration = deps.onExploration;
|
|
2757
|
+
this.toolsRegistry = _nullishCoalesce(deps.toolsRegistry, () => ( new ToolsRegistry()));
|
|
2758
|
+
this.getRegisteredTools = _nullishCoalesce(deps.getRegisteredTools, () => ( (() => [])));
|
|
2759
|
+
this.permissionManager = _nullishCoalesce(deps.permissionManager, () => ( new (0, _chunkJ6QET7EFcjs.PermissionManager)(deps.runtime.config.permissions)));
|
|
2760
|
+
this.memoryManager = deps.memoryManager;
|
|
2761
|
+
this.onToolOutput = deps.onToolOutput;
|
|
2762
|
+
this.onFileModified = deps.onFileModified;
|
|
2763
|
+
this.onAskFollowup = deps.onAskFollowup;
|
|
2764
|
+
this.onPlanCreated = deps.onPlanCreated;
|
|
2765
|
+
this.onPermissionRequest = deps.onPermissionRequest;
|
|
2766
|
+
this.securityScanner = new SecurityScanner();
|
|
2767
|
+
}
|
|
2768
|
+
/**
|
|
2769
|
+
* Check permission hooks before prompting user.
|
|
2770
|
+
* Returns true if allowed, false if denied/blocked, undefined if should ask user.
|
|
2771
|
+
*/
|
|
2772
|
+
async checkPermissionHook(context) {
|
|
2773
|
+
if (!this.onPermissionRequest) {
|
|
2774
|
+
return {};
|
|
2775
|
+
}
|
|
2776
|
+
const hookResponse = await this.onPermissionRequest(context);
|
|
2777
|
+
if (!_optionalChain([hookResponse, 'optionalAccess', _43 => _43.decision])) {
|
|
2778
|
+
return {};
|
|
2779
|
+
}
|
|
2780
|
+
switch (hookResponse.decision) {
|
|
2781
|
+
case "allow":
|
|
2782
|
+
return { allowed: true, updatedInput: hookResponse.updatedInput };
|
|
2783
|
+
case "deny":
|
|
2784
|
+
return { allowed: false, reason: _nullishCoalesce(hookResponse.reason, () => ( "Denied by hook")) };
|
|
2785
|
+
case "block":
|
|
2786
|
+
return { blocked: true, reason: _nullishCoalesce(hookResponse.reason, () => ( "Blocked by hook")) };
|
|
2787
|
+
case "ask":
|
|
2788
|
+
default:
|
|
2789
|
+
return {};
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
async execute(action, context) {
|
|
2793
|
+
if (this.runtime.options.dryRun && action.type !== "search" && action.type !== "plan") {
|
|
2794
|
+
return "Dry-run mode: skipped mutation";
|
|
2795
|
+
}
|
|
2796
|
+
switch (action.type) {
|
|
2797
|
+
case "plan": {
|
|
2798
|
+
const notes = _nullishCoalesce(action.notes, () => ( ""));
|
|
2799
|
+
if (!notes) {
|
|
2800
|
+
return "No plan notes provided";
|
|
2801
|
+
}
|
|
2802
|
+
const storage = new PlanFileStorage();
|
|
2803
|
+
const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
2804
|
+
const now = Date.now();
|
|
2805
|
+
const existingPlanIds = await storage.listPlans();
|
|
2806
|
+
let cleanedCount = 0;
|
|
2807
|
+
for (const planId of existingPlanIds) {
|
|
2808
|
+
const existingPlan = await storage.loadPlan(planId);
|
|
2809
|
+
if (existingPlan && now - existingPlan.createdAt > THIRTY_DAYS_MS) {
|
|
2810
|
+
await storage.deletePlan(planId);
|
|
2811
|
+
cleanedCount++;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
if (cleanedCount > 0) {
|
|
2815
|
+
console.log(_chalk2.default.gray(`
|
|
2816
|
+
\u{1F9F9} Cleaned up ${cleanedCount} plan(s) older than 30 days`));
|
|
2817
|
+
}
|
|
2818
|
+
const planModeManager = _chunkULQ6MDSJcjs.getPlanModeManager.call(void 0, );
|
|
2819
|
+
if (planModeManager.isEnabled() && this.onAskFollowup) {
|
|
2820
|
+
const refreshedPlanIds = await storage.listPlans();
|
|
2821
|
+
const incompletePlans = [];
|
|
2822
|
+
for (const planId of refreshedPlanIds) {
|
|
2823
|
+
const existingPlan = await storage.loadPlan(planId);
|
|
2824
|
+
if (existingPlan) {
|
|
2825
|
+
const pendingCount = existingPlan.steps.filter((s) => s.status === "pending").length;
|
|
2826
|
+
const inProgressCount = existingPlan.steps.filter((s) => s.status === "in_progress").length;
|
|
2827
|
+
if (pendingCount > 0 || inProgressCount > 0) {
|
|
2828
|
+
incompletePlans.push({ plan: existingPlan, pendingCount, inProgressCount });
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
if (incompletePlans.length > 0) {
|
|
2833
|
+
console.log(_chalk2.default.yellow(`
|
|
2834
|
+
\u{1F4CB} Found ${incompletePlans.length} incomplete plan(s):`));
|
|
2835
|
+
for (const { plan: plan2, pendingCount, inProgressCount } of incompletePlans) {
|
|
2836
|
+
const age = Math.floor((now - plan2.createdAt) / (1e3 * 60 * 60 * 24));
|
|
2837
|
+
const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
|
|
2838
|
+
const statusStr = inProgressCount > 0 ? `${inProgressCount} in progress, ${pendingCount} pending` : `${pendingCount} pending`;
|
|
2839
|
+
console.log(_chalk2.default.gray(` \u2022 ${plan2.id} (${ageStr}) - ${plan2.steps.length} steps, ${statusStr}`));
|
|
2840
|
+
}
|
|
2841
|
+
console.log();
|
|
2842
|
+
const suggestedAnswers = [
|
|
2843
|
+
"Create new plan",
|
|
2844
|
+
...incompletePlans.slice(0, 3).map(({ plan: plan2 }) => `Resume: ${plan2.id}`)
|
|
2845
|
+
];
|
|
2846
|
+
const answer = await this.onAskFollowup(
|
|
2847
|
+
"Would you like to resume an incomplete plan or create a new one?",
|
|
2848
|
+
suggestedAnswers
|
|
2849
|
+
);
|
|
2850
|
+
const answerText = answer.replace(/<\/?answer>/g, "").trim();
|
|
2851
|
+
if (answerText.toLowerCase().includes("resume:") || answerText.toLowerCase().startsWith("resume")) {
|
|
2852
|
+
const resumeMatch = answerText.match(/resume[:\s]+(\S+)/i);
|
|
2853
|
+
if (resumeMatch) {
|
|
2854
|
+
const planIdToResume = resumeMatch[1];
|
|
2855
|
+
const planToResume = incompletePlans.find((p) => p.plan.id === planIdToResume);
|
|
2856
|
+
if (planToResume) {
|
|
2857
|
+
const filePath2 = `${storage.getPlansDirectory()}/${planToResume.plan.id}.md`;
|
|
2858
|
+
console.log(_chalk2.default.cyan(`
|
|
2859
|
+
\u{1F4CB} Resuming plan: ${planToResume.plan.id}`));
|
|
2860
|
+
console.log(_chalk2.default.gray(` File: ${filePath2}
|
|
2861
|
+
`));
|
|
2862
|
+
if (this.onPlanCreated) {
|
|
2863
|
+
return this.onPlanCreated(planToResume.plan, filePath2);
|
|
2864
|
+
}
|
|
2865
|
+
return `Resumed plan ${planToResume.plan.id}
|
|
2866
|
+
|
|
2867
|
+
Steps:
|
|
2868
|
+
${planToResume.plan.steps.map((s) => {
|
|
2869
|
+
const status = s.status === "completed" ? "\u2713" : s.status === "in_progress" ? ">" : "\u25CB";
|
|
2870
|
+
return `${status} ${s.number}. ${s.description}`;
|
|
2871
|
+
}).join("\n")}`;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
console.log(_chalk2.default.cyan("\n\u{1F4CB} Creating new plan..."));
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
const lines = notes.split("\n");
|
|
2879
|
+
const steps = [];
|
|
2880
|
+
let stepNumber = 0;
|
|
2881
|
+
for (const line of lines) {
|
|
2882
|
+
const trimmed = line.trim();
|
|
2883
|
+
const numberedMatch = trimmed.match(/^(\d+)[.)]\s*(.+)$/);
|
|
2884
|
+
const bulletMatch = trimmed.match(/^[-*]\s+(.+)$/);
|
|
2885
|
+
if (numberedMatch) {
|
|
2886
|
+
stepNumber = parseInt(numberedMatch[1], 10);
|
|
2887
|
+
steps.push({
|
|
2888
|
+
number: stepNumber,
|
|
2889
|
+
description: numberedMatch[2].trim(),
|
|
2890
|
+
status: "pending"
|
|
2891
|
+
});
|
|
2892
|
+
} else if (bulletMatch) {
|
|
2893
|
+
stepNumber++;
|
|
2894
|
+
steps.push({
|
|
2895
|
+
number: stepNumber,
|
|
2896
|
+
description: bulletMatch[1].trim(),
|
|
2897
|
+
status: "pending"
|
|
2898
|
+
});
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
if (steps.length === 0) {
|
|
2902
|
+
steps.push({
|
|
2903
|
+
number: 1,
|
|
2904
|
+
description: notes.substring(0, 200),
|
|
2905
|
+
status: "pending"
|
|
2906
|
+
});
|
|
2907
|
+
}
|
|
2908
|
+
const plan = {
|
|
2909
|
+
id: `plan-${_crypto.randomUUID.call(void 0, ).split("-")[0]}`,
|
|
2910
|
+
steps,
|
|
2911
|
+
rawText: notes,
|
|
2912
|
+
createdAt: Date.now()
|
|
2913
|
+
};
|
|
2914
|
+
const filePath = await storage.savePlan(plan);
|
|
2915
|
+
console.log(_chalk2.default.cyan(`
|
|
2916
|
+
\u{1F4CB} Plan created with ${steps.length} step(s)`));
|
|
2917
|
+
console.log(_chalk2.default.gray(` Saved to: ${filePath}
|
|
2918
|
+
`));
|
|
2919
|
+
if (this.onPlanCreated) {
|
|
2920
|
+
return this.onPlanCreated(plan, filePath);
|
|
2921
|
+
}
|
|
2922
|
+
return `Plan saved to ${filePath}
|
|
2923
|
+
|
|
2924
|
+
Steps:
|
|
2925
|
+
${steps.map((s) => `${s.number}. ${s.description}`).join("\n")}`;
|
|
2926
|
+
}
|
|
2927
|
+
case "read_file": {
|
|
2928
|
+
if (!action.path) {
|
|
2929
|
+
throw new Error('read_file requires a "path" argument.');
|
|
2930
|
+
}
|
|
2931
|
+
const offset = typeof action.offset === "number" ? action.offset : 0;
|
|
2932
|
+
const limit = typeof action.limit === "number" ? action.limit : 0;
|
|
2933
|
+
const fullContents = await this.files.readFile(action.path);
|
|
2934
|
+
this.recordExploration("read", action.path);
|
|
2935
|
+
const allLines = fullContents.split("\n");
|
|
2936
|
+
const totalLines = allLines.length;
|
|
2937
|
+
const fileSize = Buffer.byteLength(fullContents, "utf8");
|
|
2938
|
+
const fileSizeKB = (fileSize / 1024).toFixed(2);
|
|
2939
|
+
const MAX_LINES = 2e3;
|
|
2940
|
+
const MAX_SIZE_BYTES = 80 * 1024;
|
|
2941
|
+
const CHUNK_SIZE = 500;
|
|
2942
|
+
if (offset > 0 || limit > 0) {
|
|
2943
|
+
const effectiveLimit = limit > 0 ? limit : CHUNK_SIZE;
|
|
2944
|
+
const startLine = Math.min(offset, totalLines);
|
|
2945
|
+
const endLine = Math.min(startLine + effectiveLimit, totalLines);
|
|
2946
|
+
const chunk = allLines.slice(startLine, endLine).join("\n");
|
|
2947
|
+
console.log(_chalk2.default.cyan(`
|
|
2948
|
+
\u{1F4C4} ${action.path}`));
|
|
2949
|
+
console.log(_chalk2.default.gray(` Lines ${startLine + 1}-${endLine} of ${totalLines} (${fileSizeKB} KB total)`));
|
|
2950
|
+
if (endLine < totalLines) {
|
|
2951
|
+
console.log(_chalk2.default.yellow(` ${totalLines - endLine} more lines remaining`));
|
|
2952
|
+
}
|
|
2953
|
+
return chunk;
|
|
2954
|
+
}
|
|
2955
|
+
if (totalLines > MAX_LINES || fileSize > MAX_SIZE_BYTES) {
|
|
2956
|
+
console.log(_chalk2.default.cyan(`
|
|
2957
|
+
\u{1F4C4} ${action.path}`));
|
|
2958
|
+
console.log(_chalk2.default.yellow(` \u26A0 Large file: ${totalLines} lines \u2022 ${fileSizeKB} KB`));
|
|
2959
|
+
console.log(_chalk2.default.gray(` Smart chunking: outline + first ${CHUNK_SIZE} lines`));
|
|
2960
|
+
const outline = this.extractFileOutline(allLines, action.path);
|
|
2961
|
+
const firstChunk = allLines.slice(0, CHUNK_SIZE).join("\n");
|
|
2962
|
+
const response = [
|
|
2963
|
+
`=== FILE OUTLINE (${action.path}) ===`,
|
|
2964
|
+
`Total: ${totalLines} lines \u2022 ${fileSizeKB} KB`,
|
|
2965
|
+
"",
|
|
2966
|
+
outline,
|
|
2967
|
+
"",
|
|
2968
|
+
`=== CONTENT (lines 1-${CHUNK_SIZE}) ===`,
|
|
2969
|
+
firstChunk,
|
|
2970
|
+
"",
|
|
2971
|
+
`=== NAVIGATION ===`,
|
|
2972
|
+
`Showing lines 1-${CHUNK_SIZE} of ${totalLines}`,
|
|
2973
|
+
`To read more sections, use: read_file with offset=<line> limit=${CHUNK_SIZE}`,
|
|
2974
|
+
`Example: read_file path="${action.path}" offset=${CHUNK_SIZE} limit=${CHUNK_SIZE}`
|
|
2975
|
+
].join("\n");
|
|
2976
|
+
return response;
|
|
2977
|
+
}
|
|
2978
|
+
console.log(_chalk2.default.cyan(`
|
|
2979
|
+
\u{1F4C4} ${action.path}`));
|
|
2980
|
+
console.log(_chalk2.default.gray(` ${totalLines} lines \u2022 ${fileSizeKB} KB`));
|
|
2981
|
+
return fullContents;
|
|
2982
|
+
}
|
|
2983
|
+
case "write_file": {
|
|
2984
|
+
if (!action.path) {
|
|
2985
|
+
const receivedKeys = Object.keys(action).filter((k) => k !== "type").join(", ") || "none";
|
|
2986
|
+
throw new Error(`write_file requires a "path" argument. Received arguments: [${receivedKeys}]`);
|
|
2987
|
+
}
|
|
2988
|
+
const filePath = this.resolveWorkspacePath(action.path);
|
|
2989
|
+
const fs7 = await Promise.resolve().then(() => _interopRequireWildcard(require("fs-extra")));
|
|
2990
|
+
const exists = this.files.root && await fs7.pathExists(filePath);
|
|
2991
|
+
const oldContent = exists ? await this.files.readFile(action.path) : "";
|
|
2992
|
+
const newContent = _nullishCoalesce(this.pickText(action.contents, action.content), () => ( ""));
|
|
2993
|
+
if (!exists) {
|
|
2994
|
+
const permContext = {
|
|
2995
|
+
tool: "write_file",
|
|
2996
|
+
path: action.path
|
|
2997
|
+
};
|
|
2998
|
+
const decision = this.permissionManager.checkPermission(permContext);
|
|
2999
|
+
if (decision.reason === "blacklisted" || decision.reason === "mode_restricted") {
|
|
3000
|
+
return `Blocked: Cannot create ${action.path} (${decision.reason})`;
|
|
3001
|
+
}
|
|
3002
|
+
if (decision.allowed) {
|
|
3003
|
+
console.log(_chalk2.default.cyan(`
|
|
3004
|
+
\u2728 Creating: ${action.path}`));
|
|
3005
|
+
} else {
|
|
3006
|
+
const hookResult = await this.checkPermissionHook({
|
|
3007
|
+
tool: "write_file",
|
|
3008
|
+
path: action.path,
|
|
3009
|
+
args: { content: newContent }
|
|
3010
|
+
});
|
|
3011
|
+
if (hookResult.blocked) {
|
|
3012
|
+
return `Blocked: ${hookResult.reason}`;
|
|
3013
|
+
}
|
|
3014
|
+
if (hookResult.allowed !== void 0) {
|
|
3015
|
+
if (hookResult.allowed) {
|
|
3016
|
+
console.log(_chalk2.default.cyan(`
|
|
3017
|
+
\u2728 Creating: ${action.path}`));
|
|
3018
|
+
await this.permissionManager.recordDecision(permContext, true);
|
|
3019
|
+
} else {
|
|
3020
|
+
await this.permissionManager.recordDecision(permContext, false);
|
|
3021
|
+
return `Denied: ${hookResult.reason}`;
|
|
3022
|
+
}
|
|
3023
|
+
} else {
|
|
3024
|
+
console.log(_chalk2.default.cyan(`
|
|
3025
|
+
\u2728 Creating new file: ${action.path}`));
|
|
3026
|
+
const preview = newContent.length > 500 ? newContent.substring(0, 500) + "\n... (truncated)" : newContent;
|
|
3027
|
+
console.log(_chalk2.default.gray(preview));
|
|
3028
|
+
const confirmed = await this.confirmDangerousAction(
|
|
3029
|
+
`Create new file ${action.path}?`,
|
|
3030
|
+
{ tool: "write_file", path: action.path }
|
|
3031
|
+
);
|
|
3032
|
+
await this.permissionManager.recordDecision(permContext, confirmed);
|
|
3033
|
+
if (!confirmed) {
|
|
3034
|
+
return `Skipped creating ${action.path}`;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
} else if (oldContent === newContent) {
|
|
3039
|
+
return `No changes needed for ${action.path} (content identical)`;
|
|
3040
|
+
} else {
|
|
3041
|
+
console.log(_chalk2.default.cyan(`
|
|
3042
|
+
\u{1F4DD} ${action.path}:`));
|
|
3043
|
+
this.showDiff(oldContent, newContent, action.path);
|
|
3044
|
+
}
|
|
3045
|
+
await this.files.writeFile(action.path, newContent);
|
|
3046
|
+
_optionalChain([this, 'access', _44 => _44.onFileModified, 'optionalCall', _45 => _45(action.path)]);
|
|
3047
|
+
return exists ? `Updated ${action.path}` : `Created ${action.path}`;
|
|
3048
|
+
}
|
|
3049
|
+
case "append_file": {
|
|
3050
|
+
if (!action.path) {
|
|
3051
|
+
throw new Error('append_file requires a "path" argument.');
|
|
3052
|
+
}
|
|
3053
|
+
const addition = _nullishCoalesce(this.pickText(action.contents, action.content), () => ( ""));
|
|
3054
|
+
const oldContent = await this.files.readFile(action.path).catch(() => "");
|
|
3055
|
+
const newContent = oldContent + addition;
|
|
3056
|
+
console.log(_chalk2.default.cyan(`
|
|
3057
|
+
\u{1F4DD} ${action.path}:`));
|
|
3058
|
+
this.showDiff(oldContent, newContent, action.path);
|
|
3059
|
+
await this.files.appendFile(action.path, addition);
|
|
3060
|
+
_optionalChain([this, 'access', _46 => _46.onFileModified, 'optionalCall', _47 => _47(action.path)]);
|
|
3061
|
+
return `Appended to ${action.path}`;
|
|
3062
|
+
}
|
|
3063
|
+
case "apply_patch": {
|
|
3064
|
+
if (!action.path) {
|
|
3065
|
+
throw new Error('apply_patch requires a "path" argument.');
|
|
3066
|
+
}
|
|
3067
|
+
const oldContent = await this.files.readFile(action.path).catch(() => "");
|
|
3068
|
+
const patch = this.pickText(action.patch, action.diff);
|
|
3069
|
+
if (!patch) {
|
|
3070
|
+
throw new Error("apply_patch requires patch or diff content.");
|
|
3071
|
+
}
|
|
3072
|
+
console.log(_chalk2.default.cyan(`
|
|
3073
|
+
\u{1F527} ${action.path}:`));
|
|
3074
|
+
console.log(_chalk2.default.gray("Applying patch..."));
|
|
3075
|
+
await this.files.applyPatch(action.path, patch);
|
|
3076
|
+
const newContent = await this.files.readFile(action.path);
|
|
3077
|
+
this.showDiff(oldContent, newContent, action.path);
|
|
3078
|
+
_optionalChain([this, 'access', _48 => _48.onFileModified, 'optionalCall', _49 => _49(action.path)]);
|
|
3079
|
+
return `Patched ${action.path}`;
|
|
3080
|
+
}
|
|
3081
|
+
case "tools_registry": {
|
|
3082
|
+
const tools = await this.toolsRegistry.listTools(this.getRegisteredTools());
|
|
3083
|
+
return JSON.stringify(tools, null, 2);
|
|
3084
|
+
}
|
|
3085
|
+
case "search": {
|
|
3086
|
+
const cacheKey = `search:${action.query}:${action.path || ""}`;
|
|
3087
|
+
if (this.searchCache.has(cacheKey)) {
|
|
3088
|
+
return `[Cached] ${this.searchCache.get(cacheKey)}`;
|
|
3089
|
+
}
|
|
3090
|
+
const hits = this.files.search(action.query, action.path);
|
|
3091
|
+
this.recordExploration("search", action.query);
|
|
3092
|
+
const result = hits.slice(0, 10).map((hit) => `${hit.file}:${hit.line}: ${hit.text}`).join("\n");
|
|
3093
|
+
this.searchCache.set(cacheKey, result);
|
|
3094
|
+
return result;
|
|
3095
|
+
}
|
|
3096
|
+
case "search_with_context": {
|
|
3097
|
+
const cacheKey = `search_ctx:${action.query}:${action.path || ""}:${action.limit || ""}:${action.context || ""}`;
|
|
3098
|
+
if (this.searchCache.has(cacheKey)) {
|
|
3099
|
+
return `[Cached] ${this.searchCache.get(cacheKey)}`;
|
|
3100
|
+
}
|
|
3101
|
+
this.recordExploration("search", action.query);
|
|
3102
|
+
const result = this.files.searchWithContext(action.query, {
|
|
3103
|
+
limit: action.limit,
|
|
3104
|
+
context: action.context,
|
|
3105
|
+
relativePath: action.path
|
|
3106
|
+
});
|
|
3107
|
+
this.searchCache.set(cacheKey, result);
|
|
3108
|
+
return result;
|
|
3109
|
+
}
|
|
3110
|
+
case "semantic_search": {
|
|
3111
|
+
const cacheKey = `semantic:${action.query}:${action.path || ""}:${action.limit || ""}:${action.window || ""}`;
|
|
3112
|
+
if (this.searchCache.has(cacheKey)) {
|
|
3113
|
+
return `[Cached] ${this.searchCache.get(cacheKey)}`;
|
|
3114
|
+
}
|
|
3115
|
+
const results = this.files.semanticSearch(action.query, {
|
|
3116
|
+
limit: action.limit,
|
|
3117
|
+
window: action.window,
|
|
3118
|
+
relativePath: action.path
|
|
3119
|
+
});
|
|
3120
|
+
if (!results.length) {
|
|
3121
|
+
this.searchCache.set(cacheKey, "No matches found.");
|
|
3122
|
+
return "No matches found.";
|
|
3123
|
+
}
|
|
3124
|
+
const result = results.map((hit) => `${_chalk2.default.cyan(hit.file)}
|
|
3125
|
+
${hit.snippet}`).join("\n\n");
|
|
3126
|
+
this.searchCache.set(cacheKey, result);
|
|
3127
|
+
return result;
|
|
3128
|
+
}
|
|
3129
|
+
case "create_directory": {
|
|
3130
|
+
await this.files.createDirectory(action.path);
|
|
3131
|
+
return `Created directory ${action.path}`;
|
|
3132
|
+
}
|
|
3133
|
+
case "delete_path": {
|
|
3134
|
+
if (!action.path) {
|
|
3135
|
+
throw new Error('delete_path requires a "path" argument.');
|
|
3136
|
+
}
|
|
3137
|
+
const confirmed = await this.confirmDangerousAction(
|
|
3138
|
+
`Delete ${action.path}?`,
|
|
3139
|
+
{ tool: "delete_path", path: action.path }
|
|
3140
|
+
);
|
|
3141
|
+
if (!confirmed) {
|
|
3142
|
+
return `Skipped deleting ${action.path}`;
|
|
3143
|
+
}
|
|
3144
|
+
await this.files.deletePath(action.path);
|
|
3145
|
+
return `Deleted ${action.path}`;
|
|
3146
|
+
}
|
|
3147
|
+
case "rename_path": {
|
|
3148
|
+
if (!action.from || !action.to) {
|
|
3149
|
+
throw new Error('rename_path requires "from" and "to" arguments.');
|
|
3150
|
+
}
|
|
3151
|
+
await this.files.renamePath(action.from, action.to);
|
|
3152
|
+
return `Renamed ${action.from} -> ${action.to}`;
|
|
3153
|
+
}
|
|
3154
|
+
case "copy_path": {
|
|
3155
|
+
if (!action.from || !action.to) {
|
|
3156
|
+
throw new Error('copy_path requires "from" and "to" arguments.');
|
|
3157
|
+
}
|
|
3158
|
+
await this.files.copyPath(action.from, action.to);
|
|
3159
|
+
return `Copied ${action.from} -> ${action.to}`;
|
|
3160
|
+
}
|
|
3161
|
+
case "search_replace": {
|
|
3162
|
+
const content = await this.files.readFile(action.path);
|
|
3163
|
+
const result = this.applySearchReplaceBlocks(content, action.blocks);
|
|
3164
|
+
if (content !== result) {
|
|
3165
|
+
console.log(_chalk2.default.cyan(`
|
|
3166
|
+
\u{1F504} ${action.path}:`));
|
|
3167
|
+
this.showDiff(content, result, action.path);
|
|
3168
|
+
await this.files.writeFile(action.path, result);
|
|
3169
|
+
_optionalChain([this, 'access', _50 => _50.onFileModified, 'optionalCall', _51 => _51(action.path)]);
|
|
3170
|
+
}
|
|
3171
|
+
return `Updated ${action.path}`;
|
|
3172
|
+
}
|
|
3173
|
+
case "format_file": {
|
|
3174
|
+
if (!action.path) {
|
|
3175
|
+
throw new Error('format_file requires a "path" argument.');
|
|
3176
|
+
}
|
|
3177
|
+
await this.files.formatFile(action.path, (contents, file) => _chunk4JNNTOGFcjs.applyFormatter.call(void 0, action.formatter, contents, file));
|
|
3178
|
+
return `Formatted ${action.path} (${action.formatter})`;
|
|
3179
|
+
}
|
|
3180
|
+
case "run_command": {
|
|
3181
|
+
if (!action.command || typeof action.command !== "string") {
|
|
3182
|
+
return 'Error: run_command requires a "command" argument (string)';
|
|
3183
|
+
}
|
|
3184
|
+
const shouldStreamOutput = Boolean(
|
|
3185
|
+
this.onToolOutput && _optionalChain([context, 'optionalAccess', _52 => _52.toolCallId]) && !action.background && process.env.AUTOHAND_STREAM_TOOL_OUTPUT === "1"
|
|
3186
|
+
);
|
|
3187
|
+
const emitOutput = (stream, data) => {
|
|
3188
|
+
if (!shouldStreamOutput) {
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3191
|
+
_optionalChain([this, 'access', _53 => _53.onToolOutput, 'optionalCall', _54 => _54({
|
|
3192
|
+
tool: action.type,
|
|
3193
|
+
toolCallId: _optionalChain([context, 'optionalAccess', _55 => _55.toolCallId]),
|
|
3194
|
+
stream,
|
|
3195
|
+
data
|
|
3196
|
+
})]);
|
|
3197
|
+
};
|
|
3198
|
+
const result = await runCommand(
|
|
3199
|
+
action.command,
|
|
3200
|
+
_nullishCoalesce(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
|
+
const cmdStr = `${action.command} ${(_nullishCoalesce(action.args, () => ( []))).join(" ")}`.trim();
|
|
3210
|
+
const header = action.description ? `$ ${action.description}
|
|
3211
|
+
> ${cmdStr}` : `$ ${cmdStr}`;
|
|
3212
|
+
const dirInfo = action.directory ? `[dir: ${action.directory}]` : "";
|
|
3213
|
+
const parts = [
|
|
3214
|
+
dirInfo ? `${header} ${dirInfo}` : header,
|
|
3215
|
+
result.stdout,
|
|
3216
|
+
result.stderr
|
|
3217
|
+
].filter(Boolean);
|
|
3218
|
+
if (result.backgroundPid) {
|
|
3219
|
+
parts.push(`[Background PID: ${result.backgroundPid}]`);
|
|
3220
|
+
}
|
|
3221
|
+
return parts.join("\n");
|
|
3222
|
+
}
|
|
3223
|
+
case "add_dependency": {
|
|
3224
|
+
await addDependency(this.runtime.workspaceRoot, action.name, action.version, { dev: action.dev });
|
|
3225
|
+
return `Added dependency ${action.name}@${action.version}${action.dev ? " (dev)" : ""}`;
|
|
3226
|
+
}
|
|
3227
|
+
case "remove_dependency": {
|
|
3228
|
+
await removeDependency(this.runtime.workspaceRoot, action.name, { dev: action.dev });
|
|
3229
|
+
return `Removed dependency ${action.name}${action.dev ? " (dev)" : ""}`;
|
|
3230
|
+
}
|
|
3231
|
+
case "list_tree": {
|
|
3232
|
+
const treeRoot = this.resolveWorkspacePath(_nullishCoalesce(action.path, () => ( ".")));
|
|
3233
|
+
const lines = await listDirectoryTree(treeRoot, {
|
|
3234
|
+
depth: action.depth,
|
|
3235
|
+
workspaceRoot: this.runtime.workspaceRoot
|
|
3236
|
+
});
|
|
3237
|
+
this.recordExploration("list", _nullishCoalesce(action.path, () => ( ".")));
|
|
3238
|
+
return lines.join("\n");
|
|
3239
|
+
}
|
|
3240
|
+
case "file_stats": {
|
|
3241
|
+
if (!action.path) {
|
|
3242
|
+
throw new Error('file_stats requires a "path" argument.');
|
|
3243
|
+
}
|
|
3244
|
+
this.resolveWorkspacePath(action.path);
|
|
3245
|
+
const stats = await fileStats(this.runtime.workspaceRoot, action.path);
|
|
3246
|
+
return stats ? JSON.stringify(stats, null, 2) : `No stats for ${action.path}`;
|
|
3247
|
+
}
|
|
3248
|
+
case "checksum": {
|
|
3249
|
+
if (!action.path) {
|
|
3250
|
+
throw new Error('checksum requires a "path" argument.');
|
|
3251
|
+
}
|
|
3252
|
+
this.resolveWorkspacePath(action.path);
|
|
3253
|
+
const sum = await checksumFile(this.runtime.workspaceRoot, action.path, action.algorithm);
|
|
3254
|
+
return `${_nullishCoalesce(action.algorithm, () => ( "sha256"))} ${action.path}: ${sum}`;
|
|
3255
|
+
}
|
|
3256
|
+
case "git_diff": {
|
|
3257
|
+
if (!action.path) {
|
|
3258
|
+
throw new Error('git_diff requires a "path" argument.');
|
|
3259
|
+
}
|
|
3260
|
+
this.resolveWorkspacePath(action.path);
|
|
3261
|
+
const rawDiff = diffFile(this.runtime.workspaceRoot, action.path);
|
|
3262
|
+
return this.colorizeGitDiff(rawDiff);
|
|
3263
|
+
}
|
|
3264
|
+
case "git_checkout": {
|
|
3265
|
+
if (!action.path) {
|
|
3266
|
+
throw new Error('git_checkout requires a "path" argument.');
|
|
3267
|
+
}
|
|
3268
|
+
this.resolveWorkspacePath(action.path);
|
|
3269
|
+
checkoutFile(this.runtime.workspaceRoot, action.path);
|
|
3270
|
+
return `Restored ${action.path} from git.`;
|
|
3271
|
+
}
|
|
3272
|
+
case "git_status":
|
|
3273
|
+
return gitStatus(this.runtime.workspaceRoot);
|
|
3274
|
+
case "git_list_untracked":
|
|
3275
|
+
return gitListUntracked(this.runtime.workspaceRoot) || "No untracked files.";
|
|
3276
|
+
case "git_diff_range": {
|
|
3277
|
+
const rawDiff = gitDiffRange(this.runtime.workspaceRoot, {
|
|
3278
|
+
range: action.range,
|
|
3279
|
+
staged: action.staged,
|
|
3280
|
+
paths: action.paths
|
|
3281
|
+
});
|
|
3282
|
+
return this.colorizeGitDiff(rawDiff);
|
|
3283
|
+
}
|
|
3284
|
+
case "git_apply_patch": {
|
|
3285
|
+
const patch = this.pickText(action.patch, action.diff);
|
|
3286
|
+
if (!patch) {
|
|
3287
|
+
throw new Error("git_apply_patch requires patch or diff content.");
|
|
3288
|
+
}
|
|
3289
|
+
applyGitPatch(this.runtime.workspaceRoot, patch);
|
|
3290
|
+
return "Applied git patch.";
|
|
3291
|
+
}
|
|
3292
|
+
case "git_worktree_list":
|
|
3293
|
+
return gitListWorktrees(this.runtime.workspaceRoot);
|
|
3294
|
+
case "git_worktree_add": {
|
|
3295
|
+
const worktreePath = this.resolveWorkspacePath(action.path);
|
|
3296
|
+
return gitAddWorktree(this.runtime.workspaceRoot, worktreePath, action.ref);
|
|
3297
|
+
}
|
|
3298
|
+
case "git_worktree_remove": {
|
|
3299
|
+
const worktreePath = this.resolveWorkspacePath(action.path);
|
|
3300
|
+
return gitRemoveWorktree(this.runtime.workspaceRoot, worktreePath, action.force);
|
|
3301
|
+
}
|
|
3302
|
+
// Advanced Worktree Operations
|
|
3303
|
+
case "git_worktree_status_all": {
|
|
3304
|
+
const manager = new WorktreeManager(this.runtime.workspaceRoot);
|
|
3305
|
+
const statuses = await manager.statusAll();
|
|
3306
|
+
if (statuses.length === 0) {
|
|
3307
|
+
return "No worktrees found.";
|
|
3308
|
+
}
|
|
3309
|
+
const lines = [_chalk2.default.cyan("\u{1F4CA} Worktree Status Summary:"), ""];
|
|
3310
|
+
for (const status of statuses) {
|
|
3311
|
+
const branchName = status.worktree.branch || "(detached)";
|
|
3312
|
+
const cleanIcon = status.isClean ? _chalk2.default.green("\u2713") : _chalk2.default.yellow("!");
|
|
3313
|
+
const syncInfo = status.gitStatus.ahead > 0 || status.gitStatus.behind > 0 ? _chalk2.default.gray(` [\u2191${status.gitStatus.ahead} \u2193${status.gitStatus.behind}]`) : "";
|
|
3314
|
+
lines.push(`${cleanIcon} ${_chalk2.default.bold(branchName)}${syncInfo}`);
|
|
3315
|
+
lines.push(_chalk2.default.gray(` ${status.worktree.path}`));
|
|
3316
|
+
if (!status.isClean) {
|
|
3317
|
+
const changes = [];
|
|
3318
|
+
if (status.gitStatus.staged > 0) changes.push(`${status.gitStatus.staged} staged`);
|
|
3319
|
+
if (status.gitStatus.modified > 0) changes.push(`${status.gitStatus.modified} modified`);
|
|
3320
|
+
if (status.gitStatus.untracked > 0) changes.push(`${status.gitStatus.untracked} untracked`);
|
|
3321
|
+
if (status.gitStatus.conflicts > 0) changes.push(_chalk2.default.red(`${status.gitStatus.conflicts} conflicts`));
|
|
3322
|
+
lines.push(_chalk2.default.yellow(` ${changes.join(", ")}`));
|
|
3323
|
+
}
|
|
3324
|
+
if (status.lastCommit) {
|
|
3325
|
+
lines.push(_chalk2.default.gray(` Last commit: ${status.lastCommit.message.substring(0, 50)}`));
|
|
3326
|
+
}
|
|
3327
|
+
lines.push("");
|
|
3328
|
+
}
|
|
3329
|
+
return lines.join("\n");
|
|
3330
|
+
}
|
|
3331
|
+
case "git_worktree_cleanup": {
|
|
3332
|
+
const manager = new WorktreeManager(this.runtime.workspaceRoot);
|
|
3333
|
+
const result = await manager.cleanup({
|
|
3334
|
+
dryRun: action.dry_run,
|
|
3335
|
+
removeMerged: action.remove_merged,
|
|
3336
|
+
removeStale: action.remove_stale
|
|
3337
|
+
});
|
|
3338
|
+
if (action.dry_run) {
|
|
3339
|
+
if (result.wouldRemove.length === 0) {
|
|
3340
|
+
return "No worktrees to clean up.";
|
|
3341
|
+
}
|
|
3342
|
+
return `Would remove ${result.wouldRemove.length} worktree(s):
|
|
3343
|
+
${result.wouldRemove.map((p) => ` - ${p}`).join("\n")}`;
|
|
3344
|
+
}
|
|
3345
|
+
if (result.removed.length === 0) {
|
|
3346
|
+
return "No worktrees were cleaned up.";
|
|
3347
|
+
}
|
|
3348
|
+
return `Cleaned up ${result.removed.length} worktree(s):
|
|
3349
|
+
${result.removed.map((p) => ` - ${p}`).join("\n")}`;
|
|
3350
|
+
}
|
|
3351
|
+
case "git_worktree_run_parallel": {
|
|
3352
|
+
const manager = new WorktreeManager(this.runtime.workspaceRoot);
|
|
3353
|
+
console.log(_chalk2.default.cyan(`
|
|
3354
|
+
\u{1F504} Running "${action.command}" across all worktrees...
|
|
3355
|
+
`));
|
|
3356
|
+
const results = await manager.runParallel(action.command, {
|
|
3357
|
+
timeout: action.timeout,
|
|
3358
|
+
maxConcurrent: action.max_concurrent
|
|
3359
|
+
});
|
|
3360
|
+
const lines = [];
|
|
3361
|
+
let successCount = 0;
|
|
3362
|
+
let failCount = 0;
|
|
3363
|
+
for (const result of results) {
|
|
3364
|
+
const branchName = result.branch || "(detached)";
|
|
3365
|
+
const statusIcon = result.success ? _chalk2.default.green("\u2713") : _chalk2.default.red("\u2717");
|
|
3366
|
+
const duration = `${(result.duration / 1e3).toFixed(1)}s`;
|
|
3367
|
+
lines.push(`${statusIcon} ${_chalk2.default.bold(branchName)} (${duration})`);
|
|
3368
|
+
if (result.success) {
|
|
3369
|
+
successCount++;
|
|
3370
|
+
if (result.output.trim()) {
|
|
3371
|
+
lines.push(_chalk2.default.gray(result.output.trim().split("\n").map((l) => ` ${l}`).join("\n")));
|
|
3372
|
+
}
|
|
3373
|
+
} else {
|
|
3374
|
+
failCount++;
|
|
3375
|
+
lines.push(_chalk2.default.red(` Error: ${result.error}`));
|
|
3376
|
+
}
|
|
3377
|
+
lines.push("");
|
|
3378
|
+
}
|
|
3379
|
+
lines.unshift(
|
|
3380
|
+
_chalk2.default.cyan(`\u{1F4CA} Results: ${successCount} succeeded, ${failCount} failed`),
|
|
3381
|
+
""
|
|
3382
|
+
);
|
|
3383
|
+
return lines.join("\n");
|
|
3384
|
+
}
|
|
3385
|
+
case "git_worktree_sync": {
|
|
3386
|
+
const manager = new WorktreeManager(this.runtime.workspaceRoot);
|
|
3387
|
+
const result = await manager.syncAll({
|
|
3388
|
+
strategy: action.strategy,
|
|
3389
|
+
mainBranch: action.main_branch,
|
|
3390
|
+
dryRun: action.dry_run
|
|
3391
|
+
});
|
|
3392
|
+
const lines = [_chalk2.default.cyan("\u{1F504} Worktree Sync Results:"), ""];
|
|
3393
|
+
if (result.synced.length > 0) {
|
|
3394
|
+
lines.push(_chalk2.default.green(`Synced (${result.synced.length}):`));
|
|
3395
|
+
for (const path8 of result.synced) {
|
|
3396
|
+
lines.push(` \u2713 ${path8}`);
|
|
3397
|
+
}
|
|
3398
|
+
lines.push("");
|
|
3399
|
+
}
|
|
3400
|
+
if (result.skipped.length > 0) {
|
|
3401
|
+
lines.push(_chalk2.default.yellow(`Skipped (${result.skipped.length}):`));
|
|
3402
|
+
for (const info of result.skipped) {
|
|
3403
|
+
lines.push(` \u2298 ${info}`);
|
|
3404
|
+
}
|
|
3405
|
+
lines.push("");
|
|
3406
|
+
}
|
|
3407
|
+
if (result.failed.length > 0) {
|
|
3408
|
+
lines.push(_chalk2.default.red(`Failed (${result.failed.length}):`));
|
|
3409
|
+
for (const info of result.failed) {
|
|
3410
|
+
lines.push(` \u2717 ${info}`);
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
return lines.join("\n");
|
|
3414
|
+
}
|
|
3415
|
+
case "git_worktree_create_for_pr": {
|
|
3416
|
+
const manager = new WorktreeManager(this.runtime.workspaceRoot);
|
|
3417
|
+
const result = await manager.createForPR(action.pr_number, action.remote);
|
|
3418
|
+
return `Created worktree for PR #${action.pr_number}:
|
|
3419
|
+
Path: ${result.path}
|
|
3420
|
+
Branch: ${result.branch}`;
|
|
3421
|
+
}
|
|
3422
|
+
case "git_worktree_create_from_template": {
|
|
3423
|
+
const manager = new WorktreeManager(this.runtime.workspaceRoot);
|
|
3424
|
+
const templates = manager.getTemplates();
|
|
3425
|
+
const template = templates.find((t) => t.name === action.template);
|
|
3426
|
+
if (!template) {
|
|
3427
|
+
const available = templates.map((t) => t.name).join(", ");
|
|
3428
|
+
throw new Error(`Unknown template "${action.template}". Available: ${available}`);
|
|
3429
|
+
}
|
|
3430
|
+
console.log(_chalk2.default.cyan(`
|
|
3431
|
+
\u{1F4C1} Creating worktree from template "${action.template}"...`));
|
|
3432
|
+
console.log(_chalk2.default.gray(` Template: ${template.description}`));
|
|
3433
|
+
const result = await manager.create({
|
|
3434
|
+
branch: action.branch,
|
|
3435
|
+
newBranch: true,
|
|
3436
|
+
baseBranch: action.base_branch,
|
|
3437
|
+
template: action.template,
|
|
3438
|
+
runSetup: action.run_setup
|
|
3439
|
+
});
|
|
3440
|
+
return `Created worktree from template "${action.template}":
|
|
3441
|
+
Path: ${result.path}
|
|
3442
|
+
Branch: ${result.branch}`;
|
|
3443
|
+
}
|
|
3444
|
+
// Git Stash Operations
|
|
3445
|
+
case "git_stash":
|
|
3446
|
+
return gitStash(this.runtime.workspaceRoot, {
|
|
3447
|
+
message: action.message,
|
|
3448
|
+
includeUntracked: action.include_untracked,
|
|
3449
|
+
keepIndex: action.keep_index
|
|
3450
|
+
});
|
|
3451
|
+
case "git_stash_list":
|
|
3452
|
+
return gitStashList(this.runtime.workspaceRoot);
|
|
3453
|
+
case "git_stash_pop":
|
|
3454
|
+
return gitStashPop(this.runtime.workspaceRoot, action.stash_ref);
|
|
3455
|
+
case "git_stash_apply":
|
|
3456
|
+
return gitStashApply(this.runtime.workspaceRoot, action.stash_ref);
|
|
3457
|
+
case "git_stash_drop":
|
|
3458
|
+
return gitStashDrop(this.runtime.workspaceRoot, action.stash_ref);
|
|
3459
|
+
// Git Branch Operations
|
|
3460
|
+
case "git_branch":
|
|
3461
|
+
return gitBranch(this.runtime.workspaceRoot, action.branch_name, {
|
|
3462
|
+
delete: action.delete,
|
|
3463
|
+
force: action.force
|
|
3464
|
+
});
|
|
3465
|
+
case "git_switch":
|
|
3466
|
+
return gitSwitch(this.runtime.workspaceRoot, action.branch_name, {
|
|
3467
|
+
create: action.create
|
|
3468
|
+
});
|
|
3469
|
+
// Git Cherry-pick Operations
|
|
3470
|
+
case "git_cherry_pick":
|
|
3471
|
+
return gitCherryPick(this.runtime.workspaceRoot, action.commits, {
|
|
3472
|
+
noCommit: action.no_commit,
|
|
3473
|
+
mainline: action.mainline
|
|
3474
|
+
});
|
|
3475
|
+
case "git_cherry_pick_abort":
|
|
3476
|
+
return gitCherryPickAbort(this.runtime.workspaceRoot);
|
|
3477
|
+
case "git_cherry_pick_continue":
|
|
3478
|
+
return gitCherryPickContinue(this.runtime.workspaceRoot);
|
|
3479
|
+
// Git Rebase Operations
|
|
3480
|
+
case "git_rebase":
|
|
3481
|
+
return gitRebase(this.runtime.workspaceRoot, action.upstream, {
|
|
3482
|
+
onto: action.onto,
|
|
3483
|
+
autosquash: action.autosquash
|
|
3484
|
+
});
|
|
3485
|
+
case "git_rebase_abort":
|
|
3486
|
+
return gitRebaseAbort(this.runtime.workspaceRoot);
|
|
3487
|
+
case "git_rebase_continue":
|
|
3488
|
+
return gitRebaseContinue(this.runtime.workspaceRoot);
|
|
3489
|
+
case "git_rebase_skip":
|
|
3490
|
+
return gitRebaseSkip(this.runtime.workspaceRoot);
|
|
3491
|
+
// Git Merge Operations
|
|
3492
|
+
case "git_merge":
|
|
3493
|
+
return gitMerge(this.runtime.workspaceRoot, action.branch, {
|
|
3494
|
+
noCommit: action.no_commit,
|
|
3495
|
+
noFastForward: action.no_ff,
|
|
3496
|
+
squash: action.squash,
|
|
3497
|
+
message: action.message
|
|
3498
|
+
});
|
|
3499
|
+
case "git_merge_abort":
|
|
3500
|
+
return gitMergeAbort(this.runtime.workspaceRoot);
|
|
3501
|
+
// Git Commit Operations
|
|
3502
|
+
case "git_commit": {
|
|
3503
|
+
const scanResult = await this.scanBeforeCommit();
|
|
3504
|
+
if (scanResult) {
|
|
3505
|
+
return scanResult;
|
|
3506
|
+
}
|
|
3507
|
+
return gitCommit(this.runtime.workspaceRoot, {
|
|
3508
|
+
message: action.message,
|
|
3509
|
+
amend: action.amend,
|
|
3510
|
+
allowEmpty: action.allow_empty
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
case "git_add":
|
|
3514
|
+
return gitAdd(this.runtime.workspaceRoot, action.paths);
|
|
3515
|
+
case "git_reset":
|
|
3516
|
+
return gitReset(this.runtime.workspaceRoot, action.mode, action.ref);
|
|
3517
|
+
case "auto_commit": {
|
|
3518
|
+
const autoCommitScanResult = await this.scanBeforeCommit();
|
|
3519
|
+
if (autoCommitScanResult) {
|
|
3520
|
+
return autoCommitScanResult;
|
|
3521
|
+
}
|
|
3522
|
+
const info = getAutoCommitInfo(this.runtime.workspaceRoot);
|
|
3523
|
+
if (!info.canCommit) {
|
|
3524
|
+
console.log(_chalk2.default.yellow(`
|
|
3525
|
+
\u26A0 ${info.error}`));
|
|
3526
|
+
return info.error || "Cannot commit";
|
|
3527
|
+
}
|
|
3528
|
+
let commitMessage = action.message || info.suggestedMessage;
|
|
3529
|
+
console.log(_chalk2.default.cyan("\n\u{1F4DD} Changes to commit:"));
|
|
3530
|
+
info.filesChanged.slice(0, 10).forEach((file) => {
|
|
3531
|
+
console.log(_chalk2.default.gray(` ${file}`));
|
|
3532
|
+
});
|
|
3533
|
+
if (info.filesChanged.length > 10) {
|
|
3534
|
+
console.log(_chalk2.default.gray(` ... and ${info.filesChanged.length - 10} more files`));
|
|
3535
|
+
}
|
|
3536
|
+
console.log();
|
|
3537
|
+
console.log(_chalk2.default.cyan("Suggested commit message:"));
|
|
3538
|
+
console.log(_chalk2.default.white(` ${commitMessage}`));
|
|
3539
|
+
console.log();
|
|
3540
|
+
const autoApproveCommit = Boolean(
|
|
3541
|
+
this.runtime.options.yes || process.env.CI === "1" || process.env.AUTOHAND_NON_INTERACTIVE === "1"
|
|
3542
|
+
);
|
|
3543
|
+
if (autoApproveCommit) {
|
|
3544
|
+
console.log(_chalk2.default.gray("Auto-commit approval enabled; committing without prompt."));
|
|
3545
|
+
const result2 = executeAutoCommit(this.runtime.workspaceRoot, commitMessage, action.stage_all !== false);
|
|
3546
|
+
if (result2.success) {
|
|
3547
|
+
console.log(_chalk2.default.green(`
|
|
3548
|
+
\u2713 ${result2.message}`));
|
|
3549
|
+
return result2.message;
|
|
3550
|
+
}
|
|
3551
|
+
console.log(_chalk2.default.red(`
|
|
3552
|
+
\u2717 ${result2.message}`));
|
|
3553
|
+
return result2.message;
|
|
3554
|
+
}
|
|
3555
|
+
const options = [
|
|
3556
|
+
{ label: "Yes - commit with this message", value: "y" },
|
|
3557
|
+
{ label: "Edit - modify the message", value: "e" },
|
|
3558
|
+
{ label: "No - cancel commit", value: "n" }
|
|
3559
|
+
];
|
|
3560
|
+
const modalResult = await _chunkZYQMLKOKcjs.showModal.call(void 0, {
|
|
3561
|
+
title: "Commit with this message?",
|
|
3562
|
+
options
|
|
3563
|
+
});
|
|
3564
|
+
if (!modalResult || modalResult.value === "n") {
|
|
3565
|
+
console.log(_chalk2.default.yellow("Commit cancelled."));
|
|
3566
|
+
return "Commit cancelled by user";
|
|
3567
|
+
}
|
|
3568
|
+
if (modalResult.value === "e") {
|
|
3569
|
+
const editedMessage = await _chunkZYQMLKOKcjs.showInput.call(void 0, {
|
|
3570
|
+
title: "Enter commit message:",
|
|
3571
|
+
defaultValue: commitMessage
|
|
3572
|
+
});
|
|
3573
|
+
if (editedMessage) {
|
|
3574
|
+
commitMessage = editedMessage;
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
const result = executeAutoCommit(this.runtime.workspaceRoot, commitMessage, action.stage_all !== false);
|
|
3578
|
+
if (result.success) {
|
|
3579
|
+
console.log(_chalk2.default.green(`
|
|
3580
|
+
\u2713 ${result.message}`));
|
|
3581
|
+
return result.message;
|
|
3582
|
+
} else {
|
|
3583
|
+
console.log(_chalk2.default.red(`
|
|
3584
|
+
\u2717 ${result.message}`));
|
|
3585
|
+
return result.message;
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
// Git Log Operations
|
|
3589
|
+
case "git_log":
|
|
3590
|
+
return gitLog(this.runtime.workspaceRoot, {
|
|
3591
|
+
maxCount: action.max_count,
|
|
3592
|
+
oneline: action.oneline,
|
|
3593
|
+
graph: action.graph,
|
|
3594
|
+
all: action.all
|
|
3595
|
+
});
|
|
3596
|
+
// Git Remote Operations
|
|
3597
|
+
case "git_fetch":
|
|
3598
|
+
return gitFetch(this.runtime.workspaceRoot, action.remote, action.branch);
|
|
3599
|
+
case "git_pull":
|
|
3600
|
+
return gitPull(this.runtime.workspaceRoot, action.remote, action.branch);
|
|
3601
|
+
case "git_push":
|
|
3602
|
+
return gitPush(this.runtime.workspaceRoot, action.remote, action.branch, {
|
|
3603
|
+
force: action.force,
|
|
3604
|
+
setUpstream: action.set_upstream
|
|
3605
|
+
});
|
|
3606
|
+
case "custom_command":
|
|
3607
|
+
return this.executeCustomCommand(action);
|
|
3608
|
+
case "multi_file_edit": {
|
|
3609
|
+
const oldContent = await this.files.readFile(action.file_path);
|
|
3610
|
+
let newContent = oldContent;
|
|
3611
|
+
console.log(_chalk2.default.cyan(`
|
|
3612
|
+
\u270F\uFE0F ${action.file_path}:`));
|
|
3613
|
+
console.log(_chalk2.default.gray(`Applying ${action.edits.length} edit(s)...`));
|
|
3614
|
+
for (let i = 0; i < action.edits.length; i++) {
|
|
3615
|
+
const edit = action.edits[i];
|
|
3616
|
+
if (edit.replace_all) {
|
|
3617
|
+
const count = (newContent.match(new RegExp(this.escapeRegex(edit.old_string), "g")) || []).length;
|
|
3618
|
+
if (count === 0) {
|
|
3619
|
+
console.log(_chalk2.default.yellow(` \u26A0 Edit ${i + 1}: No occurrences found to replace`));
|
|
3620
|
+
console.log(_chalk2.default.gray(` Looking for: "${edit.old_string.substring(0, 60)}${edit.old_string.length > 60 ? "..." : ""}"`));
|
|
3621
|
+
const similar = this.findSimilarText(newContent, edit.old_string);
|
|
3622
|
+
if (similar) {
|
|
3623
|
+
console.log(_chalk2.default.gray(` Similar text found: "${similar.substring(0, 60)}${similar.length > 60 ? "..." : ""}"`));
|
|
3624
|
+
}
|
|
3625
|
+
continue;
|
|
3626
|
+
}
|
|
3627
|
+
newContent = newContent.replaceAll(edit.old_string, edit.new_string);
|
|
3628
|
+
console.log(_chalk2.default.green(` \u2713 Edit ${i + 1}: Replaced ${count} occurrence(s)`));
|
|
3629
|
+
} else {
|
|
3630
|
+
let firstIndex = newContent.indexOf(edit.old_string);
|
|
3631
|
+
if (firstIndex === -1) {
|
|
3632
|
+
const normalizedOld = this.normalizeText(edit.old_string);
|
|
3633
|
+
const normalizedContent = this.normalizeText(newContent);
|
|
3634
|
+
const normalizedIndex = normalizedContent.indexOf(normalizedOld);
|
|
3635
|
+
if (normalizedIndex !== -1) {
|
|
3636
|
+
console.log(_chalk2.default.yellow(` \u26A0 Edit ${i + 1}: Found match with normalized text (unicode chars differ)`));
|
|
3637
|
+
const actualOldString = this.extractMatchingText(newContent, normalizedContent, normalizedOld, normalizedIndex);
|
|
3638
|
+
if (actualOldString) {
|
|
3639
|
+
firstIndex = newContent.indexOf(actualOldString);
|
|
3640
|
+
if (firstIndex !== -1) {
|
|
3641
|
+
newContent = newContent.substring(0, firstIndex) + edit.new_string + newContent.substring(firstIndex + actualOldString.length);
|
|
3642
|
+
console.log(_chalk2.default.green(` \u2713 Edit ${i + 1}: Applied with normalized match`));
|
|
3643
|
+
continue;
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
if (firstIndex === -1) {
|
|
3649
|
+
console.log(_chalk2.default.red(` \u2717 Edit ${i + 1}: Could not find text to replace`));
|
|
3650
|
+
console.log(_chalk2.default.gray(` Looking for (${edit.old_string.length} chars):`));
|
|
3651
|
+
console.log(_chalk2.default.gray(` "${edit.old_string.substring(0, 80)}${edit.old_string.length > 80 ? "..." : ""}"`));
|
|
3652
|
+
const similar = this.findSimilarText(newContent, edit.old_string);
|
|
3653
|
+
if (similar) {
|
|
3654
|
+
console.log(_chalk2.default.yellow(` Did you mean:`));
|
|
3655
|
+
console.log(_chalk2.default.yellow(` "${similar.substring(0, 80)}${similar.length > 80 ? "..." : ""}"`));
|
|
3656
|
+
}
|
|
3657
|
+
if (edit.old_string.length < 100) {
|
|
3658
|
+
const nonAscii = edit.old_string.match(/[^\x20-\x7E\n\r\t]/g);
|
|
3659
|
+
if (nonAscii && nonAscii.length > 0) {
|
|
3660
|
+
console.log(_chalk2.default.gray(` Non-ASCII chars: ${nonAscii.map((c) => `'${c}' (U+${c.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")})`).join(", ")}`));
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
throw new Error(`Could not find text to replace in edit ${i + 1}. See details above.`);
|
|
3664
|
+
}
|
|
3665
|
+
newContent = newContent.substring(0, firstIndex) + edit.new_string + newContent.substring(firstIndex + edit.old_string.length);
|
|
3666
|
+
console.log(_chalk2.default.green(` \u2713 Edit ${i + 1}: Applied successfully`));
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
if (oldContent !== newContent) {
|
|
3670
|
+
this.showDiff(oldContent, newContent, action.file_path);
|
|
3671
|
+
await this.files.writeFile(action.file_path, newContent);
|
|
3672
|
+
_optionalChain([this, 'access', _56 => _56.onFileModified, 'optionalCall', _57 => _57(action.file_path)]);
|
|
3673
|
+
}
|
|
3674
|
+
return `Applied ${action.edits.length} edit(s) to ${action.file_path}`;
|
|
3675
|
+
}
|
|
3676
|
+
case "todo_write": {
|
|
3677
|
+
const todoPath = ".autohand/agents/tasks/todos.json";
|
|
3678
|
+
if (!Array.isArray(action.tasks)) {
|
|
3679
|
+
console.log(_chalk2.default.yellow("\u26A0\uFE0F todo_write received invalid tasks (not an array), skipping"));
|
|
3680
|
+
return "todo_write skipped: tasks must be an array";
|
|
3681
|
+
}
|
|
3682
|
+
const validTasks = action.tasks.filter((task) => {
|
|
3683
|
+
if (!task) return false;
|
|
3684
|
+
const hasId = !!task.id;
|
|
3685
|
+
const hasContent = !!(task.content || task.title);
|
|
3686
|
+
return hasId && hasContent;
|
|
3687
|
+
});
|
|
3688
|
+
const normalizedTasks = validTasks.map((task) => {
|
|
3689
|
+
const content = task.content || task.title || "";
|
|
3690
|
+
const title = content;
|
|
3691
|
+
return {
|
|
3692
|
+
...task,
|
|
3693
|
+
// Preserve extra properties like priority, tags, etc.
|
|
3694
|
+
id: task.id,
|
|
3695
|
+
title,
|
|
3696
|
+
content,
|
|
3697
|
+
// Keep original content field
|
|
3698
|
+
status: task.status || "pending",
|
|
3699
|
+
activeForm: task.activeForm || title,
|
|
3700
|
+
description: task.description
|
|
3701
|
+
};
|
|
3702
|
+
});
|
|
3703
|
+
const allTodos = normalizedTasks;
|
|
3704
|
+
await this.files.writeFile(todoPath, JSON.stringify(allTodos, null, 2));
|
|
3705
|
+
console.log(_chalk2.default.cyan("\n\u{1F4CB} Task Progress:"));
|
|
3706
|
+
const total = allTodos.length;
|
|
3707
|
+
const completed = allTodos.filter((t) => t.status === "completed").length;
|
|
3708
|
+
const inProgress = allTodos.filter((t) => t.status === "in_progress");
|
|
3709
|
+
const pending = allTodos.filter((t) => t.status === "pending").length;
|
|
3710
|
+
const percent = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
3711
|
+
const barWidth = 20;
|
|
3712
|
+
const filled = Math.round(barWidth * percent / 100);
|
|
3713
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
|
|
3714
|
+
console.log(` ${_chalk2.default.green(bar)} ${percent}%`);
|
|
3715
|
+
console.log(_chalk2.default.gray(` ${completed} done \xB7 ${inProgress.length} in progress \xB7 ${pending} pending`));
|
|
3716
|
+
if (inProgress.length > 0) {
|
|
3717
|
+
console.log(_chalk2.default.yellow("\n \u{1F504} Active Tasks:"));
|
|
3718
|
+
for (const task of inProgress) {
|
|
3719
|
+
console.log(` \u2022 ${task.title || task.content}`);
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
console.log();
|
|
3723
|
+
return `Updated task list: ${percent}% complete (${completed}/${total})`;
|
|
3724
|
+
}
|
|
3725
|
+
case "save_memory": {
|
|
3726
|
+
if (!this.memoryManager) {
|
|
3727
|
+
return "Memory manager not available";
|
|
3728
|
+
}
|
|
3729
|
+
const level = _nullishCoalesce(action.level, () => ( "user"));
|
|
3730
|
+
await this.memoryManager.store(action.fact, level);
|
|
3731
|
+
console.log(_chalk2.default.green(`
|
|
3732
|
+
\u{1F4BE} Memory saved (${level} level): "${action.fact.slice(0, 60)}${action.fact.length > 60 ? "..." : ""}"`));
|
|
3733
|
+
return `Saved to ${level} memory: ${action.fact}`;
|
|
3734
|
+
}
|
|
3735
|
+
case "recall_memory": {
|
|
3736
|
+
if (!this.memoryManager) {
|
|
3737
|
+
return "Memory manager not available";
|
|
3738
|
+
}
|
|
3739
|
+
const memories = await this.memoryManager.recall(action.query, action.level);
|
|
3740
|
+
if (memories.length === 0) {
|
|
3741
|
+
return action.query ? `No memories found matching "${action.query}"` : "No memories stored yet";
|
|
3742
|
+
}
|
|
3743
|
+
const formatted = memories.map((m) => `- [${m.level}] ${m.content}`).join("\n");
|
|
3744
|
+
console.log(_chalk2.default.cyan(`
|
|
3745
|
+
\u{1F9E0} Recalled ${memories.length} memor${memories.length === 1 ? "y" : "ies"}:`));
|
|
3746
|
+
console.log(_chalk2.default.gray(formatted));
|
|
3747
|
+
return formatted;
|
|
3748
|
+
}
|
|
3749
|
+
case "create_meta_tool": {
|
|
3750
|
+
if (!action.name || !action.description || !action.handler) {
|
|
3751
|
+
throw new Error("create_meta_tool requires name, description, and handler");
|
|
3752
|
+
}
|
|
3753
|
+
const builtInNames = this.getRegisteredTools().map((t) => t.name);
|
|
3754
|
+
if (builtInNames.includes(action.name)) {
|
|
3755
|
+
throw new Error(`Cannot create meta-tool "${action.name}": conflicts with built-in tool`);
|
|
3756
|
+
}
|
|
3757
|
+
const dangerousPatterns = [
|
|
3758
|
+
// Destructive file operations
|
|
3759
|
+
{ pattern: /rm\s+(-[rf]+\s+)*\/(?!\w)/i, description: "rm with root path" },
|
|
3760
|
+
{ pattern: /rm\s+.*--no-preserve-root/i, description: "rm --no-preserve-root" },
|
|
3761
|
+
{ pattern: /dd\s+.*(?:of|if)=\/dev\/[sh]d/i, description: "dd to disk device" },
|
|
3762
|
+
{ pattern: /mkfs\./i, description: "filesystem format" },
|
|
3763
|
+
{ pattern: /wipefs/i, description: "disk wipe" },
|
|
3764
|
+
// Privilege escalation
|
|
3765
|
+
{ pattern: /\bsudo\s/i, description: "sudo command" },
|
|
3766
|
+
{ pattern: /\bsu\s+-?\s*\w/i, description: "su command" },
|
|
3767
|
+
{ pattern: /chmod\s+[0-7]*7[0-7]*/i, description: "world-writable chmod" },
|
|
3768
|
+
{ pattern: /chown\s+root/i, description: "chown to root" },
|
|
3769
|
+
// Remote code execution
|
|
3770
|
+
{ pattern: /curl\s+.*\|\s*(ba)?sh/i, description: "curl | bash" },
|
|
3771
|
+
{ pattern: /wget\s+.*\|\s*(ba)?sh/i, description: "wget | sh" },
|
|
3772
|
+
{ pattern: /\beval\s+[`$]/i, description: "eval with expansion" },
|
|
3773
|
+
// Fork bomb and resource exhaustion
|
|
3774
|
+
{ pattern: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/i, description: "fork bomb" },
|
|
3775
|
+
{ pattern: /while\s+true.*do.*done/i, description: "infinite loop" },
|
|
3776
|
+
// Reverse shell indicators
|
|
3777
|
+
{ pattern: /nc\s+.*-e\s*\/bin/i, description: "netcat reverse shell" },
|
|
3778
|
+
{ pattern: /ncat\s+.*-e\s*\/bin/i, description: "ncat reverse shell" },
|
|
3779
|
+
{ pattern: /bash\s+-i\s+>&?\s*\/dev\/tcp/i, description: "bash reverse shell" },
|
|
3780
|
+
// Dangerous network operations
|
|
3781
|
+
{ pattern: /iptables\s+-F/i, description: "flush firewall rules" },
|
|
3782
|
+
// Crypto operations that could lock out user
|
|
3783
|
+
{ pattern: /gpg\s+.*--encrypt.*-r\s+\S+\s+\//i, description: "gpg encrypt root" }
|
|
3784
|
+
];
|
|
3785
|
+
for (const { pattern, description } of dangerousPatterns) {
|
|
3786
|
+
if (pattern.test(action.handler)) {
|
|
3787
|
+
throw new Error(`Handler contains dangerous pattern: ${description}`);
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
await this.toolsRegistry.saveMetaTool({
|
|
3791
|
+
name: action.name,
|
|
3792
|
+
description: action.description,
|
|
3793
|
+
parameters: _nullishCoalesce(action.parameters, () => ( { type: "object", properties: {} })),
|
|
3794
|
+
handler: action.handler,
|
|
3795
|
+
source: "agent"
|
|
3796
|
+
});
|
|
3797
|
+
console.log(_chalk2.default.green(`
|
|
3798
|
+
\u{1F527} Created meta-tool: ${action.name}`));
|
|
3799
|
+
console.log(_chalk2.default.gray(` ${action.description}`));
|
|
3800
|
+
console.log(_chalk2.default.gray(` Handler: ${action.handler}`));
|
|
3801
|
+
return `Created meta-tool "${action.name}" - available in this and future sessions`;
|
|
3802
|
+
}
|
|
3803
|
+
// Web Search Operations
|
|
3804
|
+
case "web_search": {
|
|
3805
|
+
if (!action.query) {
|
|
3806
|
+
throw new Error('web_search requires a "query" argument.');
|
|
3807
|
+
}
|
|
3808
|
+
console.log(_chalk2.default.cyan(`
|
|
3809
|
+
\u{1F50D} Searching web: "${action.query}"...`));
|
|
3810
|
+
const results = await _chunkRKJTGGMUcjs.webSearch.call(void 0, action.query, {
|
|
3811
|
+
maxResults: action.max_results,
|
|
3812
|
+
searchType: action.search_type
|
|
3813
|
+
});
|
|
3814
|
+
const formatted = _chunkRKJTGGMUcjs.formatSearchResults.call(void 0, results);
|
|
3815
|
+
console.log(_chalk2.default.gray(formatted.split("\n").slice(0, 10).join("\n")));
|
|
3816
|
+
if (results.length > 3) {
|
|
3817
|
+
console.log(_chalk2.default.gray(" ..."));
|
|
3818
|
+
}
|
|
3819
|
+
return formatted;
|
|
3820
|
+
}
|
|
3821
|
+
case "fetch_url": {
|
|
3822
|
+
if (!action.url) {
|
|
3823
|
+
throw new Error('fetch_url requires a "url" argument.');
|
|
3824
|
+
}
|
|
3825
|
+
console.log(_chalk2.default.cyan(`
|
|
3826
|
+
\u{1F310} Fetching: ${action.url}...`));
|
|
3827
|
+
const content = await _chunkRKJTGGMUcjs.fetchUrl.call(void 0, action.url, {
|
|
3828
|
+
maxLength: action.max_length
|
|
3829
|
+
});
|
|
3830
|
+
const preview = content.slice(0, 500);
|
|
3831
|
+
console.log(_chalk2.default.gray(preview + (content.length > 500 ? "\n ... (truncated)" : "")));
|
|
3832
|
+
return content;
|
|
3833
|
+
}
|
|
3834
|
+
case "package_info": {
|
|
3835
|
+
if (!action.package_name) {
|
|
3836
|
+
throw new Error('package_info requires a "package_name" argument.');
|
|
3837
|
+
}
|
|
3838
|
+
const registryLabel = action.registry ? ` (${action.registry})` : "";
|
|
3839
|
+
console.log(_chalk2.default.cyan(`
|
|
3840
|
+
\u{1F4E6} Getting package info: ${action.package_name}${action.version ? `@${action.version}` : ""}${registryLabel}...`));
|
|
3841
|
+
const info = await _chunkRKJTGGMUcjs.getPackageInfo.call(void 0, action.package_name, {
|
|
3842
|
+
registry: action.registry,
|
|
3843
|
+
version: action.version
|
|
3844
|
+
});
|
|
3845
|
+
const formatted = _chunkRKJTGGMUcjs.formatPackageInfo.call(void 0, info);
|
|
3846
|
+
console.log(_chalk2.default.gray(formatted));
|
|
3847
|
+
return formatted;
|
|
3848
|
+
}
|
|
3849
|
+
case "web_repo": {
|
|
3850
|
+
if (!action.repo) {
|
|
3851
|
+
throw new Error('web_repo requires a "repo" argument.');
|
|
3852
|
+
}
|
|
3853
|
+
if (!action.operation) {
|
|
3854
|
+
throw new Error('web_repo requires an "operation" argument (info, list, or fetch).');
|
|
3855
|
+
}
|
|
3856
|
+
console.log(_chalk2.default.cyan(`
|
|
3857
|
+
\u{1F517} ${action.operation}: ${action.repo}${action.path ? ` \u2192 ${action.path}` : ""}...`));
|
|
3858
|
+
const result = await webRepo({
|
|
3859
|
+
repo: action.repo,
|
|
3860
|
+
operation: action.operation,
|
|
3861
|
+
path: action.path,
|
|
3862
|
+
branch: action.branch
|
|
3863
|
+
});
|
|
3864
|
+
let formattedResult;
|
|
3865
|
+
switch (result.type) {
|
|
3866
|
+
case "info":
|
|
3867
|
+
formattedResult = formatRepoInfo(result.data);
|
|
3868
|
+
break;
|
|
3869
|
+
case "list":
|
|
3870
|
+
formattedResult = formatRepoDir(result.data, result.path);
|
|
3871
|
+
break;
|
|
3872
|
+
case "fetch":
|
|
3873
|
+
formattedResult = result.data;
|
|
3874
|
+
break;
|
|
3875
|
+
}
|
|
3876
|
+
const previewResult = formattedResult.slice(0, 500);
|
|
3877
|
+
console.log(_chalk2.default.gray(previewResult + (formattedResult.length > 500 ? "\n ... (truncated)" : "")));
|
|
3878
|
+
return formattedResult;
|
|
3879
|
+
}
|
|
3880
|
+
// Skills Discovery
|
|
3881
|
+
case "find_agent_skills": {
|
|
3882
|
+
const query = _nullishCoalesce(action.query, () => ( ""));
|
|
3883
|
+
console.log(_chalk2.default.cyan(`
|
|
3884
|
+
Searching skills: "${query}"${action.category ? ` [${action.category}]` : ""}...`));
|
|
3885
|
+
const { searchCommunitySkills } = await Promise.resolve().then(() => _interopRequireWildcard(require("./skills-FYY6F2WV.cjs")));
|
|
3886
|
+
const result = await searchCommunitySkills(query, {
|
|
3887
|
+
category: action.category,
|
|
3888
|
+
limit: action.limit
|
|
3889
|
+
});
|
|
3890
|
+
console.log(_chalk2.default.gray(result.split("\n").slice(0, 15).join("\n")));
|
|
3891
|
+
return result;
|
|
3892
|
+
}
|
|
3893
|
+
// User interaction
|
|
3894
|
+
case "ask_followup_question": {
|
|
3895
|
+
if (!action.question) {
|
|
3896
|
+
throw new Error('ask_followup_question requires a "question" parameter.');
|
|
3897
|
+
}
|
|
3898
|
+
if (this.onAskFollowup) {
|
|
3899
|
+
return this.onAskFollowup(action.question, action.suggested_answers);
|
|
3900
|
+
}
|
|
3901
|
+
console.log(_chalk2.default.cyan("\n\u2753 " + action.question + "\n"));
|
|
3902
|
+
if (Array.isArray(action.suggested_answers) && action.suggested_answers.length > 0) {
|
|
3903
|
+
const options = action.suggested_answers.map((answer, i) => ({
|
|
3904
|
+
label: `${i + 1}. ${answer}`,
|
|
3905
|
+
value: answer
|
|
3906
|
+
}));
|
|
3907
|
+
options.push({
|
|
3908
|
+
label: `${options.length + 1}. Other (type your own answer)`,
|
|
3909
|
+
value: "__other__"
|
|
3910
|
+
});
|
|
3911
|
+
const result = await _chunkZYQMLKOKcjs.showModal.call(void 0, {
|
|
3912
|
+
title: "Select an answer:",
|
|
3913
|
+
options
|
|
3914
|
+
});
|
|
3915
|
+
const selected = _optionalChain([result, 'optionalAccess', _58 => _58.value]);
|
|
3916
|
+
if (!selected) {
|
|
3917
|
+
console.log(_chalk2.default.yellow("\nAnswer cancelled.\n"));
|
|
3918
|
+
return "<answer>No answer provided</answer>";
|
|
3919
|
+
}
|
|
3920
|
+
if (selected === "__other__") {
|
|
3921
|
+
const answer = await _chunkZYQMLKOKcjs.showInput.call(void 0, {
|
|
3922
|
+
title: "Your answer:"
|
|
3923
|
+
});
|
|
3924
|
+
if (!answer) {
|
|
3925
|
+
console.log(_chalk2.default.yellow("\nAnswer cancelled.\n"));
|
|
3926
|
+
return "<answer>No answer provided</answer>";
|
|
3927
|
+
}
|
|
3928
|
+
console.log(_chalk2.default.green(`
|
|
3929
|
+
\u2713 Answer: ${answer}
|
|
3930
|
+
`));
|
|
3931
|
+
return `<answer>${answer}</answer>`;
|
|
3932
|
+
}
|
|
3933
|
+
console.log(_chalk2.default.green(`
|
|
3934
|
+
\u2713 Answer: ${selected}
|
|
3935
|
+
`));
|
|
3936
|
+
return `<answer>${selected}</answer>`;
|
|
3937
|
+
} else {
|
|
3938
|
+
const answer = await _chunkZYQMLKOKcjs.showInput.call(void 0, {
|
|
3939
|
+
title: "Your answer:"
|
|
3940
|
+
});
|
|
3941
|
+
const finalAnswer = answer || "No answer provided";
|
|
3942
|
+
console.log(_chalk2.default.green(`
|
|
3943
|
+
\u2713 Answer: ${finalAnswer}
|
|
3944
|
+
`));
|
|
3945
|
+
return `<answer>${finalAnswer}</answer>`;
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
default: {
|
|
3949
|
+
const actionType = action.type;
|
|
3950
|
+
const metaTool = this.toolsRegistry.getMetaTool(actionType);
|
|
3951
|
+
if (metaTool) {
|
|
3952
|
+
return this.executeMetaTool(metaTool, action);
|
|
3953
|
+
}
|
|
3954
|
+
throw new Error(`Unsupported action type ${actionType}`);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
pickText(...values) {
|
|
3959
|
+
for (const value of values) {
|
|
3960
|
+
if (typeof value === "string") {
|
|
3961
|
+
return value;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
return void 0;
|
|
3965
|
+
}
|
|
3966
|
+
/**
|
|
3967
|
+
* Extract file outline/structure for smart chunking of large files.
|
|
3968
|
+
* Identifies imports, classes, functions, and key sections with line numbers.
|
|
3969
|
+
*/
|
|
3970
|
+
extractFileOutline(lines, filePath) {
|
|
3971
|
+
const ext = _optionalChain([filePath, 'access', _59 => _59.split, 'call', _60 => _60("."), 'access', _61 => _61.pop, 'call', _62 => _62(), 'optionalAccess', _63 => _63.toLowerCase, 'call', _64 => _64()]) || "";
|
|
3972
|
+
const outline = [];
|
|
3973
|
+
const patterns = {
|
|
3974
|
+
ts: [
|
|
3975
|
+
/^(import|export)\s+/,
|
|
3976
|
+
/^(export\s+)?(async\s+)?function\s+(\w+)/,
|
|
3977
|
+
/^(export\s+)?(abstract\s+)?class\s+(\w+)/,
|
|
3978
|
+
/^(export\s+)?interface\s+(\w+)/,
|
|
3979
|
+
/^(export\s+)?type\s+(\w+)/,
|
|
3980
|
+
/^(export\s+)?enum\s+(\w+)/,
|
|
3981
|
+
/^(export\s+)?const\s+(\w+)\s*[=:]/
|
|
3982
|
+
],
|
|
3983
|
+
js: [
|
|
3984
|
+
/^(import|export)\s+/,
|
|
3985
|
+
/^(export\s+)?(async\s+)?function\s+(\w+)/,
|
|
3986
|
+
/^(export\s+)?class\s+(\w+)/,
|
|
3987
|
+
/^(export\s+)?const\s+(\w+)\s*=/,
|
|
3988
|
+
/^module\.exports/
|
|
3989
|
+
],
|
|
3990
|
+
py: [
|
|
3991
|
+
/^(from|import)\s+/,
|
|
3992
|
+
/^(async\s+)?def\s+(\w+)/,
|
|
3993
|
+
/^class\s+(\w+)/,
|
|
3994
|
+
/^(\w+)\s*=\s*(lambda|def)/
|
|
3995
|
+
],
|
|
3996
|
+
rs: [
|
|
3997
|
+
/^(use|mod)\s+/,
|
|
3998
|
+
/^(pub\s+)?(async\s+)?fn\s+(\w+)/,
|
|
3999
|
+
/^(pub\s+)?struct\s+(\w+)/,
|
|
4000
|
+
/^(pub\s+)?enum\s+(\w+)/,
|
|
4001
|
+
/^(pub\s+)?trait\s+(\w+)/,
|
|
4002
|
+
/^impl\s+/
|
|
4003
|
+
],
|
|
4004
|
+
go: [
|
|
4005
|
+
/^import\s+/,
|
|
4006
|
+
/^func\s+(\w+|\(\w+\s+\*?\w+\)\s+\w+)/,
|
|
4007
|
+
/^type\s+(\w+)\s+(struct|interface)/,
|
|
4008
|
+
/^var\s+(\w+)/,
|
|
4009
|
+
/^const\s+/
|
|
4010
|
+
]
|
|
4011
|
+
};
|
|
4012
|
+
const langPatterns = patterns[ext] || patterns["ts"] || [];
|
|
4013
|
+
if (["tsx", "jsx", "mts", "cts"].includes(ext)) {
|
|
4014
|
+
langPatterns.push(...patterns["ts"] || []);
|
|
4015
|
+
}
|
|
4016
|
+
let importStart = -1;
|
|
4017
|
+
let importEnd = -1;
|
|
4018
|
+
lines.forEach((line, idx) => {
|
|
4019
|
+
const trimmed = line.trim();
|
|
4020
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*")) {
|
|
4021
|
+
return;
|
|
4022
|
+
}
|
|
4023
|
+
const lineNum = idx + 1;
|
|
4024
|
+
if (/^(import|from|use|require)\s+/.test(trimmed)) {
|
|
4025
|
+
if (importStart === -1) {
|
|
4026
|
+
importStart = lineNum;
|
|
4027
|
+
}
|
|
4028
|
+
importEnd = lineNum;
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
4031
|
+
for (const pattern of langPatterns) {
|
|
4032
|
+
if (pattern.test(trimmed) && !/^(import|from|use)\s+/.test(trimmed)) {
|
|
4033
|
+
let identifier = trimmed.slice(0, 60);
|
|
4034
|
+
if (identifier.length < trimmed.length) identifier += "...";
|
|
4035
|
+
outline.push(` ${String(lineNum).padStart(4)}: ${identifier}`);
|
|
4036
|
+
break;
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
});
|
|
4040
|
+
const result = [];
|
|
4041
|
+
if (importStart !== -1) {
|
|
4042
|
+
result.push(`Imports: lines ${importStart}-${importEnd}`);
|
|
4043
|
+
}
|
|
4044
|
+
if (outline.length > 0) {
|
|
4045
|
+
result.push("");
|
|
4046
|
+
result.push("Definitions:");
|
|
4047
|
+
result.push(...outline.slice(0, 50));
|
|
4048
|
+
if (outline.length > 50) {
|
|
4049
|
+
result.push(` ... and ${outline.length - 50} more`);
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
return result.length > 0 ? result.join("\n") : "No structure detected";
|
|
4053
|
+
}
|
|
4054
|
+
recordExploration(kind, target) {
|
|
4055
|
+
if (!target) {
|
|
4056
|
+
return;
|
|
4057
|
+
}
|
|
4058
|
+
_optionalChain([this, 'access', _65 => _65.logExploration, 'optionalCall', _66 => _66({ kind, target })]);
|
|
4059
|
+
}
|
|
4060
|
+
async executeCustomCommand(action) {
|
|
4061
|
+
const existing = await loadCustomCommand(action.name);
|
|
4062
|
+
const definition = _nullishCoalesce(existing, () => ( {
|
|
4063
|
+
name: action.name,
|
|
4064
|
+
command: action.command,
|
|
4065
|
+
args: action.args,
|
|
4066
|
+
description: action.description,
|
|
4067
|
+
dangerous: action.dangerous
|
|
4068
|
+
}));
|
|
4069
|
+
if (!definition.command || typeof definition.command !== "string") {
|
|
4070
|
+
return `Error: custom_command "${action.name}" requires a "command" argument (string)`;
|
|
4071
|
+
}
|
|
4072
|
+
if (!existing) {
|
|
4073
|
+
console.log(_chalk2.default.cyan(`Custom command: ${definition.name}`));
|
|
4074
|
+
console.log(_chalk2.default.gray(_nullishCoalesce(definition.description, () => ( "No description provided."))));
|
|
4075
|
+
console.log(_chalk2.default.gray(`Command: ${definition.command} ${(_nullishCoalesce(definition.args, () => ( []))).join(" ")}`));
|
|
4076
|
+
if (this.isDestructiveCommand(definition.command)) {
|
|
4077
|
+
console.log(_chalk2.default.red("Warning: command may be destructive."));
|
|
4078
|
+
}
|
|
4079
|
+
const answer = await this.confirmDangerousAction(
|
|
4080
|
+
"Add and run this custom command?",
|
|
4081
|
+
{ tool: "run_command", command: definition.command }
|
|
4082
|
+
);
|
|
4083
|
+
if (!answer) {
|
|
4084
|
+
return "Custom command rejected by user.";
|
|
4085
|
+
}
|
|
4086
|
+
await saveCustomCommand(definition);
|
|
4087
|
+
}
|
|
4088
|
+
const result = await runCommand(definition.command, _nullishCoalesce(definition.args, () => ( [])), this.runtime.workspaceRoot);
|
|
4089
|
+
return [`$ ${definition.command} ${(_nullishCoalesce(definition.args, () => ( []))).join(" ")}`, result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
4090
|
+
}
|
|
4091
|
+
isDestructiveCommand(command) {
|
|
4092
|
+
const lowered = command.toLowerCase();
|
|
4093
|
+
return lowered.includes("rm ") || lowered.includes("sudo ") || lowered.includes("dd ");
|
|
4094
|
+
}
|
|
4095
|
+
static {
|
|
4096
|
+
/**
|
|
4097
|
+
* Shell metacharacters that could enable command injection
|
|
4098
|
+
*/
|
|
4099
|
+
this.SHELL_METACHARACTERS = /[|;&$`><(){}[\]!#*?~'"\\]/;
|
|
4100
|
+
}
|
|
4101
|
+
/**
|
|
4102
|
+
* Safely escape a value for shell interpolation
|
|
4103
|
+
* Uses single quotes which prevent all shell expansion except for single quotes themselves
|
|
4104
|
+
*/
|
|
4105
|
+
shellEscape(value) {
|
|
4106
|
+
return "'" + value.replace(/'/g, `'"'"'`) + "'";
|
|
4107
|
+
}
|
|
4108
|
+
/**
|
|
4109
|
+
* Execute a dynamic meta-tool by substituting {{param}} placeholders
|
|
4110
|
+
*/
|
|
4111
|
+
async executeMetaTool(metaTool, args) {
|
|
4112
|
+
let command = metaTool.handler;
|
|
4113
|
+
const placeholderRegex = /\{\{(\w+)\}\}/g;
|
|
4114
|
+
let match;
|
|
4115
|
+
while ((match = placeholderRegex.exec(metaTool.handler)) !== null) {
|
|
4116
|
+
const paramName = match[1];
|
|
4117
|
+
const value = args[paramName];
|
|
4118
|
+
if (value === void 0 || value === null) {
|
|
4119
|
+
throw new Error(`Missing required parameter "${paramName}" for meta-tool "${metaTool.name}"`);
|
|
4120
|
+
}
|
|
4121
|
+
const stringValue = String(value);
|
|
4122
|
+
let safeValue;
|
|
4123
|
+
if (_ActionExecutor.SHELL_METACHARACTERS.test(stringValue)) {
|
|
4124
|
+
safeValue = this.shellEscape(stringValue);
|
|
4125
|
+
console.log(_chalk2.default.yellow(` \u26A0 Parameter "${paramName}" contains shell metacharacters, escaped for safety`));
|
|
4126
|
+
} else {
|
|
4127
|
+
safeValue = stringValue;
|
|
4128
|
+
}
|
|
4129
|
+
command = command.replace(new RegExp(`\\{\\{${paramName}\\}\\}`, "g"), safeValue);
|
|
4130
|
+
}
|
|
4131
|
+
console.log(_chalk2.default.cyan(`
|
|
4132
|
+
\u{1F527} Running meta-tool: ${metaTool.name}`));
|
|
4133
|
+
console.log(_chalk2.default.gray(` $ ${command}`));
|
|
4134
|
+
const result = await runCommand(command, [], this.runtime.workspaceRoot, { shell: true });
|
|
4135
|
+
return [`$ ${command}`, result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
4136
|
+
}
|
|
4137
|
+
applySearchReplaceBlocks(content, blocks) {
|
|
4138
|
+
let result = content;
|
|
4139
|
+
if (blocks.includes("SEARCH:") && blocks.includes("REPLACE:") && !blocks.includes("<<<<<<< SEARCH")) {
|
|
4140
|
+
const searchIdx = blocks.indexOf("SEARCH:");
|
|
4141
|
+
const replaceIdx = blocks.indexOf("REPLACE:");
|
|
4142
|
+
if (searchIdx !== -1 && replaceIdx !== -1 && replaceIdx > searchIdx) {
|
|
4143
|
+
const searchText = blocks.slice(searchIdx + 7, replaceIdx).trim();
|
|
4144
|
+
const replaceText = blocks.slice(replaceIdx + 8).trim();
|
|
4145
|
+
const idx = result.indexOf(searchText);
|
|
4146
|
+
if (idx === -1) {
|
|
4147
|
+
throw new Error(`SEARCH text not found: "${searchText.slice(0, 50)}..."`);
|
|
4148
|
+
}
|
|
4149
|
+
result = result.slice(0, idx) + replaceText + result.slice(idx + searchText.length);
|
|
4150
|
+
return result;
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
const MARKERS = { search: "<<<<<<< SEARCH", div: "=======", replace: ">>>>>>> REPLACE" };
|
|
4154
|
+
let remaining = blocks;
|
|
4155
|
+
while (remaining.includes(MARKERS.search)) {
|
|
4156
|
+
const searchStart = remaining.indexOf(MARKERS.search);
|
|
4157
|
+
const divPos = remaining.indexOf(MARKERS.div, searchStart);
|
|
4158
|
+
const replaceEnd = remaining.indexOf(MARKERS.replace, divPos);
|
|
4159
|
+
if (divPos === -1 || replaceEnd === -1) {
|
|
4160
|
+
throw new Error("Malformed SEARCH/REPLACE block");
|
|
4161
|
+
}
|
|
4162
|
+
const searchText = remaining.slice(searchStart + MARKERS.search.length, divPos).replace(/^\n/, "").replace(/\n$/, "");
|
|
4163
|
+
const replaceText = remaining.slice(divPos + MARKERS.div.length, replaceEnd).replace(/^\n/, "").replace(/\n$/, "");
|
|
4164
|
+
const idx = result.indexOf(searchText);
|
|
4165
|
+
if (idx === -1) {
|
|
4166
|
+
throw new Error(`SEARCH text not found: "${searchText.slice(0, 50)}..."`);
|
|
4167
|
+
}
|
|
4168
|
+
result = result.slice(0, idx) + replaceText + result.slice(idx + searchText.length);
|
|
4169
|
+
remaining = remaining.slice(replaceEnd + MARKERS.replace.length);
|
|
4170
|
+
}
|
|
4171
|
+
return result;
|
|
4172
|
+
}
|
|
4173
|
+
/**
|
|
4174
|
+
* Escape special regex characters in a string
|
|
4175
|
+
*/
|
|
4176
|
+
escapeRegex(str) {
|
|
4177
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4178
|
+
}
|
|
4179
|
+
/**
|
|
4180
|
+
* Normalize text by converting common unicode variants to ASCII equivalents
|
|
4181
|
+
*/
|
|
4182
|
+
normalizeText(text) {
|
|
4183
|
+
return text.replace(/[\u2014\u2013]/g, "-").replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"').replace(/\u2026/g, "...").replace(/\u00A0/g, " ").replace(/[\u200B\u200C\u200D\uFEFF]/g, "");
|
|
4184
|
+
}
|
|
4185
|
+
/**
|
|
4186
|
+
* Extract the actual text from original content that matches a normalized position
|
|
4187
|
+
*/
|
|
4188
|
+
extractMatchingText(originalContent, normalizedContent, normalizedSearch, normalizedIndex) {
|
|
4189
|
+
let origIdx = 0;
|
|
4190
|
+
let normIdx = 0;
|
|
4191
|
+
while (normIdx < normalizedIndex && origIdx < originalContent.length) {
|
|
4192
|
+
const origChar = originalContent[origIdx];
|
|
4193
|
+
const normChar = this.normalizeText(origChar);
|
|
4194
|
+
origIdx++;
|
|
4195
|
+
normIdx += normChar.length;
|
|
4196
|
+
}
|
|
4197
|
+
const startIdx = origIdx;
|
|
4198
|
+
const targetNormEnd = normalizedIndex + normalizedSearch.length;
|
|
4199
|
+
while (normIdx < targetNormEnd && origIdx < originalContent.length) {
|
|
4200
|
+
const origChar = originalContent[origIdx];
|
|
4201
|
+
const normChar = this.normalizeText(origChar);
|
|
4202
|
+
origIdx++;
|
|
4203
|
+
normIdx += normChar.length;
|
|
4204
|
+
}
|
|
4205
|
+
return originalContent.substring(startIdx, origIdx);
|
|
4206
|
+
}
|
|
4207
|
+
/**
|
|
4208
|
+
* Find text similar to the search string in the content
|
|
4209
|
+
* Uses a simple approach: look for lines that share significant words
|
|
4210
|
+
*/
|
|
4211
|
+
findSimilarText(content, search) {
|
|
4212
|
+
const commonWords = /* @__PURE__ */ new Set(["the", "and", "for", "are", "but", "not", "you", "all", "can", "had", "her", "was", "one", "our", "out"]);
|
|
4213
|
+
const searchWords = search.toLowerCase().split(/\s+/).filter((w) => w.length >= 3 && !commonWords.has(w)).slice(0, 5);
|
|
4214
|
+
if (searchWords.length === 0) return null;
|
|
4215
|
+
const lines = content.split("\n");
|
|
4216
|
+
let bestMatch = null;
|
|
4217
|
+
for (const line of lines) {
|
|
4218
|
+
const lineLower = line.toLowerCase();
|
|
4219
|
+
let score = 0;
|
|
4220
|
+
for (const word of searchWords) {
|
|
4221
|
+
if (lineLower.includes(word)) {
|
|
4222
|
+
score++;
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
const searchStart = search.substring(0, Math.min(20, search.length)).toLowerCase();
|
|
4226
|
+
if (lineLower.includes(searchStart)) {
|
|
4227
|
+
score += 2;
|
|
4228
|
+
}
|
|
4229
|
+
if (score > 0 && (!bestMatch || score > bestMatch.score)) {
|
|
4230
|
+
bestMatch = { line: line.trim(), score };
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
return bestMatch && bestMatch.score >= 2 ? bestMatch.line : null;
|
|
4234
|
+
}
|
|
4235
|
+
/**
|
|
4236
|
+
* Colorize raw git diff output with green for additions and red for removals
|
|
4237
|
+
*/
|
|
4238
|
+
colorizeGitDiff(diffOutput) {
|
|
4239
|
+
const useTheme = _chunkPGRH5Q77cjs.isThemeInitialized.call(void 0, );
|
|
4240
|
+
const theme = useTheme ? _chunkPGRH5Q77cjs.getTheme.call(void 0, ) : null;
|
|
4241
|
+
if (!diffOutput || diffOutput === "No diff") {
|
|
4242
|
+
return theme ? theme.fg("muted", "No changes") : _chalk2.default.gray("No changes");
|
|
4243
|
+
}
|
|
4244
|
+
const termWidth = process.stdout.columns || 100;
|
|
4245
|
+
const lines = diffOutput.split("\n");
|
|
4246
|
+
const colorizedLines = [];
|
|
4247
|
+
let additions = 0;
|
|
4248
|
+
let deletions = 0;
|
|
4249
|
+
const addedColor = _optionalChain([theme, 'optionalAccess', _67 => _67.getColor, 'call', _68 => _68("diffAdded")]) || "#4caf50";
|
|
4250
|
+
const removedColor = _optionalChain([theme, 'optionalAccess', _69 => _69.getColor, 'call', _70 => _70("diffRemoved")]) || "#f44336";
|
|
4251
|
+
const contextColor = _optionalChain([theme, 'optionalAccess', _71 => _71.getColor, 'call', _72 => _72("diffContext")]) || "#9e9e9e";
|
|
4252
|
+
const accentColor = _optionalChain([theme, 'optionalAccess', _73 => _73.getColor, 'call', _74 => _74("accent")]) || "#00bcd4";
|
|
4253
|
+
const addedRgb = _chunkPGRH5Q77cjs.hexToRgb.call(void 0, addedColor);
|
|
4254
|
+
const removedRgb = _chunkPGRH5Q77cjs.hexToRgb.call(void 0, removedColor);
|
|
4255
|
+
const addBgR = addedRgb ? Math.floor(addedRgb.r * 0.15) : 30;
|
|
4256
|
+
const addBgG = addedRgb ? Math.floor(addedRgb.g * 0.2) : 50;
|
|
4257
|
+
const addBgB = addedRgb ? Math.floor(addedRgb.b * 0.15) : 30;
|
|
4258
|
+
const remBgR = removedRgb ? Math.floor(removedRgb.r * 0.25) : 60;
|
|
4259
|
+
const remBgG = removedRgb ? Math.floor(removedRgb.g * 0.15) : 30;
|
|
4260
|
+
const remBgB = removedRgb ? Math.floor(removedRgb.b * 0.15) : 30;
|
|
4261
|
+
for (const line of lines) {
|
|
4262
|
+
if (line.startsWith("+++") || line.startsWith("---")) {
|
|
4263
|
+
colorizedLines.push(_chalk2.default.bold(line));
|
|
4264
|
+
} else if (line.startsWith("@@")) {
|
|
4265
|
+
colorizedLines.push(_chalk2.default.hex(accentColor)(line));
|
|
4266
|
+
} else if (line.startsWith("+")) {
|
|
4267
|
+
additions++;
|
|
4268
|
+
const content = line.slice(1);
|
|
4269
|
+
const prefix = _chalk2.default.bgHex(addedColor).black(" + ");
|
|
4270
|
+
const lineContent = _chalk2.default.bgRgb(addBgR, addBgG, addBgB)(` ${content} `.padEnd(Math.max(termWidth - 5, content.length + 2)));
|
|
4271
|
+
colorizedLines.push(prefix + lineContent);
|
|
4272
|
+
} else if (line.startsWith("-")) {
|
|
4273
|
+
deletions++;
|
|
4274
|
+
const content = line.slice(1);
|
|
4275
|
+
const prefix = _chalk2.default.bgHex(removedColor).white(" - ");
|
|
4276
|
+
const lineContent = _chalk2.default.bgRgb(remBgR, remBgG, remBgB)(` ${content} `.padEnd(Math.max(termWidth - 5, content.length + 2)));
|
|
4277
|
+
colorizedLines.push(prefix + lineContent);
|
|
4278
|
+
} else if (line.startsWith("diff --git")) {
|
|
4279
|
+
colorizedLines.push(_chalk2.default.bold.hex(accentColor)(line));
|
|
4280
|
+
} else if (line.startsWith("index ") || line.startsWith("new file") || line.startsWith("deleted file")) {
|
|
4281
|
+
colorizedLines.push(_chalk2.default.hex(contextColor)(line));
|
|
4282
|
+
} else {
|
|
4283
|
+
colorizedLines.push(_chalk2.default.hex(contextColor)(" ") + line);
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
const addText = additions === 1 ? "1 line" : `${additions} lines`;
|
|
4287
|
+
const delText = deletions === 1 ? "1 line" : `${deletions} lines`;
|
|
4288
|
+
const statsLine = _chalk2.default.hex(contextColor)(` Added ${_chalk2.default.hex(addedColor)(addText)}, removed ${_chalk2.default.hex(removedColor)(delText)}
|
|
4289
|
+
`);
|
|
4290
|
+
return statsLine + colorizedLines.join("\n");
|
|
4291
|
+
}
|
|
4292
|
+
/**
|
|
4293
|
+
* Scan staged changes for secrets before commit
|
|
4294
|
+
* @returns Error message if blocked, undefined if safe to proceed
|
|
4295
|
+
*/
|
|
4296
|
+
async scanBeforeCommit() {
|
|
4297
|
+
try {
|
|
4298
|
+
const diff = _child_process.execSync.call(void 0, "git diff --cached", {
|
|
4299
|
+
cwd: this.runtime.workspaceRoot,
|
|
4300
|
+
encoding: "utf-8",
|
|
4301
|
+
maxBuffer: 10 * 1024 * 1024
|
|
4302
|
+
// 10MB
|
|
4303
|
+
});
|
|
4304
|
+
if (!diff.trim()) {
|
|
4305
|
+
return void 0;
|
|
4306
|
+
}
|
|
4307
|
+
const result = this.securityScanner.scanDiff(diff);
|
|
4308
|
+
console.log(this.securityScanner.formatDisplay(result));
|
|
4309
|
+
if (this.securityScanner.shouldBlockCommit(result)) {
|
|
4310
|
+
return `[BLOCKED] Commit blocked: ${result.blockedCount} high-severity secret(s) detected. Remove secrets before committing.`;
|
|
4311
|
+
}
|
|
4312
|
+
return void 0;
|
|
4313
|
+
} catch (e9) {
|
|
4314
|
+
console.log(_chalk2.default.yellow("\n[WARN] Could not scan for secrets (git diff failed)"));
|
|
4315
|
+
return void 0;
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
showDiff(oldContent, newContent, filePath) {
|
|
4319
|
+
const diff = _diff.diffLines.call(void 0, oldContent, newContent);
|
|
4320
|
+
const contextLines = 3;
|
|
4321
|
+
const lang = filePath ? detectLanguage(filePath) : "text";
|
|
4322
|
+
const shouldHighlight = lang !== "text";
|
|
4323
|
+
const useTheme = _chunkPGRH5Q77cjs.isThemeInitialized.call(void 0, );
|
|
4324
|
+
const theme = useTheme ? _chunkPGRH5Q77cjs.getTheme.call(void 0, ) : null;
|
|
4325
|
+
let additions = 0;
|
|
4326
|
+
let deletions = 0;
|
|
4327
|
+
for (const part of diff) {
|
|
4328
|
+
const lineCount = part.value.split("\n").filter((l, i, a) => i < a.length - 1 || l !== "").length;
|
|
4329
|
+
if (part.added) additions += lineCount;
|
|
4330
|
+
else if (part.removed) deletions += lineCount;
|
|
4331
|
+
}
|
|
4332
|
+
const termWidth = process.stdout.columns || 100;
|
|
4333
|
+
const addText = additions === 1 ? "1 line" : `${additions} lines`;
|
|
4334
|
+
const delText = deletions === 1 ? "1 line" : `${deletions} lines`;
|
|
4335
|
+
if (theme) {
|
|
4336
|
+
console.log(theme.fg("muted", ` Added ${theme.fg("diffAdded", addText)}, removed ${theme.fg("diffRemoved", delText)}`));
|
|
4337
|
+
} else {
|
|
4338
|
+
console.log(_chalk2.default.gray(` Added ${_chalk2.default.green(addText)}, removed ${_chalk2.default.red(delText)}`));
|
|
4339
|
+
}
|
|
4340
|
+
const hunks = [];
|
|
4341
|
+
let currentHunk = null;
|
|
4342
|
+
let oldLineNum = 1;
|
|
4343
|
+
let newLineNum = 1;
|
|
4344
|
+
let contextBuffer = [];
|
|
4345
|
+
for (const part of diff) {
|
|
4346
|
+
const lines = part.value.split("\n").filter((line, idx, arr) => {
|
|
4347
|
+
return idx < arr.length - 1 || line !== "";
|
|
4348
|
+
});
|
|
4349
|
+
if (!part.added && !part.removed) {
|
|
4350
|
+
if (currentHunk) {
|
|
4351
|
+
const trailing = lines.slice(0, contextLines);
|
|
4352
|
+
for (const line of trailing) {
|
|
4353
|
+
currentHunk.changes.push({ line, type: "context", oldNum: oldLineNum, newNum: newLineNum });
|
|
4354
|
+
oldLineNum++;
|
|
4355
|
+
newLineNum++;
|
|
4356
|
+
}
|
|
4357
|
+
if (lines.length > contextLines * 2) {
|
|
4358
|
+
hunks.push(currentHunk);
|
|
4359
|
+
currentHunk = null;
|
|
4360
|
+
const skipped = lines.length - trailing.length;
|
|
4361
|
+
oldLineNum += skipped - contextLines;
|
|
4362
|
+
newLineNum += skipped - contextLines;
|
|
4363
|
+
contextBuffer = lines.slice(-contextLines).map((line, i) => ({
|
|
4364
|
+
line,
|
|
4365
|
+
oldNum: oldLineNum + i,
|
|
4366
|
+
newNum: newLineNum + i
|
|
4367
|
+
}));
|
|
4368
|
+
oldLineNum += contextLines;
|
|
4369
|
+
newLineNum += contextLines;
|
|
4370
|
+
} else {
|
|
4371
|
+
for (let i = contextLines; i < lines.length; i++) {
|
|
4372
|
+
currentHunk.changes.push({ line: lines[i], type: "context", oldNum: oldLineNum, newNum: newLineNum });
|
|
4373
|
+
oldLineNum++;
|
|
4374
|
+
newLineNum++;
|
|
4375
|
+
}
|
|
4376
|
+
}
|
|
4377
|
+
} else {
|
|
4378
|
+
contextBuffer = lines.slice(-contextLines).map((line, i) => ({
|
|
4379
|
+
line,
|
|
4380
|
+
oldNum: oldLineNum + lines.length - contextLines + i,
|
|
4381
|
+
newNum: newLineNum + lines.length - contextLines + i
|
|
4382
|
+
}));
|
|
4383
|
+
oldLineNum += lines.length;
|
|
4384
|
+
newLineNum += lines.length;
|
|
4385
|
+
}
|
|
4386
|
+
} else {
|
|
4387
|
+
if (!currentHunk) {
|
|
4388
|
+
currentHunk = {
|
|
4389
|
+
oldStart: contextBuffer.length > 0 ? contextBuffer[0].oldNum : oldLineNum,
|
|
4390
|
+
newStart: contextBuffer.length > 0 ? contextBuffer[0].newNum : newLineNum,
|
|
4391
|
+
changes: contextBuffer.map((c) => ({ line: c.line, type: "context", oldNum: c.oldNum, newNum: c.newNum }))
|
|
4392
|
+
};
|
|
4393
|
+
contextBuffer = [];
|
|
4394
|
+
}
|
|
4395
|
+
if (part.added) {
|
|
4396
|
+
for (const line of lines) {
|
|
4397
|
+
currentHunk.changes.push({ line, type: "add", newNum: newLineNum });
|
|
4398
|
+
newLineNum++;
|
|
4399
|
+
}
|
|
4400
|
+
} else {
|
|
4401
|
+
for (const line of lines) {
|
|
4402
|
+
currentHunk.changes.push({ line, type: "remove", oldNum: oldLineNum });
|
|
4403
|
+
oldLineNum++;
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
if (currentHunk) {
|
|
4409
|
+
hunks.push(currentHunk);
|
|
4410
|
+
}
|
|
4411
|
+
for (const hunk of hunks) {
|
|
4412
|
+
for (const change of hunk.changes) {
|
|
4413
|
+
const lineNum = change.type === "add" ? change.newNum : change.oldNum;
|
|
4414
|
+
const lineNumStr = String(lineNum || 0).padStart(3);
|
|
4415
|
+
const highlighted = shouldHighlight ? highlightLine(change.line, lang) : change.line;
|
|
4416
|
+
if (theme) {
|
|
4417
|
+
const addedColor = theme.getColor("diffAdded");
|
|
4418
|
+
const removedColor = theme.getColor("diffRemoved");
|
|
4419
|
+
const contextColor = theme.getColor("diffContext");
|
|
4420
|
+
if (change.type === "add") {
|
|
4421
|
+
const addedRgb = _chunkPGRH5Q77cjs.hexToRgb.call(void 0, addedColor);
|
|
4422
|
+
const bgR = addedRgb ? Math.floor(addedRgb.r * 0.15) : 30;
|
|
4423
|
+
const bgG = addedRgb ? Math.floor(addedRgb.g * 0.2) : 50;
|
|
4424
|
+
const bgB = addedRgb ? Math.floor(addedRgb.b * 0.15) : 30;
|
|
4425
|
+
const prefix = _chalk2.default.bgHex(addedColor).black(` ${lineNumStr} + `);
|
|
4426
|
+
const content = _chalk2.default.bgRgb(bgR, bgG, bgB)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
|
|
4427
|
+
console.log(prefix + content);
|
|
4428
|
+
} else if (change.type === "remove") {
|
|
4429
|
+
const removedRgb = _chunkPGRH5Q77cjs.hexToRgb.call(void 0, removedColor);
|
|
4430
|
+
const bgR = removedRgb ? Math.floor(removedRgb.r * 0.25) : 60;
|
|
4431
|
+
const bgG = removedRgb ? Math.floor(removedRgb.g * 0.15) : 30;
|
|
4432
|
+
const bgB = removedRgb ? Math.floor(removedRgb.b * 0.15) : 30;
|
|
4433
|
+
const prefix = _chalk2.default.bgHex(removedColor).white(` ${lineNumStr} - `);
|
|
4434
|
+
const content = _chalk2.default.bgRgb(bgR, bgG, bgB)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
|
|
4435
|
+
console.log(prefix + content);
|
|
4436
|
+
} else {
|
|
4437
|
+
console.log(_chalk2.default.hex(contextColor)(` ${lineNumStr} `) + ` ${highlighted}`);
|
|
4438
|
+
}
|
|
4439
|
+
} else {
|
|
4440
|
+
if (change.type === "add") {
|
|
4441
|
+
const prefix = _chalk2.default.bgGreen.black(` ${lineNumStr} + `);
|
|
4442
|
+
const content = _chalk2.default.bgRgb(30, 50, 30)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
|
|
4443
|
+
console.log(prefix + content);
|
|
4444
|
+
} else if (change.type === "remove") {
|
|
4445
|
+
const prefix = _chalk2.default.bgRed.white(` ${lineNumStr} - `);
|
|
4446
|
+
const content = _chalk2.default.bgRgb(60, 30, 30)(` ${highlighted} `.padEnd(Math.max(termWidth - 10, change.line.length + 2)));
|
|
4447
|
+
console.log(prefix + content);
|
|
4448
|
+
} else {
|
|
4449
|
+
console.log(_chalk2.default.gray(` ${lineNumStr} `) + ` ${highlighted}`);
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4454
|
+
console.log();
|
|
4455
|
+
}
|
|
4456
|
+
};
|
|
4457
|
+
|
|
4458
|
+
|
|
4459
|
+
|
|
4460
|
+
|
|
4461
|
+
|
|
4462
|
+
exports.getAutoCommitInfo = getAutoCommitInfo; exports.ToolsRegistry = ToolsRegistry; exports.ActionExecutor = ActionExecutor;
|
|
4463
|
+
/**
|
|
4464
|
+
* @license
|
|
4465
|
+
* Copyright 2025 Autohand AI LLC
|
|
4466
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4467
|
+
*
|
|
4468
|
+
* Syntax Highlighting for Terminal Output
|
|
4469
|
+
* Uses regex-based tokenization for common languages
|
|
4470
|
+
*/
|
|
4471
|
+
/**
|
|
4472
|
+
* @license
|
|
4473
|
+
* Copyright 2025 Autohand AI LLC
|
|
4474
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4475
|
+
*/
|
|
4476
|
+
/**
|
|
4477
|
+
* @license
|
|
4478
|
+
* Copyright 2025 Autohand AI LLC
|
|
4479
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4480
|
+
*
|
|
4481
|
+
* Advanced Git Worktree Manager
|
|
4482
|
+
* Provides intelligent worktree automation for parallel development workflows
|
|
4483
|
+
*/
|
|
4484
|
+
/**
|
|
4485
|
+
* @license
|
|
4486
|
+
* Copyright 2025 Autohand AI LLC
|
|
4487
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4488
|
+
*
|
|
4489
|
+
* GitHub and GitLab repository browsing capabilities.
|
|
4490
|
+
*/
|
|
4491
|
+
/**
|
|
4492
|
+
* @license
|
|
4493
|
+
* Copyright 2025 Autohand AI LLC
|
|
4494
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4495
|
+
*
|
|
4496
|
+
* Plan File Storage
|
|
4497
|
+
* Saves/loads plans to/from .autohand/plans/ directory
|
|
4498
|
+
*/
|