token-goat 2.9.30 → 2.9.31
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 +24 -7
- package/SECURITY.md +10 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/native/linux-arm64/tg-hook +0 -0
- package/dist/native/linux-x64/tg-hook +0 -0
- package/dist/native/win32-arm64/tg-hook.exe +0 -0
- package/dist/native/win32-x64/tg-hook.exe +0 -0
- package/dist/{token-goat-chunk-QSBRTGM2.mjs → token-goat-chunk-25TRAWET.mjs} +17 -7
- package/dist/token-goat-chunk-2KXO6V2B.mjs +203 -0
- package/dist/{token-goat-chunk-IVKIZWAB.mjs → token-goat-chunk-2M7E4PF7.mjs} +41 -12
- package/dist/{token-goat-chunk-SMVRN2HU.mjs → token-goat-chunk-2UPZGKKY.mjs} +2 -2
- package/dist/token-goat-chunk-2WLPWKHV.mjs +135 -0
- package/dist/token-goat-chunk-2XBMMDBD.mjs +126 -0
- package/dist/token-goat-chunk-32ZOY4N5.mjs +23 -0
- package/dist/token-goat-chunk-3EZRNKTK.mjs +188 -0
- package/dist/token-goat-chunk-4ADZHI7J.mjs +63 -0
- package/dist/token-goat-chunk-4GBVPH3D.mjs +242 -0
- package/dist/{token-goat-chunk-KWPNJBDH.mjs → token-goat-chunk-4GILLLWY.mjs} +7 -4
- package/dist/{token-goat-chunk-67TQJ76B.mjs → token-goat-chunk-4NAPX7BA.mjs} +36 -3
- package/dist/{token-goat-chunk-PU7V3FD6.mjs → token-goat-chunk-4SC7UJYV.mjs} +3354 -2553
- package/dist/token-goat-chunk-4SMRO4TO.mjs +366 -0
- package/dist/{token-goat-chunk-AFLYLHDH.mjs → token-goat-chunk-4ZBFWLXF.mjs} +283 -427
- package/dist/{token-goat-chunk-JDWCRD6M.mjs → token-goat-chunk-57BIAXJL.mjs} +73 -40
- package/dist/token-goat-chunk-5AC66HNP.mjs +115 -0
- package/dist/token-goat-chunk-5IMLKSOP.mjs +145 -0
- package/dist/token-goat-chunk-646E6U44.mjs +398 -0
- package/dist/token-goat-chunk-65N4RHP4.mjs +33 -0
- package/dist/{token-goat-chunk-JN4XMLSD.mjs → token-goat-chunk-6DG6HWP3.mjs} +8 -2
- package/dist/token-goat-chunk-6ENQSNRZ.mjs +573 -0
- package/dist/token-goat-chunk-6ZY4GNF2.mjs +91 -0
- package/dist/{token-goat-chunk-VPBBTSJR.mjs → token-goat-chunk-76FGDLRO.mjs} +262 -255
- package/dist/{token-goat-chunk-7IN2MKDS.mjs → token-goat-chunk-7EZTZOMC.mjs} +121 -289
- package/dist/{token-goat-chunk-OJBNYTJP.mjs → token-goat-chunk-7MAOA6X4.mjs} +640 -80
- package/dist/token-goat-chunk-7R2ZVVAX.mjs +218 -0
- package/dist/{token-goat-chunk-PD5VKH46.mjs → token-goat-chunk-AUNGYYQX.mjs} +2 -2
- package/dist/{token-goat-chunk-HG45AF2B.mjs → token-goat-chunk-AUQYRMMH.mjs} +794 -37
- package/dist/token-goat-chunk-B3PEKBYG.mjs +128 -0
- package/dist/{token-goat-chunk-A3CK6N5Z.mjs → token-goat-chunk-BA4HI6BW.mjs} +1309 -110
- package/dist/{token-goat-chunk-JB6QN6VF.mjs → token-goat-chunk-BFCWY73P.mjs} +59 -23
- package/dist/{token-goat-chunk-B45VMIIH.mjs → token-goat-chunk-BWGFUZZE.mjs} +7 -5
- package/dist/{token-goat-chunk-P4QMM4WK.mjs → token-goat-chunk-C75NA7PL.mjs} +50 -23
- package/dist/token-goat-chunk-CBIQ7WTD.mjs +87 -0
- package/dist/token-goat-chunk-CETM74KC.mjs +307 -0
- package/dist/{token-goat-chunk-H2PHHGM5.mjs → token-goat-chunk-CIMQ2BB3.mjs} +57 -12
- package/dist/{token-goat-chunk-N2NMER56.mjs → token-goat-chunk-CJLS3CDL.mjs} +46 -4
- package/dist/{token-goat-chunk-VES7EL7O.mjs → token-goat-chunk-CO3UYX6G.mjs} +1 -1
- package/dist/token-goat-chunk-COPNXRJM.mjs +154 -0
- package/dist/token-goat-chunk-CSJ5L3NL.mjs +479 -0
- package/dist/{token-goat-chunk-4LCCNRTG.mjs → token-goat-chunk-CVCEVUGK.mjs} +8 -6
- package/dist/{token-goat-chunk-B2RZZCSS.mjs → token-goat-chunk-CXHOFN7R.mjs} +37 -20
- package/dist/token-goat-chunk-D735C4QM.mjs +603 -0
- package/dist/{token-goat-chunk-APGZXSPS.mjs → token-goat-chunk-DGS2LVNA.mjs} +47 -30
- package/dist/{token-goat-chunk-54K3EUV3.mjs → token-goat-chunk-DOWRSMWO.mjs} +1 -1
- package/dist/token-goat-chunk-E45INMY3.mjs +71 -0
- package/dist/token-goat-chunk-EC5F37F4.mjs +244 -0
- package/dist/{token-goat-chunk-XTQAOTSO.mjs → token-goat-chunk-EDBDRVH5.mjs} +4 -1
- package/dist/{token-goat-chunk-5BCIJXMO.mjs → token-goat-chunk-EELTBKDM.mjs} +14 -10
- package/dist/{token-goat-chunk-W3QCJS6G.mjs → token-goat-chunk-F4HYL64R.mjs} +8 -6
- package/dist/token-goat-chunk-F6PAUIEK.mjs +624 -0
- package/dist/token-goat-chunk-FDSREWLX.mjs +692 -0
- package/dist/token-goat-chunk-GGGG7RWB.mjs +236 -0
- package/dist/{token-goat-chunk-C63JQV5K.mjs → token-goat-chunk-GL3ETKSC.mjs} +233 -15
- package/dist/token-goat-chunk-GVB2MUST.mjs +1298 -0
- package/dist/token-goat-chunk-GWFLAUMQ.mjs +307 -0
- package/dist/{token-goat-chunk-ZEJVMUQH.mjs → token-goat-chunk-HLPW63A7.mjs} +2 -2
- package/dist/{token-goat-chunk-GOLEOXZP.mjs → token-goat-chunk-HU4TORC5.mjs} +44 -26
- package/dist/{token-goat-chunk-Z57YODA2.mjs → token-goat-chunk-I7VKZZAW.mjs} +2 -2
- package/dist/{token-goat-chunk-NYKZLXMK.mjs → token-goat-chunk-IA6XSPE3.mjs} +6 -6
- package/dist/{token-goat-chunk-E3OQX2XT.mjs → token-goat-chunk-IAGXMHIK.mjs} +81 -42
- package/dist/{token-goat-chunk-HYBFA5X3.mjs → token-goat-chunk-IK2DRMFX.mjs} +24 -68
- package/dist/{token-goat-chunk-COBQGL5S.mjs → token-goat-chunk-INNPTRUX.mjs} +249 -299
- package/dist/{token-goat-chunk-7NADEDKJ.mjs → token-goat-chunk-JJ5BX4F4.mjs} +6 -1
- package/dist/{token-goat-chunk-PEPTHRFF.mjs → token-goat-chunk-JVFSHVFJ.mjs} +6 -6
- package/dist/token-goat-chunk-KGEFFSHD.mjs +50 -0
- package/dist/token-goat-chunk-KQBLRPBN.mjs +433 -0
- package/dist/{token-goat-chunk-Q5VTGRH7.mjs → token-goat-chunk-LCNQ2CNN.mjs} +55 -319
- package/dist/token-goat-chunk-M3LFJDPU.mjs +58 -0
- package/dist/token-goat-chunk-M52KPT3C.mjs +87 -0
- package/dist/token-goat-chunk-MBEG42H7.mjs +150 -0
- package/dist/{token-goat-chunk-EVC4TOLE.mjs → token-goat-chunk-METECDOH.mjs} +3 -12
- package/dist/{token-goat-chunk-T4FK7VOV.mjs → token-goat-chunk-MHX7XCC3.mjs} +2 -1
- package/dist/token-goat-chunk-N5LSOVSU.mjs +137 -0
- package/dist/{token-goat-chunk-LOBGHXPB.mjs → token-goat-chunk-N5MMR2IW.mjs} +1 -1
- package/dist/token-goat-chunk-N7B3JQ2C.mjs +160 -0
- package/dist/{token-goat-chunk-RHXKCZX3.mjs → token-goat-chunk-NISNMD7T.mjs} +2182 -2959
- package/dist/token-goat-chunk-NMD42SFQ.mjs +434 -0
- package/dist/token-goat-chunk-NOSS5VWY.mjs +174 -0
- package/dist/{token-goat-chunk-KQNZDCBD.mjs → token-goat-chunk-NSNSPPBG.mjs} +25 -3
- package/dist/token-goat-chunk-OBOFIHKN.mjs +38 -0
- package/dist/token-goat-chunk-OBRFJ5O5.mjs +76 -0
- package/dist/{token-goat-chunk-JTQQGXZH.mjs → token-goat-chunk-ODBT2USM.mjs} +124 -57
- package/dist/token-goat-chunk-OS5EM5PP.mjs +107 -0
- package/dist/token-goat-chunk-OUKDTVNU.mjs +1178 -0
- package/dist/token-goat-chunk-OXFMP75N.mjs +566 -0
- package/dist/{token-goat-chunk-DZX2YDPT.mjs → token-goat-chunk-OYIYKDDT.mjs} +2 -2
- package/dist/{token-goat-chunk-QCLDAQTS.mjs → token-goat-chunk-P6HQSWQ3.mjs} +50 -53
- package/dist/token-goat-chunk-P6VGNTQT.mjs +71 -0
- package/dist/{token-goat-chunk-7BXSNBY5.mjs → token-goat-chunk-PFWIMH2E.mjs} +23 -15
- package/dist/token-goat-chunk-PJZXIS3G.mjs +39 -0
- package/dist/token-goat-chunk-PUO5INXP.mjs +606 -0
- package/dist/{token-goat-chunk-GFBSN7BD.mjs → token-goat-chunk-PX5PQWTY.mjs} +22 -250
- package/dist/token-goat-chunk-Q5QDBC6O.mjs +111 -0
- package/dist/token-goat-chunk-QFNQXSDT.mjs +40 -0
- package/dist/{token-goat-chunk-FBNQNSJK.mjs → token-goat-chunk-QH7YVYOY.mjs} +2198 -2782
- package/dist/{token-goat-chunk-64FBPDTK.mjs → token-goat-chunk-QLMUHEPB.mjs} +12 -9
- package/dist/{token-goat-chunk-RZRTDST7.mjs → token-goat-chunk-QQOJIHL3.mjs} +37 -11
- package/dist/{token-goat-chunk-XKI4SEMX.mjs → token-goat-chunk-QQTWJY2O.mjs} +19 -0
- package/dist/{token-goat-chunk-U4N3BN2W.mjs → token-goat-chunk-QYHO5CPH.mjs} +3 -2
- package/dist/token-goat-chunk-RELAVWAD.mjs +27 -0
- package/dist/{token-goat-chunk-ETBHVDB6.mjs → token-goat-chunk-RSLPOBKQ.mjs} +22 -11
- package/dist/token-goat-chunk-SAMFDIS2.mjs +712 -0
- package/dist/token-goat-chunk-SDELXLDM.mjs +48 -0
- package/dist/{token-goat-chunk-4JJS54TR.mjs → token-goat-chunk-SOAFZNNZ.mjs} +161 -38
- package/dist/{token-goat-chunk-664RTTUV.mjs → token-goat-chunk-SRTHT7KV.mjs} +21 -16
- package/dist/{token-goat-chunk-4FVIAHSI.mjs → token-goat-chunk-T5P5ACNQ.mjs} +7 -6
- package/dist/token-goat-chunk-T5YGG4V3.mjs +45 -0
- package/dist/token-goat-chunk-TAUZ4OFS.mjs +110 -0
- package/dist/{token-goat-chunk-COTXZHTS.mjs → token-goat-chunk-TM6MC754.mjs} +4 -3
- package/dist/token-goat-chunk-TWUKBOMP.mjs +72 -0
- package/dist/{token-goat-chunk-3CHUNJ2O.mjs → token-goat-chunk-U2KSB4SG.mjs} +8 -5
- package/dist/{token-goat-chunk-3Z5S4KGM.mjs → token-goat-chunk-U4WHMG6Y.mjs} +29 -49
- package/dist/{token-goat-chunk-PY533T7F.mjs → token-goat-chunk-UGJHGR5A.mjs} +21 -15
- package/dist/{token-goat-chunk-CYCEYAEO.mjs → token-goat-chunk-UOUDVA5U.mjs} +1 -1
- package/dist/{token-goat-chunk-JDHE26RP.mjs → token-goat-chunk-URRJ33GR.mjs} +16 -5
- package/dist/{token-goat-chunk-56EIMS3E.mjs → token-goat-chunk-UZOROKIS.mjs} +23 -17
- package/dist/{token-goat-chunk-CZ3LUXYZ.mjs → token-goat-chunk-VIHGGGEI.mjs} +427 -308
- package/dist/token-goat-chunk-VTJLAA7P.mjs +21 -0
- package/dist/token-goat-chunk-VWDXLT4F.mjs +678 -0
- package/dist/{token-goat-chunk-YARZFCZS.mjs → token-goat-chunk-VWZA2H4P.mjs} +62 -53
- package/dist/{token-goat-chunk-SQSSNQPE.mjs → token-goat-chunk-VXEXMJDL.mjs} +3979 -4745
- package/dist/token-goat-chunk-WBA5SKT4.mjs +547 -0
- package/dist/{token-goat-chunk-SJHWVNDQ.mjs → token-goat-chunk-WDH44VAU.mjs} +7 -4
- package/dist/token-goat-chunk-X2SUP3EI.mjs +107 -0
- package/dist/token-goat-chunk-X3BELZSJ.mjs +125 -0
- package/dist/{token-goat-chunk-FZ3NEW5O.mjs → token-goat-chunk-XLRBNZCC.mjs} +13 -8
- package/dist/{token-goat-chunk-Z44YO3QZ.mjs → token-goat-chunk-XRKUX6CM.mjs} +1 -1
- package/dist/token-goat-chunk-XX6O25RN.mjs +82 -0
- package/dist/token-goat-chunk-XZSUHBVS.mjs +65 -0
- package/dist/{token-goat-chunk-WY3L4DVT.mjs → token-goat-chunk-Y2ZTQOCL.mjs} +13 -8
- package/dist/{token-goat-chunk-UQ5UK2T7.mjs → token-goat-chunk-YB4VMGK2.mjs} +1 -1
- package/dist/token-goat-chunk-YBIIY3HR.mjs +51 -0
- package/dist/{token-goat-chunk-ZQSJMO2R.mjs → token-goat-chunk-YHVDKQBR.mjs} +105 -105
- package/dist/{token-goat-chunk-H2K3QP6H.mjs → token-goat-chunk-YJB5XLWI.mjs} +8 -6
- package/dist/{token-goat-chunk-ZCDTCVIS.mjs → token-goat-chunk-YKB6AQG3.mjs} +5 -2
- package/dist/token-goat-chunk-YMTG2YA5.mjs +35 -0
- package/dist/token-goat-chunk-YPBZYQRV.mjs +40 -0
- package/dist/{token-goat-chunk-HOJLCGDP.mjs → token-goat-chunk-YY6AVUFL.mjs} +2033 -2251
- package/dist/token-goat-chunk-Z7N5OMMP.mjs +98 -0
- package/dist/token-goat-chunk-ZNMNUPAZ.mjs +938 -0
- package/dist/token-goat-hook-client.cjs +1 -1
- package/dist/token-goat-hook-client.mjs +3 -3
- package/dist/token-goat-hook.mjs +67 -38
- package/dist/token-goat.core.mjs +4 -4
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +40 -21
- package/docs/install.md +19 -9
- package/docs/security.md +5 -3
- package/package.json +2 -1
- package/dist/token-goat-chunk-5KDMCMF2.mjs +0 -362
- package/dist/token-goat-chunk-6QMAS7AB.mjs +0 -42
- package/dist/token-goat-chunk-7F2UBPD3.mjs +0 -397
- package/dist/token-goat-chunk-ARDNEC2U.mjs +0 -248
- package/dist/token-goat-chunk-ATCPY7EQ.mjs +0 -134
- package/dist/token-goat-chunk-EFHMMIGP.mjs +0 -68
- package/dist/token-goat-chunk-IVALLT3S.mjs +0 -48
- package/dist/token-goat-chunk-JDU3EXSU.mjs +0 -65
- package/dist/token-goat-chunk-MKPF4CL5.mjs +0 -37
- package/dist/token-goat-chunk-RC35RBUL.mjs +0 -312
- package/dist/token-goat-chunk-USILZ3T3.mjs +0 -53
- package/dist/token-goat-chunk-VL3YIFHZ.mjs +0 -2975
- package/dist/token-goat-chunk-XU4P6Z24.mjs +0 -133
- package/dist/token-goat-chunk-Y776AENC.mjs +0 -1597
|
@@ -14,34 +14,61 @@ import {
|
|
|
14
14
|
anchoredMarkerPattern,
|
|
15
15
|
buildGuidanceBlock,
|
|
16
16
|
buildGuidanceBody,
|
|
17
|
+
bundledCliPath,
|
|
18
|
+
canonicalProjectRoot,
|
|
19
|
+
claudeHooksInstalledAnyScope,
|
|
20
|
+
copilotHooksFilePaths,
|
|
21
|
+
copilotMcpConfigPath,
|
|
22
|
+
dropEmptyServers,
|
|
17
23
|
editAt,
|
|
24
|
+
findStrayClaudeMdBlocks,
|
|
18
25
|
finishHookUninstall,
|
|
19
26
|
groupHasTokenGoat,
|
|
27
|
+
hasManagedServer,
|
|
20
28
|
hookCommandRewrittenSpan,
|
|
29
|
+
installCopilotHooksFile,
|
|
30
|
+
isManagedServer,
|
|
31
|
+
isResidueServersJson,
|
|
21
32
|
legacyShimForwarder,
|
|
33
|
+
managedServer,
|
|
34
|
+
managedServerEntry,
|
|
22
35
|
nativeHookBinary,
|
|
23
36
|
nativeHookCommandLine,
|
|
37
|
+
noteRootKeyCreation,
|
|
38
|
+
readServersJson,
|
|
39
|
+
releaseCopilotHooksFile,
|
|
40
|
+
serversOf,
|
|
41
|
+
setTokenGoatServer,
|
|
42
|
+
shimFallbackTimeoutConst,
|
|
24
43
|
skillDescriptionLine,
|
|
25
|
-
splitHookCommand
|
|
26
|
-
|
|
44
|
+
splitHookCommand,
|
|
45
|
+
syncVisualStudioProjectGuidance,
|
|
46
|
+
visualStudioManagedEntry
|
|
47
|
+
} from "./token-goat-chunk-7MAOA6X4.mjs";
|
|
27
48
|
import {
|
|
28
49
|
parseJsonOrJsonc,
|
|
29
50
|
stripBom
|
|
30
|
-
} from "./token-goat-chunk-
|
|
51
|
+
} from "./token-goat-chunk-QQTWJY2O.mjs";
|
|
52
|
+
import {
|
|
53
|
+
readCopilotHooksOwners
|
|
54
|
+
} from "./token-goat-chunk-G5PJ7AGM.mjs";
|
|
31
55
|
import {
|
|
32
56
|
toolMatcherFor
|
|
33
|
-
} from "./token-goat-chunk-
|
|
57
|
+
} from "./token-goat-chunk-C75NA7PL.mjs";
|
|
34
58
|
import {
|
|
35
59
|
GEMINI_TOOL_NAME_MAP
|
|
36
|
-
} from "./token-goat-chunk-
|
|
60
|
+
} from "./token-goat-chunk-76FGDLRO.mjs";
|
|
37
61
|
import {
|
|
38
62
|
loadConfig,
|
|
39
63
|
parse,
|
|
40
64
|
stringify
|
|
41
|
-
} from "./token-goat-chunk-
|
|
65
|
+
} from "./token-goat-chunk-7EZTZOMC.mjs";
|
|
42
66
|
import {
|
|
67
|
+
assertProjectScopeTarget,
|
|
68
|
+
atomicWriteBytes,
|
|
43
69
|
atomicWriteText,
|
|
44
70
|
backupFile,
|
|
71
|
+
countNoun,
|
|
45
72
|
ensureDirRecordingCreation,
|
|
46
73
|
ensureDirSync,
|
|
47
74
|
extractErrorMessage,
|
|
@@ -49,28 +76,41 @@ import {
|
|
|
49
76
|
hookCommandFor,
|
|
50
77
|
hookPowershellCommand,
|
|
51
78
|
installSingleFilePlugin,
|
|
79
|
+
projectPathIsConsultable,
|
|
52
80
|
projectScopeRoot,
|
|
53
81
|
recordCreatedBy,
|
|
54
82
|
recordCreatedConfig,
|
|
83
|
+
recordTurnedOffSetting,
|
|
55
84
|
removeCreatedBackups,
|
|
56
85
|
removeCreatedIfEmpty,
|
|
57
86
|
removeCreatedTree,
|
|
87
|
+
removeEmptyDirInScope,
|
|
88
|
+
removeFileInScope,
|
|
58
89
|
stripDelimitedBlock,
|
|
59
90
|
stripOwnHooksFromMap,
|
|
60
91
|
stripStaleGroupHooks,
|
|
61
92
|
takeCreatedConfig,
|
|
93
|
+
takeTurnedOffSetting,
|
|
62
94
|
uninstallSingleFilePlugin,
|
|
63
95
|
upsertDelimitedBlock,
|
|
64
96
|
withInstallScope,
|
|
65
97
|
writeConfigText,
|
|
66
98
|
writeIfDifferent,
|
|
67
99
|
writeJsonSettings
|
|
68
|
-
} from "./token-goat-chunk-
|
|
100
|
+
} from "./token-goat-chunk-HU4TORC5.mjs";
|
|
101
|
+
import {
|
|
102
|
+
echoedValue,
|
|
103
|
+
fencedCommand
|
|
104
|
+
} from "./token-goat-chunk-GGGG7RWB.mjs";
|
|
69
105
|
import {
|
|
70
106
|
foldPath,
|
|
71
107
|
powershellHookLine,
|
|
72
108
|
quoteShellPath
|
|
73
109
|
} from "./token-goat-chunk-AM5V6GSX.mjs";
|
|
110
|
+
import {
|
|
111
|
+
displaySafeText,
|
|
112
|
+
normalizePath
|
|
113
|
+
} from "./token-goat-chunk-FNFBKLRD.mjs";
|
|
74
114
|
import {
|
|
75
115
|
init_define_import_meta_env
|
|
76
116
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -157,7 +197,7 @@ function geminiSettingsPath() {
|
|
|
157
197
|
}
|
|
158
198
|
function readGeminiSettings(p, opts = {}) {
|
|
159
199
|
const refuse = (problem, detail) => new GeminiSettingsParseError(
|
|
160
|
-
(opts.command === "uninstall" ? `Gemini settings file
|
|
200
|
+
(opts.command === "uninstall" ? `Gemini settings file ${echoedValue(p)} is unreadable: it ${problem}. Uninstall left it and its backups untouched; fix the file and run uninstall again.` : `Gemini settings file ${echoedValue(p)} ${problem}. Fix or back up the file before running install.`) + (detail === void 0 ? "" : ` (${detail})`)
|
|
161
201
|
);
|
|
162
202
|
let raw;
|
|
163
203
|
try {
|
|
@@ -290,7 +330,7 @@ function qwenSettingsPath() {
|
|
|
290
330
|
}
|
|
291
331
|
function readQwenSettings(p, opts = {}) {
|
|
292
332
|
const refuse = (problem, detail) => new QwenSettingsParseError(
|
|
293
|
-
(opts.command === "uninstall" ? `Qwen Code settings file
|
|
333
|
+
(opts.command === "uninstall" ? `Qwen Code settings file ${echoedValue(p)} is unreadable: it ${problem}. Uninstall left it and its backups untouched; fix the file and run uninstall again.` : `Qwen Code settings file ${echoedValue(p)} ${problem}. Fix or back up the file before running install.`) + (detail === void 0 ? "" : ` (${detail})`)
|
|
294
334
|
);
|
|
295
335
|
let raw;
|
|
296
336
|
try {
|
|
@@ -431,6 +471,7 @@ ${SHIM_TRY_SERVER}
|
|
|
431
471
|
${SHIM_TRY_IN_PROCESS}
|
|
432
472
|
|
|
433
473
|
${SHIM_MAX_BUFFER_CONST}
|
|
474
|
+
${shimFallbackTimeoutConst("kimi")}
|
|
434
475
|
|
|
435
476
|
async function main() {
|
|
436
477
|
const eventName = process.argv[2] || ''
|
|
@@ -444,7 +485,7 @@ async function main() {
|
|
|
444
485
|
// process.argv[3], when present, is the absolute path to the token-goat CLI entry that ran
|
|
445
486
|
// 'token-goat install --kimi' (baked in by hookCommandFor in kimi_install.ts). Same three-step
|
|
446
487
|
// ladder as the Codex shim: in-process hook lib, then the baked entry via process.execPath,
|
|
447
|
-
// then a PATH-based shell:true invocation for an older cached hook config. The
|
|
488
|
+
// then a PATH-based shell:true invocation for an older cached hook config. The SHIM_FALLBACK_TIMEOUT_MS
|
|
448
489
|
// timeout keeps both spawn fallbacks well inside Kimi's own 30s default hook timeout
|
|
449
490
|
// (docs/en/customization/hooks.md), so token-goat degrades to its own fail-open no-op.
|
|
450
491
|
const entryPath = process.argv[3]
|
|
@@ -455,7 +496,7 @@ ${SHIM_SPAWN_LADDER}
|
|
|
455
496
|
input,
|
|
456
497
|
encoding: 'utf8',
|
|
457
498
|
shell: true,
|
|
458
|
-
timeout:
|
|
499
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
459
500
|
killSignal: 'SIGKILL',
|
|
460
501
|
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
461
502
|
})
|
|
@@ -514,7 +555,7 @@ function kimiSkillPath() {
|
|
|
514
555
|
}
|
|
515
556
|
function readKimiConfig(p, opts = {}) {
|
|
516
557
|
const refuse = (problem, detail) => new KimiConfigParseError(
|
|
517
|
-
(opts.command === "uninstall" ? `Kimi Code config file
|
|
558
|
+
(opts.command === "uninstall" ? `Kimi Code config file ${echoedValue(p)} is unreadable: it ${problem}. Uninstall left it untouched, along with the hook shim it may still name, the AGENTS.md block, the skill and the file's backups; fix the file and run uninstall again.` : `Kimi Code config file ${echoedValue(p)} ${problem}. Fix or back up the file before running install.`) + (detail === void 0 ? "" : ` (${detail})`)
|
|
518
559
|
);
|
|
519
560
|
let raw;
|
|
520
561
|
try {
|
|
@@ -586,7 +627,7 @@ function installKimiFiles() {
|
|
|
586
627
|
for (const { command } of process.platform === "win32" ? desired : []) {
|
|
587
628
|
const span = hookCommandRewrittenSpan(command, "cmd");
|
|
588
629
|
if (span !== void 0) {
|
|
589
|
-
throw new HookCommandRewriteError(`Kimi Code runs hook commands through cmd.exe, which replaces ${span} with the value of the environment variable of that name whenever one is set, before it reads any quotes, so the hook command token-goat would write (${command}) would not run token-goat. Nothing was written. Move the path that contains
|
|
630
|
+
throw new HookCommandRewriteError(`Kimi Code runs hook commands through cmd.exe, which replaces ${span} with the value of the environment variable of that name whenever one is set, before it reads any quotes, so the hook command token-goat would write (${command}) would not run token-goat. Nothing was written. Move the path that contains ${echoedValue(span)} (the Kimi Code home, token-goat's install directory, or Node's) to one without a %...% pair, then run \`token-goat install --kimi\` again.`);
|
|
590
631
|
}
|
|
591
632
|
}
|
|
592
633
|
ensureDirSync(path4.dirname(scriptPath));
|
|
@@ -771,6 +812,8 @@ async function resolveRelayInProcess(): Promise<RelayInProcessFn | undefined> {
|
|
|
771
812
|
}
|
|
772
813
|
}
|
|
773
814
|
|
|
815
|
+
${shimFallbackTimeoutConst("pi")}
|
|
816
|
+
|
|
774
817
|
function callHookViaSpawn(event: string, payload: Record<string, unknown>): Record<string, unknown> | null {
|
|
775
818
|
try {
|
|
776
819
|
// TOKEN_GOAT_HARNESS_OVERRIDE=pi guarantees detectHarness() resolves to
|
|
@@ -789,7 +832,7 @@ function callHookViaSpawn(event: string, payload: Record<string, unknown>): Reco
|
|
|
789
832
|
? spawnSync(process.execPath, [entryPath, "hook", event], {
|
|
790
833
|
input: JSON.stringify(payload),
|
|
791
834
|
encoding: "utf8",
|
|
792
|
-
timeout:
|
|
835
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
793
836
|
killSignal: "SIGKILL",
|
|
794
837
|
windowsHide: true,
|
|
795
838
|
maxBuffer: 32 * 1024 * 1024,
|
|
@@ -798,7 +841,7 @@ function callHookViaSpawn(event: string, payload: Record<string, unknown>): Reco
|
|
|
798
841
|
: spawnSync('token-goat hook ' + event, {
|
|
799
842
|
input: JSON.stringify(payload),
|
|
800
843
|
encoding: "utf8",
|
|
801
|
-
timeout:
|
|
844
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
802
845
|
killSignal: "SIGKILL",
|
|
803
846
|
shell: true,
|
|
804
847
|
windowsHide: true,
|
|
@@ -817,9 +860,8 @@ function callHookViaSpawn(event: string, payload: Record<string, unknown>): Reco
|
|
|
817
860
|
// Tries the in-process hook call first (resolveRelayInProcess above), which avoids
|
|
818
861
|
// spawning a second node process altogether for every single tool call in this
|
|
819
862
|
// long-lived agent process. Falls back to callHookViaSpawn (the original
|
|
820
|
-
// spawnSync-based path, now
|
|
821
|
-
// its own fail-open null rather than
|
|
822
|
-
// budget, ~5000ms) when the in-process path is unavailable or throws.
|
|
863
|
+
// spawnSync-based path, now bounded by SHIM_FALLBACK_TIMEOUT_MS and a killSignal so token-goat degrades to
|
|
864
|
+
// its own fail-open null rather than hanging pi, which applies no hook timeout of its own) when the in-process path is unavailable or throws.
|
|
823
865
|
async function callHook(event: string, payload: Record<string, unknown>): Promise<Record<string, unknown> | null> {
|
|
824
866
|
// Timed from here, not from process start: this host lives far longer than one call, so the hook library's default clock (process age) would record the host's uptime as this call's duration.
|
|
825
867
|
const start = performance.now();
|
|
@@ -1184,6 +1226,8 @@ async function resolveRelayInProcess() {
|
|
|
1184
1226
|
}
|
|
1185
1227
|
}
|
|
1186
1228
|
|
|
1229
|
+
${shimFallbackTimeoutConst("relay")}
|
|
1230
|
+
|
|
1187
1231
|
function callHookViaSpawn(event, payload) {
|
|
1188
1232
|
try {
|
|
1189
1233
|
// Invoking "token-goat" as a bare command here depends on PATH resolution
|
|
@@ -1199,7 +1243,7 @@ function callHookViaSpawn(event, payload) {
|
|
|
1199
1243
|
? spawnSync(process.execPath, [entryPath, "hook", event], {
|
|
1200
1244
|
input: JSON.stringify(payload),
|
|
1201
1245
|
encoding: "utf8",
|
|
1202
|
-
timeout:
|
|
1246
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
1203
1247
|
killSignal: "SIGKILL",
|
|
1204
1248
|
windowsHide: true,
|
|
1205
1249
|
maxBuffer: 32 * 1024 * 1024,
|
|
@@ -1207,7 +1251,7 @@ function callHookViaSpawn(event, payload) {
|
|
|
1207
1251
|
: spawnSync("token-goat hook " + event, {
|
|
1208
1252
|
input: JSON.stringify(payload),
|
|
1209
1253
|
encoding: "utf8",
|
|
1210
|
-
timeout:
|
|
1254
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
1211
1255
|
killSignal: "SIGKILL",
|
|
1212
1256
|
shell: true,
|
|
1213
1257
|
windowsHide: true,
|
|
@@ -1225,9 +1269,9 @@ function callHookViaSpawn(event, payload) {
|
|
|
1225
1269
|
// Tries the in-process hook call first (resolveRelayInProcess above), which avoids
|
|
1226
1270
|
// spawning a second node process altogether for every single tool call in this
|
|
1227
1271
|
// long-lived host process. Falls back to callHookViaSpawn (the original
|
|
1228
|
-
// spawnSync-based path, now
|
|
1272
|
+
// spawnSync-based path, now bounded by SHIM_FALLBACK_TIMEOUT_MS and a killSignal so token-goat degrades to
|
|
1229
1273
|
// its own fail-open null rather than being force-killed by the host's own hook timeout
|
|
1230
|
-
// budget,
|
|
1274
|
+
// budget, 15000ms for OpenClaw's before_tool_call) when the in-process path is unavailable or throws.
|
|
1231
1275
|
async function callHook(event, payload) {
|
|
1232
1276
|
// Timed from here, not from process start: this host lives far longer than one call, so the hook library's default clock (process age) would record the host's uptime as this call's duration.
|
|
1233
1277
|
const start = performance.now();
|
|
@@ -1341,8 +1385,6 @@ function extractUpdatedInput(resp) {
|
|
|
1341
1385
|
return hso && typeof hso === "object" ? hso.updatedInput : undefined
|
|
1342
1386
|
}
|
|
1343
1387
|
|
|
1344
|
-
${MATERIALIZE_SHRUNK_IMAGE_JS}
|
|
1345
|
-
|
|
1346
1388
|
export const TokenGoatPlugin = async ({ directory }) => {
|
|
1347
1389
|
return {
|
|
1348
1390
|
"tool.execute.before": async (input, output) => {
|
|
@@ -1367,14 +1409,6 @@ export const TokenGoatPlugin = async ({ directory }) => {
|
|
|
1367
1409
|
for (const [tgKey, val] of Object.entries(updated)) {
|
|
1368
1410
|
output.args[rev[tgKey] ?? tgKey] = val
|
|
1369
1411
|
}
|
|
1370
|
-
return
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
// Image shrink has no context channel here -- translate it into a
|
|
1374
|
-
// rewritten filePath pointing at a materialized shrunk copy instead.
|
|
1375
|
-
if (tg === "Read") {
|
|
1376
|
-
const shrunkPath = materializeShrunkImage(extractContext(resp))
|
|
1377
|
-
if (shrunkPath) output.args.filePath = shrunkPath
|
|
1378
1412
|
}
|
|
1379
1413
|
},
|
|
1380
1414
|
|
|
@@ -1692,7 +1726,7 @@ function openclawEntrySidecarPath() {
|
|
|
1692
1726
|
}
|
|
1693
1727
|
function readOpenclawConfig(p, opts = {}) {
|
|
1694
1728
|
const refuse = (problem, detail) => new OpenclawConfigParseError(
|
|
1695
|
-
(opts.command === "uninstall" ? `OpenClaw config file
|
|
1729
|
+
(opts.command === "uninstall" ? `OpenClaw config file ${echoedValue(p)} is unreadable: it ${problem}. Uninstall left it untouched, along with the plugin it may still load, the plugin's entry sidecar and the file's backups; fix the file and run uninstall again.` : `OpenClaw config file ${echoedValue(p)} ${problem}. Fix or back up the file before running install.`) + (detail === void 0 ? "" : ` (${detail})`)
|
|
1696
1730
|
);
|
|
1697
1731
|
let raw;
|
|
1698
1732
|
try {
|
|
@@ -1855,6 +1889,7 @@ ${SHIM_TRY_SERVER}
|
|
|
1855
1889
|
${SHIM_TRY_IN_PROCESS}
|
|
1856
1890
|
|
|
1857
1891
|
${SHIM_MAX_BUFFER_CONST}
|
|
1892
|
+
${shimFallbackTimeoutConst("grok")}
|
|
1858
1893
|
|
|
1859
1894
|
async function main() {
|
|
1860
1895
|
const eventName = process.argv[2] || ''
|
|
@@ -1887,7 +1922,7 @@ ${SHIM_SPAWN_LADDER}
|
|
|
1887
1922
|
input,
|
|
1888
1923
|
encoding: 'utf8',
|
|
1889
1924
|
shell: true,
|
|
1890
|
-
timeout:
|
|
1925
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
1891
1926
|
killSignal: 'SIGKILL',
|
|
1892
1927
|
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
1893
1928
|
})
|
|
@@ -2000,7 +2035,7 @@ function installGrokFiles() {
|
|
|
2000
2035
|
for (const command of Object.values(config.hooks).flatMap((groups) => (groups ?? []).flatMap((g) => g.hooks.map((h) => h.command)))) {
|
|
2001
2036
|
const span = hookCommandRewrittenSpan(command, "grok");
|
|
2002
2037
|
if (span !== void 0) {
|
|
2003
|
-
throw new HookCommandRewriteError(`Grok CLI substitutes ${span} in a hook command with the value of that environment variable when it loads the hook, whatever the quoting, and refuses to run the hook when it is unset, so the hook command token-goat would write (${command}) would not run token-goat. Nothing was written. Move the path that contains
|
|
2038
|
+
throw new HookCommandRewriteError(`Grok CLI substitutes ${span} in a hook command with the value of that environment variable when it loads the hook, whatever the quoting, and refuses to run the hook when it is unset, so the hook command token-goat would write (${command}) would not run token-goat. Nothing was written. Move the path that contains ${echoedValue(span)} (the home directory, token-goat's install directory, or Node's) to one without a $ name, then run \`token-goat install --grok\` again.`);
|
|
2004
2039
|
}
|
|
2005
2040
|
}
|
|
2006
2041
|
const forwarderChanged = writeIfDifferent(grokLegacyHookScriptPath(), legacyShimForwarder("{}"));
|
|
@@ -2074,7 +2109,7 @@ function shimPath() {
|
|
|
2074
2109
|
}
|
|
2075
2110
|
function readJsonObject(p, label, opts = {}) {
|
|
2076
2111
|
const refuse = (problem, detail) => new AntigravitySettingsParseError(
|
|
2077
|
-
(opts.command === "uninstall" ? `Antigravity ${label}
|
|
2112
|
+
(opts.command === "uninstall" ? `Antigravity ${label} ${echoedValue(p)} is unreadable: it ${problem}. Uninstall left it and its backups untouched; fix the file and run uninstall again.` : `Antigravity ${label} ${echoedValue(p)} ${problem}. Fix or back up the file before running install.`) + (detail === void 0 ? "" : ` (${detail})`)
|
|
2078
2113
|
);
|
|
2079
2114
|
let raw;
|
|
2080
2115
|
try {
|
|
@@ -2193,6 +2228,692 @@ function isAntigravityInstalled() {
|
|
|
2193
2228
|
return readShim()?.includes(SHIM_MARKER) === true;
|
|
2194
2229
|
}
|
|
2195
2230
|
|
|
2231
|
+
// src/bridges/vscode_install.ts
|
|
2232
|
+
init_define_import_meta_env();
|
|
2233
|
+
import * as fs10 from "node:fs";
|
|
2234
|
+
import * as os9 from "node:os";
|
|
2235
|
+
import * as path10 from "node:path";
|
|
2236
|
+
var VSCODE_GUIDANCE_BEGIN = "<!-- token-goat-vscode-begin -->";
|
|
2237
|
+
var VSCODE_GUIDANCE_END = "<!-- token-goat-vscode-end -->";
|
|
2238
|
+
var BEGIN = VSCODE_GUIDANCE_BEGIN;
|
|
2239
|
+
var END = VSCODE_GUIDANCE_END;
|
|
2240
|
+
function vscodeUserConfigDir() {
|
|
2241
|
+
if (process.platform === "win32") {
|
|
2242
|
+
const appData = process.env["APPDATA"];
|
|
2243
|
+
const base = appData !== void 0 && appData.trim() !== "" ? appData : path10.join(os9.homedir(), "AppData", "Roaming");
|
|
2244
|
+
return path10.join(base, "Code", "User");
|
|
2245
|
+
}
|
|
2246
|
+
if (process.platform === "darwin") {
|
|
2247
|
+
return path10.join(os9.homedir(), "Library", "Application Support", "Code", "User");
|
|
2248
|
+
}
|
|
2249
|
+
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
2250
|
+
return path10.join(xdg !== void 0 && path10.isAbsolute(xdg) ? xdg : path10.join(os9.homedir(), ".config"), "Code", "User");
|
|
2251
|
+
}
|
|
2252
|
+
function vscodeUserMcpPath() {
|
|
2253
|
+
return path10.join(vscodeUserConfigDir(), "mcp.json");
|
|
2254
|
+
}
|
|
2255
|
+
function vscodeProjectMcpPath(projectRoot = process.cwd()) {
|
|
2256
|
+
return path10.join(path10.resolve(projectRoot), ".vscode", "mcp.json");
|
|
2257
|
+
}
|
|
2258
|
+
function vscodeMcpPath(opts = {}) {
|
|
2259
|
+
return opts.project === true ? vscodeProjectMcpPath(opts.projectRoot) : vscodeUserMcpPath();
|
|
2260
|
+
}
|
|
2261
|
+
function otherScopeMcpPath(opts) {
|
|
2262
|
+
return opts.project === true ? vscodeUserMcpPath() : vscodeProjectMcpPath(opts.projectRoot);
|
|
2263
|
+
}
|
|
2264
|
+
function vscodeInstructionsPath(opts = {}) {
|
|
2265
|
+
return opts.project === true ? path10.join(path10.resolve(opts.projectRoot ?? process.cwd()), ".github", "copilot-instructions.md") : path10.join(os9.homedir(), ".copilot", "instructions", "token-goat.instructions.md");
|
|
2266
|
+
}
|
|
2267
|
+
var USER_INSTRUCTIONS_FRONTMATTER = "---\ndescription: 'token-goat: when to use its MCP tools and CLI instead of reading whole files'\napplyTo: '**'\n---\n";
|
|
2268
|
+
function vscodeHooksDir(opts = {}) {
|
|
2269
|
+
return opts.project === true ? path10.join(canonicalProjectRoot(path10.resolve(opts.projectRoot ?? process.cwd())), ".github", "hooks") : path10.join(os9.homedir(), ".copilot", "hooks");
|
|
2270
|
+
}
|
|
2271
|
+
function vscodeScopeFromFlags(flags) {
|
|
2272
|
+
return { project: flags.user !== true };
|
|
2273
|
+
}
|
|
2274
|
+
function vscodeHooksInstalled(opts = {}) {
|
|
2275
|
+
return readCopilotHooksOwners(vscodeHooksDir(opts)).has("vscode");
|
|
2276
|
+
}
|
|
2277
|
+
var CLAUDE_HOOKS_SETTING = "chat.useClaudeHooks";
|
|
2278
|
+
function vscodeUserSettingsPath() {
|
|
2279
|
+
return path10.join(vscodeUserConfigDir(), "settings.json");
|
|
2280
|
+
}
|
|
2281
|
+
function vscodeUsesClaudeHooks(settingsPath = vscodeUserSettingsPath()) {
|
|
2282
|
+
return readSettingsObject(settingsPath)?.[CLAUDE_HOOKS_SETTING] === true;
|
|
2283
|
+
}
|
|
2284
|
+
function vscodeRunsTokenGoatClaudeHooks(settingsPath = vscodeUserSettingsPath()) {
|
|
2285
|
+
return vscodeUsesClaudeHooks(settingsPath) && claudeHooksInstalledAnyScope();
|
|
2286
|
+
}
|
|
2287
|
+
function readSettingsObject(settingsPath) {
|
|
2288
|
+
try {
|
|
2289
|
+
const prev = parseJsonOrJsonc(stripBom(fs10.readFileSync(settingsPath, "utf8")), { allowTrailingComma: true });
|
|
2290
|
+
return prev === null || typeof prev !== "object" || Array.isArray(prev) ? null : prev;
|
|
2291
|
+
} catch {
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
function disableVscodeClaudeHooks(settingsPath = vscodeUserSettingsPath()) {
|
|
2296
|
+
const prev = readSettingsObject(settingsPath);
|
|
2297
|
+
if (prev?.[CLAUDE_HOOKS_SETTING] !== true) return false;
|
|
2298
|
+
withInstallScope(void 0, () => {
|
|
2299
|
+
writeSettingsKeepingComments(settingsPath, { ...prev, [CLAUDE_HOOKS_SETTING]: false }, { allowTrailingComma: true });
|
|
2300
|
+
});
|
|
2301
|
+
recordTurnedOffSetting(settingsPath, CLAUDE_HOOKS_SETTING);
|
|
2302
|
+
return true;
|
|
2303
|
+
}
|
|
2304
|
+
function restoreVscodeClaudeHooks(settingsPath = vscodeUserSettingsPath()) {
|
|
2305
|
+
if (!takeTurnedOffSetting(settingsPath, CLAUDE_HOOKS_SETTING)) return false;
|
|
2306
|
+
const current = readSettingsObject(settingsPath);
|
|
2307
|
+
if (current === null || current[CLAUDE_HOOKS_SETTING] !== false) return false;
|
|
2308
|
+
try {
|
|
2309
|
+
withInstallScope(void 0, () => {
|
|
2310
|
+
writeSettingsKeepingComments(settingsPath, { ...current, [CLAUDE_HOOKS_SETTING]: true }, { allowTrailingComma: true });
|
|
2311
|
+
});
|
|
2312
|
+
} catch (e) {
|
|
2313
|
+
recordTurnedOffSetting(settingsPath, CLAUDE_HOOKS_SETTING);
|
|
2314
|
+
throw e;
|
|
2315
|
+
}
|
|
2316
|
+
return true;
|
|
2317
|
+
}
|
|
2318
|
+
function readConfig(filePath) {
|
|
2319
|
+
return readServersJson(filePath, "VS Code");
|
|
2320
|
+
}
|
|
2321
|
+
var updateConfig = setTokenGoatServer;
|
|
2322
|
+
function otherScopeHasManagedServer(opts) {
|
|
2323
|
+
const otherPath = otherScopeMcpPath(opts);
|
|
2324
|
+
if (opts.project !== true && !projectPathIsConsultable(otherPath, path10.resolve(opts.projectRoot ?? process.cwd()))) return false;
|
|
2325
|
+
return hasManagedServer(otherPath, "VS Code");
|
|
2326
|
+
}
|
|
2327
|
+
function vscodeDecoderConfigured(opts = {}) {
|
|
2328
|
+
const checkedPaths = [vscodeUserMcpPath()];
|
|
2329
|
+
if (opts.projectRoot !== void 0) {
|
|
2330
|
+
const projectPath = vscodeProjectMcpPath(opts.projectRoot);
|
|
2331
|
+
if (projectPathIsConsultable(projectPath, path10.resolve(opts.projectRoot))) checkedPaths.push(projectPath);
|
|
2332
|
+
}
|
|
2333
|
+
return { configured: checkedPaths.some((candidate) => hasManagedServer(candidate, "VS Code")), checkedPaths };
|
|
2334
|
+
}
|
|
2335
|
+
function writeGuidance(filePath, userScope) {
|
|
2336
|
+
let created = false;
|
|
2337
|
+
if (userScope && !fs10.existsSync(filePath)) {
|
|
2338
|
+
ensureDirSync(path10.dirname(filePath));
|
|
2339
|
+
atomicWriteText(filePath, USER_INSTRUCTIONS_FRONTMATTER);
|
|
2340
|
+
created = true;
|
|
2341
|
+
}
|
|
2342
|
+
const body = [
|
|
2343
|
+
BEGIN,
|
|
2344
|
+
buildGuidanceBody("VS Code\u2019s supported MCP integration and its built-in file-read tools", { gdrive: loadConfig().gdrive?.enabled ?? false }),
|
|
2345
|
+
"",
|
|
2346
|
+
"**Compressed payloads:** a message containing a token-goat payload block (recognizable by a `recovery: token-goat retrieve <id>` line) is compressed text, not an answer. Call the MCP tool `retrieve_text` with that id to recover the original text, then answer the question the message asks using the recovered text. Never present the raw payload to the user as the response; if the `retrieve_text` tool is unavailable (the MCP server is not running, or the chat is not in Agent mode), say so plainly and ask the user to switch to Agent mode or run `token-goat install --vscode`.",
|
|
2347
|
+
"",
|
|
2348
|
+
"VS Code support: token-goat install --vscode configures a stdio MCP server under the servers root key in your user-profile mcp.json by default (add --project for the workspace .vscode/mcp.json instead), and agent hooks that see VS Code\u2019s built-in tool calls. The hooks can deny a repeated read, add a hint, and shrink an image before view_image loads it; they cannot fold or trim what a built-in read returns, and they leave terminal commands unchanged.",
|
|
2349
|
+
END
|
|
2350
|
+
].join("\n");
|
|
2351
|
+
return upsertDelimitedBlock(filePath, BEGIN, END, body) || created;
|
|
2352
|
+
}
|
|
2353
|
+
function assertProjectTargetsAreInTheProject(opts) {
|
|
2354
|
+
if (opts.project !== true) return;
|
|
2355
|
+
const root = path10.resolve(opts.projectRoot ?? process.cwd());
|
|
2356
|
+
for (const target of [vscodeMcpPath(opts), vscodeInstructionsPath(opts), ...copilotHooksFilePaths(vscodeHooksDir(opts))]) {
|
|
2357
|
+
assertProjectScopeTarget(target, root);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
function installVscode(opts = {}) {
|
|
2361
|
+
return withInstallScope(projectScopeRoot(opts), () => installVscodeScoped(opts));
|
|
2362
|
+
}
|
|
2363
|
+
function installVscodeScoped(opts) {
|
|
2364
|
+
assertProjectTargetsAreInTheProject(opts);
|
|
2365
|
+
const scope = opts.project === true ? "project" : "user";
|
|
2366
|
+
const mcpPath = vscodeMcpPath(opts);
|
|
2367
|
+
const instructionsPath = vscodeInstructionsPath(opts);
|
|
2368
|
+
let migratedFromUserScope = false;
|
|
2369
|
+
if (scope === "project" && (otherScopeHasManagedServer(opts) || vscodeHooksInstalled())) {
|
|
2370
|
+
migratedFromUserScope = uninstallVscode({ keepBackups: true });
|
|
2371
|
+
} else if (otherScopeHasManagedServer(opts)) {
|
|
2372
|
+
const otherPath = otherScopeMcpPath(opts);
|
|
2373
|
+
throw new Error(
|
|
2374
|
+
`token-goat is already registered in VS Code project scope (${otherPath}). Installing into user scope too would register it twice and duplicate its tool schemas in this workspace. Run \`token-goat uninstall --vscode --project\` first if you want to move it, or drop --vscode from this run.`
|
|
2375
|
+
);
|
|
2376
|
+
}
|
|
2377
|
+
const config = readConfig(mcpPath);
|
|
2378
|
+
const existingServers = config.value["servers"];
|
|
2379
|
+
if (existingServers !== void 0 && (existingServers === null || typeof existingServers !== "object" || Array.isArray(existingServers))) {
|
|
2380
|
+
throw new Error(`malformed VS Code MCP JSON at ${mcpPath}: servers must be an object`);
|
|
2381
|
+
}
|
|
2382
|
+
const servers = existingServers ?? {};
|
|
2383
|
+
const current = servers["token-goat"];
|
|
2384
|
+
if (current !== void 0 && !isManagedServer(current)) {
|
|
2385
|
+
throw new Error(`VS Code MCP JSON at ${mcpPath} already has a non-token-goat-managed server named "token-goat"`);
|
|
2386
|
+
}
|
|
2387
|
+
const next = updateConfig(config.text, managedServer());
|
|
2388
|
+
const mcpExisted = fs10.existsSync(mcpPath);
|
|
2389
|
+
ensureDirSync(path10.dirname(mcpPath));
|
|
2390
|
+
if (config.text !== next) {
|
|
2391
|
+
backupFile(mcpPath);
|
|
2392
|
+
writeConfigText(mcpPath, next);
|
|
2393
|
+
noteRootKeyCreation(mcpPath, config);
|
|
2394
|
+
if (!mcpExisted) recordCreatedConfig(mcpPath);
|
|
2395
|
+
}
|
|
2396
|
+
const guidanceDir = path10.dirname(instructionsPath);
|
|
2397
|
+
const guidanceDirExisted = fs10.existsSync(guidanceDir);
|
|
2398
|
+
const guidanceExisted = fs10.existsSync(instructionsPath);
|
|
2399
|
+
const guidanceChanged = writeGuidance(instructionsPath, scope === "user");
|
|
2400
|
+
if (scope === "project") {
|
|
2401
|
+
if (!guidanceDirExisted && fs10.existsSync(guidanceDir)) recordCreatedConfig(guidanceDir);
|
|
2402
|
+
if (!guidanceExisted && fs10.existsSync(instructionsPath)) recordCreatedConfig(instructionsPath);
|
|
2403
|
+
}
|
|
2404
|
+
if (scope === "project") syncVisualStudioProjectGuidance(instructionsPath);
|
|
2405
|
+
const hooks = installCopilotHooksFile(vscodeHooksDir(opts), "vscode");
|
|
2406
|
+
let disabledClaudeHooks = false;
|
|
2407
|
+
if (scope === "user" && vscodeRunsTokenGoatClaudeHooks()) {
|
|
2408
|
+
disabledClaudeHooks = disableVscodeClaudeHooks();
|
|
2409
|
+
}
|
|
2410
|
+
return {
|
|
2411
|
+
mcpPath,
|
|
2412
|
+
instructionsPath,
|
|
2413
|
+
hooksConfigPath: hooks.configPath,
|
|
2414
|
+
alreadyInstalled: config.text === next && !guidanceChanged && !hooks.changed && !migratedFromUserScope && !disabledClaudeHooks,
|
|
2415
|
+
scope,
|
|
2416
|
+
migratedFromUserScope,
|
|
2417
|
+
disabledClaudeHooks
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
function uninstallVscode(opts = {}) {
|
|
2421
|
+
return withInstallScope(projectScopeRoot(opts), () => uninstallVscodeScoped(opts));
|
|
2422
|
+
}
|
|
2423
|
+
function uninstallVscodeScoped(opts) {
|
|
2424
|
+
assertProjectTargetsAreInTheProject(opts);
|
|
2425
|
+
const mcpPath = vscodeMcpPath(opts);
|
|
2426
|
+
let removed = false;
|
|
2427
|
+
if (fs10.existsSync(mcpPath)) {
|
|
2428
|
+
const config = readConfig(mcpPath);
|
|
2429
|
+
const servers = config.value["servers"];
|
|
2430
|
+
if (servers !== void 0 && (servers === null || typeof servers !== "object" || Array.isArray(servers))) {
|
|
2431
|
+
throw new Error(`malformed VS Code MCP JSON at ${mcpPath}: servers must be an object`);
|
|
2432
|
+
}
|
|
2433
|
+
if (servers && isManagedServer(servers["token-goat"])) {
|
|
2434
|
+
const next = dropEmptyServers(updateConfig(config.text, void 0), mcpPath);
|
|
2435
|
+
if (/^\s*\{\s*\}\s*$/.test(next) && takeCreatedConfig(mcpPath)) {
|
|
2436
|
+
removeFileInScope(mcpPath);
|
|
2437
|
+
if (opts.project === true) {
|
|
2438
|
+
removeEmptyDirInScope(path10.dirname(mcpPath));
|
|
2439
|
+
}
|
|
2440
|
+
} else {
|
|
2441
|
+
backupFile(mcpPath);
|
|
2442
|
+
writeConfigText(mcpPath, next);
|
|
2443
|
+
}
|
|
2444
|
+
if (opts.keepBackups !== true) removeCreatedBackups(mcpPath);
|
|
2445
|
+
removed = true;
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
const instructionsPath = vscodeInstructionsPath(opts);
|
|
2449
|
+
if (stripDelimitedBlock(instructionsPath, BEGIN, END, opts.keepBackups === true)) {
|
|
2450
|
+
removed = true;
|
|
2451
|
+
if (opts.project !== true && fs10.readFileSync(instructionsPath, "utf8").trim() === USER_INSTRUCTIONS_FRONTMATTER.trim()) removeFileInScope(instructionsPath);
|
|
2452
|
+
}
|
|
2453
|
+
if (opts.project === true) syncVisualStudioProjectGuidance(instructionsPath);
|
|
2454
|
+
if (releaseCopilotHooksFile(vscodeHooksDir(opts), "vscode", opts.keepBackups === true)) removed = true;
|
|
2455
|
+
if (opts.project === true) {
|
|
2456
|
+
removeCreatedIfEmpty(instructionsPath);
|
|
2457
|
+
removeCreatedIfEmpty(path10.dirname(instructionsPath));
|
|
2458
|
+
}
|
|
2459
|
+
return removed;
|
|
2460
|
+
}
|
|
2461
|
+
function cleanupDeprecatedVscodeProjectMcp(projectRoot) {
|
|
2462
|
+
const root = path10.resolve(projectRoot ?? process.cwd());
|
|
2463
|
+
return withInstallScope(root, () => {
|
|
2464
|
+
const projectMcp = path10.join(root, ".vscode", "mcp.json");
|
|
2465
|
+
if (!fs10.existsSync(projectMcp)) return false;
|
|
2466
|
+
const text = fs10.readFileSync(projectMcp, "utf8");
|
|
2467
|
+
if (isResidueServersJson(text) && takeCreatedConfig(projectMcp)) {
|
|
2468
|
+
removeFileInScope(projectMcp);
|
|
2469
|
+
removeEmptyDirInScope(path10.dirname(projectMcp));
|
|
2470
|
+
removeCreatedBackups(projectMcp);
|
|
2471
|
+
return true;
|
|
2472
|
+
}
|
|
2473
|
+
return false;
|
|
2474
|
+
});
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
// src/cli_doctor_platforms.ts
|
|
2478
|
+
init_define_import_meta_env();
|
|
2479
|
+
import * as fs13 from "node:fs";
|
|
2480
|
+
import * as path13 from "node:path";
|
|
2481
|
+
|
|
2482
|
+
// src/bridges/cursor_install.ts
|
|
2483
|
+
init_define_import_meta_env();
|
|
2484
|
+
import * as fs11 from "node:fs";
|
|
2485
|
+
import * as os10 from "node:os";
|
|
2486
|
+
import * as path11 from "node:path";
|
|
2487
|
+
var MCP_SERVERS_KEY = "mcpServers";
|
|
2488
|
+
var TOKEN_GOAT_ENTRY_KEY = "token-goat";
|
|
2489
|
+
function cursorUserConfigDir() {
|
|
2490
|
+
return path11.join(os10.homedir(), ".cursor");
|
|
2491
|
+
}
|
|
2492
|
+
function cursorMcpPath(opts = {}) {
|
|
2493
|
+
if (opts.project === true) {
|
|
2494
|
+
return path11.join(opts.projectRoot ?? process.cwd(), ".cursor", "mcp.json");
|
|
2495
|
+
}
|
|
2496
|
+
return path11.join(cursorUserConfigDir(), "mcp.json");
|
|
2497
|
+
}
|
|
2498
|
+
function cursorManagedServer() {
|
|
2499
|
+
return { command: process.execPath, args: [bundledCliPath(), "mcp-serve"] };
|
|
2500
|
+
}
|
|
2501
|
+
function isCursorManagedServer(value) {
|
|
2502
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2503
|
+
const entry = value;
|
|
2504
|
+
const args = entry["args"];
|
|
2505
|
+
return entry["command"] === process.execPath && Array.isArray(args) && args.length === 2 && typeof args[0] === "string" && path11.basename(args[0]).toLowerCase() === "token-goat.mjs" && args[1] === "mcp-serve";
|
|
2506
|
+
}
|
|
2507
|
+
function installCursor(opts = {}) {
|
|
2508
|
+
return withInstallScope(projectScopeRoot(opts), () => installCursorScoped(opts));
|
|
2509
|
+
}
|
|
2510
|
+
function installCursorScoped(opts) {
|
|
2511
|
+
const scope = opts.project === true ? "project" : "user";
|
|
2512
|
+
const mcpPath = cursorMcpPath(opts);
|
|
2513
|
+
const config = readServersJson(mcpPath, "Cursor");
|
|
2514
|
+
const current = serversOf(config, mcpPath, "Cursor", MCP_SERVERS_KEY)[TOKEN_GOAT_ENTRY_KEY];
|
|
2515
|
+
if (current !== void 0 && !isCursorManagedServer(current)) {
|
|
2516
|
+
throw new Error(`Cursor mcp.json already has a ${echoedValue(TOKEN_GOAT_ENTRY_KEY)} MCP server entry token-goat did not write, at ${mcpPath}; remove it manually first`);
|
|
2517
|
+
}
|
|
2518
|
+
const desired = cursorManagedServer();
|
|
2519
|
+
const nextText = setTokenGoatServer(config.text, desired, MCP_SERVERS_KEY);
|
|
2520
|
+
const alreadyInstalled = config.text === nextText;
|
|
2521
|
+
if (!alreadyInstalled) {
|
|
2522
|
+
recordCreatedBy([mcpPath], () => {
|
|
2523
|
+
ensureDirSync(path11.dirname(mcpPath));
|
|
2524
|
+
backupFile(mcpPath);
|
|
2525
|
+
writeConfigText(mcpPath, nextText);
|
|
2526
|
+
noteRootKeyCreation(mcpPath, config, MCP_SERVERS_KEY);
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
return { mcpPath, alreadyInstalled, scope };
|
|
2530
|
+
}
|
|
2531
|
+
function uninstallCursor(opts = {}) {
|
|
2532
|
+
return withInstallScope(projectScopeRoot(opts), () => uninstallCursorScoped(opts));
|
|
2533
|
+
}
|
|
2534
|
+
function uninstallCursorScoped(opts) {
|
|
2535
|
+
const mcpPath = cursorMcpPath(opts);
|
|
2536
|
+
if (!fs11.existsSync(mcpPath)) return false;
|
|
2537
|
+
const config = readServersJson(mcpPath, "Cursor");
|
|
2538
|
+
if (!isCursorManagedServer(serversOf(config, mcpPath, "Cursor", MCP_SERVERS_KEY)[TOKEN_GOAT_ENTRY_KEY])) return false;
|
|
2539
|
+
const next = dropEmptyServers(setTokenGoatServer(config.text, void 0, MCP_SERVERS_KEY), mcpPath, MCP_SERVERS_KEY);
|
|
2540
|
+
if (/^\s*\{\s*\}\s*$/.test(next) && takeCreatedConfig(mcpPath)) {
|
|
2541
|
+
removeFileInScope(mcpPath);
|
|
2542
|
+
} else {
|
|
2543
|
+
backupFile(mcpPath);
|
|
2544
|
+
writeConfigText(mcpPath, next);
|
|
2545
|
+
}
|
|
2546
|
+
removeCreatedBackups(mcpPath);
|
|
2547
|
+
removeCreatedTree([mcpPath]);
|
|
2548
|
+
return true;
|
|
2549
|
+
}
|
|
2550
|
+
function isCursorInstalled(opts = {}) {
|
|
2551
|
+
return hasManagedServer(cursorMcpPath(opts), "Cursor", MCP_SERVERS_KEY, isCursorManagedServer);
|
|
2552
|
+
}
|
|
2553
|
+
function cursorManagedEntry(mcpPath) {
|
|
2554
|
+
const entry = managedServerEntry(mcpPath, "Cursor", MCP_SERVERS_KEY, isCursorManagedServer);
|
|
2555
|
+
return entry === null ? null : { command: entry["command"] };
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// src/bridges/zed_install.ts
|
|
2559
|
+
init_define_import_meta_env();
|
|
2560
|
+
import * as fs12 from "node:fs";
|
|
2561
|
+
import * as os11 from "node:os";
|
|
2562
|
+
import * as path12 from "node:path";
|
|
2563
|
+
var CONTEXT_SERVERS_KEY = "context_servers";
|
|
2564
|
+
var TOKEN_GOAT_ENTRY_KEY2 = "token-goat";
|
|
2565
|
+
var ZED_TIMEOUT_MS = 3e4;
|
|
2566
|
+
function zedConfigDir() {
|
|
2567
|
+
if (process.platform === "win32") {
|
|
2568
|
+
const appData = process.env["APPDATA"];
|
|
2569
|
+
const base2 = appData !== void 0 && appData.trim() !== "" ? appData : path12.join(os11.homedir(), "AppData", "Roaming");
|
|
2570
|
+
return path12.join(base2, "Zed");
|
|
2571
|
+
}
|
|
2572
|
+
const xdgConfig = process.env["XDG_CONFIG_HOME"];
|
|
2573
|
+
const base = xdgConfig !== void 0 && xdgConfig.trim() !== "" ? xdgConfig : path12.join(os11.homedir(), ".config");
|
|
2574
|
+
return path12.join(base, "zed");
|
|
2575
|
+
}
|
|
2576
|
+
function zedSettingsPath() {
|
|
2577
|
+
return path12.join(zedConfigDir(), "settings.json");
|
|
2578
|
+
}
|
|
2579
|
+
function zedShimPath() {
|
|
2580
|
+
return path12.join(zedConfigDir(), process.platform === "win32" ? "token-goat-mcp.cmd" : "token-goat-mcp.sh");
|
|
2581
|
+
}
|
|
2582
|
+
function buildShimScript() {
|
|
2583
|
+
const nodePath = process.execPath;
|
|
2584
|
+
const cliPath = bundledCliPath();
|
|
2585
|
+
if (process.platform === "win32") {
|
|
2586
|
+
return `@echo off\r
|
|
2587
|
+
"${nodePath}" "${cliPath}" mcp-serve\r
|
|
2588
|
+
`;
|
|
2589
|
+
}
|
|
2590
|
+
return `#!/bin/sh
|
|
2591
|
+
exec "${nodePath}" "${cliPath}" mcp-serve
|
|
2592
|
+
`;
|
|
2593
|
+
}
|
|
2594
|
+
function zedManagedServer() {
|
|
2595
|
+
return { command: zedShimPath(), timeout: ZED_TIMEOUT_MS };
|
|
2596
|
+
}
|
|
2597
|
+
function isZedManagedServer(value) {
|
|
2598
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2599
|
+
const entry = value;
|
|
2600
|
+
return entry["command"] === zedShimPath() && typeof entry["timeout"] === "number";
|
|
2601
|
+
}
|
|
2602
|
+
function installZed() {
|
|
2603
|
+
return recordCreatedBy([zedSettingsPath(), zedShimPath()], installZedFiles);
|
|
2604
|
+
}
|
|
2605
|
+
function installZedFiles() {
|
|
2606
|
+
const settingsPath = zedSettingsPath();
|
|
2607
|
+
const shimPath2 = zedShimPath();
|
|
2608
|
+
const config = readServersJson(settingsPath, "Zed");
|
|
2609
|
+
const current = serversOf(config, settingsPath, "Zed", CONTEXT_SERVERS_KEY)[TOKEN_GOAT_ENTRY_KEY2];
|
|
2610
|
+
if (current !== void 0 && !isZedManagedServer(current)) {
|
|
2611
|
+
throw new Error(`Zed settings.json already has a ${echoedValue(TOKEN_GOAT_ENTRY_KEY2)} context server entry token-goat did not write, at ${settingsPath}; remove it manually first`);
|
|
2612
|
+
}
|
|
2613
|
+
const priorShim = snapshotShim(shimPath2);
|
|
2614
|
+
const shimChanged = writeIfDifferent(shimPath2, buildShimScript());
|
|
2615
|
+
if (process.platform !== "win32") fs12.chmodSync(shimPath2, 493);
|
|
2616
|
+
const desired = zedManagedServer();
|
|
2617
|
+
const alreadyInstalled = current !== void 0 && !shimChanged;
|
|
2618
|
+
if (!alreadyInstalled) {
|
|
2619
|
+
try {
|
|
2620
|
+
const nextText = setTokenGoatServer(config.text, desired, CONTEXT_SERVERS_KEY);
|
|
2621
|
+
backupFile(settingsPath);
|
|
2622
|
+
writeConfigText(settingsPath, nextText);
|
|
2623
|
+
} catch (err) {
|
|
2624
|
+
try {
|
|
2625
|
+
restoreShim(shimPath2, priorShim, shimChanged);
|
|
2626
|
+
} catch {
|
|
2627
|
+
}
|
|
2628
|
+
throw err;
|
|
2629
|
+
}
|
|
2630
|
+
noteRootKeyCreation(settingsPath, config, CONTEXT_SERVERS_KEY);
|
|
2631
|
+
}
|
|
2632
|
+
return { settingsPath, shimPath: shimPath2, alreadyInstalled };
|
|
2633
|
+
}
|
|
2634
|
+
function snapshotShim(shimPath2) {
|
|
2635
|
+
let mode;
|
|
2636
|
+
try {
|
|
2637
|
+
mode = fs12.statSync(shimPath2).mode & 511;
|
|
2638
|
+
} catch (err) {
|
|
2639
|
+
if (err.code === "ENOENT") return { existed: false, bytes: void 0, mode: void 0 };
|
|
2640
|
+
}
|
|
2641
|
+
let bytes;
|
|
2642
|
+
try {
|
|
2643
|
+
bytes = fs12.readFileSync(shimPath2);
|
|
2644
|
+
} catch {
|
|
2645
|
+
bytes = void 0;
|
|
2646
|
+
}
|
|
2647
|
+
return { existed: true, bytes, mode };
|
|
2648
|
+
}
|
|
2649
|
+
function restoreShim(shimPath2, prior, shimChanged) {
|
|
2650
|
+
if (!prior.existed) {
|
|
2651
|
+
fs12.rmSync(shimPath2, { force: true });
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
if (shimChanged && prior.bytes !== void 0) atomicWriteBytes(shimPath2, prior.bytes);
|
|
2655
|
+
if (prior.mode !== void 0 && process.platform !== "win32") fs12.chmodSync(shimPath2, prior.mode);
|
|
2656
|
+
}
|
|
2657
|
+
function uninstallZed() {
|
|
2658
|
+
const settingsPath = zedSettingsPath();
|
|
2659
|
+
const shimPath2 = zedShimPath();
|
|
2660
|
+
let entryRemoved = false;
|
|
2661
|
+
let shimRemoved;
|
|
2662
|
+
try {
|
|
2663
|
+
if (fs12.existsSync(settingsPath)) {
|
|
2664
|
+
const config = readServersJson(settingsPath, "Zed");
|
|
2665
|
+
if (isZedManagedServer(serversOf(config, settingsPath, "Zed", CONTEXT_SERVERS_KEY)[TOKEN_GOAT_ENTRY_KEY2])) {
|
|
2666
|
+
const next = dropEmptyServers(setTokenGoatServer(config.text, void 0, CONTEXT_SERVERS_KEY), settingsPath, CONTEXT_SERVERS_KEY);
|
|
2667
|
+
if (/^\s*\{\s*\}\s*$/.test(next) && takeCreatedConfig(settingsPath)) {
|
|
2668
|
+
fs12.rmSync(settingsPath, { force: true });
|
|
2669
|
+
} else {
|
|
2670
|
+
backupFile(settingsPath);
|
|
2671
|
+
writeConfigText(settingsPath, next);
|
|
2672
|
+
}
|
|
2673
|
+
removeCreatedBackups(settingsPath);
|
|
2674
|
+
entryRemoved = true;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
} finally {
|
|
2678
|
+
shimRemoved = fs12.existsSync(shimPath2);
|
|
2679
|
+
if (shimRemoved) fs12.rmSync(shimPath2, { force: true });
|
|
2680
|
+
removeCreatedTree([settingsPath, shimPath2]);
|
|
2681
|
+
}
|
|
2682
|
+
return entryRemoved || shimRemoved;
|
|
2683
|
+
}
|
|
2684
|
+
function isZedInstalled() {
|
|
2685
|
+
return hasManagedServer(zedSettingsPath(), "Zed", CONTEXT_SERVERS_KEY, isZedManagedServer);
|
|
2686
|
+
}
|
|
2687
|
+
function zedManagedEntry(settingsPath) {
|
|
2688
|
+
const entry = managedServerEntry(settingsPath, "Zed", CONTEXT_SERVERS_KEY, isZedManagedServer);
|
|
2689
|
+
return entry === null ? null : { command: entry["command"] };
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
// src/cli_doctor_platforms.ts
|
|
2693
|
+
function globalMcpConfigPath() {
|
|
2694
|
+
return copilotMcpConfigPath();
|
|
2695
|
+
}
|
|
2696
|
+
function checkGlobalMcpConfig(configPath = globalMcpConfigPath()) {
|
|
2697
|
+
if (!fs13.existsSync(configPath)) {
|
|
2698
|
+
return { name: "Global MCP configuration", status: "ok", message: `no global Copilot MCP configuration found at ${displaySafeText(configPath)}` };
|
|
2699
|
+
}
|
|
2700
|
+
let parsed;
|
|
2701
|
+
try {
|
|
2702
|
+
parsed = JSON.parse(stripBom(fs13.readFileSync(configPath, "utf8")));
|
|
2703
|
+
} catch {
|
|
2704
|
+
return {
|
|
2705
|
+
name: "Global MCP configuration",
|
|
2706
|
+
status: "warn",
|
|
2707
|
+
message: `could not read global Copilot MCP configuration at ${displaySafeText(configPath)}; unable to audit heavy launchers.`
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2711
|
+
return {
|
|
2712
|
+
name: "Global MCP configuration",
|
|
2713
|
+
status: "warn",
|
|
2714
|
+
message: `global Copilot MCP configuration at ${displaySafeText(configPath)} has an unsupported format; unable to audit heavy launchers.`
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
const configuredServers = parsed["mcpServers"];
|
|
2718
|
+
if (typeof configuredServers !== "object" || configuredServers === null || Array.isArray(configuredServers)) {
|
|
2719
|
+
return { name: "Global MCP configuration", status: "ok", message: `no global stdio MCP servers configured at ${displaySafeText(configPath)}` };
|
|
2720
|
+
}
|
|
2721
|
+
let chromeDevTools = 0;
|
|
2722
|
+
let playwright = 0;
|
|
2723
|
+
for (const server of Object.values(configuredServers)) {
|
|
2724
|
+
if (typeof server !== "object" || server === null || Array.isArray(server)) continue;
|
|
2725
|
+
const entry = server;
|
|
2726
|
+
const rawCommand = entry["command"];
|
|
2727
|
+
const command = typeof rawCommand === "string" ? rawCommand : "";
|
|
2728
|
+
const args = entry["args"];
|
|
2729
|
+
if (!/\bnpx(?:\.cmd)?\b/i.test(command) || !Array.isArray(args)) continue;
|
|
2730
|
+
const invocation = args.filter((arg) => typeof arg === "string").join(" ");
|
|
2731
|
+
if (/\bchrome-devtools-mcp\b/i.test(invocation)) chromeDevTools += 1;
|
|
2732
|
+
if (/@playwright[\\/]mcp\b/i.test(invocation)) playwright += 1;
|
|
2733
|
+
}
|
|
2734
|
+
if (chromeDevTools > 0 || playwright > 0) {
|
|
2735
|
+
const launchers = [];
|
|
2736
|
+
if (chromeDevTools > 0) launchers.push(`${chromeDevTools} Chrome DevTools MCP launcher${chromeDevTools === 1 ? "" : "s"}`);
|
|
2737
|
+
if (playwright > 0) launchers.push(`${playwright} Playwright MCP launcher${playwright === 1 ? "" : "s"}`);
|
|
2738
|
+
return {
|
|
2739
|
+
name: "Global MCP configuration",
|
|
2740
|
+
status: "warn",
|
|
2741
|
+
message: `${launchers.join(" and ")} configured at ${displaySafeText(configPath)}. Move heavy launchers to project scope or remove them when not actively needed.`
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
return { name: "Global MCP configuration", status: "ok", message: `no known heavy global MCP launchers configured at ${displaySafeText(configPath)}` };
|
|
2745
|
+
}
|
|
2746
|
+
var VSCODE_USER_SCOPE_MIGRATED_NOTE = "Moved the VS Code integration from user scope to this project. It used to live in ~/.copilot/hooks, where VS Code resolves its working directory to the FIRST folder of a multi-root workspace and nothing else, so read hints, image shrinking and edit interception were silently doing nothing for every other folder. The shared ~/.copilot/hooks files stay in place if `token-goat install --copilot` still needs them.";
|
|
2747
|
+
var VSCODE_PROJECT_SCOPE_COVERAGE_NOTE = "This covers this project only. Run `token-goat install --vscode` once in each project you want it in, or `token-goat install --vscode --user` for one install covering every project (single-root workspaces only \u2014 see below).";
|
|
2748
|
+
var VSCODE_USER_SCOPE_MULTIROOT_NOTE = "NOTE: a user-scope install works in every project, but VS Code runs it with the first folder of a multi-root workspace as its working directory, so it does nothing for the other folders. Use `token-goat install --vscode` (project scope, the default) in each folder that needs it.";
|
|
2749
|
+
function checkVscodeUserScopeHooks(userScope, projectScope) {
|
|
2750
|
+
if (!userScope) return null;
|
|
2751
|
+
return {
|
|
2752
|
+
name: "VS Code hooks scope",
|
|
2753
|
+
status: "warn",
|
|
2754
|
+
message: projectScope ? "token-goat VS Code hooks are installed in BOTH ~/.copilot/hooks and this project's .github/hooks, and VS Code runs every hooks file it finds, so each hook fires twice. The user-scope copy is also pinned to the first folder of a multi-root workspace. Run `token-goat uninstall --vscode --user` to keep only the project install." : "token-goat VS Code hooks are installed in user scope (~/.copilot/hooks). VS Code runs them with the FIRST folder of a multi-root workspace as their working directory, so read hints, image shrinking and edit interception do nothing for any other folder. Run `token-goat install --vscode` in each project to move it to project scope."
|
|
2755
|
+
};
|
|
2756
|
+
}
|
|
2757
|
+
var VSCODE_DOUBLE_FIRE_NOTE = 'NOTE: VS Code has chat.useClaudeHooks turned on, so it also runs the token-goat hooks in ~/.claude/settings.json and each one fires twice. token-goat left it alone because it is a user-wide VS Code setting, and turning it off also stops every other Claude hook you have in VS Code. If you see hooks run twice, set "chat.useClaudeHooks": false in your VS Code user settings.';
|
|
2758
|
+
var VSCODE_CLAUDE_HOOKS_TURNED_OFF_TEXT = "Turned off chat.useClaudeHooks in your VS Code user settings so token-goat hooks do not run twice. That setting covers every Claude hook in VS Code, not only the token-goat ones. To turn it back on, set " + fencedCommand('"chat.useClaudeHooks": true') + " in VS Code settings, or run " + fencedCommand("token-goat uninstall --vscode --user") + " to put it back.";
|
|
2759
|
+
var VSCODE_CLAUDE_HOOKS_TURNED_OFF_NOTE = `NOTE: ${VSCODE_CLAUDE_HOOKS_TURNED_OFF_TEXT}`;
|
|
2760
|
+
var VSCODE_CLAUDE_HOOKS_RESTORED_NOTE = "Turned chat.useClaudeHooks back on in your VS Code user settings, as it was before token-goat turned it off.";
|
|
2761
|
+
function checkVscodeClaudeHooks(useClaudeHooks, claudeHooksInstalled, vscodeHooks) {
|
|
2762
|
+
if (!useClaudeHooks || !claudeHooksInstalled) return null;
|
|
2763
|
+
const vscodeHooksInstalled2 = vscodeHooks.user || vscodeHooks.project;
|
|
2764
|
+
if (vscodeHooks.project && !vscodeHooks.user) {
|
|
2765
|
+
return {
|
|
2766
|
+
name: "VS Code hooks",
|
|
2767
|
+
status: "ok",
|
|
2768
|
+
message: `VS Code has chat.useClaudeHooks on and token-goat hooks are in both ~/.claude/settings.json and this project's .github/hooks, so each hook fires twice in VS Code. token-goat leaves the setting alone for a project-scope install because it is user-wide and also stops every other Claude hook you have in VS Code. If you see hooks run twice, set "chat.useClaudeHooks": false in your VS Code user settings.`
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
return {
|
|
2772
|
+
name: "VS Code hooks",
|
|
2773
|
+
status: "warn",
|
|
2774
|
+
message: vscodeHooksInstalled2 ? "VS Code has chat.useClaudeHooks on and token-goat hooks are in both ~/.claude/settings.json and the Copilot hooks file, so each hook fires twice in VS Code. Turn chat.useClaudeHooks off in VS Code settings, or run `token-goat doctor --repair` to do it." : "VS Code has chat.useClaudeHooks on, so it runs the token-goat hooks from ~/.claude/settings.json in Claude Code wire format, which VS Code reads only in part. Run `token-goat install --vscode` and turn chat.useClaudeHooks off in VS Code settings."
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
function checkClaudeHookEvents(gaps) {
|
|
2778
|
+
const name = "Claude Code hook events";
|
|
2779
|
+
if (gaps.user === null && gaps.project === null) return null;
|
|
2780
|
+
const scopes = [
|
|
2781
|
+
{ scope: "user", events: gaps.user, run: "token-goat install" },
|
|
2782
|
+
{ scope: "project", events: gaps.project, run: "token-goat install --project" }
|
|
2783
|
+
];
|
|
2784
|
+
const missing = [];
|
|
2785
|
+
const outdated = [];
|
|
2786
|
+
const broken = [];
|
|
2787
|
+
for (const { scope, events, run } of scopes) {
|
|
2788
|
+
if (events === null) continue;
|
|
2789
|
+
if (events.broken.length > 0) broken.push(`${scope} scope wires ${events.broken.join(", ")} to a native hook client binary that no longer exists; run: ${run}`);
|
|
2790
|
+
if (events.missing.length > 0) missing.push(`${scope} scope lacks ${events.missing.join(", ")}; run: ${run}`);
|
|
2791
|
+
if (events.outdated.length > 0) outdated.push(`${scope} scope wires ${events.outdated.join(", ")} to a token-goat hook command this build no longer writes; run: ${run}`);
|
|
2792
|
+
}
|
|
2793
|
+
if (broken.length === 0 && missing.length === 0 && outdated.length === 0) return { name, status: "ok", message: "every event this build handles is wired" };
|
|
2794
|
+
const sentences = [];
|
|
2795
|
+
if (broken.length > 0) sentences.push(`${broken.join(". ")}. Claude Code cannot start those hooks, so each of those events reports a hook error until then`);
|
|
2796
|
+
if (missing.length > 0) sentences.push(`${missing.join(". ")}. Those events never reach token-goat until then`);
|
|
2797
|
+
if (outdated.length > 0) sentences.push(`${outdated.join(". ")}. Those events still reach token-goat through the older command until the install rewrites it`);
|
|
2798
|
+
return { name, status: broken.length > 0 ? "fail" : "warn", message: `${sentences.join(". ")}; restart any running session afterwards.` };
|
|
2799
|
+
}
|
|
2800
|
+
function dedupeByResolvedPath(paths) {
|
|
2801
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2802
|
+
return paths.filter((p) => {
|
|
2803
|
+
const key = normalizePath(path13.resolve(p)).toLowerCase();
|
|
2804
|
+
if (seen.has(key)) return false;
|
|
2805
|
+
seen.add(key);
|
|
2806
|
+
return true;
|
|
2807
|
+
});
|
|
2808
|
+
}
|
|
2809
|
+
function checkVisualStudio(mcpPaths, alsoReadPaths = []) {
|
|
2810
|
+
const found = dedupeByResolvedPath(mcpPaths).flatMap((mcpPath) => {
|
|
2811
|
+
const entry = visualStudioManagedEntry(mcpPath);
|
|
2812
|
+
return entry === null ? [] : [{ mcpPath, ...entry }];
|
|
2813
|
+
});
|
|
2814
|
+
if (found.length === 0) return null;
|
|
2815
|
+
const stale = found.find((e) => !fs13.existsSync(e.command) || !fs13.existsSync(e.bundlePath));
|
|
2816
|
+
if (stale !== void 0) {
|
|
2817
|
+
return {
|
|
2818
|
+
name: "Visual Studio",
|
|
2819
|
+
status: "warn",
|
|
2820
|
+
message: `the token-goat MCP entry in ${displaySafeText(stale.mcpPath)} points at ${displaySafeText(stale.command)} ${displaySafeText(stale.bundlePath)}, which no longer exists; run ${fencedCommand("token-goat uninstall --visualstudio")} and then ${fencedCommand("token-goat install --visualstudio")} again (add -p for the project entry).`
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
const registered = dedupeByResolvedPath([...found.map((e) => e.mcpPath), ...alsoReadPaths.filter((p) => visualStudioManagedEntry(p) !== null)]);
|
|
2824
|
+
if (registered.length > 1) {
|
|
2825
|
+
return {
|
|
2826
|
+
name: "Visual Studio",
|
|
2827
|
+
status: "warn",
|
|
2828
|
+
message: `Visual Studio reads ${registered.map(displaySafeText).join(" and ")}, and each registers token-goat, so it lists the token-goat server more than once. Keep one: ${fencedCommand("token-goat uninstall --vscode -p")} drops the .vscode/mcp.json entry, ${fencedCommand("token-goat uninstall --visualstudio")} (add -p for the project entry) drops a Visual Studio one.`
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
return {
|
|
2832
|
+
name: "Visual Studio",
|
|
2833
|
+
status: "ok",
|
|
2834
|
+
message: `MCP server registered in ${found.map((e) => displaySafeText(e.mcpPath)).join(", ")}. Visual Studio runs no token-goat hooks: it gets the MCP tools and instructions only, and only once the token-goat tools are ticked in the chat Tools picker.`
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
function checkZed(settingsPath) {
|
|
2838
|
+
const entry = zedManagedEntry(settingsPath);
|
|
2839
|
+
if (entry === null) return null;
|
|
2840
|
+
const shimPath2 = entry.command;
|
|
2841
|
+
if (!fs13.existsSync(shimPath2)) {
|
|
2842
|
+
return {
|
|
2843
|
+
name: "Zed",
|
|
2844
|
+
status: "warn",
|
|
2845
|
+
message: `the token-goat MCP entry in ${displaySafeText(settingsPath)} points at ${displaySafeText(shimPath2)}, which no longer exists; run \`token-goat uninstall --zed\` and then \`token-goat install --zed\` again.`
|
|
2846
|
+
};
|
|
2847
|
+
}
|
|
2848
|
+
const script = fs13.readFileSync(shimPath2, "utf8");
|
|
2849
|
+
const referencedPaths = [...script.matchAll(/"([^"]+)"/g)].map((m) => m[1]).filter((p) => typeof p === "string" && p.length > 0);
|
|
2850
|
+
const stalePath = referencedPaths.find((p) => !fs13.existsSync(p));
|
|
2851
|
+
if (stalePath !== void 0) {
|
|
2852
|
+
return {
|
|
2853
|
+
name: "Zed",
|
|
2854
|
+
status: "warn",
|
|
2855
|
+
message: `the token-goat shim at ${displaySafeText(shimPath2)} points at ${displaySafeText(stalePath)}, which no longer exists; run \`token-goat uninstall --zed\` and then \`token-goat install --zed\` again.`
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
return {
|
|
2859
|
+
name: "Zed",
|
|
2860
|
+
status: "ok",
|
|
2861
|
+
message: `MCP context server registered in ${displaySafeText(settingsPath)}. Zed runs no token-goat hooks: it gets the MCP tools only, and only once the token-goat server is enabled in Zed's Agent panel.`
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
function checkCursor(mcpPath, claudeHooksInstalled) {
|
|
2865
|
+
const entry = cursorManagedEntry(mcpPath);
|
|
2866
|
+
if (entry === null) return null;
|
|
2867
|
+
return {
|
|
2868
|
+
name: "Cursor",
|
|
2869
|
+
status: "ok",
|
|
2870
|
+
message: claudeHooksInstalled ? `MCP server registered in ${displaySafeText(mcpPath)}. Cursor also imports the token-goat hooks already installed in ~/.claude/settings.json by default, so hooks work in Cursor too with no separate hooks.json entry.` : `MCP server registered in ${displaySafeText(mcpPath)}. Cursor writes and runs no token-goat hooks here: it imports Claude Code hooks from ~/.claude/settings.json by default, but none are installed there yet -- run \`token-goat install\` to get hooks in Cursor too.`
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
function checkStrayClaudeMdBlocks(searchRoot) {
|
|
2874
|
+
const strays = findStrayClaudeMdBlocks(searchRoot);
|
|
2875
|
+
if (strays.length === 0) {
|
|
2876
|
+
return { name: "CLAUDE.md block", status: "ok", message: "no stray copies outside CLAUDE.md" };
|
|
2877
|
+
}
|
|
2878
|
+
return {
|
|
2879
|
+
name: "CLAUDE.md block",
|
|
2880
|
+
status: "warn",
|
|
2881
|
+
message: `${strays.length} stray cop${strays.length === 1 ? "y" : "ies"} outside CLAUDE.md (never refreshed by install, never removed by uninstall, will go stale): ${strays.join(", ")}`
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
function checkVscodeProjectMcp(projectRoot = process.cwd()) {
|
|
2885
|
+
const mcpPath = path13.join(path13.resolve(projectRoot), ".vscode", "mcp.json");
|
|
2886
|
+
if (!fs13.existsSync(mcpPath)) return null;
|
|
2887
|
+
if (hasManagedServer(mcpPath, "VS Code")) return null;
|
|
2888
|
+
try {
|
|
2889
|
+
const content = fs13.readFileSync(mcpPath, "utf8");
|
|
2890
|
+
if (isResidueServersJson(content)) {
|
|
2891
|
+
return {
|
|
2892
|
+
name: "VS Code project MCP",
|
|
2893
|
+
status: "warn",
|
|
2894
|
+
message: `empty residue file at ${displaySafeText(mcpPath)} triggers Copilot CLI deprecation warnings; delete it or run \`token-goat uninstall --vscode\``
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
return {
|
|
2898
|
+
name: "VS Code project MCP",
|
|
2899
|
+
status: "warn",
|
|
2900
|
+
message: `${displaySafeText(mcpPath)} is deprecated by Copilot CLI v1.0.84+; migrate configured servers to .mcp.json or .github/mcp.json (https://gh.io/copilotcli-mcpmigrate)`
|
|
2901
|
+
};
|
|
2902
|
+
} catch {
|
|
2903
|
+
return null;
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
function checkClaudeHooksGone(wired, activity) {
|
|
2907
|
+
if (wired || activity === null || activity.count === 0 || activity.lastTs === null) return null;
|
|
2908
|
+
if (activity.uninstalledTs !== null && activity.uninstalledTs >= activity.lastTs) return null;
|
|
2909
|
+
const last = new Date(activity.lastTs * 1e3).toISOString().replace("T", " ").slice(0, 16);
|
|
2910
|
+
return {
|
|
2911
|
+
name: "Claude Code hooks",
|
|
2912
|
+
status: "warn",
|
|
2913
|
+
message: `Claude Code ran ${countNoun(activity.count, "token-goat hook")} in the last 7 days, the last at ${last} UTC, but no token-goat hook is wired in ~/.claude/settings.json or this project's .claude/settings.json now, and no uninstall was recorded. The file was probably overwritten or deleted. Run: token-goat install (or token-goat install --project), then restart any running session.`
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2196
2917
|
export {
|
|
2197
2918
|
installGemini,
|
|
2198
2919
|
uninstallGemini,
|
|
@@ -2224,5 +2945,41 @@ export {
|
|
|
2224
2945
|
isGrokInstalled,
|
|
2225
2946
|
installAntigravity,
|
|
2226
2947
|
uninstallAntigravity,
|
|
2227
|
-
isAntigravityInstalled
|
|
2948
|
+
isAntigravityInstalled,
|
|
2949
|
+
vscodeScopeFromFlags,
|
|
2950
|
+
vscodeHooksInstalled,
|
|
2951
|
+
vscodeUsesClaudeHooks,
|
|
2952
|
+
vscodeRunsTokenGoatClaudeHooks,
|
|
2953
|
+
disableVscodeClaudeHooks,
|
|
2954
|
+
restoreVscodeClaudeHooks,
|
|
2955
|
+
otherScopeHasManagedServer,
|
|
2956
|
+
vscodeDecoderConfigured,
|
|
2957
|
+
installVscode,
|
|
2958
|
+
uninstallVscode,
|
|
2959
|
+
cleanupDeprecatedVscodeProjectMcp,
|
|
2960
|
+
cursorMcpPath,
|
|
2961
|
+
installCursor,
|
|
2962
|
+
uninstallCursor,
|
|
2963
|
+
isCursorInstalled,
|
|
2964
|
+
zedSettingsPath,
|
|
2965
|
+
installZed,
|
|
2966
|
+
uninstallZed,
|
|
2967
|
+
isZedInstalled,
|
|
2968
|
+
checkGlobalMcpConfig,
|
|
2969
|
+
VSCODE_USER_SCOPE_MIGRATED_NOTE,
|
|
2970
|
+
VSCODE_PROJECT_SCOPE_COVERAGE_NOTE,
|
|
2971
|
+
VSCODE_USER_SCOPE_MULTIROOT_NOTE,
|
|
2972
|
+
checkVscodeUserScopeHooks,
|
|
2973
|
+
VSCODE_DOUBLE_FIRE_NOTE,
|
|
2974
|
+
VSCODE_CLAUDE_HOOKS_TURNED_OFF_TEXT,
|
|
2975
|
+
VSCODE_CLAUDE_HOOKS_TURNED_OFF_NOTE,
|
|
2976
|
+
VSCODE_CLAUDE_HOOKS_RESTORED_NOTE,
|
|
2977
|
+
checkVscodeClaudeHooks,
|
|
2978
|
+
checkClaudeHookEvents,
|
|
2979
|
+
checkVisualStudio,
|
|
2980
|
+
checkZed,
|
|
2981
|
+
checkCursor,
|
|
2982
|
+
checkStrayClaudeMdBlocks,
|
|
2983
|
+
checkVscodeProjectMcp,
|
|
2984
|
+
checkClaudeHooksGone
|
|
2228
2985
|
};
|