@pushary/agent-hooks 1.9.66 → 1.9.67
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/CHANGELOG.md +9 -0
- package/data/SKILL.md +1 -1
- package/data/cursor-plugin/skills/pushary/SKILL.md +1 -1
- package/data/vscode-plugin/skills/pushary/SKILL.md +1 -1
- package/dist/{api-GDSVUTI2.js → api-3WIPD5GC.js} +10 -10
- package/dist/bin/pushary-bell-hook.js +25 -25
- package/dist/bin/pushary-bell.js +10 -10
- package/dist/bin/pushary-claude.js +26 -26
- package/dist/bin/pushary-clean.js +73 -74
- package/dist/bin/pushary-codex-bridge.js +21 -21
- package/dist/bin/pushary-codex-hook.js +30 -30
- package/dist/bin/pushary-codex-session.js +8 -8
- package/dist/bin/pushary-codex.js +13 -13
- package/dist/bin/pushary-connect.js +28 -27
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +40 -39
- package/dist/bin/pushary-disconnect.js +25 -25
- package/dist/bin/pushary-doctor.js +115 -71
- package/dist/bin/pushary-elicitation-hook.js +25 -25
- package/dist/bin/pushary-gemini-bridge.js +15 -15
- package/dist/bin/pushary-gemini-hook.js +27 -27
- package/dist/bin/pushary-hook.js +29 -29
- package/dist/bin/pushary-login.js +20 -19
- package/dist/bin/pushary-logout.js +26 -27
- package/dist/bin/pushary-mcp.js +11 -11
- package/dist/bin/pushary-mode.js +16 -15
- package/dist/bin/pushary-notification-hook.js +24 -24
- package/dist/bin/pushary-opencode-hook.js +26 -26
- package/dist/bin/pushary-permission-denied-hook.js +29 -29
- package/dist/bin/pushary-permission-hook.js +29 -29
- package/dist/bin/pushary-post-hook.js +24 -24
- package/dist/bin/pushary-prompt-hook.js +24 -24
- package/dist/bin/pushary-session-end-hook.js +26 -26
- package/dist/bin/pushary-session-start-hook.js +34 -33
- package/dist/bin/pushary-setup.js +107 -93
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +37 -35
- package/dist/bin/pushary-stop-hook.js +24 -24
- package/dist/bin/pushary-stopfailure-hook.js +24 -24
- package/dist/bin/pushary-suggestions.js +15 -14
- package/dist/bin/pushary-transcript-register.js +16 -16
- package/dist/bin/pushary-transcripts.js +18 -17
- package/dist/bin/pushary-upgrade.js +45 -38
- package/dist/bin/pushary-wait.js +26 -20
- package/dist/bin/pushary.js +7 -7
- package/dist/{chunk-A47UKPC7.js → chunk-32OSSYGZ.js} +1 -1
- package/dist/{chunk-QR2QWGQI.js → chunk-3THDNNVL.js} +3 -3
- package/dist/{chunk-DPU6H4JY.js → chunk-564IPPBL.js} +3 -3
- package/dist/chunk-5MOJCOCF.js +25 -0
- package/dist/{chunk-V4EOFOB2.js → chunk-6H4E5UBA.js} +6 -6
- package/dist/{chunk-DKMVVBXO.js → chunk-6LXXKMV6.js} +38 -10
- package/dist/chunk-76SWHCAR.js +299 -0
- package/dist/{chunk-G6VJZ3RB.js → chunk-77L2TNJW.js} +1 -1
- package/dist/{chunk-6BGVSV2Q.js → chunk-7YJSDXLM.js} +1 -1
- package/dist/{chunk-AWGBL4P3.js → chunk-B3V2WOJX.js} +89 -41
- package/dist/{chunk-RJ6JEHCP.js → chunk-BBFGDYYI.js} +1 -1
- package/dist/{chunk-YQDCTID4.js → chunk-BFF3ABFV.js} +4 -4
- package/dist/{chunk-R7C2FS33.js → chunk-C4EDAHXI.js} +1 -1
- package/dist/{chunk-YB3HMDKE.js → chunk-CHYBOTQN.js} +2 -2
- package/dist/{chunk-26W46E46.js → chunk-CJZLIAJF.js} +27 -10
- package/dist/{chunk-ZJV7SFIJ.js → chunk-CPLQ5OHX.js} +1 -1
- package/dist/{chunk-ZAZUDYSF.js → chunk-D3U6CN7T.js} +5 -5
- package/dist/{chunk-MNNQEUQC.js → chunk-D7Z4USSB.js} +27 -8
- package/dist/{chunk-V3YBVOTZ.js → chunk-DP6LU6BB.js} +2 -2
- package/dist/{chunk-32ZT2ATY.js → chunk-DXCHIKAN.js} +8 -8
- package/dist/{chunk-QN25WBXL.js → chunk-EQR6JSFE.js} +7 -7
- package/dist/{chunk-IQ3GKPFM.js → chunk-FONWVVDD.js} +1 -1
- package/dist/{chunk-XXIJBYNW.js → chunk-GMHQYX3D.js} +1 -1
- package/dist/{chunk-3EZP5ZPS.js → chunk-ICQCQAUO.js} +4 -4
- package/dist/{chunk-5J7OZBJ4.js → chunk-IPG3RS23.js} +2 -2
- package/dist/{chunk-4APD2DPI.js → chunk-IRV2MNMA.js} +53 -3
- package/dist/{chunk-MDIGG3Q7.js → chunk-IVB5O6NY.js} +3 -3
- package/dist/{chunk-VURDLVAQ.js → chunk-IXTVUINH.js} +3 -3
- package/dist/{chunk-HUGVJBDN.js → chunk-JBRMIADM.js} +2 -2
- package/dist/{chunk-IGO3AGZK.js → chunk-JHB5SR5B.js} +4 -4
- package/dist/{chunk-CNJ7SI5V.js → chunk-JOBSKQ2Z.js} +4 -4
- package/dist/{chunk-5FJDODOA.js → chunk-JOD2325S.js} +3 -3
- package/dist/{chunk-376TUN72.js → chunk-JSPCGPSH.js} +3 -3
- package/dist/{chunk-T6YLXO6N.js → chunk-KE4TSJUD.js} +4 -4
- package/dist/{chunk-TQNFDNJA.js → chunk-KIXRHNCW.js} +6 -6
- package/dist/{chunk-Q5RNYE2L.js → chunk-KNUYUQBP.js} +4 -4
- package/dist/{chunk-3FQX74E4.js → chunk-KTLE7NIK.js} +23 -8
- package/dist/{chunk-57NTMVMI.js → chunk-LJHDUKBB.js} +6 -6
- package/dist/{chunk-CN6JFM62.js → chunk-LS42NV6E.js} +15 -6
- package/dist/{chunk-6ZXCWK27.js → chunk-LYTWK5TF.js} +5 -5
- package/dist/{chunk-XIJTXRWB.js → chunk-METUT7V4.js} +2 -2
- package/dist/{chunk-7LK3Y6ET.js → chunk-NSS2AIGI.js} +6 -6
- package/dist/{chunk-XEXF2OFZ.js → chunk-NWN6GGJL.js} +2 -2
- package/dist/{chunk-GECU7V44.js → chunk-P4RXSZDT.js} +3 -3
- package/dist/{chunk-IA5VWIHV.js → chunk-PIBWX3EF.js} +2 -2
- package/dist/{chunk-GWYCKTXS.js → chunk-PO2P3XDC.js} +5 -5
- package/dist/{chunk-DIUTSF6I.js → chunk-R2BZH2NK.js} +4 -4
- package/dist/{chunk-6RJZSBPD.js → chunk-RO2B4YBF.js} +1 -1
- package/dist/{chunk-EJR5JXMV.js → chunk-RQOT6QL3.js} +43 -5
- package/dist/{chunk-SVKCYUYP.js → chunk-SB6TYV4M.js} +9 -9
- package/dist/{chunk-GDLT32I4.js → chunk-TI4JYPGQ.js} +6 -2
- package/dist/{chunk-B6JC676X.js → chunk-UC2J5RR4.js} +1 -1
- package/dist/{chunk-XDDSURY3.js → chunk-UQIPWXBL.js} +3 -3
- package/dist/{chunk-EU2ZOZC3.js → chunk-UXN73YAE.js} +3 -3
- package/dist/{chunk-RSZQEEEW.js → chunk-VEYFXRZ6.js} +7 -6
- package/dist/{chunk-XFQAK35L.js → chunk-VHMZPN3N.js} +1 -1
- package/dist/{chunk-45KMS76L.js → chunk-VKZ3RJ5U.js} +1 -1
- package/dist/chunk-VVIQWGS7.js +2298 -0
- package/dist/{chunk-KS3O6O7E.js → chunk-WJLHZ6E6.js} +3 -3
- package/dist/{chunk-GO7YKPT3.js → chunk-X2MTVD4W.js} +1 -1
- package/dist/{chunk-2BL6KWN7.js → chunk-XFDWLDHG.js} +9 -9
- package/dist/{chunk-3ELJIG2R.js → chunk-XNUWKKGS.js} +3 -3
- package/dist/{chunk-QOIPUYUD.js → chunk-XZKSUJ55.js} +1 -1
- package/dist/{chunk-JL3SZJJP.js → chunk-Y4MRHAE5.js} +2 -2
- package/dist/{chunk-DBUHSUL7.js → chunk-Y6ZN454E.js} +1 -1
- package/dist/{chunk-XGFFNERF.js → chunk-YSD4SMHE.js} +1 -1
- package/dist/{chunk-UTAARXYQ.js → chunk-ZK5WYAMI.js} +1 -1
- package/dist/{chunk-AVWECJSN.js → chunk-ZPIYJLQ3.js} +2 -2
- package/dist/{codex-app-cards-switch-W7OLKOMO.js → codex-app-cards-switch-25TDTMUZ.js} +11 -11
- package/dist/{codex-desktop-launch-U6V3AXZF.js → codex-desktop-launch-JDKUCD2Y.js} +1 -1
- package/dist/{codex-session-launch-EFU46WBL.js → codex-session-launch-6N35AAEO.js} +18 -18
- package/dist/{config-UC4LMCNX.js → config-OBGYURFX.js} +3 -3
- package/dist/{global-upgrade-ABF5O3HU.js → global-upgrade-2QNQD5OM.js} +10 -10
- package/dist/{identity-3ZUZPHN6.js → identity-CUPX2U7Q.js} +4 -4
- package/dist/{live-capabilities-BXWXJARK.js → live-capabilities-CUEIWMSM.js} +5 -5
- package/dist/{paths-FO76CFRQ.js → paths-BHLBZ6FY.js} +7 -3
- package/dist/{reapply-3T3DFUAI.js → reapply-4GXLWZ5M.js} +27 -26
- package/dist/src/index.js +28 -28
- package/dist/{upgrade-lock-EZ5JIPJE.js → upgrade-lock-RDKTAHWR.js} +4 -4
- package/dist/{usage-Y6BL7RUD.js → usage-KX2HN47E.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-QBNICXMC.js +0 -615
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureCodexTrustsPusharyHooks
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Y4MRHAE5.js";
|
|
4
|
+
import {
|
|
5
|
+
hermesReceiptPaths,
|
|
6
|
+
hermesSetupPredatesThePluginFolder,
|
|
7
|
+
installHermesPlugin,
|
|
8
|
+
refreshHermesPluginFiles,
|
|
9
|
+
shippedHermesPlugin
|
|
10
|
+
} from "./chunk-VVIQWGS7.js";
|
|
4
11
|
import {
|
|
5
12
|
fxIgnoresInstructionsFile,
|
|
6
13
|
fxInstructionsBody,
|
|
7
14
|
fxServerHolder,
|
|
8
15
|
macAppFxBridge,
|
|
9
16
|
refreshClineKey
|
|
10
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-LYTWK5TF.js";
|
|
11
18
|
import {
|
|
12
19
|
OPENCODE_HOOK_BINARY,
|
|
13
20
|
agentHookOwner,
|
|
@@ -19,16 +26,16 @@ import {
|
|
|
19
26
|
openCodePluginDir,
|
|
20
27
|
openCodeWired,
|
|
21
28
|
withOpenCodeAskRules
|
|
22
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-NSS2AIGI.js";
|
|
23
30
|
import {
|
|
24
31
|
detectClaudeVersion
|
|
25
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-IVB5O6NY.js";
|
|
26
33
|
import {
|
|
27
34
|
addCompatibleHooks,
|
|
28
35
|
compatibleConfigFile,
|
|
29
36
|
compatibleInstructionsFile,
|
|
30
37
|
hasCompatibleHooks
|
|
31
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-ICQCQAUO.js";
|
|
32
39
|
import {
|
|
33
40
|
GEMINI_HOOK_BINARY,
|
|
34
41
|
addGeminiHooks,
|
|
@@ -40,7 +47,7 @@ import {
|
|
|
40
47
|
geminiWired,
|
|
41
48
|
hasGeminiHooks,
|
|
42
49
|
planHooks
|
|
43
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-BFF3ABFV.js";
|
|
44
51
|
import {
|
|
45
52
|
CLAUDE_HOOK_BINARY,
|
|
46
53
|
CLAUDE_HOOK_EVENTS,
|
|
@@ -85,7 +92,7 @@ import {
|
|
|
85
92
|
renderAgentInstructions,
|
|
86
93
|
supportedClaudeEvents,
|
|
87
94
|
vscodePluginMcp
|
|
88
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-TI4JYPGQ.js";
|
|
89
96
|
import {
|
|
90
97
|
formatVersion,
|
|
91
98
|
readJsonSafe,
|
|
@@ -93,12 +100,12 @@ import {
|
|
|
93
100
|
resolveGlobalBinary,
|
|
94
101
|
writeFileAtomic,
|
|
95
102
|
writeJsonAtomic
|
|
96
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-WJLHZ6E6.js";
|
|
97
104
|
import {
|
|
98
105
|
HOOK_COMPATIBLE_PROFILES,
|
|
99
106
|
isCompatibleAgentType,
|
|
100
107
|
isRecord
|
|
101
|
-
} from "./chunk-
|
|
108
|
+
} from "./chunk-RQOT6QL3.js";
|
|
102
109
|
|
|
103
110
|
// src/reapply.ts
|
|
104
111
|
import { dirname, join } from "path";
|
|
@@ -428,7 +435,8 @@ var SETUP_CHOICE_BY_REAPPLY_ID = {
|
|
|
428
435
|
vscode: "vscode",
|
|
429
436
|
opencode: "opencode",
|
|
430
437
|
fx: "fx",
|
|
431
|
-
cline: "cline"
|
|
438
|
+
cline: "cline",
|
|
439
|
+
hermes: "hermes"
|
|
432
440
|
};
|
|
433
441
|
var setupChoiceOf = (id) => isCompatibleAgentType(id) ? id : SETUP_CHOICE_BY_REAPPLY_ID[id];
|
|
434
442
|
var reapplyHookOwner = (id, appAvailable) => agentHookOwner(setupChoiceOf(id), appAvailable);
|
|
@@ -437,6 +445,25 @@ var fxHeldByApp = (appAvailable) => appAvailable && fxHolder() === "app";
|
|
|
437
445
|
var reapplyHolder = (id, appAvailable) => (id === "fx" ? fxHeldByApp(appAvailable) : hooksHeldByApp(id, appAvailable)) ? "app" : "cli";
|
|
438
446
|
var KEPT_FOR_APP = { configured: true, reapplied: false, hooks: false, mcp: false };
|
|
439
447
|
var FAILED_REAPPLY = { configured: false, reapplied: false, hooks: false, mcp: false, failed: true };
|
|
448
|
+
var HERMES_PLUGIN_WRITTEN = { configured: true, reapplied: true, hooks: false, mcp: false, pluginFiles: true, hermesRestartRequired: true };
|
|
449
|
+
var HERMES_REFRESH_RESULTS = {
|
|
450
|
+
refreshed: HERMES_PLUGIN_WRITTEN,
|
|
451
|
+
current: { configured: true, reapplied: false, hooks: false, mcp: false },
|
|
452
|
+
not_ours: { configured: false, reapplied: false, hooks: false, mcp: false },
|
|
453
|
+
failed: FAILED_REAPPLY
|
|
454
|
+
};
|
|
455
|
+
var HERMES_MIGRATION_RESULTS = {
|
|
456
|
+
installed: HERMES_PLUGIN_WRITTEN,
|
|
457
|
+
no_launcher: { configured: false, reapplied: false, hooks: false, mcp: false },
|
|
458
|
+
failed: FAILED_REAPPLY
|
|
459
|
+
};
|
|
460
|
+
var reapplyHermesPlugin = () => {
|
|
461
|
+
const plugin = shippedHermesPlugin();
|
|
462
|
+
if (hermesSetupPredatesThePluginFolder()) {
|
|
463
|
+
return HERMES_MIGRATION_RESULTS[installHermesPlugin({ installedBy: "cli", plugin, receipts: hermesReceiptPaths() }).kind];
|
|
464
|
+
}
|
|
465
|
+
return HERMES_REFRESH_RESULTS[refreshHermesPluginFiles("cli", plugin)];
|
|
466
|
+
};
|
|
440
467
|
var noInstructionBlock = () => null;
|
|
441
468
|
var reapplyAllAgents = (apiKey, options = {}) => {
|
|
442
469
|
const appAvailable = (options.appAvailable ?? macAppBridgeAvailable)();
|
|
@@ -464,6 +491,7 @@ var reapplyAllAgents = (apiKey, options = {}) => {
|
|
|
464
491
|
},
|
|
465
492
|
{ id: "fx", label: "fx", run: inspectFxWiring, instructions: fxInstructionTarget },
|
|
466
493
|
{ id: "cline", label: CLINE_LABEL, run: () => reapplyClineSettings(apiKey), instructions: clineInstructionTarget },
|
|
494
|
+
{ id: "hermes", label: "Hermes", run: reapplyHermesPlugin, instructions: noInstructionBlock },
|
|
467
495
|
...HOOK_COMPATIBLE_PROFILES.map((profile) => ({
|
|
468
496
|
id: profile.agentType,
|
|
469
497
|
label: profile.label,
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import {
|
|
2
|
+
openCodeConfigDirFrom
|
|
3
|
+
} from "./chunk-NSS2AIGI.js";
|
|
4
|
+
import {
|
|
5
|
+
nativeClaudeInstallPath
|
|
6
|
+
} from "./chunk-BBFGDYYI.js";
|
|
7
|
+
import {
|
|
8
|
+
grokHome
|
|
9
|
+
} from "./chunk-ICQCQAUO.js";
|
|
10
|
+
import {
|
|
11
|
+
claudeConfigDir,
|
|
12
|
+
clineDataDirectory,
|
|
13
|
+
codexHomeFrom
|
|
14
|
+
} from "./chunk-TI4JYPGQ.js";
|
|
15
|
+
import {
|
|
16
|
+
HOOK_COMPATIBLE_PROFILES
|
|
17
|
+
} from "./chunk-RQOT6QL3.js";
|
|
18
|
+
|
|
19
|
+
// src/vscode-config.ts
|
|
20
|
+
import { homedir } from "os";
|
|
21
|
+
import { join } from "path";
|
|
22
|
+
var PLUGIN_LOCATIONS_KEY = "chat.pluginLocations";
|
|
23
|
+
var blankJsonComments = (text) => {
|
|
24
|
+
let out = "";
|
|
25
|
+
let i = 0;
|
|
26
|
+
let inString = false;
|
|
27
|
+
let escaped = false;
|
|
28
|
+
while (i < text.length) {
|
|
29
|
+
const ch = text[i];
|
|
30
|
+
if (inString) {
|
|
31
|
+
out += ch;
|
|
32
|
+
if (escaped) escaped = false;
|
|
33
|
+
else if (ch === "\\") escaped = true;
|
|
34
|
+
else if (ch === '"') inString = false;
|
|
35
|
+
i += 1;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (ch === '"') {
|
|
39
|
+
inString = true;
|
|
40
|
+
out += ch;
|
|
41
|
+
i += 1;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (ch === "/" && text[i + 1] === "/") {
|
|
45
|
+
while (i < text.length && text[i] !== "\n") {
|
|
46
|
+
out += " ";
|
|
47
|
+
i += 1;
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (ch === "/" && text[i + 1] === "*") {
|
|
52
|
+
const end = text.indexOf("*/", i + 2);
|
|
53
|
+
const stop = end === -1 ? text.length : end + 2;
|
|
54
|
+
while (i < stop) {
|
|
55
|
+
out += text[i] === "\n" ? "\n" : " ";
|
|
56
|
+
i += 1;
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
out += ch;
|
|
61
|
+
i += 1;
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
};
|
|
65
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
66
|
+
var parseJsonc = (text) => {
|
|
67
|
+
const blanked = blankJsonComments(text);
|
|
68
|
+
const withoutTrailingCommas = blanked.replace(/,(\s*[}\]])/g, "$1");
|
|
69
|
+
try {
|
|
70
|
+
return asRecord(JSON.parse(withoutTrailingCommas));
|
|
71
|
+
} catch {
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var rootBraceIndex = (text) => blankJsonComments(text).indexOf("{");
|
|
76
|
+
var detectIndent = (text) => {
|
|
77
|
+
const match = /\n([ \t]+)"/.exec(text);
|
|
78
|
+
return match?.[1] ?? " ";
|
|
79
|
+
};
|
|
80
|
+
var freshDocument = (pluginDir, indent = " ") => `${JSON.stringify({ [PLUGIN_LOCATIONS_KEY]: { [pluginDir]: true } }, null, indent)}
|
|
81
|
+
`;
|
|
82
|
+
var registerPluginLocation = (existing, pluginDir) => {
|
|
83
|
+
if (existing === null || existing.trim() === "") {
|
|
84
|
+
return { kind: "created", content: freshDocument(pluginDir) };
|
|
85
|
+
}
|
|
86
|
+
const indent = detectIndent(existing);
|
|
87
|
+
try {
|
|
88
|
+
const parsed2 = asRecord(JSON.parse(existing));
|
|
89
|
+
if (parsed2) {
|
|
90
|
+
const locations2 = asRecord(parsed2[PLUGIN_LOCATIONS_KEY]) ?? {};
|
|
91
|
+
if (locations2[pluginDir] === true) return { kind: "already" };
|
|
92
|
+
parsed2[PLUGIN_LOCATIONS_KEY] = { ...locations2, [pluginDir]: true };
|
|
93
|
+
return { kind: "merged", content: `${JSON.stringify(parsed2, null, indent)}
|
|
94
|
+
` };
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
const parsed = parseJsonc(existing);
|
|
99
|
+
if (!parsed) return { kind: "manual" };
|
|
100
|
+
const locations = asRecord(parsed[PLUGIN_LOCATIONS_KEY]);
|
|
101
|
+
if (locations?.[pluginDir] === true) return { kind: "already" };
|
|
102
|
+
if (parsed[PLUGIN_LOCATIONS_KEY] !== void 0) return { kind: "manual" };
|
|
103
|
+
const brace = rootBraceIndex(existing);
|
|
104
|
+
if (brace === -1) return { kind: "manual" };
|
|
105
|
+
const needsComma = Object.keys(parsed).length > 0;
|
|
106
|
+
const line = `
|
|
107
|
+
${indent}${JSON.stringify(PLUGIN_LOCATIONS_KEY)}: { ${JSON.stringify(pluginDir)}: true }${needsComma ? "," : ""}`;
|
|
108
|
+
return {
|
|
109
|
+
kind: "inserted",
|
|
110
|
+
content: `${existing.slice(0, brace + 1)}${line}${existing.slice(brace + 1)}`
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
var unregisterPluginLocation = (existing, pluginDir) => {
|
|
114
|
+
if (existing === null || existing.trim() === "") return { kind: "absent" };
|
|
115
|
+
const indent = detectIndent(existing);
|
|
116
|
+
try {
|
|
117
|
+
const parsed2 = asRecord(JSON.parse(existing));
|
|
118
|
+
if (parsed2) {
|
|
119
|
+
const locations2 = asRecord(parsed2[PLUGIN_LOCATIONS_KEY]);
|
|
120
|
+
if (!locations2 || !(pluginDir in locations2)) return { kind: "absent" };
|
|
121
|
+
const remaining = { ...locations2 };
|
|
122
|
+
delete remaining[pluginDir];
|
|
123
|
+
if (Object.keys(remaining).length === 0) delete parsed2[PLUGIN_LOCATIONS_KEY];
|
|
124
|
+
else parsed2[PLUGIN_LOCATIONS_KEY] = remaining;
|
|
125
|
+
return { kind: "removed", content: `${JSON.stringify(parsed2, null, indent)}
|
|
126
|
+
` };
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
const parsed = parseJsonc(existing);
|
|
131
|
+
if (!parsed) return { kind: "manual" };
|
|
132
|
+
const locations = asRecord(parsed[PLUGIN_LOCATIONS_KEY]);
|
|
133
|
+
if (!locations || !(pluginDir in locations)) return { kind: "absent" };
|
|
134
|
+
const line = new RegExp(
|
|
135
|
+
`\\n[ \\t]*${escapeRegExp(JSON.stringify(PLUGIN_LOCATIONS_KEY))}\\s*:\\s*\\{\\s*${escapeRegExp(JSON.stringify(pluginDir))}\\s*:\\s*true\\s*\\},?`
|
|
136
|
+
);
|
|
137
|
+
if (!line.test(existing)) return { kind: "manual" };
|
|
138
|
+
return { kind: "removed", content: existing.replace(line, "") };
|
|
139
|
+
};
|
|
140
|
+
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
141
|
+
var isPluginRegistered = (existing, pluginDir) => {
|
|
142
|
+
if (!existing) return false;
|
|
143
|
+
const parsed = parseJsonc(existing);
|
|
144
|
+
return asRecord(parsed?.[PLUGIN_LOCATIONS_KEY])?.[pluginDir] === true;
|
|
145
|
+
};
|
|
146
|
+
var pluginLocationSnippet = (pluginDir) => `"${PLUGIN_LOCATIONS_KEY}": {
|
|
147
|
+
${JSON.stringify(pluginDir)}: true
|
|
148
|
+
}`;
|
|
149
|
+
var VSCODE_PRODUCTS = ["Code", "Code - Insiders"];
|
|
150
|
+
var settingsRoot = (home, platform) => {
|
|
151
|
+
if (platform === "darwin") return join(home, "Library", "Application Support");
|
|
152
|
+
if (platform === "win32") return process.env.APPDATA?.trim() || join(home, "AppData", "Roaming");
|
|
153
|
+
return process.env.XDG_CONFIG_HOME?.trim() || join(home, ".config");
|
|
154
|
+
};
|
|
155
|
+
var vscodeSettingsCandidates = (home = homedir(), platform = process.platform) => {
|
|
156
|
+
const root = settingsRoot(home, platform);
|
|
157
|
+
return VSCODE_PRODUCTS.map((product) => join(root, product, "User", "settings.json"));
|
|
158
|
+
};
|
|
159
|
+
var vscodeSettingsTargets = (exists, home, platform) => {
|
|
160
|
+
const candidates = vscodeSettingsCandidates(home, platform);
|
|
161
|
+
const present = candidates.filter((path) => exists(path));
|
|
162
|
+
return present.length > 0 ? present : candidates.slice(0, 1);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// src/setup/detect.ts
|
|
166
|
+
import { execSync } from "child_process";
|
|
167
|
+
import { existsSync } from "fs";
|
|
168
|
+
import { homedir as homedir2 } from "os";
|
|
169
|
+
import { dirname, join as join2 } from "path";
|
|
170
|
+
var whichCommand = () => process.platform === "win32" ? "where" : "which";
|
|
171
|
+
var binaryOnPath = (binary) => {
|
|
172
|
+
try {
|
|
173
|
+
execSync(`${whichCommand()} ${binary}`, { stdio: "ignore", timeout: 5e3 });
|
|
174
|
+
return true;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
var defaultDeps = { onPath: binaryOnPath, exists: existsSync };
|
|
180
|
+
var detectAgent = (probe, deps = defaultDeps) => {
|
|
181
|
+
const checked = [];
|
|
182
|
+
if (probe.binary) {
|
|
183
|
+
checked.push(`${whichCommand()} ${probe.binary}`);
|
|
184
|
+
if (deps.onPath(probe.binary)) {
|
|
185
|
+
return { kind: "installed", evidence: `${probe.binary} on PATH`, strong: true };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const path of probe.paths) {
|
|
189
|
+
checked.push(path);
|
|
190
|
+
if (deps.exists(path)) return { kind: "installed", evidence: path, strong: true };
|
|
191
|
+
}
|
|
192
|
+
for (const marker of probe.configMarkers) {
|
|
193
|
+
checked.push(marker);
|
|
194
|
+
if (deps.exists(marker)) return { kind: "installed", evidence: marker, strong: false };
|
|
195
|
+
}
|
|
196
|
+
if (probe.binary === null && probe.paths.length === 0 && probe.configMarkers.length === 0) {
|
|
197
|
+
return { kind: "unknown" };
|
|
198
|
+
}
|
|
199
|
+
return { kind: "not-found", checked };
|
|
200
|
+
};
|
|
201
|
+
var agentProbes = (home = homedir2(), platform = process.platform) => ({
|
|
202
|
+
claude_code: {
|
|
203
|
+
binary: "claude",
|
|
204
|
+
// The native installer puts it here, outside a default non-login PATH.
|
|
205
|
+
paths: [nativeClaudeInstallPath(home, platform)],
|
|
206
|
+
configMarkers: [claudeConfigDir(home)]
|
|
207
|
+
},
|
|
208
|
+
codex: {
|
|
209
|
+
binary: "codex",
|
|
210
|
+
paths: [],
|
|
211
|
+
configMarkers: [codexHomeFrom(home)]
|
|
212
|
+
},
|
|
213
|
+
gemini_cli: {
|
|
214
|
+
binary: "gemini",
|
|
215
|
+
paths: [],
|
|
216
|
+
configMarkers: [join2(home, ".gemini")]
|
|
217
|
+
},
|
|
218
|
+
hermes: {
|
|
219
|
+
binary: "hermes",
|
|
220
|
+
paths: [],
|
|
221
|
+
configMarkers: [join2(home, ".hermes")]
|
|
222
|
+
},
|
|
223
|
+
cursor: {
|
|
224
|
+
// GUI editor. `cursor` on PATH only exists if the user ran "Install 'cursor'
|
|
225
|
+
// command in PATH" from the command palette, which most never do.
|
|
226
|
+
binary: "cursor",
|
|
227
|
+
paths: platform === "darwin" ? ["/Applications/Cursor.app"] : platform === "win32" ? [join2(process.env.LOCALAPPDATA ?? join2(home, "AppData", "Local"), "Programs", "cursor")] : ["/usr/share/cursor", join2(home, ".local", "share", "cursor")],
|
|
228
|
+
configMarkers: [join2(home, ".cursor")]
|
|
229
|
+
},
|
|
230
|
+
vscode: {
|
|
231
|
+
// Like Cursor, a GUI editor: `code` on PATH only exists if the user ran
|
|
232
|
+
// "Shell Command: Install 'code' command in PATH", which on macOS is opt-in.
|
|
233
|
+
binary: "code",
|
|
234
|
+
paths: platform === "darwin" ? ["/Applications/Visual Studio Code.app"] : platform === "win32" ? [join2(process.env.LOCALAPPDATA ?? join2(home, "AppData", "Local"), "Programs", "Microsoft VS Code")] : ["/usr/share/code", join2(home, ".local", "share", "code")],
|
|
235
|
+
// The settings directory rather than ~/.vscode, because the settings
|
|
236
|
+
// directory is what this installer actually writes to. Insiders counts:
|
|
237
|
+
// someone running only Insiders still has VS Code.
|
|
238
|
+
configMarkers: vscodeSettingsCandidates(home).map((path) => dirname(path))
|
|
239
|
+
},
|
|
240
|
+
opencode: {
|
|
241
|
+
binary: "opencode",
|
|
242
|
+
paths: [],
|
|
243
|
+
// Resolved rather than spelled: OpenCode honours OPENCODE_CONFIG_DIR and then
|
|
244
|
+
// XDG_CONFIG_HOME, and `~/.opencode` — the obvious guess — is a path it has
|
|
245
|
+
// never used.
|
|
246
|
+
configMarkers: [openCodeConfigDirFrom(home)]
|
|
247
|
+
},
|
|
248
|
+
fx: {
|
|
249
|
+
binary: null,
|
|
250
|
+
paths: [join2(home, ".fx", "settings.json"), join2(home, ".fx", "sessions"), join2(home, ".fx", "history.jsonl")],
|
|
251
|
+
configMarkers: []
|
|
252
|
+
},
|
|
253
|
+
cline: {
|
|
254
|
+
binary: "cline",
|
|
255
|
+
paths: [clineDataDirectory({ home, environment: process.env })],
|
|
256
|
+
configMarkers: []
|
|
257
|
+
},
|
|
258
|
+
...compatibleProbes(),
|
|
259
|
+
grok: { binary: "grok", paths: [join2(home, ".local", "bin", "grok")], configMarkers: [grokHome(home)] },
|
|
260
|
+
// "Other" is a set of printed instructions for any MCP or HTTP client. There is
|
|
261
|
+
// nothing to detect, and it must never be auto-selected.
|
|
262
|
+
custom: { binary: null, paths: [], configMarkers: [] }
|
|
263
|
+
});
|
|
264
|
+
var compatibleProbes = () => HOOK_COMPATIBLE_PROFILES.reduce((probes, profile) => ({
|
|
265
|
+
...probes,
|
|
266
|
+
[profile.agentType]: {
|
|
267
|
+
binary: profile.binary,
|
|
268
|
+
paths: [],
|
|
269
|
+
configMarkers: []
|
|
270
|
+
}
|
|
271
|
+
}), {});
|
|
272
|
+
var detectAllAgents = (deps = defaultDeps, home) => {
|
|
273
|
+
const probes = agentProbes(home);
|
|
274
|
+
return Object.keys(probes).map((agent) => ({
|
|
275
|
+
agent,
|
|
276
|
+
result: detectAgent(probes[agent], deps)
|
|
277
|
+
}));
|
|
278
|
+
};
|
|
279
|
+
var isDetected = (result) => result.kind === "installed";
|
|
280
|
+
var shortenHome = (path, home = homedir2()) => home && path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
281
|
+
var describeDetection = (result, home) => {
|
|
282
|
+
if (result.kind !== "installed") return null;
|
|
283
|
+
const evidence = shortenHome(result.evidence, home);
|
|
284
|
+
return result.strong ? evidence : `${evidence}, config only`;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export {
|
|
288
|
+
registerPluginLocation,
|
|
289
|
+
unregisterPluginLocation,
|
|
290
|
+
isPluginRegistered,
|
|
291
|
+
pluginLocationSnippet,
|
|
292
|
+
vscodeSettingsTargets,
|
|
293
|
+
detectAgent,
|
|
294
|
+
agentProbes,
|
|
295
|
+
detectAllAgents,
|
|
296
|
+
isDetected,
|
|
297
|
+
shortenHome,
|
|
298
|
+
describeDetection
|
|
299
|
+
};
|