@pushary/agent-hooks 0.87.2 → 0.87.4
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/data/cursor-plugin/hooks/hooks.json +7 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +94 -28
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +81 -1
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +7 -6
- package/dist/bin/pushary-claude.js +14 -11
- package/dist/bin/pushary-clean.js +36 -20
- package/dist/bin/pushary-codex-bridge.js +6 -5
- package/dist/bin/pushary-codex-hook.js +129 -52
- package/dist/bin/pushary-codex.js +4 -3
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +5 -4
- package/dist/bin/pushary-disconnect.js +65 -8
- package/dist/bin/pushary-doctor.js +79 -18
- package/dist/bin/pushary-elicitation-hook.js +1 -1
- package/dist/bin/pushary-gemini-bridge.js +6 -5
- package/dist/bin/pushary-gemini-hook.js +12 -10
- package/dist/bin/pushary-hook.js +6 -5
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +5 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +4 -3
- package/dist/bin/pushary-opencode-hook.d.ts +1 -0
- package/dist/bin/pushary-opencode-hook.js +329 -0
- package/dist/bin/pushary-permission-denied-hook.js +6 -5
- package/dist/bin/pushary-permission-hook.js +6 -5
- package/dist/bin/pushary-post-hook.js +4 -3
- package/dist/bin/pushary-prompt-hook.js +4 -3
- package/dist/bin/pushary-session-end-hook.js +4 -3
- package/dist/bin/pushary-session-start-hook.js +4 -3
- package/dist/bin/pushary-setup.js +238 -84
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +5 -5
- package/dist/bin/pushary-stop-hook.js +4 -3
- package/dist/bin/pushary-stopfailure-hook.js +4 -3
- package/dist/bin/pushary-upgrade.js +7 -6
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-5646VKR4.js → chunk-2JYQAW4B.js} +5 -53
- package/dist/chunk-7J67NYK4.js +287 -0
- package/dist/{chunk-M7RYIZX6.js → chunk-B7ZBHWOW.js} +10 -1
- package/dist/{chunk-QBWYNVKY.js → chunk-BFBAHOGR.js} +1 -1
- package/dist/{chunk-7YJS2VTG.js → chunk-DMW54VWK.js} +12 -1
- package/dist/{chunk-3LKYBFWA.js → chunk-EJL3PJU7.js} +1 -1
- package/dist/chunk-GIRT7677.js +11 -0
- package/dist/{chunk-SJVLFIYP.js → chunk-H5PIPIKQ.js} +1 -1
- package/dist/{chunk-HAUTLCMA.js → chunk-ICERLAE2.js} +1 -1
- package/dist/{chunk-ZZE6FN7T.js → chunk-K42RXJPG.js} +52 -1
- package/dist/{chunk-Y3UTTFTZ.js → chunk-KBS53WOE.js} +1 -1
- package/dist/{chunk-A4I5JGCW.js → chunk-KFAH6KRE.js} +1 -1
- package/dist/{chunk-LT2VLPUK.js → chunk-KULUNLRO.js} +18 -2
- package/dist/{chunk-B3V2H6SA.js → chunk-NAPRGHZ3.js} +54 -13
- package/dist/{chunk-HJ44HIUW.js → chunk-OMA2OZRE.js} +11 -9
- package/dist/{chunk-ECEUMWBH.js → chunk-QLMX5LLV.js} +1 -1
- package/dist/{chunk-XW5CHAQX.js → chunk-QXZ2CKRK.js} +2 -2
- package/dist/{chunk-JAICP7BA.js → chunk-TD3M232T.js} +1 -1
- package/dist/{chunk-WLLBFDVY.js → chunk-TV3UFGT7.js} +1 -1
- package/dist/{chunk-7MBDFNUC.js → chunk-VIHRP25S.js} +1 -1
- package/dist/{chunk-V2XPF77V.js → chunk-VOE4SYPR.js} +42 -279
- package/dist/{chunk-OT4YD54Q.js → chunk-YX6ZKCC5.js} +1 -1
- package/dist/{chunk-2T3GX7WG.js → chunk-YYTLTEGU.js} +1 -1
- package/dist/chunk-ZGC3W23N.js +371 -0
- package/dist/src/index.js +6 -5
- package/package.json +2 -1
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
detectClaudeVersion
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-ICERLAE2.js";
|
|
5
5
|
import {
|
|
6
6
|
claudeWired,
|
|
7
7
|
codexWired,
|
|
8
8
|
geminiWired
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-QXZ2CKRK.js";
|
|
10
10
|
import {
|
|
11
11
|
runBrowserLogin
|
|
12
12
|
} from "../chunk-Z3Z4VK5F.js";
|
|
13
13
|
import {
|
|
14
14
|
ensureDaemonRunning
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-H5PIPIKQ.js";
|
|
16
16
|
import {
|
|
17
17
|
addClaudeMcpServer,
|
|
18
18
|
addPusharyHooks,
|
|
19
|
-
addPusharyToolPermissions
|
|
20
|
-
|
|
21
|
-
parseVersion
|
|
22
|
-
} from "../chunk-5646VKR4.js";
|
|
19
|
+
addPusharyToolPermissions
|
|
20
|
+
} from "../chunk-2JYQAW4B.js";
|
|
23
21
|
import {
|
|
22
|
+
CURSOR_GATE_EVENTS,
|
|
24
23
|
describeDetection,
|
|
25
24
|
detectAllAgents,
|
|
26
25
|
isDetected,
|
|
@@ -31,17 +30,30 @@ import {
|
|
|
31
30
|
shortenHome,
|
|
32
31
|
vscodeSettingsTargets,
|
|
33
32
|
writeInstructionBlock
|
|
34
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-KULUNLRO.js";
|
|
34
|
+
import {
|
|
35
|
+
OPENCODE_HOOK_BINARY,
|
|
36
|
+
addOpenCodeMcpServer,
|
|
37
|
+
installOpenCodePlugin,
|
|
38
|
+
openCodeConfigDir,
|
|
39
|
+
openCodeConfigFile,
|
|
40
|
+
openCodePluginDir,
|
|
41
|
+
openCodePluginFile,
|
|
42
|
+
openCodeWired
|
|
43
|
+
} from "../chunk-ZGC3W23N.js";
|
|
35
44
|
import {
|
|
36
45
|
GEMINI_HOOK_BINARY,
|
|
37
46
|
addGeminiHooks,
|
|
38
|
-
addGeminiMcpServer
|
|
39
|
-
|
|
47
|
+
addGeminiMcpServer,
|
|
48
|
+
compareVersion,
|
|
49
|
+
parseVersion
|
|
50
|
+
} from "../chunk-K42RXJPG.js";
|
|
40
51
|
import "../chunk-ATBKJNWS.js";
|
|
41
52
|
import {
|
|
42
53
|
guardBinary,
|
|
43
|
-
guardNodeScript
|
|
44
|
-
|
|
54
|
+
guardNodeScript,
|
|
55
|
+
guardedArgv
|
|
56
|
+
} from "../chunk-B7ZBHWOW.js";
|
|
45
57
|
import "../chunk-XHKBHWLX.js";
|
|
46
58
|
import {
|
|
47
59
|
installGlobally,
|
|
@@ -61,17 +73,20 @@ import {
|
|
|
61
73
|
codexHome,
|
|
62
74
|
codexHookPositions,
|
|
63
75
|
codexSkillDir2 as codexSkillDir,
|
|
76
|
+
codexTrustSchemeAgrees,
|
|
64
77
|
cursorPluginDir,
|
|
65
78
|
cursorUserHooks,
|
|
66
79
|
geminiMd,
|
|
67
80
|
geminiSettings,
|
|
68
81
|
pusharyConfigFile,
|
|
69
82
|
vscodePluginDir
|
|
70
|
-
} from "../chunk-
|
|
83
|
+
} from "../chunk-NAPRGHZ3.js";
|
|
84
|
+
import "../chunk-GIRT7677.js";
|
|
71
85
|
import {
|
|
72
86
|
reportEvent
|
|
73
|
-
} from "../chunk-
|
|
87
|
+
} from "../chunk-VOE4SYPR.js";
|
|
74
88
|
import "../chunk-J3YDT4HM.js";
|
|
89
|
+
import "../chunk-7J67NYK4.js";
|
|
75
90
|
import "../chunk-RN3NOEJF.js";
|
|
76
91
|
import {
|
|
77
92
|
confirmAppConnection,
|
|
@@ -81,12 +96,12 @@ import {
|
|
|
81
96
|
printConnectInstructions,
|
|
82
97
|
setHumanStream,
|
|
83
98
|
verifyRoundTrip
|
|
84
|
-
} from "../chunk-
|
|
99
|
+
} from "../chunk-EJL3PJU7.js";
|
|
85
100
|
import "../chunk-3EGEA4KH.js";
|
|
86
101
|
import {
|
|
87
102
|
checkApiKey,
|
|
88
103
|
describeKeyCheck
|
|
89
|
-
} from "../chunk-
|
|
104
|
+
} from "../chunk-KFAH6KRE.js";
|
|
90
105
|
import {
|
|
91
106
|
useColor
|
|
92
107
|
} from "../chunk-5RUIFDTP.js";
|
|
@@ -97,7 +112,7 @@ import {
|
|
|
97
112
|
readConfigFileKey,
|
|
98
113
|
readKeySource,
|
|
99
114
|
writeKey
|
|
100
|
-
} from "../chunk-
|
|
115
|
+
} from "../chunk-YX6ZKCC5.js";
|
|
101
116
|
import {
|
|
102
117
|
parseFlags
|
|
103
118
|
} from "../chunk-GMXKITVA.js";
|
|
@@ -120,7 +135,7 @@ import {
|
|
|
120
135
|
} from "../chunk-6MTNS63X.js";
|
|
121
136
|
import "../chunk-BSZYIAZL.js";
|
|
122
137
|
import "../chunk-SAF6HGAA.js";
|
|
123
|
-
import "../chunk-
|
|
138
|
+
import "../chunk-KBS53WOE.js";
|
|
124
139
|
import {
|
|
125
140
|
UsageError
|
|
126
141
|
} from "../chunk-7QLSKOSU.js";
|
|
@@ -130,11 +145,11 @@ import {
|
|
|
130
145
|
import "../chunk-2UMNXADU.js";
|
|
131
146
|
import {
|
|
132
147
|
isValidApiKey
|
|
133
|
-
} from "../chunk-
|
|
148
|
+
} from "../chunk-DMW54VWK.js";
|
|
134
149
|
|
|
135
150
|
// bin/pushary-setup.ts
|
|
136
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, rmSync, renameSync, realpathSync, readdirSync, statSync } from "fs";
|
|
137
|
-
import { join as
|
|
151
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, rmSync, renameSync, realpathSync, readdirSync, statSync, openSync, readSync, closeSync } from "fs";
|
|
152
|
+
import { join as join3, dirname as dirname2, basename } from "path";
|
|
138
153
|
import { homedir, tmpdir } from "os";
|
|
139
154
|
import { execSync as execSync2 } from "child_process";
|
|
140
155
|
import { checkbox, input, confirm } from "@inquirer/prompts";
|
|
@@ -274,7 +289,12 @@ var readCodexVersion = (probe) => {
|
|
|
274
289
|
if (fromManifest) return fromManifest;
|
|
275
290
|
}
|
|
276
291
|
const brew = probe.brewVersions();
|
|
277
|
-
|
|
292
|
+
if (brew) {
|
|
293
|
+
const parsed = parseCodexVersion(brew);
|
|
294
|
+
if (parsed) return parsed;
|
|
295
|
+
}
|
|
296
|
+
const bundled = probe.bundledVersion?.();
|
|
297
|
+
return bundled ? parseCodexVersion(bundled) : null;
|
|
278
298
|
};
|
|
279
299
|
|
|
280
300
|
// src/setup/codex-install.ts
|
|
@@ -317,6 +337,30 @@ var checkCodexInstall = (probe) => {
|
|
|
317
337
|
return { kind: "binary-missing", vendorDir: vendors[0] };
|
|
318
338
|
};
|
|
319
339
|
|
|
340
|
+
// src/setup/codex-bundled.ts
|
|
341
|
+
import { join as join2 } from "path";
|
|
342
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
343
|
+
var codexCliPathFromConfig = (config) => {
|
|
344
|
+
const servers = asRecord(config.mcp_servers);
|
|
345
|
+
if (!servers) return null;
|
|
346
|
+
for (const server of Object.values(servers)) {
|
|
347
|
+
const declared = asRecord(asRecord(server)?.env)?.CODEX_CLI_PATH;
|
|
348
|
+
if (typeof declared === "string" && declared.trim()) return declared.trim();
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
};
|
|
352
|
+
var bundledCodexCandidates = (home, platform) => platform === "darwin" ? [
|
|
353
|
+
"/Applications/ChatGPT.app/Contents/Resources/codex",
|
|
354
|
+
join2(home, "Applications", "ChatGPT.app", "Contents", "Resources", "codex")
|
|
355
|
+
] : [];
|
|
356
|
+
var findBundledCodex = (probe) => {
|
|
357
|
+
const config = probe.config();
|
|
358
|
+
const declared = config ? codexCliPathFromConfig(config) : null;
|
|
359
|
+
if (declared && probe.exists(declared)) return declared;
|
|
360
|
+
return bundledCodexCandidates(probe.home, probe.platform).find(probe.exists) ?? null;
|
|
361
|
+
};
|
|
362
|
+
var parseEmbeddedCodexVersion = (haystack) => haystack.match(/cli_version(\d+\.\d+\.\d+)/)?.[1] ?? null;
|
|
363
|
+
|
|
320
364
|
// src/skills-cli.ts
|
|
321
365
|
import { execSync } from "child_process";
|
|
322
366
|
var installSkillViaSkillsCli = (agent) => {
|
|
@@ -414,7 +458,7 @@ var reportSetupAbortWith = async (deps, reason, exitCode, keyCheck = "unknown")
|
|
|
414
458
|
var reportSetupAbort = (reason, exitCode, keyCheck) => reportSetupAbortWith(defaultDeps, reason, exitCode, keyCheck);
|
|
415
459
|
|
|
416
460
|
// src/setup/options.ts
|
|
417
|
-
var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "custom"];
|
|
461
|
+
var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "opencode", "custom"];
|
|
418
462
|
var CONNECT_MODES = ["app", "browser", "web", "none"];
|
|
419
463
|
var CONNECT_ALIASES = { auto: "app", pwa: "web" };
|
|
420
464
|
var parseConnectMode = (raw) => {
|
|
@@ -677,8 +721,8 @@ var fetchSkillContent = async () => {
|
|
|
677
721
|
if (_cachedSkillContent) return _cachedSkillContent;
|
|
678
722
|
const __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
679
723
|
const candidates = [
|
|
680
|
-
|
|
681
|
-
|
|
724
|
+
join3(__dirname, "..", "..", "data", "SKILL.md"),
|
|
725
|
+
join3(__dirname, "..", "data", "SKILL.md")
|
|
682
726
|
];
|
|
683
727
|
const source = candidates.find((path) => existsSync(path));
|
|
684
728
|
if (!source) throw new Error("packaged skill not found");
|
|
@@ -688,12 +732,12 @@ var fetchSkillContent = async () => {
|
|
|
688
732
|
var writePackagedSkill = async (dir) => {
|
|
689
733
|
const content = await fetchSkillContent();
|
|
690
734
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
691
|
-
writeFileSync(
|
|
735
|
+
writeFileSync(join3(dir, "SKILL.md"), content, "utf-8");
|
|
692
736
|
};
|
|
693
|
-
var skillsCliInUse = () => isInstalled("skills") || existsSync(
|
|
737
|
+
var skillsCliInUse = () => isInstalled("skills") || existsSync(join3(homedir(), ".agents", "skills"));
|
|
694
738
|
var skillInstallEffects = {
|
|
695
739
|
runSkillsCli: installSkillViaSkillsCli,
|
|
696
|
-
skillModifiedAt: (dir) => statSync(
|
|
740
|
+
skillModifiedAt: (dir) => statSync(join3(dir, "SKILL.md"), { throwIfNoEntry: false })?.mtimeMs ?? null,
|
|
697
741
|
writePackagedSkill,
|
|
698
742
|
now: () => Date.now()
|
|
699
743
|
};
|
|
@@ -746,8 +790,8 @@ var resolveHermesPython = () => {
|
|
|
746
790
|
}
|
|
747
791
|
} catch {
|
|
748
792
|
}
|
|
749
|
-
const venvRoot =
|
|
750
|
-
const candidates = IS_WINDOWS ? [
|
|
793
|
+
const venvRoot = join3(homedir(), ".hermes", "hermes-agent", "venv");
|
|
794
|
+
const candidates = IS_WINDOWS ? [join3(venvRoot, "Scripts", "python.exe"), join3(venvRoot, "Scripts", "python3.exe")] : [join3(venvRoot, "bin", "python3"), join3(venvRoot, "bin", "python")];
|
|
751
795
|
return candidates.find(existsSync) ?? null;
|
|
752
796
|
};
|
|
753
797
|
var ensurePip = (python) => {
|
|
@@ -763,7 +807,7 @@ var ensurePip = (python) => {
|
|
|
763
807
|
};
|
|
764
808
|
var enablePusharyPlugin = (python) => {
|
|
765
809
|
const snippet = 'from hermes_cli.config import load_config, save_config; c = load_config(); p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}; e = p.get("enabled") if isinstance(p.get("enabled"), list) else []; p["enabled"] = (e + ["pushary"]) if "pushary" not in e else e; c["plugins"] = p; a = c.get("agent") if isinstance(c.get("agent"), dict) else {}; d = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []; a["disabled_toolsets"] = (d + ["clarify"]) if "clarify" not in d else d; c["agent"] = a; save_config(c)';
|
|
766
|
-
const scriptPath =
|
|
810
|
+
const scriptPath = join3(tmpdir(), `pushary-hermes-${process.pid}.py`);
|
|
767
811
|
try {
|
|
768
812
|
writeFileSync(scriptPath, snippet.split("; ").join("\n"), "utf-8");
|
|
769
813
|
execSync2(`"${python}" "${scriptPath}"`, { stdio: "pipe", timeout: 15e3 });
|
|
@@ -812,9 +856,9 @@ var setupHermes = async (_apiKey) => {
|
|
|
812
856
|
console.log(` ${dim("\u2022")} Permission gating: set ${bold("PUSHARY_GATE_TOOLS")} to require lock-screen approval for risky tools`);
|
|
813
857
|
console.log(` ${dim("To re-enable terminal prompts:")} remove ${bold("clarify")} from ${dim("agent.disabled_toolsets")} in ~/.hermes/config.yaml`);
|
|
814
858
|
};
|
|
815
|
-
var CODEX_HOOKS_JSON =
|
|
859
|
+
var CODEX_HOOKS_JSON = join3(CODEX_HOME, "hooks.json");
|
|
816
860
|
var CODEX_HOOKS_MIN_VERSION = [0, 122, 0];
|
|
817
|
-
var CODEX_TRUST_VERIFIED_MAX = [0,
|
|
861
|
+
var CODEX_TRUST_VERIFIED_MAX = [0, 147, 0];
|
|
818
862
|
var codexOnPath = () => {
|
|
819
863
|
const whichCmd = IS_WINDOWS ? "where" : "which";
|
|
820
864
|
try {
|
|
@@ -831,6 +875,49 @@ var codexRealpath = (path) => {
|
|
|
831
875
|
return null;
|
|
832
876
|
}
|
|
833
877
|
};
|
|
878
|
+
var EMBEDDED_SCAN_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
879
|
+
var EMBEDDED_SCAN_MAX_BYTES = 320 * 1024 * 1024;
|
|
880
|
+
var readEmbeddedCodexVersion = (path) => {
|
|
881
|
+
let fd;
|
|
882
|
+
try {
|
|
883
|
+
fd = openSync(path, "r");
|
|
884
|
+
const chunk = Buffer.allocUnsafe(EMBEDDED_SCAN_CHUNK_BYTES);
|
|
885
|
+
let carry = "";
|
|
886
|
+
let scanned = 0;
|
|
887
|
+
while (scanned < EMBEDDED_SCAN_MAX_BYTES) {
|
|
888
|
+
const bytes = readSync(fd, chunk, 0, chunk.length, null);
|
|
889
|
+
if (bytes <= 0) return null;
|
|
890
|
+
scanned += bytes;
|
|
891
|
+
const text = carry + chunk.toString("latin1", 0, bytes);
|
|
892
|
+
const found = parseEmbeddedCodexVersion(text);
|
|
893
|
+
if (found) return found;
|
|
894
|
+
carry = text.slice(-32);
|
|
895
|
+
}
|
|
896
|
+
return null;
|
|
897
|
+
} catch {
|
|
898
|
+
return null;
|
|
899
|
+
} finally {
|
|
900
|
+
if (fd !== void 0) {
|
|
901
|
+
try {
|
|
902
|
+
closeSync(fd);
|
|
903
|
+
} catch {
|
|
904
|
+
fd = void 0;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
var bundledCodex = () => findBundledCodex({
|
|
910
|
+
config: () => {
|
|
911
|
+
try {
|
|
912
|
+
return parseTOML(readFileSync(codexConfigToml(), "utf-8"));
|
|
913
|
+
} catch {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
exists: existsSync,
|
|
918
|
+
home: homedir(),
|
|
919
|
+
platform: process.platform
|
|
920
|
+
});
|
|
834
921
|
var detectCodexVersion = () => readCodexVersion({
|
|
835
922
|
resolveOnPath: codexOnPath,
|
|
836
923
|
realpath: codexRealpath,
|
|
@@ -841,6 +928,10 @@ var detectCodexVersion = () => readCodexVersion({
|
|
|
841
928
|
return null;
|
|
842
929
|
}
|
|
843
930
|
},
|
|
931
|
+
bundledVersion: () => {
|
|
932
|
+
const path = bundledCodex();
|
|
933
|
+
return path ? readEmbeddedCodexVersion(path) : null;
|
|
934
|
+
},
|
|
844
935
|
brewVersions: () => {
|
|
845
936
|
try {
|
|
846
937
|
return execSync2("brew list --versions codex", {
|
|
@@ -872,7 +963,7 @@ var detectCodexInstall = () => checkCodexInstall({
|
|
|
872
963
|
return null;
|
|
873
964
|
}
|
|
874
965
|
},
|
|
875
|
-
join:
|
|
966
|
+
join: join3,
|
|
876
967
|
dirname: dirname2
|
|
877
968
|
});
|
|
878
969
|
var codexSupportsHooks = (version) => version !== null && compareCodexVersion(version, CODEX_HOOKS_MIN_VERSION) >= 0;
|
|
@@ -915,12 +1006,19 @@ var setupCodex = async (apiKey) => {
|
|
|
915
1006
|
console.log(`
|
|
916
1007
|
${bold("Setting up Codex")}
|
|
917
1008
|
`);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1009
|
+
const onPath = isInstalled("codex");
|
|
1010
|
+
const bundled = onPath ? null : bundledCodex();
|
|
1011
|
+
if (!onPath && !bundled) {
|
|
1012
|
+
console.log(` ${yellow("!")} Codex not found. Skipping.`);
|
|
1013
|
+
console.log(` ${dim("Install the Codex CLI or the ChatGPT desktop app, then re-run setup.")}`);
|
|
921
1014
|
return "skipped";
|
|
922
1015
|
}
|
|
923
|
-
if (
|
|
1016
|
+
if (bundled) {
|
|
1017
|
+
console.log(` ${dim("Using the Codex inside the ChatGPT desktop app.")}`);
|
|
1018
|
+
console.log(` ${dim(shortenHome(bundled))}`);
|
|
1019
|
+
console.log();
|
|
1020
|
+
}
|
|
1021
|
+
if (onPath && detectCodexInstall().kind === "binary-missing") {
|
|
924
1022
|
console.log(` ${yellow("!")} Your Codex install is missing the binary it runs.`);
|
|
925
1023
|
console.log(` ${dim("A Pushary setup before 0.71.0 asked Codex its version. On macOS that hands")}`);
|
|
926
1024
|
console.log(` ${dim("the binary to XProtect, which quarantines the Codex CLI and moves it to the")}`);
|
|
@@ -939,7 +1037,7 @@ var setupCodex = async (apiKey) => {
|
|
|
939
1037
|
});
|
|
940
1038
|
const codexVersion = detectCodexVersion();
|
|
941
1039
|
const hooksSupported = codexSupportsHooks(codexVersion);
|
|
942
|
-
const trustAuto = codexTrustAutoSupported(codexVersion);
|
|
1040
|
+
const trustAuto = hooksSupported && (codexTrustSchemeAgrees(readAgentJson(CODEX_HOOKS_JSON), readAgentToml(codexConfig), CODEX_HOOKS_JSON) || codexTrustAutoSupported(codexVersion));
|
|
943
1041
|
let trusted = false;
|
|
944
1042
|
if (hooksSupported) {
|
|
945
1043
|
const hookCommand = guardBinary(resolveGlobalBinary("pushary-codex-hook") ?? "pushary-codex-hook");
|
|
@@ -994,20 +1092,25 @@ var setupCodex = async (apiKey) => {
|
|
|
994
1092
|
if (hooksSupported) {
|
|
995
1093
|
console.log(` ${dim("\u2022")} Native hooks: phone approvals, policy enforcement, kill switch, session tracking`);
|
|
996
1094
|
if (trusted) {
|
|
997
|
-
console.log(` ${dim("\u2022")} Hook pre-trusted: no ${cyan("/hooks")} step
|
|
1095
|
+
console.log(` ${dim("\u2022")} Hook pre-trusted: no ${cyan("/hooks")} step. If approvals never arrive, ${cyan("/hooks")} re-trusts it`);
|
|
998
1096
|
} else {
|
|
999
1097
|
console.log();
|
|
1000
1098
|
console.log(` ${bold("One step left, and it happens inside Codex")}`);
|
|
1001
1099
|
console.log(` ${dim("Codex makes you trust any hook before it runs. This is Codex's own")}`);
|
|
1002
1100
|
console.log(` ${dim("security check, not a Pushary thing, and you only do it once.")}`);
|
|
1003
1101
|
console.log();
|
|
1004
|
-
|
|
1005
|
-
|
|
1102
|
+
if (bundled) {
|
|
1103
|
+
console.log(` ${cyan("1.")} Open ChatGPT, then Settings and ${cyan("Hooks")}`);
|
|
1104
|
+
console.log(` ${cyan("2.")} You will see the Pushary hook commands. Trust them.`);
|
|
1105
|
+
} else {
|
|
1106
|
+
console.log(` ${cyan("1.")} Open Codex and type ${cyan("/hooks")}`);
|
|
1107
|
+
console.log(` ${cyan("2.")} You will see the Pushary hook commands. Trust them.`);
|
|
1108
|
+
}
|
|
1006
1109
|
console.log();
|
|
1007
1110
|
console.log(` ${dim("Until you do, Codex just runs as usual without the phone approvals.")}`);
|
|
1008
1111
|
console.log(` ${dim("To confirm:")} run a command that needs approval and watch your phone.`);
|
|
1009
1112
|
console.log(` ${dim("Pushary updates keep the same trust, so Codex will not ask again.")}`);
|
|
1010
|
-
noteManual("Open Codex, type /hooks, and trust the Pushary hook. Until then Codex runs without phone approvals.");
|
|
1113
|
+
noteManual(bundled ? "Open ChatGPT, then Settings and Hooks, and trust the Pushary hook. Until then Codex runs without phone approvals." : "Open Codex, type /hooks, and trust the Pushary hook. Until then Codex runs without phone approvals.");
|
|
1011
1114
|
}
|
|
1012
1115
|
} else {
|
|
1013
1116
|
console.log(` ${dim("\u2022")} Notify handler (deprecated): captures turn completions and approval requests`);
|
|
@@ -1016,17 +1119,15 @@ var setupCodex = async (apiKey) => {
|
|
|
1016
1119
|
var resolveBundledPlugin = () => {
|
|
1017
1120
|
const dir = dirname2(fileURLToPath(import.meta.url));
|
|
1018
1121
|
const candidates = [
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1122
|
+
join3(dir, "..", "..", "data", "cursor-plugin"),
|
|
1123
|
+
join3(dir, "..", "data", "cursor-plugin"),
|
|
1124
|
+
join3(dir, "..", "..", "..", "cursor-plugin"),
|
|
1125
|
+
join3(dir, "..", "..", "cursor-plugin")
|
|
1023
1126
|
];
|
|
1024
|
-
return candidates.find((p) => existsSync(
|
|
1127
|
+
return candidates.find((p) => existsSync(join3(p, ".cursor-plugin", "plugin.json"))) ?? null;
|
|
1025
1128
|
};
|
|
1026
1129
|
var installCursorUserHooks = (gateScript) => {
|
|
1027
|
-
const
|
|
1028
|
-
if (!template) throw new Error("bundled Cursor hooks.json missing a beforeShellExecution entry");
|
|
1029
|
-
const entry = { ...template, command: guardNodeScript(gateScript) };
|
|
1130
|
+
const bundled = readJson(join3(CURSOR_PLUGIN_DIR, "hooks", "hooks.json")).hooks ?? {};
|
|
1030
1131
|
let userHooks = {};
|
|
1031
1132
|
if (existsSync(CURSOR_USER_HOOKS)) {
|
|
1032
1133
|
try {
|
|
@@ -1040,19 +1141,24 @@ var installCursorUserHooks = (gateScript) => {
|
|
|
1040
1141
|
}
|
|
1041
1142
|
}
|
|
1042
1143
|
const hooks = userHooks.hooks ?? {};
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
|
|
1144
|
+
for (const event of CURSOR_GATE_EVENTS) {
|
|
1145
|
+
const template = bundled[event]?.[0];
|
|
1146
|
+
if (!template) throw new Error(`bundled Cursor hooks.json missing a ${event} entry`);
|
|
1147
|
+
const existing = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
1148
|
+
const others = existing.filter((h) => !String(h.command ?? "").includes("pushary-gate"));
|
|
1149
|
+
hooks[event] = [...others, { ...template, command: guardNodeScript(gateScript) }];
|
|
1150
|
+
}
|
|
1046
1151
|
writeJson(CURSOR_USER_HOOKS, { ...userHooks, version: userHooks.version ?? 1, hooks });
|
|
1047
1152
|
};
|
|
1048
1153
|
var neutralizePluginGate = () => {
|
|
1049
|
-
const path =
|
|
1154
|
+
const path = join3(CURSOR_PLUGIN_DIR, "hooks", "hooks.json");
|
|
1050
1155
|
if (!existsSync(path)) return;
|
|
1051
1156
|
const data = readJson(path);
|
|
1052
|
-
if (data.hooks
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1157
|
+
if (!data.hooks) return;
|
|
1158
|
+
const stripped = CURSOR_GATE_EVENTS.filter((event) => event in data.hooks);
|
|
1159
|
+
if (stripped.length === 0) return;
|
|
1160
|
+
for (const event of stripped) delete data.hooks[event];
|
|
1161
|
+
writeJson(path, { version: data.version ?? 1, hooks: data.hooks });
|
|
1056
1162
|
};
|
|
1057
1163
|
var setupCursor = async (apiKey) => {
|
|
1058
1164
|
console.log(`
|
|
@@ -1061,7 +1167,7 @@ var setupCursor = async (apiKey) => {
|
|
|
1061
1167
|
const source = resolveBundledPlugin();
|
|
1062
1168
|
if (!source) throw new Error("bundled Cursor plugin not found in this package");
|
|
1063
1169
|
await spinner("Installing Pushary plugin", async () => {
|
|
1064
|
-
const staging =
|
|
1170
|
+
const staging = join3(dirname2(CURSOR_PLUGIN_DIR), `.pushary-staging-${process.pid}`);
|
|
1065
1171
|
const backup = `${CURSOR_PLUGIN_DIR}.pushary-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
1066
1172
|
rmSync(staging, { recursive: true, force: true });
|
|
1067
1173
|
try {
|
|
@@ -1069,7 +1175,7 @@ var setupCursor = async (apiKey) => {
|
|
|
1069
1175
|
recursive: true,
|
|
1070
1176
|
filter: (p) => !["tools", "node_modules", ".git", ".DS_Store"].includes(basename(p))
|
|
1071
1177
|
});
|
|
1072
|
-
const staged = readJsonSafe(
|
|
1178
|
+
const staged = readJsonSafe(join3(staging, ".cursor-plugin", "plugin.json"));
|
|
1073
1179
|
if (staged.kind !== "ok") {
|
|
1074
1180
|
throw new Error("staged Cursor plugin is missing or has an unreadable plugin.json");
|
|
1075
1181
|
}
|
|
@@ -1087,7 +1193,7 @@ var setupCursor = async (apiKey) => {
|
|
|
1087
1193
|
}
|
|
1088
1194
|
});
|
|
1089
1195
|
await spinner("Linking your API key", async () => {
|
|
1090
|
-
const mcpPath =
|
|
1196
|
+
const mcpPath = join3(CURSOR_PLUGIN_DIR, "mcp.json");
|
|
1091
1197
|
const mcp = readAgentJson(mcpPath);
|
|
1092
1198
|
const servers = mcp.mcpServers ?? {};
|
|
1093
1199
|
if (servers.pushary) {
|
|
@@ -1097,7 +1203,7 @@ var setupCursor = async (apiKey) => {
|
|
|
1097
1203
|
}
|
|
1098
1204
|
});
|
|
1099
1205
|
await spinner("Registering permission gate (~/.cursor/hooks.json)", async () => {
|
|
1100
|
-
installCursorUserHooks(
|
|
1206
|
+
installCursorUserHooks(join3(CURSOR_PLUGIN_DIR, "scripts", "pushary-gate.mjs"));
|
|
1101
1207
|
neutralizePluginGate();
|
|
1102
1208
|
});
|
|
1103
1209
|
console.log();
|
|
@@ -1111,21 +1217,21 @@ var setupCursor = async (apiKey) => {
|
|
|
1111
1217
|
var resolveBundledVsCodePlugin = () => {
|
|
1112
1218
|
const dir = dirname2(fileURLToPath(import.meta.url));
|
|
1113
1219
|
const candidates = [
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1220
|
+
join3(dir, "..", "..", "data", "vscode-plugin"),
|
|
1221
|
+
join3(dir, "..", "data", "vscode-plugin"),
|
|
1222
|
+
join3(dir, "..", "..", "..", "vscode-plugin"),
|
|
1223
|
+
join3(dir, "..", "..", "vscode-plugin")
|
|
1118
1224
|
];
|
|
1119
|
-
return candidates.find((p) => existsSync(
|
|
1225
|
+
return candidates.find((p) => existsSync(join3(p, ".claude-plugin", "plugin.json"))) ?? null;
|
|
1120
1226
|
};
|
|
1121
1227
|
var pinVsCodeGatePath = (pluginDir) => {
|
|
1122
|
-
const hooksPath =
|
|
1228
|
+
const hooksPath = join3(pluginDir, "hooks", "hooks.json");
|
|
1123
1229
|
const data = readJson(hooksPath);
|
|
1124
1230
|
const entries = data.hooks?.PreToolUse;
|
|
1125
1231
|
if (!Array.isArray(entries) || entries.length === 0) {
|
|
1126
1232
|
throw new Error("bundled VS Code hooks.json is missing a PreToolUse entry");
|
|
1127
1233
|
}
|
|
1128
|
-
const gate =
|
|
1234
|
+
const gate = join3(pluginDir, "scripts", "pushary-gate.mjs");
|
|
1129
1235
|
data.hooks.PreToolUse = entries.map((entry) => ({ ...entry, command: `node "${gate}"` }));
|
|
1130
1236
|
writeJson(hooksPath, data);
|
|
1131
1237
|
};
|
|
@@ -1154,7 +1260,7 @@ var setupVsCode = async (apiKey) => {
|
|
|
1154
1260
|
const source = resolveBundledVsCodePlugin();
|
|
1155
1261
|
if (!source) throw new Error("bundled VS Code plugin not found in this package");
|
|
1156
1262
|
await spinner("Installing Pushary plugin", async () => {
|
|
1157
|
-
const staging =
|
|
1263
|
+
const staging = join3(dirname2(VSCODE_PLUGIN_DIR), `.pushary-staging-vscode-${process.pid}`);
|
|
1158
1264
|
const backup = `${VSCODE_PLUGIN_DIR}.pushary-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
1159
1265
|
mkdirSync(dirname2(VSCODE_PLUGIN_DIR), { recursive: true });
|
|
1160
1266
|
rmSync(staging, { recursive: true, force: true });
|
|
@@ -1163,7 +1269,7 @@ var setupVsCode = async (apiKey) => {
|
|
|
1163
1269
|
recursive: true,
|
|
1164
1270
|
filter: (p) => !["tools", "node_modules", ".git", ".DS_Store"].includes(basename(p))
|
|
1165
1271
|
});
|
|
1166
|
-
const staged = readJsonSafe(
|
|
1272
|
+
const staged = readJsonSafe(join3(staging, ".claude-plugin", "plugin.json"));
|
|
1167
1273
|
if (staged.kind !== "ok") {
|
|
1168
1274
|
throw new Error("staged VS Code plugin is missing or has an unreadable plugin.json");
|
|
1169
1275
|
}
|
|
@@ -1181,7 +1287,7 @@ var setupVsCode = async (apiKey) => {
|
|
|
1181
1287
|
}
|
|
1182
1288
|
});
|
|
1183
1289
|
await spinner("Linking your API key", async () => {
|
|
1184
|
-
const mcpPath =
|
|
1290
|
+
const mcpPath = join3(VSCODE_PLUGIN_DIR, ".mcp.json");
|
|
1185
1291
|
const mcp = readAgentJson(mcpPath);
|
|
1186
1292
|
const servers = mcp.mcpServers ?? {};
|
|
1187
1293
|
if (servers.pushary) {
|
|
@@ -1308,6 +1414,43 @@ var setupGemini = async (apiKey) => {
|
|
|
1308
1414
|
console.log(` ${dim("Restart Gemini CLI to load the new config.")}`);
|
|
1309
1415
|
noteManual("Restart Gemini CLI to load the new config.");
|
|
1310
1416
|
};
|
|
1417
|
+
var OPENCODE_INSTRUCTIONS = () => join3(openCodeConfigDir(), "AGENTS.md");
|
|
1418
|
+
var setupOpenCode = async (apiKey) => {
|
|
1419
|
+
console.log(`
|
|
1420
|
+
${bold("Setting up OpenCode")}
|
|
1421
|
+
`);
|
|
1422
|
+
if (!isInstalled("opencode") && !existsSync(openCodeConfigDir())) {
|
|
1423
|
+
console.log(` ${yellow("!")} OpenCode not found. Skipping.`);
|
|
1424
|
+
console.log(` ${dim("Install OpenCode and re-run setup to configure.")}`);
|
|
1425
|
+
return "skipped";
|
|
1426
|
+
}
|
|
1427
|
+
await installGlobally2();
|
|
1428
|
+
await spinner(`Writing the Pushary plugin (${openCodePluginFile()})`, async () => {
|
|
1429
|
+
const target = resolveGlobalBinary(OPENCODE_HOOK_BINARY) ?? OPENCODE_HOOK_BINARY;
|
|
1430
|
+
installOpenCodePlugin(
|
|
1431
|
+
guardedArgv({ kind: "binary", path: target, runtime: "node" }, ["--source", "opencode"]),
|
|
1432
|
+
openCodePluginDir()
|
|
1433
|
+
);
|
|
1434
|
+
});
|
|
1435
|
+
await spinner(`Adding MCP server (${openCodeConfigFile()})`, async () => {
|
|
1436
|
+
const config = readAgentJson(openCodeConfigFile());
|
|
1437
|
+
addOpenCodeMcpServer(config, apiKey);
|
|
1438
|
+
writeJsonAtomic(openCodeConfigFile(), config, KEY_FILE_MODE);
|
|
1439
|
+
});
|
|
1440
|
+
await spinner("Teaching OpenCode to ask via push", async () => {
|
|
1441
|
+
writeInstructionBlock(OPENCODE_INSTRUCTIONS(), renderAgentInstructions("OpenCode"));
|
|
1442
|
+
});
|
|
1443
|
+
console.log();
|
|
1444
|
+
console.log(` ${dim("What this configured:")}`);
|
|
1445
|
+
console.log(` ${dim("\u2022")} Plugin: ${openCodePluginFile()}`);
|
|
1446
|
+
console.log(` ${dim("\u2022")} MCP server: OpenCode can send notifications and ask questions`);
|
|
1447
|
+
console.log(` ${dim("\u2022")} Approvals: a permission OpenCode raises is answered from your phone`);
|
|
1448
|
+
console.log(` ${dim("\u2022")} Gate covers ${bold("bash")}, ${bold("edit")}, ${bold("write")} and ${bold("webfetch")}`);
|
|
1449
|
+
console.log(` ${dim("\u2022")} Receipts: tool results, prompts and turn boundaries reach your account`);
|
|
1450
|
+
console.log(` ${dim("Restart OpenCode to load the plugin.")}`);
|
|
1451
|
+
noteManual("Restart OpenCode to load the plugin.");
|
|
1452
|
+
return "configured";
|
|
1453
|
+
};
|
|
1311
1454
|
var agentIsWired = (agent) => {
|
|
1312
1455
|
const readJson2 = (path) => {
|
|
1313
1456
|
try {
|
|
@@ -1321,27 +1464,29 @@ var agentIsWired = (agent) => {
|
|
|
1321
1464
|
return claudeWired({
|
|
1322
1465
|
claudeJson: readJson2(CLAUDE_JSON),
|
|
1323
1466
|
settings: readJson2(CLAUDE_SETTINGS),
|
|
1324
|
-
skillExists: existsSync(
|
|
1467
|
+
skillExists: existsSync(join3(CLAUDE_SKILL_DIR, "SKILL.md"))
|
|
1325
1468
|
});
|
|
1326
1469
|
case "codex": {
|
|
1327
1470
|
let config = null;
|
|
1328
1471
|
try {
|
|
1329
|
-
config = parseTOML(readFileSync(
|
|
1472
|
+
config = parseTOML(readFileSync(join3(CODEX_HOME, "config.toml"), "utf-8"));
|
|
1330
1473
|
} catch {
|
|
1331
1474
|
config = null;
|
|
1332
1475
|
}
|
|
1333
1476
|
return codexWired({
|
|
1334
1477
|
config,
|
|
1335
1478
|
hooks: readJson2(CODEX_HOOKS_JSON),
|
|
1336
|
-
skillExists: existsSync(
|
|
1479
|
+
skillExists: existsSync(join3(CODEX_SKILL_DIR, "SKILL.md"))
|
|
1337
1480
|
});
|
|
1338
1481
|
}
|
|
1339
1482
|
case "gemini_cli":
|
|
1340
1483
|
return geminiWired({ settings: readJson2(GEMINI_SETTINGS) });
|
|
1341
1484
|
case "cursor":
|
|
1342
|
-
return existsSync(
|
|
1485
|
+
return existsSync(join3(CURSOR_PLUGIN_DIR, "mcp.json"));
|
|
1343
1486
|
case "vscode":
|
|
1344
|
-
return existsSync(
|
|
1487
|
+
return existsSync(join3(VSCODE_PLUGIN_DIR, ".mcp.json"));
|
|
1488
|
+
case "opencode":
|
|
1489
|
+
return openCodeWired();
|
|
1345
1490
|
default:
|
|
1346
1491
|
return true;
|
|
1347
1492
|
}
|
|
@@ -1353,6 +1498,7 @@ var AGENT_SETUP = {
|
|
|
1353
1498
|
hermes: setupHermes,
|
|
1354
1499
|
cursor: setupCursor,
|
|
1355
1500
|
vscode: setupVsCode,
|
|
1501
|
+
opencode: setupOpenCode,
|
|
1356
1502
|
custom: setupCustom
|
|
1357
1503
|
};
|
|
1358
1504
|
var PROJECT_INSTRUCTION_TARGETS = {
|
|
@@ -1369,8 +1515,8 @@ var offerProjectInstructions = async (agents, options) => {
|
|
|
1369
1515
|
});
|
|
1370
1516
|
if (!wanted) return;
|
|
1371
1517
|
for (const target of targets) {
|
|
1372
|
-
await spinner(`Writing managed block to ${
|
|
1373
|
-
writeInstructionBlock(
|
|
1518
|
+
await spinner(`Writing managed block to ${join3(process.cwd(), target.file)}`, async () => {
|
|
1519
|
+
writeInstructionBlock(join3(process.cwd(), target.file), renderProjectAgentInstructions(target.label));
|
|
1374
1520
|
}, { optional: true });
|
|
1375
1521
|
}
|
|
1376
1522
|
console.log(` ${dim("Commit the file to share it. Teammates without a key fall back to the terminal.")}`);
|
|
@@ -1455,6 +1601,7 @@ var AGENT_NAMES = {
|
|
|
1455
1601
|
hermes: "Hermes",
|
|
1456
1602
|
cursor: "Cursor",
|
|
1457
1603
|
vscode: "VS Code",
|
|
1604
|
+
opencode: "OpenCode",
|
|
1458
1605
|
custom: "Other"
|
|
1459
1606
|
};
|
|
1460
1607
|
var AGENT_CAPABILITIES = {
|
|
@@ -1464,6 +1611,7 @@ var AGENT_CAPABILITIES = {
|
|
|
1464
1611
|
hermes: "native plugin + auto-error notifications",
|
|
1465
1612
|
cursor: "plugin + permission gate",
|
|
1466
1613
|
vscode: "agent plugin + permission gate",
|
|
1614
|
+
opencode: "generated plugin + permission gate",
|
|
1467
1615
|
custom: "any MCP or HTTP agent (Windsurf, n8n, custom)"
|
|
1468
1616
|
};
|
|
1469
1617
|
var NAME_COLUMN = Math.max(...Object.values(AGENT_NAMES).map((name) => name.length)) + 2;
|
|
@@ -1522,12 +1670,18 @@ var resolveAgents = async (options) => {
|
|
|
1522
1670
|
});
|
|
1523
1671
|
};
|
|
1524
1672
|
var AGENT_TARGETS = {
|
|
1525
|
-
claude_code: [CLAUDE_JSON, CLAUDE_SETTINGS,
|
|
1526
|
-
codex: [
|
|
1673
|
+
claude_code: [CLAUDE_JSON, CLAUDE_SETTINGS, join3(CLAUDE_SKILL_DIR, "SKILL.md")],
|
|
1674
|
+
codex: [join3(CODEX_HOME, "config.toml"), CODEX_HOOKS_JSON, join3(CODEX_SKILL_DIR, "SKILL.md"), CODEX_AGENTS_MD],
|
|
1527
1675
|
gemini_cli: [GEMINI_SETTINGS, GEMINI_MD],
|
|
1528
1676
|
hermes: ["the Hermes virtualenv (pip install pushary-hermes)"],
|
|
1529
|
-
cursor: [
|
|
1530
|
-
vscode: [
|
|
1677
|
+
cursor: [join3(CURSOR_PLUGIN_DIR, "mcp.json"), CURSOR_USER_HOOKS],
|
|
1678
|
+
vscode: [join3(VSCODE_PLUGIN_DIR, ".mcp.json"), ...vscodeSettingsTargets(existsSync)],
|
|
1679
|
+
opencode: [
|
|
1680
|
+
openCodePluginFile(),
|
|
1681
|
+
join3(openCodePluginDir(), "package.json"),
|
|
1682
|
+
openCodeConfigFile(),
|
|
1683
|
+
OPENCODE_INSTRUCTIONS()
|
|
1684
|
+
],
|
|
1531
1685
|
custom: ["nothing (prints connection details only)"]
|
|
1532
1686
|
};
|
|
1533
1687
|
var reportDryRun = (apiKey, agents, keyCheck) => {
|