@pushary/agent-hooks 0.95.8 → 0.97.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 (82) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/bin/pushary-bell-hook.js +19 -21
  3. package/dist/bin/pushary-bell.js +6 -8
  4. package/dist/bin/pushary-claude.js +21 -23
  5. package/dist/bin/pushary-clean.js +19 -22
  6. package/dist/bin/pushary-codex-bridge.js +13 -14
  7. package/dist/bin/pushary-codex-hook.js +452 -104
  8. package/dist/bin/pushary-codex.js +8 -8
  9. package/dist/bin/pushary-connect.js +6 -6
  10. package/dist/bin/pushary-cowork.js +3 -3
  11. package/dist/bin/pushary-daemon.js +25 -27
  12. package/dist/bin/pushary-disconnect.js +6 -8
  13. package/dist/bin/pushary-doctor.js +28 -23
  14. package/dist/bin/pushary-elicitation-hook.js +19 -21
  15. package/dist/bin/pushary-gemini-bridge.js +11 -12
  16. package/dist/bin/pushary-gemini-hook.js +22 -24
  17. package/dist/bin/pushary-hook.js +24 -26
  18. package/dist/bin/pushary-login.js +7 -8
  19. package/dist/bin/pushary-logout.js +4 -4
  20. package/dist/bin/pushary-mode.js +3 -3
  21. package/dist/bin/pushary-notification-hook.js +21 -23
  22. package/dist/bin/pushary-opencode-hook.js +22 -24
  23. package/dist/bin/pushary-permission-denied-hook.js +28 -27
  24. package/dist/bin/pushary-permission-hook.js +24 -26
  25. package/dist/bin/pushary-post-hook.js +21 -23
  26. package/dist/bin/pushary-prompt-hook.js +21 -23
  27. package/dist/bin/pushary-session-end-hook.js +21 -23
  28. package/dist/bin/pushary-session-start-hook.js +24 -26
  29. package/dist/bin/pushary-setup.js +34 -38
  30. package/dist/bin/pushary-stats.js +2 -2
  31. package/dist/bin/pushary-status.js +10 -11
  32. package/dist/bin/pushary-stop-hook.js +21 -23
  33. package/dist/bin/pushary-stopfailure-hook.js +21 -23
  34. package/dist/bin/pushary-transcript-register.js +17 -19
  35. package/dist/bin/pushary-transcripts.js +5 -5
  36. package/dist/bin/pushary-upgrade.js +17 -20
  37. package/dist/bin/pushary-wait.js +3 -3
  38. package/dist/{chunk-RE25RJ5A.js → chunk-3SNTEHOH.js} +4 -4
  39. package/dist/{chunk-R3OZ5ISU.js → chunk-4AXWBCCO.js} +4 -4
  40. package/dist/{chunk-XEVOPOKT.js → chunk-5ODZEFP3.js} +1 -1
  41. package/dist/{chunk-NOVA7KZ2.js → chunk-67RNDRKL.js} +260 -32
  42. package/dist/{chunk-3YFJGKJQ.js → chunk-6VXZKOBB.js} +8 -8
  43. package/dist/{chunk-ATKGKAP6.js → chunk-6WJKYD3Q.js} +8 -8
  44. package/dist/{chunk-EMGGUO2Q.js → chunk-A4AIISZZ.js} +1 -1
  45. package/dist/{chunk-N6FAXQDD.js → chunk-AXSDMDJI.js} +1 -1
  46. package/dist/{chunk-XO3JR3OP.js → chunk-B3557BDL.js} +2 -2
  47. package/dist/{chunk-QJPYJVL3.js → chunk-BYH75I7Y.js} +1 -1
  48. package/dist/{chunk-VERXHIQF.js → chunk-C2FYSFTQ.js} +1 -1
  49. package/dist/{chunk-SKPXQRKN.js → chunk-CDSA3NTB.js} +1 -1
  50. package/dist/{chunk-HUGIKB5O.js → chunk-DGUMR5LZ.js} +6 -6
  51. package/dist/{chunk-54W7ELDQ.js → chunk-DNLFSRCF.js} +2 -2
  52. package/dist/{chunk-R6LBGKWQ.js → chunk-DOKFLZWL.js} +10 -14
  53. package/dist/{chunk-U5RIXR7I.js → chunk-EHQC3GWL.js} +1 -1
  54. package/dist/{chunk-QD73WATQ.js → chunk-FU5SQKAY.js} +15 -4
  55. package/dist/{chunk-TY6IJY6I.js → chunk-GGYH6LIE.js} +1 -1
  56. package/dist/{chunk-XZWUOCJZ.js → chunk-H4VNAOCG.js} +1 -1
  57. package/dist/{chunk-MC5WUOJJ.js → chunk-HUZW6SEO.js} +75 -1
  58. package/dist/{chunk-QV266EGS.js → chunk-IBRX7LRE.js} +8 -10
  59. package/dist/{chunk-UKMLSQXA.js → chunk-ICUBJNFL.js} +2 -2
  60. package/dist/{chunk-E47WV7BJ.js → chunk-IZKCA35M.js} +2 -2
  61. package/dist/{chunk-F4TMY6XI.js → chunk-K7MLXXPY.js} +2 -2
  62. package/dist/{chunk-Y6SCMXR6.js → chunk-KDPYXMUI.js} +2 -2
  63. package/dist/{chunk-XS33QBJA.js → chunk-LGXUGYUH.js} +50 -1
  64. package/dist/{chunk-I7LKI4WO.js → chunk-LYWTLNMG.js} +6 -8
  65. package/dist/chunk-OYTOLQ3N.js +15 -0
  66. package/dist/{chunk-XUTSZYYJ.js → chunk-QFAZ6QCT.js} +1 -4
  67. package/dist/{chunk-FRXZMCVA.js → chunk-ROTIPRM4.js} +1 -1
  68. package/dist/{chunk-BQL7UDIB.js → chunk-SW37HB27.js} +1 -1
  69. package/dist/{chunk-Z4JVEL7R.js → chunk-VCKQ45QA.js} +191 -2
  70. package/dist/{chunk-SDNTRS76.js → chunk-VDTC5DEM.js} +1 -1
  71. package/dist/{chunk-VMQU7YEJ.js → chunk-VF7YOT2W.js} +1 -1
  72. package/dist/{chunk-5GKNAQTG.js → chunk-VOC55PQV.js} +1 -1
  73. package/dist/{chunk-L7NOG547.js → chunk-VTUN7WSG.js} +1 -1
  74. package/dist/{chunk-GOPIMNVC.js → chunk-WIKEADOS.js} +1 -1
  75. package/dist/chunk-XDMHLQMH.js +202 -0
  76. package/dist/{chunk-4DUEIUUO.js → chunk-YRXVC4HL.js} +1 -1
  77. package/dist/{chunk-A22VQ5ZL.js → chunk-YX3U2CLP.js} +111 -55
  78. package/dist/src/index.js +11 -11
  79. package/package.json +1 -1
  80. package/dist/chunk-3AJIK5AO.js +0 -57
  81. package/dist/chunk-L4ZCFMWW.js +0 -177
  82. package/dist/chunk-VDI645ZX.js +0 -18
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reapplyAllAgents
4
- } from "../chunk-R6LBGKWQ.js";
5
- import "../chunk-4DUEIUUO.js";
4
+ } from "../chunk-DOKFLZWL.js";
5
+ import "../chunk-YRXVC4HL.js";
6
6
  import {
7
7
  waitForMachineReadiness
8
- } from "../chunk-RE25RJ5A.js";
9
- import "../chunk-ATKGKAP6.js";
8
+ } from "../chunk-3SNTEHOH.js";
9
+ import "../chunk-6WJKYD3Q.js";
10
10
  import {
11
11
  detectProviderLiveCapabilities,
12
12
  hasSpawnProvider,
13
13
  resolveControlPolicy
14
- } from "../chunk-54W7ELDQ.js";
15
- import "../chunk-QD73WATQ.js";
14
+ } from "../chunk-DNLFSRCF.js";
15
+ import "../chunk-FU5SQKAY.js";
16
16
  import {
17
17
  daemonHasRemoteProof,
18
18
  daemonServiceManagerAvailable,
@@ -23,26 +23,23 @@ import {
23
23
  replaceDaemonService,
24
24
  stopDaemonService,
25
25
  waitForDaemonRemoteProof
26
- } from "../chunk-I7LKI4WO.js";
27
- import "../chunk-QV266EGS.js";
28
- import "../chunk-3AJIK5AO.js";
26
+ } from "../chunk-LYWTLNMG.js";
27
+ import "../chunk-IBRX7LRE.js";
29
28
  import {
30
29
  needsShell
31
30
  } from "../chunk-XHKBHWLX.js";
32
- import {
33
- execNpm,
34
- npmErrorMessage,
35
- resolveGlobalBinary
36
- } from "../chunk-L4ZCFMWW.js";
37
- import "../chunk-XUTSZYYJ.js";
38
- import "../chunk-E47WV7BJ.js";
31
+ import "../chunk-QFAZ6QCT.js";
32
+ import "../chunk-IZKCA35M.js";
39
33
  import {
40
34
  claudeJson,
41
35
  codexConfigToml,
42
36
  cursorPluginDir,
37
+ execNpm,
43
38
  geminiSettings,
39
+ npmErrorMessage,
40
+ resolveGlobalBinary,
44
41
  vscodePluginDir
45
- } from "../chunk-NOVA7KZ2.js";
42
+ } from "../chunk-67RNDRKL.js";
46
43
  import {
47
44
  exitOnHelpFlag
48
45
  } from "../chunk-GOQHN2UH.js";
@@ -52,13 +49,13 @@ import {
52
49
  } from "../chunk-DINDL2CF.js";
53
50
  import {
54
51
  readControlMode
55
- } from "../chunk-VMQU7YEJ.js";
52
+ } from "../chunk-VF7YOT2W.js";
56
53
  import "../chunk-KZERVKTD.js";
57
- import "../chunk-L7NOG547.js";
54
+ import "../chunk-VTUN7WSG.js";
58
55
  import {
59
56
  getApiKey
60
57
  } from "../chunk-2UMNXADU.js";
61
- import "../chunk-MC5WUOJJ.js";
58
+ import "../chunk-HUZW6SEO.js";
62
59
 
63
60
  // bin/pushary-upgrade.ts
64
61
  import { existsSync } from "fs";
@@ -4,8 +4,8 @@ import {
4
4
  } from "../chunk-CVWG2N3Y.js";
5
5
  import {
6
6
  exitOnFailedResponse
7
- } from "../chunk-SDNTRS76.js";
8
- import "../chunk-BQL7UDIB.js";
7
+ } from "../chunk-VDTC5DEM.js";
8
+ import "../chunk-SW37HB27.js";
9
9
  import {
10
10
  createIo
11
11
  } from "../chunk-5RUIFDTP.js";
@@ -28,7 +28,7 @@ import {
28
28
  getApiKey,
29
29
  getBaseUrl
30
30
  } from "../chunk-2UMNXADU.js";
31
- import "../chunk-MC5WUOJJ.js";
31
+ import "../chunk-HUZW6SEO.js";
32
32
 
33
33
  // bin/pushary-wait.ts
34
34
  exitOnHelpFlag("wait");
@@ -2,24 +2,24 @@ import {
2
2
  detectProviderLiveCapabilities,
3
3
  hasSpawnProvider,
4
4
  resolveControlPolicy
5
- } from "./chunk-54W7ELDQ.js";
5
+ } from "./chunk-DNLFSRCF.js";
6
6
  import {
7
7
  daemonHasRemoteProof,
8
8
  daemonServiceManagerAvailable,
9
9
  hasDaemonServiceDefinition,
10
10
  readDaemonNeedsLogin,
11
11
  readDaemonStatus
12
- } from "./chunk-I7LKI4WO.js";
12
+ } from "./chunk-LYWTLNMG.js";
13
13
  import {
14
14
  getMachineId
15
- } from "./chunk-E47WV7BJ.js";
15
+ } from "./chunk-IZKCA35M.js";
16
16
  import {
17
17
  getPackageVersion
18
18
  } from "./chunk-DINDL2CF.js";
19
19
  import {
20
20
  readControlMode,
21
21
  readKeySource
22
- } from "./chunk-VMQU7YEJ.js";
22
+ } from "./chunk-VF7YOT2W.js";
23
23
  import {
24
24
  getBaseUrl
25
25
  } from "./chunk-2UMNXADU.js";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  AGENT_CHOICES
3
- } from "./chunk-XZWUOCJZ.js";
3
+ } from "./chunk-H4VNAOCG.js";
4
4
  import {
5
5
  isBridgeLocator
6
- } from "./chunk-QJPYJVL3.js";
6
+ } from "./chunk-BYH75I7Y.js";
7
7
  import {
8
8
  pusharyHookExecutable
9
- } from "./chunk-QD73WATQ.js";
9
+ } from "./chunk-FU5SQKAY.js";
10
10
  import {
11
11
  BELL_HOOK_BINARY
12
- } from "./chunk-GOPIMNVC.js";
12
+ } from "./chunk-WIKEADOS.js";
13
13
 
14
14
  // src/setup/hook-ownership.ts
15
15
  var commandsIn = (value) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  registerTranscript
3
- } from "./chunk-3YFJGKJQ.js";
3
+ } from "./chunk-6VXZKOBB.js";
4
4
 
5
5
  // src/admission.ts
6
6
  import { readFileSync } from "fs";
@@ -1,7 +1,222 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
- HOOK_LOCATOR_BINARY
4
- } from "./chunk-MC5WUOJJ.js";
3
+ HOOK_LOCATOR_BINARY,
4
+ withHookHoldBudget
5
+ } from "./chunk-HUZW6SEO.js";
6
+
7
+ // src/npm.ts
8
+ import { execFileSync, execSync } from "child_process";
9
+ import { existsSync, readFileSync, realpathSync } from "fs";
10
+ import { dirname, isAbsolute, join, relative, resolve } from "path";
11
+ var cleanNpmEnv = () => {
12
+ const env = {};
13
+ for (const [key, value] of Object.entries(process.env)) {
14
+ if (key.toLowerCase().startsWith("npm_config_workspace")) continue;
15
+ env[key] = value;
16
+ }
17
+ return env;
18
+ };
19
+ var npmErrorMessage = (err) => {
20
+ const e = err;
21
+ const text = [e?.stderr, e?.stdout].map((part) => part ? part.toString() : "").join("\n");
22
+ const line = text.split("\n").map((l) => l.replace(/^npm error\s*/i, "").trim()).find((l) => l && !l.startsWith("A complete log") && !/^code\s/i.test(l));
23
+ return line || e?.message || String(err);
24
+ };
25
+ var execNpm = (args, options = {}) => {
26
+ return execSync(`npm ${args}`, {
27
+ timeout: 12e4,
28
+ stdio: "pipe",
29
+ ...options,
30
+ env: { ...cleanNpmEnv(), ...options.env ?? {} }
31
+ });
32
+ };
33
+ var resolveGlobalBinary = (name) => {
34
+ try {
35
+ const prefix = execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim();
36
+ const candidates = process.platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
37
+ return candidates.find(existsSync);
38
+ } catch {
39
+ return void 0;
40
+ }
41
+ };
42
+ var resolvePathBinary = (name, platform = process.platform) => {
43
+ try {
44
+ return execFileSync(platform === "win32" ? "where" : "which", [name], {
45
+ encoding: "utf8",
46
+ stdio: "pipe",
47
+ timeout: 5e3
48
+ }).split(/\r?\n/, 1)[0]?.trim() || void 0;
49
+ } catch {
50
+ return void 0;
51
+ }
52
+ };
53
+ var resolveBinary = (name) => resolveGlobalBinary(name) ?? resolvePathBinary(name);
54
+ var resolvePackageVersionFromBinary = (binary, packageName) => {
55
+ try {
56
+ let dir = dirname(realpathSync(binary));
57
+ const packagePath = packageName.split("/");
58
+ for (let depth = 0; depth < 8; depth++) {
59
+ for (const manifest of [
60
+ join(dir, "package.json"),
61
+ join(dir, "node_modules", ...packagePath, "package.json")
62
+ ]) {
63
+ try {
64
+ const value = JSON.parse(readFileSync(manifest, "utf8"));
65
+ if (value.name === packageName && typeof value.version === "string") return value.version;
66
+ } catch {
67
+ }
68
+ }
69
+ const parent = dirname(dir);
70
+ if (parent === dir) break;
71
+ dir = parent;
72
+ }
73
+ } catch {
74
+ }
75
+ return void 0;
76
+ };
77
+ var resolveGlobalPackageVersion = (name) => {
78
+ try {
79
+ const result = JSON.parse(execNpm("list -g --depth=0 --json --no-workspaces", { timeout: 5e3 }).toString());
80
+ const value = result.dependencies?.[name]?.version;
81
+ return typeof value === "string" ? value : void 0;
82
+ } catch {
83
+ return void 0;
84
+ }
85
+ };
86
+ var localImportsPresent = (entry, packageDir, seen = /* @__PURE__ */ new Set()) => {
87
+ if (seen.has(entry)) return true;
88
+ seen.add(entry);
89
+ try {
90
+ const source = readFileSync(entry, "utf8");
91
+ for (const match of source.matchAll(/\b(?:import|export)\s+(?:[^"'()]*?\s+from\s+)?["'](\.{1,2}\/[^"']+)["']|\bimport\s*\(\s*["'](\.{1,2}\/[^"']+)["']/g)) {
92
+ const dependency = resolve(dirname(entry), match[1] ?? match[2]);
93
+ const localPath = relative(packageDir, dependency);
94
+ if (localPath.startsWith("..") || isAbsolute(localPath) || !existsSync(dependency)) return false;
95
+ if (/\.[cm]?js$/.test(dependency) && !localImportsPresent(dependency, packageDir, seen)) return false;
96
+ }
97
+ return true;
98
+ } catch {
99
+ return false;
100
+ }
101
+ };
102
+ var packageBinsPresent = (packageDir, prefix, platform = process.platform, requiredFiles = []) => {
103
+ try {
104
+ const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
105
+ if (!manifest.bin || typeof manifest.bin !== "object" || Array.isArray(manifest.bin)) return false;
106
+ const bins = Object.entries(manifest.bin);
107
+ if (bins.length === 0) return false;
108
+ if (!requiredFiles.every((path) => existsSync(join(packageDir, path)))) return false;
109
+ const seen = /* @__PURE__ */ new Set();
110
+ return bins.every(([name, target]) => {
111
+ if (typeof target !== "string") return false;
112
+ const shims = platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
113
+ const entry = join(packageDir, target);
114
+ return existsSync(entry) && shims.some(existsSync) && localImportsPresent(entry, packageDir, seen);
115
+ });
116
+ } catch {
117
+ return false;
118
+ }
119
+ };
120
+ var AGENT_HOOKS_RUNTIME_FILES = [
121
+ "data/SKILL.md",
122
+ "data/cursor-plugin/.cursor-plugin/plugin.json",
123
+ "data/cursor-plugin/hooks/hooks.json",
124
+ "data/cursor-plugin/mcp.json",
125
+ "data/cursor-plugin/scripts/pushary-gate.mjs",
126
+ "data/vscode-plugin/.claude-plugin/plugin.json",
127
+ "data/vscode-plugin/hooks/hooks.json",
128
+ "data/vscode-plugin/.mcp.json",
129
+ "data/vscode-plugin/scripts/pushary-gate.mjs"
130
+ ];
131
+ var globalPackageBinsPresent = (name) => {
132
+ try {
133
+ const root = execNpm("root -g --no-workspaces", { timeout: 5e3 }).toString().trim();
134
+ const prefix = execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim();
135
+ return packageBinsPresent(join(root, ...name.split("/")), prefix, process.platform, AGENT_HOOKS_RUNTIME_FILES);
136
+ } catch {
137
+ return false;
138
+ }
139
+ };
140
+ var resolveGlobalBinDir = (probe) => {
141
+ if (process.platform === "win32") return void 0;
142
+ const resolved = resolveGlobalBinary(probe);
143
+ return resolved ? resolved.slice(0, resolved.length - probe.length - 1) : void 0;
144
+ };
145
+ var quoteIfNeeded = (path) => /\s/.test(path) ? `"${path}"` : path;
146
+ var needsGlobalInstall = (requested, installed, binaryPresent) => requested !== installed || !binaryPresent;
147
+ var installState;
148
+ var installGlobally = (options) => {
149
+ if (installState) {
150
+ return installState.then((result) => result === "installed" ? "cached" : result);
151
+ }
152
+ const started = (async () => {
153
+ const pinned = process.env.PUSHARY_PIN_GLOBAL_VERSION !== "0";
154
+ if (pinned && !needsGlobalInstall(
155
+ options.version,
156
+ resolveGlobalPackageVersion("@pushary/agent-hooks"),
157
+ globalPackageBinsPresent("@pushary/agent-hooks")
158
+ )) {
159
+ return "skipped";
160
+ }
161
+ const spec = pinned && options.version && options.version !== "0.0.0" ? `@pushary/agent-hooks@${options.version}` : "@pushary/agent-hooks@latest";
162
+ execNpm(`install -g --no-workspaces ${spec}`, { timeout: options.timeoutMs ?? 12e4 });
163
+ return "installed";
164
+ })();
165
+ installState = started;
166
+ started.catch(() => {
167
+ if (installState === started) installState = void 0;
168
+ });
169
+ return started;
170
+ };
171
+
172
+ // src/hook-command.ts
173
+ var HOOK_SHELL = "/bin/sh -c";
174
+ var holdBudgetOf = (hook) => hook.holds ? hook.timeout : void 0;
175
+ var LOCATE = 'B=$(command -v "$0") || exit 0; [ -f "$B" ] || exit 0; [ -x "$B" ] || exit 0';
176
+ var NODE_PRESENT = "command -v node >/dev/null 2>&1 || exit 0";
177
+ var CURSOR_NO_OPINION = { permission: "deny", user_message: "Command denied via Pushary.", agent_message: "Pushary hook is unavailable. Repair the integration before retrying." };
178
+ var shellDoubleQuoted = (text) => `"${text.replace(/(["\\$`])/g, "\\$1")}"`;
179
+ var CURSOR_NATIVE_PROMPT = { permission: "ask" };
180
+ var CURSOR_NATIVE_PROMPT_EVENTS = ["beforeShellExecution", "beforeMCPExecution"];
181
+ var hookEventIsOneOf = (events) => events.flatMap((event) => [`*\\"hook_event_name\\":\\"${event}\\"*`, `*\\"hook_event_name\\":\\ \\"${event}\\"*`]).join("|");
182
+ var CURSOR_MISS = `{ E=$(cat); case "$E" in ${hookEventIsOneOf(CURSOR_NATIVE_PROMPT_EVENTS)}) printf %s ${shellDoubleQuoted(JSON.stringify(CURSOR_NATIVE_PROMPT))} ;; *) printf %s ${shellDoubleQuoted(JSON.stringify(CURSOR_NO_OPINION))} ;; esac; exit 0; }`;
183
+ var HOOK_GUARD_SCRIPTS = {
184
+ native_binary: `${LOCATE}; exec "$B" "$@"`,
185
+ node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B" "$@"`,
186
+ node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0" "$@"`,
187
+ cursor_gate_script: `[ -f "$0" ] || ${CURSOR_MISS}; command -v node >/dev/null 2>&1 || ${CURSOR_MISS}; exec node "$0" "$@"`
188
+ };
189
+ var BINARY_GUARDS = {
190
+ native: "native_binary",
191
+ node: "node_binary"
192
+ };
193
+ var guardKindOf = (target) => {
194
+ switch (target.kind) {
195
+ case "node":
196
+ return "node_script";
197
+ case "cursor_gate":
198
+ return "cursor_gate_script";
199
+ case "binary":
200
+ return BINARY_GUARDS[target.runtime];
201
+ }
202
+ };
203
+ var targetPath = (target) => target.kind === "binary" ? target.path : target.script;
204
+ var runsThroughNode = (target) => target.kind !== "binary";
205
+ var guardHookCommand = (target, platform = process.platform) => {
206
+ const path = quoteIfNeeded(targetPath(target));
207
+ if (platform === "win32") return runsThroughNode(target) ? `node ${path}` : path;
208
+ return `${HOOK_SHELL} '${HOOK_GUARD_SCRIPTS[guardKindOf(target)]}' ${path}`;
209
+ };
210
+ var guardBinary = (path, platform) => guardHookCommand({ kind: "binary", path, runtime: "node" }, platform);
211
+ var guardCursorGate = (script, platform) => guardHookCommand({ kind: "cursor_gate", script }, platform);
212
+ var guardedArgv = (target, args = [], platform = process.platform) => {
213
+ const path = targetPath(target);
214
+ if (platform === "win32") {
215
+ return runsThroughNode(target) ? ["node", path, ...args] : [path, ...args];
216
+ }
217
+ const [shell, ...shellArgs] = HOOK_SHELL.split(/\s+/);
218
+ return [shell, ...shellArgs, HOOK_GUARD_SCRIPTS[guardKindOf(target)], path, ...args];
219
+ };
5
220
 
6
221
  // src/removal-scope.ts
7
222
  var namesAppLocator = (command) => command.includes(HOOK_LOCATOR_BINARY);
@@ -10,7 +225,7 @@ var isProtectedByScope = (command, scope) => scope?.keepAppWiring === true && na
10
225
  // src/codex-config.ts
11
226
  import { createHash } from "crypto";
12
227
  import { homedir } from "os";
13
- import { join } from "path";
228
+ import { join as join2 } from "path";
14
229
 
15
230
  // src/canonical-json.ts
16
231
  var canonicalJson = (value) => {
@@ -23,17 +238,17 @@ var canonicalJson = (value) => {
23
238
  // src/codex-config.ts
24
239
  var CODEX_HOOK_BINARY = "pushary-codex-hook";
25
240
  var codexHome = () => codexHomeFrom(homedir());
26
- var codexHomeFrom = (home) => process.env.CODEX_HOME?.trim() || join(home, ".codex");
27
- var codexConfigToml = () => join(codexHome(), "config.toml");
28
- var codexHooksJson = () => join(codexHome(), "hooks.json");
29
- var codexAgentsMd = () => join(codexHome(), "AGENTS.md");
30
- var codexSkillDir = () => join(codexHome(), "skills", "pushary");
241
+ var codexHomeFrom = (home) => process.env.CODEX_HOME?.trim() || join2(home, ".codex");
242
+ var codexConfigToml = () => join2(codexHome(), "config.toml");
243
+ var codexHooksJson = () => join2(codexHome(), "hooks.json");
244
+ var codexAgentsMd = () => join2(codexHome(), "AGENTS.md");
245
+ var codexSkillDir = () => join2(codexHome(), "skills", "pushary");
31
246
  var CODEX_HOOK_EVENTS = [
32
247
  { event: "PermissionRequest", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Waiting for your phone" },
33
248
  { event: "PreToolUse", matcher: "Bash|apply_patch|mcp__.*", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Checking Pushary policy" },
34
249
  { event: "PostToolUse", matcher: "Bash|apply_patch|mcp__.*", delivery: "telemetry", timeout: 10 },
35
250
  { event: "UserPromptSubmit", delivery: "telemetry", timeout: 10 },
36
- { event: "Stop", delivery: "telemetry", timeout: 10 },
251
+ { event: "Stop", delivery: "rescue", timeout: HOOK_BUDGETS.codex.budgetSeconds, holds: true },
37
252
  // `clear` alongside startup and resume, matching what we already do on Claude
38
253
  // Code. Codex names all three in its own SessionStart sources, and without it a
39
254
  // session the person started by clearing is one we never see begin.
@@ -115,7 +330,7 @@ var addCodexHooks = (config, command) => {
115
330
  ...definition.matcher ? { matcher: definition.matcher } : {},
116
331
  hooks: [{
117
332
  type: "command",
118
- command,
333
+ command: withHookHoldBudget(command, holdBudgetOf(definition)),
119
334
  timeout: definition.timeout,
120
335
  ...definition.statusMessage ? { statusMessage: definition.statusMessage } : {}
121
336
  }]
@@ -159,7 +374,7 @@ var missingCodexHookEvents = (config) => {
159
374
  }).map((definition) => definition.event);
160
375
  };
161
376
  var codexHookTrustHash = (definition, command) => {
162
- const hook = { command, timeout: definition.timeout, type: "command" };
377
+ const hook = { command: withHookHoldBudget(command, holdBudgetOf(definition)), timeout: definition.timeout, type: "command" };
163
378
  if (definition.statusMessage) hook.statusMessage = definition.statusMessage;
164
379
  const entry = { hooks: [hook] };
165
380
  if (definition.matcher) entry.matcher = definition.matcher;
@@ -308,29 +523,42 @@ var staleTrustKeys = (state, hooksJsonPath, event, currentKey, ourHash) => {
308
523
 
309
524
  // src/setup/paths.ts
310
525
  import { homedir as homedir2 } from "os";
311
- import { join as join2 } from "path";
312
- var claudeSettings = () => join2(homedir2(), ".claude", "settings.json");
313
- var claudeJson = () => join2(homedir2(), ".claude.json");
314
- var claudeSkillDir = () => join2(homedir2(), ".claude", "skills", "pushary");
315
- var cursorPluginDir = () => join2(homedir2(), ".cursor", "plugins", "local", "pushary");
316
- var cursorUserHooks = () => join2(homedir2(), ".cursor", "hooks.json");
317
- var cursorUserMcp = () => join2(homedir2(), ".cursor", "mcp.json");
318
- var vscodePluginDir = () => join2(homedir2(), ".pushary", "plugins", "vscode");
319
- var vscodePluginMcp = () => join2(vscodePluginDir(), ".mcp.json");
320
- var claudeSettingsLocal = () => join2(homedir2(), ".claude", "settings.local.json");
321
- var pusharyDir = () => join2(homedir2(), ".pushary");
322
- var claudeMcpReceipt = () => join2(pusharyDir(), "setup", "claude-mcp.json");
323
- var bridgeAgentKey = () => join2(pusharyDir(), "bridge-auth", "agent-key.json");
324
- var claudeRegistrationReceipt = () => join2(pusharyDir(), "setup", "claude-registration.json");
325
- var hermesConfigReceipt = () => join2(pusharyDir(), "setup", "hermes-config.json");
326
- var hookTakeoverReceipt = (agent) => join2(pusharyDir(), "setup", `hook-takeover-${agent}.json`);
327
- var codexSkillDir2 = () => join2(codexHome(), "skills", "pushary");
328
- var codexAgentsMd2 = () => join2(codexHome(), "AGENTS.md");
329
- var geminiSettings = () => join2(homedir2(), ".gemini", "settings.json");
330
- var geminiMd = () => join2(homedir2(), ".gemini", "GEMINI.md");
331
- var pusharyConfigFile = () => join2(homedir2(), ".pushary", "config.json");
526
+ import { join as join3 } from "path";
527
+ var claudeSettings = () => join3(homedir2(), ".claude", "settings.json");
528
+ var claudeJson = () => join3(homedir2(), ".claude.json");
529
+ var claudeSkillDir = () => join3(homedir2(), ".claude", "skills", "pushary");
530
+ var cursorPluginDir = () => join3(homedir2(), ".cursor", "plugins", "local", "pushary");
531
+ var cursorUserHooks = () => join3(homedir2(), ".cursor", "hooks.json");
532
+ var cursorUserMcp = () => join3(homedir2(), ".cursor", "mcp.json");
533
+ var vscodePluginDir = () => join3(homedir2(), ".pushary", "plugins", "vscode");
534
+ var vscodePluginMcp = () => join3(vscodePluginDir(), ".mcp.json");
535
+ var claudeSettingsLocal = () => join3(homedir2(), ".claude", "settings.local.json");
536
+ var pusharyDir = () => join3(homedir2(), ".pushary");
537
+ var claudeMcpReceipt = () => join3(pusharyDir(), "setup", "claude-mcp.json");
538
+ var bridgeAgentKey = () => join3(pusharyDir(), "bridge-auth", "agent-key.json");
539
+ var claudeRegistrationReceipt = () => join3(pusharyDir(), "setup", "claude-registration.json");
540
+ var hermesConfigReceipt = () => join3(pusharyDir(), "setup", "hermes-config.json");
541
+ var hookTakeoverReceipt = (agent) => join3(pusharyDir(), "setup", `hook-takeover-${agent}.json`);
542
+ var codexSkillDir2 = () => join3(codexHome(), "skills", "pushary");
543
+ var codexAgentsMd2 = () => join3(codexHome(), "AGENTS.md");
544
+ var geminiSettings = () => join3(homedir2(), ".gemini", "settings.json");
545
+ var geminiMd = () => join3(homedir2(), ".gemini", "GEMINI.md");
546
+ var pusharyConfigFile = () => join3(homedir2(), ".pushary", "config.json");
332
547
 
333
548
  export {
549
+ npmErrorMessage,
550
+ execNpm,
551
+ resolveGlobalBinary,
552
+ resolvePathBinary,
553
+ resolveBinary,
554
+ resolvePackageVersionFromBinary,
555
+ resolveGlobalPackageVersion,
556
+ resolveGlobalBinDir,
557
+ installGlobally,
558
+ holdBudgetOf,
559
+ guardBinary,
560
+ guardCursorGate,
561
+ guardedArgv,
334
562
  isProtectedByScope,
335
563
  canonicalJson,
336
564
  CODEX_HOOK_BINARY,
@@ -3,19 +3,19 @@ import {
3
3
  loadPin,
4
4
  savePin,
5
5
  verifyRecipients
6
- } from "./chunk-EMGGUO2Q.js";
6
+ } from "./chunk-A4AIISZZ.js";
7
7
  import {
8
8
  hookOwnerOf
9
- } from "./chunk-R3OZ5ISU.js";
9
+ } from "./chunk-4AXWBCCO.js";
10
10
  import {
11
11
  ensureDaemonRunning
12
- } from "./chunk-I7LKI4WO.js";
12
+ } from "./chunk-LYWTLNMG.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
16
  generateSessionKey,
17
17
  wrapSessionKey
18
- } from "./chunk-SKPXQRKN.js";
18
+ } from "./chunk-CDSA3NTB.js";
19
19
  import {
20
20
  fetchTranscriptRecipients,
21
21
  uploadTranscriptRecords
@@ -23,17 +23,17 @@ import {
23
23
  import {
24
24
  transcriptEnvironmentPreference,
25
25
  transcriptsEnabled
26
- } from "./chunk-VERXHIQF.js";
26
+ } from "./chunk-C2FYSFTQ.js";
27
27
  import {
28
28
  codexHome,
29
29
  pusharyDir
30
- } from "./chunk-NOVA7KZ2.js";
30
+ } from "./chunk-67RNDRKL.js";
31
31
  import {
32
32
  readTranscriptPreference
33
- } from "./chunk-VMQU7YEJ.js";
33
+ } from "./chunk-VF7YOT2W.js";
34
34
  import {
35
35
  writeJsonAtomic
36
- } from "./chunk-L7NOG547.js";
36
+ } from "./chunk-VTUN7WSG.js";
37
37
  import {
38
38
  getApiKey,
39
39
  getBaseUrl
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-QV266EGS.js";
5
- import {
6
- guardBinary
7
- } from "./chunk-3AJIK5AO.js";
4
+ } from "./chunk-IBRX7LRE.js";
8
5
  import {
6
+ guardBinary,
7
+ holdBudgetOf,
9
8
  isProtectedByScope
10
- } from "./chunk-NOVA7KZ2.js";
9
+ } from "./chunk-67RNDRKL.js";
11
10
  import {
12
- HOOK_LOCATOR_BINARY
13
- } from "./chunk-MC5WUOJJ.js";
11
+ HOOK_LOCATOR_BINARY,
12
+ withHookHoldBudget
13
+ } from "./chunk-HUZW6SEO.js";
14
14
 
15
15
  // src/claude-config.ts
16
16
  import { join } from "path";
@@ -142,7 +142,7 @@ var entry = (definition, resolve) => ({
142
142
  ...definition.matcher ? { matcher: definition.matcher } : {},
143
143
  hooks: [{
144
144
  type: "command",
145
- command: resolve(definition.binary),
145
+ command: withHookHoldBudget(resolve(definition.binary), holdBudgetOf(definition)),
146
146
  timeout: definition.timeout
147
147
  }]
148
148
  });
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  writeJsonAtomicDurable
3
- } from "./chunk-L7NOG547.js";
3
+ } from "./chunk-VTUN7WSG.js";
4
4
 
5
5
  // src/transcript-recipient-pin.ts
6
6
  import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolvePathBinary
3
- } from "./chunk-L4ZCFMWW.js";
3
+ } from "./chunk-67RNDRKL.js";
4
4
 
5
5
  // src/hermes-config.ts
6
6
  import { execFileSync } from "child_process";
@@ -15,10 +15,10 @@ import {
15
15
  } from "./chunk-EYRGBYZG.js";
16
16
  import {
17
17
  reportedInstallSource
18
- } from "./chunk-L7NOG547.js";
18
+ } from "./chunk-VTUN7WSG.js";
19
19
  import {
20
20
  isAnswerSource
21
- } from "./chunk-MC5WUOJJ.js";
21
+ } from "./chunk-HUZW6SEO.js";
22
22
 
23
23
  // src/reach.ts
24
24
  var reachVerdict = (channels) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_LOCATOR_BINARY
3
- } from "./chunk-MC5WUOJJ.js";
3
+ } from "./chunk-HUZW6SEO.js";
4
4
 
5
5
  // src/diagnostics/bridge.ts
6
6
  import { execFileSync } from "child_process";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-VMQU7YEJ.js";
3
+ } from "./chunk-VF7YOT2W.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var transcriptEnvironmentPreference = () => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  redactSecrets
3
- } from "./chunk-MC5WUOJJ.js";
3
+ } from "./chunk-HUZW6SEO.js";
4
4
 
5
5
  // src/crypto.ts
6
6
  import nacl from "tweetnacl";