@pushary/agent-hooks 0.90.1 → 0.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/bin/pushary-bell.js +6 -61
  3. package/dist/bin/pushary-claude.js +5 -5
  4. package/dist/bin/pushary-clean.js +98 -14
  5. package/dist/bin/pushary-codex-bridge.js +3 -3
  6. package/dist/bin/pushary-codex-hook.js +3 -3
  7. package/dist/bin/pushary-codex.js +3 -3
  8. package/dist/bin/pushary-connect.js +2 -2
  9. package/dist/bin/pushary-daemon.js +9 -9
  10. package/dist/bin/pushary-disconnect.js +2 -2
  11. package/dist/bin/pushary-doctor.js +13 -13
  12. package/dist/bin/pushary-elicitation-hook.js +2 -2
  13. package/dist/bin/pushary-gemini-bridge.js +3 -3
  14. package/dist/bin/pushary-gemini-hook.js +3 -3
  15. package/dist/bin/pushary-hook.js +4 -4
  16. package/dist/bin/pushary-login.js +4 -4
  17. package/dist/bin/pushary-logout.js +1 -1
  18. package/dist/bin/pushary-mode.js +2 -2
  19. package/dist/bin/pushary-notification-hook.js +3 -3
  20. package/dist/bin/pushary-opencode-hook.js +3 -3
  21. package/dist/bin/pushary-permission-denied-hook.js +4 -4
  22. package/dist/bin/pushary-permission-hook.js +4 -4
  23. package/dist/bin/pushary-post-hook.js +3 -3
  24. package/dist/bin/pushary-prompt-hook.js +3 -3
  25. package/dist/bin/pushary-session-end-hook.js +3 -3
  26. package/dist/bin/pushary-session-start-hook.js +3 -3
  27. package/dist/bin/pushary-setup.js +127 -180
  28. package/dist/bin/pushary-status.js +4 -4
  29. package/dist/bin/pushary-stop-hook.js +3 -3
  30. package/dist/bin/pushary-stopfailure-hook.js +3 -3
  31. package/dist/bin/pushary-upgrade.js +8 -8
  32. package/dist/bin/pushary-wait.js +2 -2
  33. package/dist/{chunk-B6H2KAQC.js → chunk-4IEW4ZRF.js} +2 -2
  34. package/dist/{chunk-54ELC7D7.js → chunk-AWHCZOZO.js} +1 -1
  35. package/dist/chunk-B65JGBIZ.js +539 -0
  36. package/dist/{chunk-KUZWXPUS.js → chunk-BVNYTQ5I.js} +23 -0
  37. package/dist/{chunk-AJYV3VGU.js → chunk-BXTJVM7O.js} +2 -0
  38. package/dist/{chunk-FQL5E6NG.js → chunk-CRVJCKIN.js} +85 -2
  39. package/dist/chunk-DSTLA2TC.js +68 -0
  40. package/dist/{chunk-GL3FFM7A.js → chunk-ELHCIEMM.js} +1 -1
  41. package/dist/{chunk-Y6BWPERV.js → chunk-GQ5ZC2QY.js} +1 -1
  42. package/dist/chunk-GU3EJCVV.js +43 -0
  43. package/dist/{chunk-4LOPSGLP.js → chunk-TVZQMPB7.js} +1 -1
  44. package/dist/{chunk-BQBEH6TV.js → chunk-VAMBUKXH.js} +4 -4
  45. package/dist/{chunk-DZDI62LY.js → chunk-WGKC6PU2.js} +2 -2
  46. package/dist/{chunk-7TPQEWUZ.js → chunk-YTD6PJM7.js} +1 -1
  47. package/dist/{chunk-H2QZRQR2.js → chunk-YTDH3XCE.js} +1 -1
  48. package/dist/{chunk-F2ICEWZL.js → chunk-Z4XCN2HH.js} +2 -2
  49. package/dist/{chunk-DYP6SMOR.js → chunk-Z6RFZKY4.js} +1 -1
  50. package/dist/src/index.js +4 -4
  51. package/package.json +1 -1
  52. package/dist/chunk-AZPIQG4G.js +0 -274
  53. package/dist/chunk-S6LYP7QA.js +0 -127
@@ -4,40 +4,54 @@ import {
4
4
  } from "../chunk-Z3Z4VK5F.js";
5
5
  import {
6
6
  detectClaudeVersion
7
- } from "../chunk-H2QZRQR2.js";
7
+ } from "../chunk-YTDH3XCE.js";
8
8
  import {
9
9
  describeVersionManagerPin,
10
- isBridgeLocator,
11
10
  versionManagerOf
12
- } from "../chunk-S6LYP7QA.js";
11
+ } from "../chunk-GU3EJCVV.js";
13
12
  import {
14
13
  waitForMachineReadiness
15
- } from "../chunk-7TPQEWUZ.js";
14
+ } from "../chunk-YTD6PJM7.js";
16
15
  import {
16
+ AGENT_CHOICES,
17
17
  HERMES_CONFIG_SCRIPT,
18
+ HERMES_PIP_PACKAGE,
19
+ KEEP_HINT,
20
+ captureHookTakeover,
21
+ captureMcpServer,
22
+ describeAppOwnership,
23
+ describeHookOwnership,
18
24
  describeSetupSideEffects,
25
+ effectiveControlMode,
26
+ effectiveVerify,
27
+ hookOwnerOf,
19
28
  isHermesConfigReceipt,
29
+ mcpBearerKey,
30
+ parseSetupOptions,
31
+ planHooks,
20
32
  resolveHermesPython,
21
33
  runHermesPythonScript
22
- } from "../chunk-AZPIQG4G.js";
34
+ } from "../chunk-B65JGBIZ.js";
23
35
  import {
24
36
  describeDetection,
25
37
  detectAllAgents,
26
38
  isDetected,
39
+ liveBridgeProbe,
27
40
  pluginLocationSnippet,
28
41
  registerPluginLocation,
29
42
  renderAgentInstructions,
30
43
  renderProjectAgentInstructions,
44
+ resolveBridgeStub,
31
45
  shortenHome,
32
46
  vscodeSettingsTargets,
33
47
  writeInstructionBlock
34
- } from "../chunk-FQL5E6NG.js";
48
+ } from "../chunk-CRVJCKIN.js";
35
49
  import {
36
50
  claudeWired,
37
51
  codexWired,
38
52
  geminiWired,
39
53
  pusharyHookExecutable
40
- } from "../chunk-DZDI62LY.js";
54
+ } from "../chunk-WGKC6PU2.js";
41
55
  import {
42
56
  OPENCODE_HOOK_BINARY,
43
57
  addOpenCodeMcpServer,
@@ -61,8 +75,9 @@ import {
61
75
  isClaudeMcpSnapshot,
62
76
  parseVersion,
63
77
  updateClaudeMcpSnapshot
64
- } from "../chunk-GL3FFM7A.js";
78
+ } from "../chunk-ELHCIEMM.js";
65
79
  import "../chunk-2LAHXHBR.js";
80
+ import "../chunk-DSTLA2TC.js";
66
81
  import {
67
82
  guardBinary,
68
83
  guardCursorGate,
@@ -80,7 +95,7 @@ import {
80
95
  replaceDaemonService,
81
96
  stopDaemonService,
82
97
  waitForDaemonRemoteProof
83
- } from "../chunk-F2ICEWZL.js";
98
+ } from "../chunk-Z4XCN2HH.js";
84
99
  import {
85
100
  detectProviderLiveCapabilities,
86
101
  hasSpawnProvider,
@@ -98,14 +113,15 @@ import {
98
113
  } from "../chunk-L4ZCFMWW.js";
99
114
  import {
100
115
  reportEvent
101
- } from "../chunk-4LOPSGLP.js";
116
+ } from "../chunk-TVZQMPB7.js";
102
117
  import "../chunk-F7OURW3C.js";
103
118
  import "../chunk-L4E2BIJ2.js";
104
- import "../chunk-54ELC7D7.js";
119
+ import "../chunk-AWHCZOZO.js";
105
120
  import {
106
121
  addCodexHookTrust,
107
122
  addCodexHooks,
108
123
  addCodexMcpServer,
124
+ captureCodexHookTrust,
109
125
  claudeJson,
110
126
  claudeMcpReceipt,
111
127
  claudeSettings,
@@ -118,12 +134,17 @@ import {
118
134
  codexTrustSchemeAgrees,
119
135
  cursorPluginDir,
120
136
  cursorUserHooks,
137
+ cursorUserMcp,
121
138
  geminiMd,
122
139
  geminiSettings,
123
140
  hermesConfigReceipt,
141
+ hookTakeoverReceipt,
124
142
  pusharyConfigFile,
125
- vscodePluginDir
126
- } from "../chunk-KUZWXPUS.js";
143
+ pusharyDir,
144
+ readCodexMcpAuth,
145
+ vscodePluginDir,
146
+ vscodePluginMcp
147
+ } from "../chunk-BVNYTQ5I.js";
127
148
  import {
128
149
  confirmAppConnection,
129
150
  connectDevice,
@@ -136,17 +157,16 @@ import {
136
157
  import "../chunk-3EGEA4KH.js";
137
158
  import {
138
159
  checkApiKey,
139
- describeKeyCheck
140
- } from "../chunk-AJYV3VGU.js";
160
+ describeKeyCheck,
161
+ samePusharyAccount
162
+ } from "../chunk-BXTJVM7O.js";
141
163
  import {
142
164
  setHumanStream
143
165
  } from "../chunk-QCGNGEGE.js";
144
166
  import {
145
167
  useColor
146
168
  } from "../chunk-5RUIFDTP.js";
147
- import {
148
- parseFlags
149
- } from "../chunk-GMXKITVA.js";
169
+ import "../chunk-GMXKITVA.js";
150
170
  import {
151
171
  renderCommandHelp
152
172
  } from "../chunk-43GMYSS4.js";
@@ -185,6 +205,7 @@ import {
185
205
  import "../chunk-2UMNXADU.js";
186
206
  import {
187
207
  CURSOR_GATE_EVENTS,
208
+ HOOK_LOCATOR_BINARY,
188
209
  isValidApiKey
189
210
  } from "../chunk-VNMKIJZB.js";
190
211
 
@@ -534,153 +555,10 @@ var reportSetupAbortWith = async (deps, reason, exitCode, keyCheck = "unknown")
534
555
  });
535
556
  var reportSetupAbort = (reason, exitCode, keyCheck) => reportSetupAbortWith(defaultDeps, reason, exitCode, keyCheck);
536
557
 
537
- // src/setup/options.ts
538
- var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "opencode", "custom"];
539
- var CONNECT_MODES = ["app", "browser", "web", "none"];
540
- var CONNECT_ALIASES = { auto: "app", pwa: "web" };
541
- var parseConnectMode = (raw) => {
542
- const value = raw.trim().toLowerCase();
543
- if (Object.hasOwn(CONNECT_ALIASES, value)) return CONNECT_ALIASES[value];
544
- if (CONNECT_MODES.includes(value)) return value;
545
- throw new UsageError(`--connect must be one of: ${CONNECT_MODES.join(", ")} (got ${raw})`);
546
- };
547
- var VERIFY_MODES = ["roundtrip", "push", "none"];
548
- var parseVerifyMode = (raw) => {
549
- const value = raw.trim().toLowerCase();
550
- if (VERIFY_MODES.includes(value)) return value;
551
- throw new UsageError(`--verify must be one of: ${VERIFY_MODES.join(", ")} (got ${raw})`);
552
- };
553
- var CONTROL_MODES = ["auto", "on", "off"];
554
- var parseControlMode = (raw) => {
555
- const value = raw.trim().toLowerCase();
556
- if (CONTROL_MODES.includes(value)) return value;
557
- throw new UsageError(`--control must be one of: ${CONTROL_MODES.join(", ")} (got ${raw})`);
558
- };
559
- var effectiveVerify = (options, context) => {
560
- if (options.verifyExplicit) return options.verify;
561
- if (options.verify === "roundtrip") return "roundtrip";
562
- return context.paired ? "roundtrip" : options.verify;
563
- };
564
- var SETUP_FLAGS = {
565
- bools: ["key-stdin", "skip-phone", "yes", "y", "json", "dry-run", "take-over-hooks", "help", "h"],
566
- values: ["key", "connect", "agents", "verify", "control", "transcripts"]
567
- };
568
- var effectiveControlMode = (options, stored, configPresent) => options.controlExplicit ? options.control : stored ?? (configPresent ? null : options.control);
569
- var isAgentChoice = (value) => AGENT_CHOICES.includes(value);
570
- var parseAgentSelection = (raw, yes) => {
571
- if (raw === void 0) {
572
- return yes ? { kind: "auto" } : { kind: "ask" };
573
- }
574
- const value = raw.trim().toLowerCase();
575
- if (value === "auto") return { kind: "auto" };
576
- if (value === "none") return { kind: "none" };
577
- const parts = value.split(",").map((part) => part.trim()).filter((part) => part !== "");
578
- if (parts.length === 0) throw new UsageError("--agents needs a value");
579
- const unknown = parts.filter((part) => !isAgentChoice(part));
580
- if (unknown.length > 0) {
581
- throw new UsageError(
582
- `--agents does not know: ${unknown.join(", ")}. Use auto, none, or any of: ${AGENT_CHOICES.join(", ")}`
583
- );
584
- }
585
- return { kind: "explicit", agents: [...new Set(parts)] };
586
- };
587
- var parseSetupOptions = (input2) => {
588
- const flags = parseFlags(input2.argv, SETUP_FLAGS);
589
- const stray = [
590
- ...flags.command !== void 0 && flags.command !== "setup" ? [flags.command] : [],
591
- ...flags.rest
592
- ];
593
- if (stray.length > 0) {
594
- throw new UsageError(
595
- `setup takes no positional arguments (got ${stray.join(", ")}). Did you mean --agents ${stray[0]}?`
596
- );
597
- }
598
- const yes = flags.bools.has("yes") || flags.bools.has("y");
599
- const help = flags.bools.has("help") || flags.bools.has("h");
600
- const agentsFlag = flags.values.get("agents");
601
- const agents = parseAgentSelection(agentsFlag, yes);
602
- const connectRaw = flags.values.get("connect");
603
- const connectMode = connectRaw === void 0 ? "app" : parseConnectMode(connectRaw);
604
- const verifyRaw = flags.values.get("verify");
605
- const controlRaw = flags.values.get("control");
606
- const transcriptsRaw = flags.values.get("transcripts")?.trim().toLowerCase();
607
- if (transcriptsRaw !== void 0 && transcriptsRaw !== "on" && transcriptsRaw !== "off") {
608
- throw new UsageError(`--transcripts must be one of: on, off (got ${transcriptsRaw})`);
609
- }
610
- const nonInteractive = yes || agentsFlag !== void 0 || flags.bools.has("json") || !input2.stdinIsTty;
611
- const verify = verifyRaw !== void 0 ? parseVerifyMode(verifyRaw) : nonInteractive ? "push" : "roundtrip";
612
- const key = flags.values.get("key")?.trim() || null;
613
- const keyStdin = flags.bools.has("key-stdin");
614
- if (key && keyStdin) {
615
- throw new UsageError("Pass either --key or --key-stdin, not both");
616
- }
617
- if (key && !isValidApiKey(key)) {
618
- throw new UsageError("--key is not a valid API key. Expected the pk_xxx.xxx shape from pushary.com/dashboard/agent/settings");
619
- }
620
- return {
621
- connectMode,
622
- connectExplicit: connectRaw !== void 0,
623
- // `--connect none` and `--skip-phone` are the same instruction, so they
624
- // resolve to the same field rather than being checked separately at the two
625
- // call sites that would inevitably drift apart.
626
- skipPhone: flags.bools.has("skip-phone") || connectMode === "none",
627
- // Anything that means "there is nobody to answer a prompt". A non-TTY stdin
628
- // is included because that is the case that used to hang: it is better to
629
- // take the defaults and say so than to wait forever for a keypress.
630
- //
631
- // --json counts. Under it stdout carries exactly one object and every human
632
- // line is redirected to stderr, so a prompt has nowhere to render: the
633
- // question goes to stderr while the run blocks on a keypress the caller
634
- // cannot see it is waiting for, and whatever is reading stdout waits forever
635
- // for an object that never arrives. Asking for machine-readable output is
636
- // itself a statement that no human is watching.
637
- nonInteractive,
638
- json: flags.bools.has("json"),
639
- dryRun: flags.bools.has("dry-run"),
640
- help,
641
- agents,
642
- key,
643
- keyStdin,
644
- verify,
645
- verifyExplicit: verifyRaw !== void 0,
646
- control: controlRaw === void 0 ? "auto" : parseControlMode(controlRaw),
647
- controlExplicit: controlRaw !== void 0,
648
- transcripts: transcriptsRaw === void 0 ? null : transcriptsRaw === "on",
649
- takeOverHooks: flags.bools.has("take-over-hooks")
650
- };
651
- };
652
-
653
- // src/setup/hook-ownership.ts
654
- var commandsIn = (value) => {
655
- if (Array.isArray(value)) return value.flatMap(commandsIn);
656
- if (!value || typeof value !== "object") return [];
657
- return Object.entries(value).flatMap(([key, item]) => key === "command" && typeof item === "string" ? [item] : commandsIn(item));
658
- };
659
- var hookOwnerOf = (document) => {
660
- const hooks = document && typeof document === "object" ? document.hooks : void 0;
661
- const executables = commandsIn(hooks).map(pusharyHookExecutable).filter((executable) => executable !== null);
662
- if (executables.some(isBridgeLocator)) return "app";
663
- if (executables.length > 0) return "cli";
664
- return "none";
665
- };
666
- var planHooks = (context) => {
667
- if (context.owner !== "app") return "install";
668
- if (context.takeOverHooks) return "install";
669
- return context.nonInteractive ? "keep" : "ask";
670
- };
671
- var describeAppOwnership = (agentName) => `The Pushary Mac app owns ${agentName}'s hooks.`;
672
- var takeoverQuestion = (agentName) => `Replace them with the CLI's? The app stops answering for ${agentName} and the notch loses its terminal jump there.`;
673
- var KEEP_HINT = "Left as they are. Pass --take-over-hooks to have the CLI own them instead.";
674
- var OWNER_LABELS = {
675
- app: "the Pushary Mac app",
676
- cli: "the CLI"
677
- };
678
- var describeHookOwnership = (entries, names) => {
679
- const owned = entries.filter((entry) => entry.owner !== "none");
680
- if (owned.length === 0) return null;
681
- const parts = owned.map((entry) => `${names[entry.agent]} by ${OWNER_LABELS[entry.owner]}${entry.kept ? " (kept)" : ""}`);
682
- return `Hooks: ${parts.join(", ")}.`;
683
- };
558
+ // src/setup/app-key.ts
559
+ var pickAppKey = (candidates) => candidates.fromAgents.find(
560
+ (candidate) => typeof candidate === "string" && candidate !== candidates.storedKey && candidate !== candidates.envKey && isValidApiKey(candidate)
561
+ ) ?? null;
684
562
 
685
563
  // bin/pushary-setup.ts
686
564
  var CLAUDE_SETTINGS = claudeSettings();
@@ -690,6 +568,7 @@ var HERMES_CONFIG_RECEIPT = hermesConfigReceipt();
690
568
  var CURSOR_PLUGIN_DIR = cursorPluginDir();
691
569
  var CURSOR_USER_HOOKS = cursorUserHooks();
692
570
  var VSCODE_PLUGIN_DIR = vscodePluginDir();
571
+ var VSCODE_PLUGIN_MCP = vscodePluginMcp();
693
572
  var CLAUDE_SKILL_DIR = claudeSkillDir();
694
573
  var CODEX_HOME = codexHome();
695
574
  var CODEX_SKILL_DIR = codexSkillDir();
@@ -947,16 +826,16 @@ var setupHermes = async (_apiKey) => {
947
826
  const python = resolveHermesPython();
948
827
  if (!python) {
949
828
  console.log(` ${yellow("!")} Could not locate the Python that Hermes runs in. Skipping.`);
950
- noteManual("Install the Hermes plugin by hand: <hermes-python> -m pip install hermes-plugin-pushary");
829
+ noteManual(`Install the Hermes plugin by hand: <hermes-python> -m pip install ${HERMES_PIP_PACKAGE}`);
951
830
  return "skipped";
952
831
  }
953
832
  const existingReceipt = readJsonSafe(HERMES_CONFIG_RECEIPT);
954
833
  if (existingReceipt.kind !== "missing" && (existingReceipt.kind !== "ok" || !isHermesConfigReceipt(existingReceipt.value))) {
955
834
  throw new Error(`Cannot safely configure Hermes: ${HERMES_CONFIG_RECEIPT} is unreadable or invalid.`);
956
835
  }
957
- await spinner("Installing hermes-plugin-pushary into Hermes\u2019 environment", async () => {
836
+ await spinner(`Installing ${HERMES_PIP_PACKAGE} into Hermes\u2019 environment`, async () => {
958
837
  ensurePip(python);
959
- execSync2(`"${python}" -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe", timeout: 18e4 });
838
+ execSync2(`"${python}" -m pip install --upgrade ${HERMES_PIP_PACKAGE}`, { stdio: "pipe", timeout: 18e4 });
960
839
  });
961
840
  let pluginEnabled = false;
962
841
  await spinner("Enabling plugin + routing questions to push", async () => {
@@ -1410,7 +1289,7 @@ var setupVsCode = async (apiKey) => {
1410
1289
  }
1411
1290
  });
1412
1291
  await spinner("Linking your API key", async () => {
1413
- const mcpPath = join3(VSCODE_PLUGIN_DIR, ".mcp.json");
1292
+ const mcpPath = VSCODE_PLUGIN_MCP;
1414
1293
  const mcp = readAgentJson(mcpPath);
1415
1294
  const servers = mcp.mcpServers ?? {};
1416
1295
  if (servers.pushary) {
@@ -1585,16 +1464,43 @@ var HOOK_FILES = {
1585
1464
  cursor: () => CURSOR_USER_HOOKS,
1586
1465
  vscode: () => join3(VSCODE_PLUGIN_DIR, "hooks", "hooks.json")
1587
1466
  };
1588
- var hookOwnerFor = (agent) => {
1467
+ var hookOwnerFor = (agent, appAvailable = true) => {
1589
1468
  const file = HOOK_FILES[agent]?.();
1590
1469
  if (!file) return "none";
1591
1470
  const document = readJsonSafe(file);
1592
- return hookOwnerOf(document.kind === "ok" ? document.value : null);
1471
+ return hookOwnerOf(document.kind === "ok" ? document.value : null, appAvailable);
1593
1472
  };
1594
- var confirmTakeover = async (agent) => {
1595
- console.log();
1596
- console.log(` ${yellow("!")} ${describeAppOwnership(AGENT_NAMES[agent])}`);
1597
- return confirm({ message: takeoverQuestion(AGENT_NAMES[agent]), default: false });
1473
+ var appMcpKeyFor = (agent) => {
1474
+ if (agent === "codex") {
1475
+ const auth = readCodexMcpAuth(readAgentToml(codexConfigToml()));
1476
+ return auth?.method === "embedded" ? auth.key : null;
1477
+ }
1478
+ const file = agent === "claude_code" ? CLAUDE_JSON : agent === "gemini_cli" ? GEMINI_SETTINGS : agent === "cursor" ? cursorUserMcp() : agent === "vscode" ? VSCODE_PLUGIN_MCP : null;
1479
+ return file ? mcpBearerKey(readAgentJson(file), "mcpServers", "headers") : null;
1480
+ };
1481
+ var appMcpKeyIfReadable = (agent) => {
1482
+ try {
1483
+ return appMcpKeyFor(agent);
1484
+ } catch {
1485
+ return null;
1486
+ }
1487
+ };
1488
+ var captureTakeoverReceipt = (agent) => {
1489
+ const hookFile = HOOK_FILES[agent]?.();
1490
+ if (!hookFile) return;
1491
+ const document = readAgentJson(hookFile);
1492
+ const captured = captureHookTakeover(agent, document);
1493
+ if (!captured) throw new Error(`Cannot safely take over ${AGENT_NAMES[agent]}'s hooks: no Mac app wiring was found.`);
1494
+ const codexConfig = agent === "codex" ? readAgentToml(codexConfigToml()) : null;
1495
+ const mcp = agent === "claude_code" ? [readAgentJson(CLAUDE_JSON), "mcpServers"] : agent === "codex" ? [codexConfig, "mcp_servers"] : agent === "gemini_cli" ? [document, "mcpServers"] : agent === "vscode" ? [readAgentJson(VSCODE_PLUGIN_MCP), "mcpServers"] : null;
1496
+ const receipt = {
1497
+ ...captured,
1498
+ ...codexConfig ? {
1499
+ codexTrust: captureCodexHookTrust(codexConfig, hookFile, codexHookPositions(document))
1500
+ } : {},
1501
+ ...mcp ? { mcpServer: captureMcpServer(mcp[0], mcp[1]) } : {}
1502
+ };
1503
+ writeJsonAtomic(hookTakeoverReceipt(agent), receipt, 384);
1598
1504
  };
1599
1505
  var agentIsWired = (agent) => {
1600
1506
  const readJson2 = (path) => {
@@ -1629,7 +1535,7 @@ var agentIsWired = (agent) => {
1629
1535
  case "cursor":
1630
1536
  return existsSync(join3(CURSOR_PLUGIN_DIR, "mcp.json"));
1631
1537
  case "vscode":
1632
- return existsSync(join3(VSCODE_PLUGIN_DIR, ".mcp.json"));
1538
+ return existsSync(VSCODE_PLUGIN_MCP);
1633
1539
  case "opencode":
1634
1540
  return openCodeWired();
1635
1541
  default:
@@ -1820,7 +1726,7 @@ var AGENT_TARGETS = {
1820
1726
  gemini_cli: [GEMINI_SETTINGS, GEMINI_MD],
1821
1727
  hermes: ["the Hermes virtualenv (pip install pushary-hermes)"],
1822
1728
  cursor: [join3(CURSOR_PLUGIN_DIR, "mcp.json"), CURSOR_USER_HOOKS],
1823
- vscode: [join3(VSCODE_PLUGIN_DIR, ".mcp.json"), ...vscodeSettingsTargets(existsSync)],
1729
+ vscode: [VSCODE_PLUGIN_MCP, ...vscodeSettingsTargets(existsSync)],
1824
1730
  opencode: [
1825
1731
  openCodePluginFile(),
1826
1732
  join3(openCodePluginDir(), "package.json"),
@@ -1914,6 +1820,8 @@ var main = async () => {
1914
1820
  const envCheck = ambientEligible && envKey && isValidApiKey(envKey) ? await usableStoredKey(envKey) : null;
1915
1821
  const storedCheck = ambientEligible && envCheck === null && storedKey !== null && isValidApiKey(storedKey) && // login writes both, so without this the same key is probed twice.
1916
1822
  storedKey !== envKey ? await usableStoredKey(storedKey) : null;
1823
+ const appKey = ambientEligible && envCheck === null && storedCheck === null ? pickAppKey({ fromAgents: AGENT_CHOICES.map(appMcpKeyIfReadable), storedKey, envKey }) : null;
1824
+ const appKeyCheck = appKey === null ? null : await usableStoredKey(appKey);
1917
1825
  let storedKeyCheck = null;
1918
1826
  if (flagKey) {
1919
1827
  const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
@@ -1939,6 +1847,12 @@ var main = async () => {
1939
1847
  const apiKey = await input({ message: "API key:" });
1940
1848
  trimmedKey = apiKey.trim();
1941
1849
  }
1850
+ } else if (appKey !== null && appKeyCheck !== null) {
1851
+ const masked = `${appKey.slice(0, 8)}...${appKey.slice(-4)}`;
1852
+ console.log(` ${check} Using the key the Pushary Mac app is signed in with: ${dim(masked)}`);
1853
+ console.log();
1854
+ trimmedKey = appKey;
1855
+ storedKeyCheck = appKeyCheck;
1942
1856
  } else if (options.dryRun) {
1943
1857
  console.log(` ${dim("No key on this machine. A real run would connect one first.")}`);
1944
1858
  console.log();
@@ -2044,6 +1958,29 @@ var main = async () => {
2044
1958
  }
2045
1959
  const rc = await saveApiKey(trimmedKey, controlMode, transcriptSync);
2046
1960
  const context = { rc };
1961
+ const appBridgeAvailable = resolveBridgeStub(
1962
+ join3(pusharyDir(), "bin", HOOK_LOCATOR_BINARY),
1963
+ liveBridgeProbe()
1964
+ ).kind === "bundle";
1965
+ const accountChecks = /* @__PURE__ */ new Map();
1966
+ if (keyCheck) accountChecks.set(trimmedKey, Promise.resolve(keyCheck));
1967
+ const accountCheckFor = (key) => {
1968
+ const existing = accountChecks.get(key);
1969
+ if (existing) return existing;
1970
+ const pending = checkApiKey(key);
1971
+ accountChecks.set(key, pending);
1972
+ return pending;
1973
+ };
1974
+ const appAccountMatches = async (agent) => {
1975
+ const appKey2 = appMcpKeyFor(agent);
1976
+ if (!appKey2) return false;
1977
+ if (appKey2 === trimmedKey) return true;
1978
+ const [cliAccount, appAccount] = await Promise.all([
1979
+ accountCheckFor(trimmedKey),
1980
+ accountCheckFor(appKey2)
1981
+ ]);
1982
+ return samePusharyAccount(cliAccount, appAccount);
1983
+ };
2047
1984
  if (envKey && envKey !== trimmedKey) {
2048
1985
  noteManual(
2049
1986
  `This shell still exports the previous key (${keyPrefix(envKey)}), and it wins over the one just saved. Open a new terminal before starting an agent here, or re-export PUSHARY_API_KEY.`
@@ -2062,22 +1999,32 @@ var main = async () => {
2062
1999
  for (const agent of agents) {
2063
2000
  markStep(`configure:${agent}`);
2064
2001
  try {
2065
- const ownerBefore = hookOwnerFor(agent);
2002
+ const ownerBefore = hookOwnerFor(agent, appBridgeAvailable);
2066
2003
  const plan = planHooks({ owner: ownerBefore, takeOverHooks: options.takeOverHooks, nonInteractive: options.nonInteractive });
2067
- const keepAppWiring = plan === "keep" || plan === "ask" && !await confirmTakeover(agent);
2004
+ const keepAppWiring = plan === "keep";
2068
2005
  if (keepAppWiring) {
2006
+ if (!await appAccountMatches(agent)) {
2007
+ hookOwners.push({ agent, owner: "app", kept: true });
2008
+ failed.push(agent.replace("_", " "));
2009
+ console.log(`
2010
+ ${yellow("!")} ${AGENT_NAMES[agent]}: Mac app hooks kept, but their Pushary account could not be matched to this setup.`);
2011
+ console.log(` ${dim("Sign the Mac app into the same account and repair this agent, then re-run setup.")}`);
2012
+ console.log(` ${dim("Or pass --take-over-hooks if you intentionally want the CLI to own these hooks.")}`);
2013
+ continue;
2014
+ }
2069
2015
  console.log(`
2070
2016
  ${check} ${AGENT_NAMES[agent]}: ${describeAppOwnership(AGENT_NAMES[agent])} ${dim(KEEP_HINT)}`);
2071
2017
  hookOwners.push({ agent, owner: "app", kept: true });
2072
2018
  completed.push(agent);
2073
2019
  continue;
2074
2020
  }
2021
+ if (ownerBefore === "app") captureTakeoverReceipt(agent);
2075
2022
  const outcome = await AGENT_SETUP[agent](trimmedKey, context);
2076
2023
  if (outcome === "skipped") {
2077
2024
  skipped.push(agent);
2078
2025
  } else if (agentIsWired(agent)) {
2079
2026
  completed.push(agent);
2080
- hookOwners.push({ agent, owner: hookOwnerFor(agent), kept: false });
2027
+ hookOwners.push({ agent, owner: hookOwnerFor(agent, appBridgeAvailable), kept: false });
2081
2028
  } else {
2082
2029
  failed.push(agent.replace("_", " "));
2083
2030
  console.log(` ${yellow("!")} ${agent.replace("_", " ")} reported success but its config is not there.`);
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  describeLastSeen,
4
4
  listMachines
5
- } from "../chunk-7TPQEWUZ.js";
6
- import "../chunk-54ELC7D7.js";
7
- import "../chunk-KUZWXPUS.js";
5
+ } from "../chunk-YTD6PJM7.js";
6
+ import "../chunk-AWHCZOZO.js";
7
+ import "../chunk-BVNYTQ5I.js";
8
8
  import {
9
9
  describeReach,
10
10
  fetchChannels,
@@ -14,7 +14,7 @@ import "../chunk-3EGEA4KH.js";
14
14
  import {
15
15
  checkApiKey,
16
16
  describeKeyCheck
17
- } from "../chunk-AJYV3VGU.js";
17
+ } from "../chunk-BXTJVM7O.js";
18
18
  import "../chunk-QCGNGEGE.js";
19
19
  import {
20
20
  createIo
@@ -6,11 +6,11 @@ import {
6
6
  handleStop,
7
7
  handleSubagentStop,
8
8
  isSubagentStop
9
- } from "../chunk-4LOPSGLP.js";
9
+ } from "../chunk-TVZQMPB7.js";
10
10
  import "../chunk-F7OURW3C.js";
11
11
  import "../chunk-L4E2BIJ2.js";
12
- import "../chunk-54ELC7D7.js";
13
- import "../chunk-KUZWXPUS.js";
12
+ import "../chunk-AWHCZOZO.js";
13
+ import "../chunk-BVNYTQ5I.js";
14
14
  import "../chunk-BSZYIAZL.js";
15
15
  import "../chunk-SAF6HGAA.js";
16
16
  import "../chunk-7QLSKOSU.js";
@@ -4,11 +4,11 @@ import {
4
4
  } from "../chunk-6CUUA7HO.js";
5
5
  import {
6
6
  handleStopFailure
7
- } from "../chunk-4LOPSGLP.js";
7
+ } from "../chunk-TVZQMPB7.js";
8
8
  import "../chunk-F7OURW3C.js";
9
9
  import "../chunk-L4E2BIJ2.js";
10
- import "../chunk-54ELC7D7.js";
11
- import "../chunk-KUZWXPUS.js";
10
+ import "../chunk-AWHCZOZO.js";
11
+ import "../chunk-BVNYTQ5I.js";
12
12
  import "../chunk-BSZYIAZL.js";
13
13
  import "../chunk-SAF6HGAA.js";
14
14
  import "../chunk-7QLSKOSU.js";
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reapplyAllAgents
4
- } from "../chunk-BQBEH6TV.js";
5
- import "../chunk-H2QZRQR2.js";
4
+ } from "../chunk-VAMBUKXH.js";
5
+ import "../chunk-YTDH3XCE.js";
6
6
  import {
7
7
  waitForMachineReadiness
8
- } from "../chunk-7TPQEWUZ.js";
9
- import "../chunk-DZDI62LY.js";
10
- import "../chunk-GL3FFM7A.js";
8
+ } from "../chunk-YTD6PJM7.js";
9
+ import "../chunk-WGKC6PU2.js";
10
+ import "../chunk-ELHCIEMM.js";
11
11
  import "../chunk-2LAHXHBR.js";
12
12
  import "../chunk-EZIEXHYM.js";
13
13
  import {
@@ -20,7 +20,7 @@ import {
20
20
  replaceDaemonService,
21
21
  stopDaemonService,
22
22
  waitForDaemonRemoteProof
23
- } from "../chunk-F2ICEWZL.js";
23
+ } from "../chunk-Z4XCN2HH.js";
24
24
  import {
25
25
  detectProviderLiveCapabilities,
26
26
  hasSpawnProvider,
@@ -34,14 +34,14 @@ import {
34
34
  npmErrorMessage,
35
35
  resolveGlobalBinary
36
36
  } from "../chunk-L4ZCFMWW.js";
37
- import "../chunk-54ELC7D7.js";
37
+ import "../chunk-AWHCZOZO.js";
38
38
  import {
39
39
  claudeJson,
40
40
  codexConfigToml,
41
41
  cursorPluginDir,
42
42
  geminiSettings,
43
43
  vscodePluginDir
44
- } from "../chunk-KUZWXPUS.js";
44
+ } from "../chunk-BVNYTQ5I.js";
45
45
  import {
46
46
  exitOnHelpFlag
47
47
  } from "../chunk-BVBEURWJ.js";
@@ -4,8 +4,8 @@ import {
4
4
  } from "../chunk-IIENZE2J.js";
5
5
  import {
6
6
  exitOnFailedResponse
7
- } from "../chunk-DYP6SMOR.js";
8
- import "../chunk-AJYV3VGU.js";
7
+ } from "../chunk-Z6RFZKY4.js";
8
+ import "../chunk-BXTJVM7O.js";
9
9
  import {
10
10
  createIo
11
11
  } from "../chunk-5RUIFDTP.js";
@@ -21,7 +21,7 @@ import {
21
21
  repoKeyFor,
22
22
  settleQuestionsForToolUse,
23
23
  throttlePass
24
- } from "./chunk-4LOPSGLP.js";
24
+ } from "./chunk-TVZQMPB7.js";
25
25
  import {
26
26
  DEFAULT_SESSION,
27
27
  askUser,
@@ -41,7 +41,7 @@ import {
41
41
  } from "./chunk-L4E2BIJ2.js";
42
42
  import {
43
43
  getMachineId
44
- } from "./chunk-54ELC7D7.js";
44
+ } from "./chunk-AWHCZOZO.js";
45
45
  import {
46
46
  getApiKey,
47
47
  getBaseUrl
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-KUZWXPUS.js";
3
+ } from "./chunk-BVNYTQ5I.js";
4
4
  import {
5
5
  writeFileAtomic
6
6
  } from "./chunk-4LL6LG5X.js";