@pushary/agent-hooks 0.90.2 → 0.92.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 (44) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/bin/pushary-bell.js +1 -1
  3. package/dist/bin/pushary-claude.js +5 -5
  4. package/dist/bin/pushary-clean.js +9 -8
  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-daemon.js +9 -9
  9. package/dist/bin/pushary-disconnect.js +2 -2
  10. package/dist/bin/pushary-doctor.js +7 -7
  11. package/dist/bin/pushary-elicitation-hook.js +2 -2
  12. package/dist/bin/pushary-gemini-bridge.js +3 -3
  13. package/dist/bin/pushary-gemini-hook.js +3 -3
  14. package/dist/bin/pushary-hook.js +4 -4
  15. package/dist/bin/pushary-login.js +3 -3
  16. package/dist/bin/pushary-logout.js +1 -1
  17. package/dist/bin/pushary-notification-hook.js +3 -3
  18. package/dist/bin/pushary-opencode-hook.js +3 -3
  19. package/dist/bin/pushary-permission-denied-hook.js +4 -4
  20. package/dist/bin/pushary-permission-hook.js +4 -4
  21. package/dist/bin/pushary-post-hook.js +3 -3
  22. package/dist/bin/pushary-prompt-hook.js +3 -3
  23. package/dist/bin/pushary-session-end-hook.js +3 -3
  24. package/dist/bin/pushary-session-start-hook.js +18 -3
  25. package/dist/bin/pushary-setup.js +39 -17
  26. package/dist/bin/pushary-status.js +3 -3
  27. package/dist/bin/pushary-stop-hook.js +3 -3
  28. package/dist/bin/pushary-stopfailure-hook.js +3 -3
  29. package/dist/bin/pushary-upgrade.js +8 -8
  30. package/dist/{chunk-AWHCZOZO.js → chunk-2ACC5M7F.js} +1 -1
  31. package/dist/{chunk-YTD6PJM7.js → chunk-2XQSN66Q.js} +1 -1
  32. package/dist/{chunk-CRVJCKIN.js → chunk-3HS4YGHB.js} +1 -1
  33. package/dist/{chunk-GQ5ZC2QY.js → chunk-4TFXV5MW.js} +1 -1
  34. package/dist/{chunk-BVNYTQ5I.js → chunk-CV2YD2JR.js} +2 -0
  35. package/dist/{chunk-YTDH3XCE.js → chunk-HDORB7WD.js} +1 -1
  36. package/dist/{chunk-VAMBUKXH.js → chunk-I3ORVBTP.js} +106 -14
  37. package/dist/{chunk-ELHCIEMM.js → chunk-I7W3UTDK.js} +16 -1
  38. package/dist/{chunk-ATUUQFDH.js → chunk-QCXKQJTG.js} +5 -2
  39. package/dist/{chunk-4IEW4ZRF.js → chunk-RQPQSJJH.js} +2 -2
  40. package/dist/{chunk-Z4XCN2HH.js → chunk-SQYBP3QQ.js} +2 -2
  41. package/dist/{chunk-TVZQMPB7.js → chunk-YXDYKECP.js} +1 -1
  42. package/dist/{chunk-WGKC6PU2.js → chunk-ZMKKK6DC.js} +2 -2
  43. package/dist/src/index.js +4 -4
  44. package/package.json +1 -1
@@ -4,16 +4,18 @@ import {
4
4
  } from "../chunk-Z3Z4VK5F.js";
5
5
  import {
6
6
  detectClaudeVersion
7
- } from "../chunk-YTDH3XCE.js";
7
+ } from "../chunk-HDORB7WD.js";
8
8
  import {
9
9
  describeVersionManagerPin,
10
10
  versionManagerOf
11
11
  } from "../chunk-GU3EJCVV.js";
12
12
  import {
13
13
  waitForMachineReadiness
14
- } from "../chunk-YTD6PJM7.js";
14
+ } from "../chunk-2XQSN66Q.js";
15
15
  import {
16
+ AGENT_CHOICES,
16
17
  HERMES_CONFIG_SCRIPT,
18
+ HERMES_PIP_PACKAGE,
17
19
  KEEP_HINT,
18
20
  captureHookTakeover,
19
21
  captureMcpServer,
@@ -29,7 +31,7 @@ import {
29
31
  planHooks,
30
32
  resolveHermesPython,
31
33
  runHermesPythonScript
32
- } from "../chunk-ATUUQFDH.js";
34
+ } from "../chunk-QCXKQJTG.js";
33
35
  import {
34
36
  describeDetection,
35
37
  detectAllAgents,
@@ -43,13 +45,13 @@ import {
43
45
  shortenHome,
44
46
  vscodeSettingsTargets,
45
47
  writeInstructionBlock
46
- } from "../chunk-CRVJCKIN.js";
48
+ } from "../chunk-3HS4YGHB.js";
47
49
  import {
48
50
  claudeWired,
49
51
  codexWired,
50
52
  geminiWired,
51
53
  pusharyHookExecutable
52
- } from "../chunk-WGKC6PU2.js";
54
+ } from "../chunk-ZMKKK6DC.js";
53
55
  import {
54
56
  OPENCODE_HOOK_BINARY,
55
57
  addOpenCodeMcpServer,
@@ -73,7 +75,7 @@ import {
73
75
  isClaudeMcpSnapshot,
74
76
  parseVersion,
75
77
  updateClaudeMcpSnapshot
76
- } from "../chunk-ELHCIEMM.js";
78
+ } from "../chunk-I7W3UTDK.js";
77
79
  import "../chunk-2LAHXHBR.js";
78
80
  import "../chunk-DSTLA2TC.js";
79
81
  import {
@@ -93,7 +95,7 @@ import {
93
95
  replaceDaemonService,
94
96
  stopDaemonService,
95
97
  waitForDaemonRemoteProof
96
- } from "../chunk-Z4XCN2HH.js";
98
+ } from "../chunk-SQYBP3QQ.js";
97
99
  import {
98
100
  detectProviderLiveCapabilities,
99
101
  hasSpawnProvider,
@@ -111,10 +113,10 @@ import {
111
113
  } from "../chunk-L4ZCFMWW.js";
112
114
  import {
113
115
  reportEvent
114
- } from "../chunk-TVZQMPB7.js";
116
+ } from "../chunk-YXDYKECP.js";
115
117
  import "../chunk-F7OURW3C.js";
116
118
  import "../chunk-L4E2BIJ2.js";
117
- import "../chunk-AWHCZOZO.js";
119
+ import "../chunk-2ACC5M7F.js";
118
120
  import {
119
121
  addCodexHookTrust,
120
122
  addCodexHooks,
@@ -142,7 +144,7 @@ import {
142
144
  readCodexMcpAuth,
143
145
  vscodePluginDir,
144
146
  vscodePluginMcp
145
- } from "../chunk-BVNYTQ5I.js";
147
+ } from "../chunk-CV2YD2JR.js";
146
148
  import {
147
149
  confirmAppConnection,
148
150
  connectDevice,
@@ -553,6 +555,11 @@ var reportSetupAbortWith = async (deps, reason, exitCode, keyCheck = "unknown")
553
555
  });
554
556
  var reportSetupAbort = (reason, exitCode, keyCheck) => reportSetupAbortWith(defaultDeps, reason, exitCode, keyCheck);
555
557
 
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;
562
+
556
563
  // bin/pushary-setup.ts
557
564
  var CLAUDE_SETTINGS = claudeSettings();
558
565
  var CLAUDE_JSON = claudeJson();
@@ -819,16 +826,16 @@ var setupHermes = async (_apiKey) => {
819
826
  const python = resolveHermesPython();
820
827
  if (!python) {
821
828
  console.log(` ${yellow("!")} Could not locate the Python that Hermes runs in. Skipping.`);
822
- 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}`);
823
830
  return "skipped";
824
831
  }
825
832
  const existingReceipt = readJsonSafe(HERMES_CONFIG_RECEIPT);
826
833
  if (existingReceipt.kind !== "missing" && (existingReceipt.kind !== "ok" || !isHermesConfigReceipt(existingReceipt.value))) {
827
834
  throw new Error(`Cannot safely configure Hermes: ${HERMES_CONFIG_RECEIPT} is unreadable or invalid.`);
828
835
  }
829
- await spinner("Installing hermes-plugin-pushary into Hermes\u2019 environment", async () => {
836
+ await spinner(`Installing ${HERMES_PIP_PACKAGE} into Hermes\u2019 environment`, async () => {
830
837
  ensurePip(python);
831
- 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 });
832
839
  });
833
840
  let pluginEnabled = false;
834
841
  await spinner("Enabling plugin + routing questions to push", async () => {
@@ -1471,6 +1478,13 @@ var appMcpKeyFor = (agent) => {
1471
1478
  const file = agent === "claude_code" ? CLAUDE_JSON : agent === "gemini_cli" ? GEMINI_SETTINGS : agent === "cursor" ? cursorUserMcp() : agent === "vscode" ? VSCODE_PLUGIN_MCP : null;
1472
1479
  return file ? mcpBearerKey(readAgentJson(file), "mcpServers", "headers") : null;
1473
1480
  };
1481
+ var appMcpKeyIfReadable = (agent) => {
1482
+ try {
1483
+ return appMcpKeyFor(agent);
1484
+ } catch {
1485
+ return null;
1486
+ }
1487
+ };
1474
1488
  var captureTakeoverReceipt = (agent) => {
1475
1489
  const hookFile = HOOK_FILES[agent]?.();
1476
1490
  if (!hookFile) return;
@@ -1806,6 +1820,8 @@ var main = async () => {
1806
1820
  const envCheck = ambientEligible && envKey && isValidApiKey(envKey) ? await usableStoredKey(envKey) : null;
1807
1821
  const storedCheck = ambientEligible && envCheck === null && storedKey !== null && isValidApiKey(storedKey) && // login writes both, so without this the same key is probed twice.
1808
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);
1809
1825
  let storedKeyCheck = null;
1810
1826
  if (flagKey) {
1811
1827
  const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
@@ -1831,6 +1847,12 @@ var main = async () => {
1831
1847
  const apiKey = await input({ message: "API key:" });
1832
1848
  trimmedKey = apiKey.trim();
1833
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;
1834
1856
  } else if (options.dryRun) {
1835
1857
  console.log(` ${dim("No key on this machine. A real run would connect one first.")}`);
1836
1858
  console.log();
@@ -1950,12 +1972,12 @@ var main = async () => {
1950
1972
  return pending;
1951
1973
  };
1952
1974
  const appAccountMatches = async (agent) => {
1953
- const appKey = appMcpKeyFor(agent);
1954
- if (!appKey) return false;
1955
- if (appKey === trimmedKey) return true;
1975
+ const appKey2 = appMcpKeyFor(agent);
1976
+ if (!appKey2) return false;
1977
+ if (appKey2 === trimmedKey) return true;
1956
1978
  const [cliAccount, appAccount] = await Promise.all([
1957
1979
  accountCheckFor(trimmedKey),
1958
- accountCheckFor(appKey)
1980
+ accountCheckFor(appKey2)
1959
1981
  ]);
1960
1982
  return samePusharyAccount(cliAccount, appAccount);
1961
1983
  };
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  describeLastSeen,
4
4
  listMachines
5
- } from "../chunk-YTD6PJM7.js";
6
- import "../chunk-AWHCZOZO.js";
7
- import "../chunk-BVNYTQ5I.js";
5
+ } from "../chunk-2XQSN66Q.js";
6
+ import "../chunk-2ACC5M7F.js";
7
+ import "../chunk-CV2YD2JR.js";
8
8
  import {
9
9
  describeReach,
10
10
  fetchChannels,
@@ -6,11 +6,11 @@ import {
6
6
  handleStop,
7
7
  handleSubagentStop,
8
8
  isSubagentStop
9
- } from "../chunk-TVZQMPB7.js";
9
+ } from "../chunk-YXDYKECP.js";
10
10
  import "../chunk-F7OURW3C.js";
11
11
  import "../chunk-L4E2BIJ2.js";
12
- import "../chunk-AWHCZOZO.js";
13
- import "../chunk-BVNYTQ5I.js";
12
+ import "../chunk-2ACC5M7F.js";
13
+ import "../chunk-CV2YD2JR.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-TVZQMPB7.js";
7
+ } from "../chunk-YXDYKECP.js";
8
8
  import "../chunk-F7OURW3C.js";
9
9
  import "../chunk-L4E2BIJ2.js";
10
- import "../chunk-AWHCZOZO.js";
11
- import "../chunk-BVNYTQ5I.js";
10
+ import "../chunk-2ACC5M7F.js";
11
+ import "../chunk-CV2YD2JR.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-VAMBUKXH.js";
5
- import "../chunk-YTDH3XCE.js";
4
+ } from "../chunk-I3ORVBTP.js";
5
+ import "../chunk-HDORB7WD.js";
6
6
  import {
7
7
  waitForMachineReadiness
8
- } from "../chunk-YTD6PJM7.js";
9
- import "../chunk-WGKC6PU2.js";
10
- import "../chunk-ELHCIEMM.js";
8
+ } from "../chunk-2XQSN66Q.js";
9
+ import "../chunk-ZMKKK6DC.js";
10
+ import "../chunk-I7W3UTDK.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-Z4XCN2HH.js";
23
+ } from "../chunk-SQYBP3QQ.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-AWHCZOZO.js";
37
+ import "../chunk-2ACC5M7F.js";
38
38
  import {
39
39
  claudeJson,
40
40
  codexConfigToml,
41
41
  cursorPluginDir,
42
42
  geminiSettings,
43
43
  vscodePluginDir
44
- } from "../chunk-BVNYTQ5I.js";
44
+ } from "../chunk-CV2YD2JR.js";
45
45
  import {
46
46
  exitOnHelpFlag
47
47
  } from "../chunk-BVBEURWJ.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-BVNYTQ5I.js";
3
+ } from "./chunk-CV2YD2JR.js";
4
4
  import {
5
5
  writeFileAtomic
6
6
  } from "./chunk-4LL6LG5X.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getMachineId
3
- } from "./chunk-AWHCZOZO.js";
3
+ } from "./chunk-2ACC5M7F.js";
4
4
  import {
5
5
  getPackageVersion
6
6
  } from "./chunk-DINDL2CF.js";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-CELX75MW.js";
4
4
  import {
5
5
  codexHomeFrom
6
- } from "./chunk-BVNYTQ5I.js";
6
+ } from "./chunk-CV2YD2JR.js";
7
7
  import {
8
8
  HOOK_LOCATOR_BINARY
9
9
  } from "./chunk-VNMKIJZB.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-BVNYTQ5I.js";
3
+ } from "./chunk-CV2YD2JR.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-4LL6LG5X.js";
@@ -288,6 +288,7 @@ var vscodePluginMcp = () => join2(vscodePluginDir(), ".mcp.json");
288
288
  var claudeSettingsLocal = () => join2(homedir2(), ".claude", "settings.local.json");
289
289
  var pusharyDir = () => join2(homedir2(), ".pushary");
290
290
  var claudeMcpReceipt = () => join2(pusharyDir(), "setup", "claude-mcp.json");
291
+ var claudeRegistrationReceipt = () => join2(pusharyDir(), "setup", "claude-registration.json");
291
292
  var hermesConfigReceipt = () => join2(pusharyDir(), "setup", "hermes-config.json");
292
293
  var hookTakeoverReceipt = (agent) => join2(pusharyDir(), "setup", `hook-takeover-${agent}.json`);
293
294
  var codexSkillDir2 = () => join2(codexHome(), "skills", "pushary");
@@ -332,6 +333,7 @@ export {
332
333
  claudeSettingsLocal,
333
334
  pusharyDir,
334
335
  claudeMcpReceipt,
336
+ claudeRegistrationReceipt,
335
337
  hermesConfigReceipt,
336
338
  hookTakeoverReceipt,
337
339
  codexSkillDir2,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-ELHCIEMM.js";
3
+ } from "./chunk-I7W3UTDK.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";
@@ -1,20 +1,25 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-YTDH3XCE.js";
3
+ } from "./chunk-HDORB7WD.js";
4
4
  import {
5
5
  claudeWired,
6
6
  codexWired,
7
7
  geminiWired
8
- } from "./chunk-WGKC6PU2.js";
8
+ } from "./chunk-ZMKKK6DC.js";
9
9
  import {
10
+ CLAUDE_HOOK_EVENTS,
10
11
  GEMINI_HOOK_BINARY,
11
12
  addClaudeMcpServer,
12
13
  addGeminiHooks,
13
14
  addGeminiMcpServer,
14
15
  addPusharyHooks,
15
16
  addPusharyToolPermissions,
16
- hasGeminiHooks
17
- } from "./chunk-ELHCIEMM.js";
17
+ formatVersion,
18
+ hasGeminiHooks,
19
+ needsReregistration,
20
+ registrationSignatures,
21
+ supportedClaudeEvents
22
+ } from "./chunk-I7W3UTDK.js";
18
23
  import {
19
24
  guardBinary
20
25
  } from "./chunk-EZIEXHYM.js";
@@ -27,21 +32,84 @@ import {
27
32
  addCodexHookTrust,
28
33
  addCodexHooks,
29
34
  addCodexMcpServer,
35
+ claudeRegistrationReceipt,
30
36
  codexConfigToml,
31
37
  codexHookPositions,
32
38
  codexHooksJson,
33
39
  hasCodexHooks,
34
40
  vscodePluginMcp
35
- } from "./chunk-BVNYTQ5I.js";
41
+ } from "./chunk-CV2YD2JR.js";
36
42
  import {
37
- readJsonSafe
43
+ readJsonSafe,
44
+ writeJsonAtomic
38
45
  } from "./chunk-4LL6LG5X.js";
46
+ import {
47
+ HOOK_LOCATOR_BINARY
48
+ } from "./chunk-VNMKIJZB.js";
39
49
 
40
50
  // src/reapply.ts
41
- import { join, dirname } from "path";
51
+ import { join } from "path";
42
52
  import { homedir } from "os";
43
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
53
+ import { readFileSync, writeFileSync } from "fs";
44
54
  import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
55
+
56
+ // src/reregister.ts
57
+ var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
58
+ var commandsUnder = (settings) => {
59
+ const hooks = asRecord(settings?.hooks);
60
+ if (!hooks) return [];
61
+ const found = [];
62
+ for (const [event, value] of Object.entries(hooks)) {
63
+ if (!Array.isArray(value)) continue;
64
+ for (const item of value) {
65
+ const inner = asRecord(item)?.hooks;
66
+ if (!Array.isArray(inner)) continue;
67
+ for (const hook of inner) {
68
+ const command = asRecord(hook)?.command;
69
+ if (typeof command === "string") found.push({ event, command });
70
+ }
71
+ }
72
+ }
73
+ return found;
74
+ };
75
+ var segments = (command) => command.split(/[\s/\\"']+/).map((segment) => segment.replace(/\.(cmd|exe)$/i, ""));
76
+ var CLI_BINARIES = new Set(CLAUDE_HOOK_EVENTS.map((hook) => hook.binary));
77
+ var namesLocator = (command) => segments(command).includes(HOOK_LOCATOR_BINARY);
78
+ var namesCliBinary = (command) => segments(command).some((segment) => CLI_BINARIES.has(segment));
79
+ var registrationHolder = (settings) => {
80
+ const commands = commandsUnder(settings);
81
+ if (commands.some(({ command }) => namesLocator(command))) return "app";
82
+ return commands.some(({ command }) => namesCliBinary(command)) ? "cli" : "none";
83
+ };
84
+ var registeredPusharyEvents = (settings) => new Set(
85
+ commandsUnder(settings).filter(({ command }) => namesLocator(command) || namesCliBinary(command)).map(({ event }) => event)
86
+ );
87
+ var reregisterVerdict = ({ settings, stored, version }) => {
88
+ const holder = registrationHolder(settings);
89
+ if (holder === "none") return { kind: "not_wired" };
90
+ if (holder === "app") return { kind: "app_owned" };
91
+ if (!needsReregistration(stored, version)) return { kind: "current" };
92
+ const supported = new Set(supportedClaudeEvents(version).map((hook) => hook.event));
93
+ const present = registeredPusharyEvents(settings);
94
+ const missing = [...supported].filter((event) => !present.has(event));
95
+ const extra = [...present].filter((event) => !supported.has(event));
96
+ return missing.length > 0 || extra.length > 0 ? { kind: "stale", version, missing, extra } : { kind: "record_only", version };
97
+ };
98
+ var receiptFor = (version, now = /* @__PURE__ */ new Date()) => ({
99
+ signatures: registrationSignatures(supportedClaudeEvents(version), version),
100
+ version: version ? formatVersion(version) : null,
101
+ recordedAt: now.toISOString()
102
+ });
103
+ var storedSignatures = (raw) => {
104
+ const signatures = asRecord(asRecord(raw)?.signatures);
105
+ if (!signatures) return void 0;
106
+ const entries = Object.entries(signatures).filter(
107
+ (pair) => typeof pair[1] === "string"
108
+ );
109
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
110
+ };
111
+
112
+ // src/reapply.ts
45
113
  var CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
46
114
  var CLAUDE_JSON = join(homedir(), ".claude.json");
47
115
  var readJson = (filePath) => {
@@ -50,11 +118,7 @@ var readJson = (filePath) => {
50
118
  if (result.kind === "missing") return {};
51
119
  return null;
52
120
  };
53
- var writeJson = (filePath, data) => {
54
- const dir = dirname(filePath);
55
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
56
- writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
57
- };
121
+ var writeJson = (filePath, data) => writeJsonAtomic(filePath, data);
58
122
  var hasPusharyClaudeHooks = (settings) => JSON.stringify(settings.hooks ?? {}).includes("pushary-hook");
59
123
  var hasPusharyMcp = (claudeJson) => !!claudeJson.mcpServers?.pushary;
60
124
  var reapplyClaudeSettings = (apiKey, paths = {}) => {
@@ -72,7 +136,7 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
72
136
  if (!settingsWritable) throw new Error("settings.json could not be parsed");
73
137
  const s = settings;
74
138
  addPusharyToolPermissions(s);
75
- addPusharyHooks(s, resolveGlobalBinDir("pushary-hook"), detectClaudeVersion());
139
+ addPusharyHooks(s, resolveGlobalBinDir("pushary-hook"), (paths.detectVersion ?? detectClaudeVersion)());
76
140
  writeJson(settingsPath, s);
77
141
  hooks = true;
78
142
  } catch {
@@ -89,6 +153,33 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
89
153
  }
90
154
  return { configured: true, reapplied: hooks || mcp, hooks, mcp };
91
155
  };
156
+ var reapplyClaudeForVersion = (apiKey, paths = {}) => {
157
+ const idle = { verdict: "current", rewrote: false, missing: [] };
158
+ try {
159
+ const settingsPath = paths.settingsPath ?? CLAUDE_SETTINGS;
160
+ const receiptPath = paths.receiptPath ?? claudeRegistrationReceipt();
161
+ const version = (paths.detectVersion ?? detectClaudeVersion)();
162
+ const receipt = readJsonSafe(receiptPath);
163
+ const verdict = reregisterVerdict({
164
+ settings: readJson(settingsPath),
165
+ stored: storedSignatures(receipt.kind === "ok" ? receipt.value : void 0),
166
+ version
167
+ });
168
+ if (verdict.kind === "not_wired" || verdict.kind === "app_owned" || verdict.kind === "current") {
169
+ return { verdict: verdict.kind, rewrote: false, missing: [] };
170
+ }
171
+ const rewrote = verdict.kind === "stale" ? reapplyClaudeSettings(apiKey, { ...paths, settingsPath }).hooks : false;
172
+ if (verdict.kind === "stale" && !rewrote) return { verdict: "stale", rewrote: false, missing: verdict.missing };
173
+ writeJsonAtomic(receiptPath, receiptFor(version));
174
+ return {
175
+ verdict: verdict.kind,
176
+ rewrote,
177
+ missing: verdict.kind === "stale" ? verdict.missing : []
178
+ };
179
+ } catch {
180
+ return idle;
181
+ }
182
+ };
92
183
  var readToml = (filePath) => {
93
184
  try {
94
185
  const raw = readFileSync(filePath, "utf-8");
@@ -205,6 +296,7 @@ var reapplyAllAgents = (apiKey) => {
205
296
  };
206
297
 
207
298
  export {
299
+ reapplyClaudeForVersion,
208
300
  inspectConfiguredManagedProviders,
209
301
  reapplyAllAgents
210
302
  };
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-EZIEXHYM.js";
8
8
  import {
9
9
  isProtectedByScope
10
- } from "./chunk-BVNYTQ5I.js";
10
+ } from "./chunk-CV2YD2JR.js";
11
11
  import {
12
12
  HOOK_BUDGETS,
13
13
  HOOK_LOCATOR_BINARY
@@ -28,6 +28,7 @@ var compareVersion = (a, b) => {
28
28
  return 0;
29
29
  };
30
30
  var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
31
+ var formatVersion = (version) => version.join(".");
31
32
 
32
33
  // src/claude-events.ts
33
34
  var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
@@ -58,6 +59,16 @@ var CLAUDE_HOOK_EVENTS = [
58
59
  { event: "Elicitation", binary: "pushary-elicitation-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: OBSERVED }
59
60
  ];
60
61
  var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
62
+ var registrationSignatures = (events, version) => Object.fromEntries(
63
+ events.map((hook) => [hook.event, `registered|${version ? formatVersion(version) : "unknown"}`])
64
+ );
65
+ var needsReregistration = (stored, version) => {
66
+ const expected = registrationSignatures(supportedClaudeEvents(version), version);
67
+ const keys = Object.keys(expected);
68
+ if (!stored) return true;
69
+ if (keys.length !== Object.keys(stored).length) return true;
70
+ return keys.some((event) => stored[event] !== expected[event]);
71
+ };
61
72
 
62
73
  // src/claude-config.ts
63
74
  import { isDeepStrictEqual } from "util";
@@ -331,7 +342,11 @@ var missingGeminiHookEvents = (settings) => {
331
342
  export {
332
343
  parseVersion,
333
344
  compareVersion,
345
+ formatVersion,
334
346
  CLAUDE_HOOK_EVENTS,
347
+ supportedClaudeEvents,
348
+ registrationSignatures,
349
+ needsReregistration,
335
350
  addClaudeMcpServer,
336
351
  isClaudeMcpSnapshot,
337
352
  captureClaudeMcpServer,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  isBridgeLocator
3
- } from "./chunk-CRVJCKIN.js";
3
+ } from "./chunk-3HS4YGHB.js";
4
4
  import {
5
5
  pusharyHookExecutable
6
- } from "./chunk-WGKC6PU2.js";
6
+ } from "./chunk-ZMKKK6DC.js";
7
7
  import {
8
8
  BELL_HOOK_BINARY
9
9
  } from "./chunk-DSTLA2TC.js";
@@ -31,6 +31,7 @@ var isHermesConfigReceipt = (value) => {
31
31
  if (!isRecord(value)) return false;
32
32
  return ["security", "approval", "transport", "fallback", "plugins", "enabled", "agent", "disabled"].every((key) => isSnapshot(value[key])) && typeof value.pushary_enabled === "boolean" && typeof value.clarify_disabled === "boolean";
33
33
  };
34
+ var HERMES_PIP_PACKAGE = "hermes-plugin-pushary";
34
35
  var HERMES_CONFIG_SCRIPT = String.raw`
35
36
  import json, os, sys
36
37
  from hermes_cli.config import load_config, save_config
@@ -514,11 +515,13 @@ var describeHookOwnership = (entries, names) => {
514
515
 
515
516
  export {
516
517
  isHermesConfigReceipt,
518
+ HERMES_PIP_PACKAGE,
517
519
  HERMES_CONFIG_SCRIPT,
518
520
  resolveHermesPython,
519
521
  runHermesPythonScript,
520
522
  createGuards,
521
523
  describeSetupSideEffects,
524
+ AGENT_CHOICES,
522
525
  effectiveVerify,
523
526
  effectiveControlMode,
524
527
  parseSetupOptions,
@@ -21,7 +21,7 @@ import {
21
21
  repoKeyFor,
22
22
  settleQuestionsForToolUse,
23
23
  throttlePass
24
- } from "./chunk-TVZQMPB7.js";
24
+ } from "./chunk-YXDYKECP.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-AWHCZOZO.js";
44
+ } from "./chunk-2ACC5M7F.js";
45
45
  import {
46
46
  getApiKey,
47
47
  getBaseUrl
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-L4ZCFMWW.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-AWHCZOZO.js";
10
+ } from "./chunk-2ACC5M7F.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-BVNYTQ5I.js";
13
+ } from "./chunk-CV2YD2JR.js";
14
14
  import {
15
15
  getPackageVersion
16
16
  } from "./chunk-DINDL2CF.js";
@@ -33,7 +33,7 @@ import {
33
33
  } from "./chunk-L4E2BIJ2.js";
34
34
  import {
35
35
  getMachineId
36
- } from "./chunk-AWHCZOZO.js";
36
+ } from "./chunk-2ACC5M7F.js";
37
37
  import {
38
38
  withRetry
39
39
  } from "./chunk-BSZYIAZL.js";
@@ -2,11 +2,11 @@ import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  GEMINI_HOOK_EVENTS,
4
4
  hasGeminiHooks
5
- } from "./chunk-ELHCIEMM.js";
5
+ } from "./chunk-I7W3UTDK.js";
6
6
  import {
7
7
  CODEX_HOOK_EVENTS,
8
8
  hasCodexHooks
9
- } from "./chunk-BVNYTQ5I.js";
9
+ } from "./chunk-CV2YD2JR.js";
10
10
  import {
11
11
  CURSOR_GATE_EVENTS,
12
12
  HOOK_LOCATOR_BINARY
package/dist/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-4IEW4ZRF.js";
3
+ } from "../chunk-RQPQSJJH.js";
4
4
  import "../chunk-2LAHXHBR.js";
5
5
  import {
6
6
  fetchAccountPublicKey
@@ -16,15 +16,15 @@ import {
16
16
  handlePostToolUse,
17
17
  handleStop,
18
18
  reportEvent
19
- } from "../chunk-TVZQMPB7.js";
19
+ } from "../chunk-YXDYKECP.js";
20
20
  import {
21
21
  askUser,
22
22
  cancelQuestion,
23
23
  waitForAnswer
24
24
  } from "../chunk-F7OURW3C.js";
25
25
  import "../chunk-L4E2BIJ2.js";
26
- import "../chunk-AWHCZOZO.js";
27
- import "../chunk-BVNYTQ5I.js";
26
+ import "../chunk-2ACC5M7F.js";
27
+ import "../chunk-CV2YD2JR.js";
28
28
  import "../chunk-BSZYIAZL.js";
29
29
  import "../chunk-SAF6HGAA.js";
30
30
  import "../chunk-7QLSKOSU.js";