@pushary/agent-hooks 0.90.0 → 0.90.2

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 (54) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/bin/pushary-bell.js +6 -61
  3. package/dist/bin/pushary-claude.js +6 -6
  4. package/dist/bin/pushary-clean.js +96 -13
  5. package/dist/bin/pushary-codex-bridge.js +3 -3
  6. package/dist/bin/pushary-codex-hook.js +4 -4
  7. package/dist/bin/pushary-codex.js +4 -4
  8. package/dist/bin/pushary-connect.js +2 -2
  9. package/dist/bin/pushary-daemon.js +10 -10
  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 +3 -3
  13. package/dist/bin/pushary-gemini-bridge.js +3 -3
  14. package/dist/bin/pushary-gemini-hook.js +4 -4
  15. package/dist/bin/pushary-hook.js +5 -5
  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 +4 -4
  20. package/dist/bin/pushary-opencode-hook.js +4 -4
  21. package/dist/bin/pushary-permission-denied-hook.js +5 -5
  22. package/dist/bin/pushary-permission-hook.js +5 -5
  23. package/dist/bin/pushary-post-hook.js +4 -4
  24. package/dist/bin/pushary-prompt-hook.js +4 -4
  25. package/dist/bin/pushary-session-end-hook.js +4 -4
  26. package/dist/bin/pushary-session-start-hook.js +4 -4
  27. package/dist/bin/pushary-setup.js +105 -180
  28. package/dist/bin/pushary-status.js +4 -4
  29. package/dist/bin/pushary-stop-hook.js +4 -4
  30. package/dist/bin/pushary-stopfailure-hook.js +4 -4
  31. package/dist/bin/pushary-upgrade.js +8 -8
  32. package/dist/bin/pushary-wait.js +2 -2
  33. package/dist/{chunk-B7UZHEBL.js → chunk-4IEW4ZRF.js} +3 -3
  34. package/dist/chunk-ATUUQFDH.js +536 -0
  35. package/dist/{chunk-54ELC7D7.js → chunk-AWHCZOZO.js} +1 -1
  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-FXEVRLHC.js → chunk-F7OURW3C.js} +4 -4
  42. package/dist/{chunk-Y6BWPERV.js → chunk-GQ5ZC2QY.js} +1 -1
  43. package/dist/chunk-GU3EJCVV.js +43 -0
  44. package/dist/{chunk-TDZFPMSZ.js → chunk-TVZQMPB7.js} +2 -2
  45. package/dist/{chunk-BQBEH6TV.js → chunk-VAMBUKXH.js} +4 -4
  46. package/dist/{chunk-DZDI62LY.js → chunk-WGKC6PU2.js} +2 -2
  47. package/dist/{chunk-7TPQEWUZ.js → chunk-YTD6PJM7.js} +1 -1
  48. package/dist/{chunk-H2QZRQR2.js → chunk-YTDH3XCE.js} +1 -1
  49. package/dist/{chunk-F2ICEWZL.js → chunk-Z4XCN2HH.js} +2 -2
  50. package/dist/{chunk-DYP6SMOR.js → chunk-Z6RFZKY4.js} +1 -1
  51. package/dist/src/index.js +5 -5
  52. package/package.json +1 -1
  53. package/dist/chunk-AZPIQG4G.js +0 -274
  54. package/dist/chunk-S6LYP7QA.js +0 -127
@@ -3,7 +3,10 @@ import {
3
3
  } from "./chunk-CELX75MW.js";
4
4
  import {
5
5
  codexHomeFrom
6
- } from "./chunk-KUZWXPUS.js";
6
+ } from "./chunk-BVNYTQ5I.js";
7
+ import {
8
+ HOOK_LOCATOR_BINARY
9
+ } from "./chunk-VNMKIJZB.js";
7
10
 
8
11
  // src/vscode-config.ts
9
12
  import { homedir } from "os";
@@ -329,6 +332,82 @@ var hasInstructionBlock = (filePath) => {
329
332
  // src/hooks-spec.ts
330
333
  var CURSOR_GATE_SCRIPT = "pushary-gate.mjs";
331
334
 
335
+ // src/diagnostics/bridge.ts
336
+ import { execFileSync } from "child_process";
337
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
338
+ import { homedir as homedir3 } from "os";
339
+ import { dirname as dirname3, join as join3 } from "path";
340
+ var BRIDGE_BUNDLE_IDENTIFIER = "com.pushary.app";
341
+ var BRIDGE_HELPER_SUFFIX = "/Contents/Helpers/pushary-bridge";
342
+ var BRIDGE_BUNDLE_ROOTS = ["/Applications/Pushary.app", "$HOME/Applications/Pushary.app"];
343
+ var expandHome = (path, home) => path.replaceAll("$HOME", home);
344
+ var parseBridgeStub = (contents, stubPath) => {
345
+ const helperSuffix = contents.match(/^H=(\S+)$/m)?.[1] ?? BRIDGE_HELPER_SUFFIX;
346
+ const cachePath = contents.match(/^C="([^"\n]+)"$/m)?.[1] ?? join3(dirname3(stubPath), ".bridge-cache");
347
+ const rootsLine = contents.match(/^for P in ((?:"[^"\n]*"\s*)+); do$/m)?.[1];
348
+ const bundleRoots = rootsLine ? [...rootsLine.matchAll(/"([^"]*)"/g)].map((match) => match[1]) : BRIDGE_BUNDLE_ROOTS;
349
+ const bundleIdentifier = contents.match(/kMDItemCFBundleIdentifier == "([^"]+)"/)?.[1] ?? BRIDGE_BUNDLE_IDENTIFIER;
350
+ return { helperSuffix, cachePath, bundleRoots, bundleIdentifier };
351
+ };
352
+ var resolveBridgeStub = (stubPath, probe) => {
353
+ const contents = probe.readFile(stubPath);
354
+ if (contents === null) return { kind: "no-stub", stub: stubPath };
355
+ const shape = parseBridgeStub(contents, stubPath);
356
+ const helperIn = (bundle) => `${bundle}${shape.helperSuffix}`;
357
+ const looked = [];
358
+ for (const root of shape.bundleRoots.map((root2) => expandHome(root2, probe.home))) {
359
+ looked.push(root);
360
+ if (probe.exists(helperIn(root))) return { kind: "bundle", helper: helperIn(root), via: "root" };
361
+ }
362
+ const cached = shape.cachePath ? probe.readFile(shape.cachePath)?.split("\n")[0]?.trim() : void 0;
363
+ if (cached) {
364
+ looked.push(cached);
365
+ if (probe.exists(helperIn(cached))) return { kind: "bundle", helper: helperIn(cached), via: "cache" };
366
+ }
367
+ for (const found of probe.mdfind(shape.bundleIdentifier)) {
368
+ looked.push(found);
369
+ if (probe.exists(helperIn(found))) return { kind: "bundle", helper: helperIn(found), via: "spotlight" };
370
+ }
371
+ return { kind: "missing", stub: stubPath, looked };
372
+ };
373
+ var BRIDGE_GONE_MESSAGE = "the Mac app is gone but its hooks remain; run pushary setup or reinstall the app";
374
+ var describeBridgeResolution = (resolution) => {
375
+ switch (resolution.kind) {
376
+ case "bundle":
377
+ return `${resolution.helper} (${resolution.via === "root" ? "installed" : resolution.via === "cache" ? "cached path" : "found by Spotlight"})`;
378
+ case "missing":
379
+ return BRIDGE_GONE_MESSAGE;
380
+ case "no-stub":
381
+ return `${resolution.stub} is not there; ${BRIDGE_GONE_MESSAGE}`;
382
+ }
383
+ };
384
+ var isBridgeLocator = (executable) => executable !== null && executable.split(/[/\\]/).pop() === HOOK_LOCATOR_BINARY;
385
+ var spotlight = (bundleIdentifier) => {
386
+ if (process.platform !== "darwin") return [];
387
+ try {
388
+ return execFileSync("/usr/bin/mdfind", [`kMDItemCFBundleIdentifier == "${bundleIdentifier}"`], {
389
+ encoding: "utf-8",
390
+ timeout: 5e3,
391
+ stdio: ["ignore", "pipe", "ignore"]
392
+ }).split("\n").map((line) => line.trim()).filter((line) => line !== "");
393
+ } catch {
394
+ return [];
395
+ }
396
+ };
397
+ var readOrNull = (path) => {
398
+ try {
399
+ return readFileSync2(path, "utf-8");
400
+ } catch {
401
+ return null;
402
+ }
403
+ };
404
+ var liveBridgeProbe = () => ({
405
+ exists: existsSync3,
406
+ readFile: readOrNull,
407
+ mdfind: spotlight,
408
+ home: homedir3()
409
+ });
410
+
332
411
  export {
333
412
  registerPluginLocation,
334
413
  unregisterPluginLocation,
@@ -346,5 +425,9 @@ export {
346
425
  writeInstructionBlock,
347
426
  removeInstructionBlock,
348
427
  hasInstructionBlock,
349
- CURSOR_GATE_SCRIPT
428
+ CURSOR_GATE_SCRIPT,
429
+ resolveBridgeStub,
430
+ describeBridgeResolution,
431
+ isBridgeLocator,
432
+ liveBridgeProbe
350
433
  };
@@ -0,0 +1,68 @@
1
+ import {
2
+ guardBinary
3
+ } from "./chunk-EZIEXHYM.js";
4
+
5
+ // src/bell/install.ts
6
+ import { join } from "path";
7
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
8
+ var ensureRecord = (parent, key) => {
9
+ const existing = asRecord(parent[key]);
10
+ if (existing) return existing;
11
+ const created = {};
12
+ parent[key] = created;
13
+ return created;
14
+ };
15
+ var BELL_HOOK_BINARY = "pushary-bell-hook";
16
+ var isBellHook = (entry) => {
17
+ const hooks = asRecord(entry)?.hooks;
18
+ if (!Array.isArray(hooks)) return false;
19
+ return hooks.some((hook) => String(asRecord(hook)?.command ?? "").includes(BELL_HOOK_BINARY));
20
+ };
21
+ var BELL_TIMEOUT_SECONDS = 5;
22
+ var BELL_NOTIFICATION_MATCHER = "idle_prompt|agent_needs_input|agent_completed";
23
+ var addBellHooks = (settings, binDir) => {
24
+ const resolve = (name) => guardBinary(binDir ? join(binDir, name) : name);
25
+ const hooks = ensureRecord(settings, "hooks");
26
+ const notification = (Array.isArray(hooks.Notification) ? hooks.Notification : []).filter((entry) => !isBellHook(entry));
27
+ notification.push({
28
+ matcher: BELL_NOTIFICATION_MATCHER,
29
+ hooks: [{ type: "command", command: resolve(BELL_HOOK_BINARY), timeout: BELL_TIMEOUT_SECONDS }]
30
+ });
31
+ hooks.Notification = notification;
32
+ const stop = Array.isArray(hooks.Stop) ? hooks.Stop.filter((entry) => !isBellHook(entry)) : [];
33
+ if (Array.isArray(hooks.Stop)) {
34
+ if (stop.length === 0) delete hooks.Stop;
35
+ else hooks.Stop = stop;
36
+ }
37
+ };
38
+ var removeBellHooks = (settings) => {
39
+ const hooks = asRecord(settings.hooks);
40
+ if (!hooks) return false;
41
+ let changed = false;
42
+ for (const key of ["Stop", "Notification"]) {
43
+ const entries = hooks[key];
44
+ if (!Array.isArray(entries)) continue;
45
+ const filtered = entries.filter((entry) => !isBellHook(entry));
46
+ if (filtered.length === entries.length) continue;
47
+ if (filtered.length === 0) delete hooks[key];
48
+ else hooks[key] = filtered;
49
+ changed = true;
50
+ }
51
+ if (Object.keys(hooks).length === 0) delete settings.hooks;
52
+ return changed;
53
+ };
54
+ var bellInstalled = (settings) => {
55
+ const hooks = asRecord(settings.hooks);
56
+ if (!hooks) return false;
57
+ return ["Stop", "Notification"].some((key) => {
58
+ const entries = hooks[key];
59
+ return Array.isArray(entries) && entries.some(isBellHook);
60
+ });
61
+ };
62
+
63
+ export {
64
+ BELL_HOOK_BINARY,
65
+ addBellHooks,
66
+ removeBellHooks,
67
+ bellInstalled
68
+ };
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-EZIEXHYM.js";
8
8
  import {
9
9
  isProtectedByScope
10
- } from "./chunk-KUZWXPUS.js";
10
+ } from "./chunk-BVNYTQ5I.js";
11
11
  import {
12
12
  HOOK_BUDGETS,
13
13
  HOOK_LOCATOR_BINARY
@@ -72,13 +72,13 @@ import { homedir, tmpdir } from "os";
72
72
  import { createHash } from "crypto";
73
73
  import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, renameSync, unlinkSync, rmSync, statSync } from "fs";
74
74
  var PENDING_DIR = join(tmpdir(), "pushary-pending");
75
- var COMMAND_DIR = join(homedir(), ".pushary", "run", "commands");
75
+ var commandRoot = () => process.env.PUSHARY_COMMAND_DIR?.trim() || join(homedir(), ".pushary", "run", "commands");
76
76
  var DEFAULT_SESSION = "_no_session";
77
77
  var GRACE_MS = 10 * 60 * 1e3;
78
78
  var sanitize = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
79
79
  var dirFor = (sessionId) => join(PENDING_DIR, sanitize(sessionId));
80
80
  var commandDirFor = (sessionId, namespace = "") => join(
81
- COMMAND_DIR,
81
+ commandRoot(),
82
82
  createHash("sha256").update(namespace).digest("hex").slice(0, 16),
83
83
  sanitize(sessionId)
84
84
  );
@@ -122,12 +122,12 @@ var pruneExpiredCommands = (dir) => {
122
122
  var pruneExpiredCommandStore = () => {
123
123
  let namespaces;
124
124
  try {
125
- namespaces = readdirSync(COMMAND_DIR, { withFileTypes: true });
125
+ namespaces = readdirSync(commandRoot(), { withFileTypes: true });
126
126
  } catch {
127
127
  return;
128
128
  }
129
129
  for (const namespace of namespaces.filter((entry) => entry.isDirectory())) {
130
- const namespaceDir = join(COMMAND_DIR, namespace.name);
130
+ const namespaceDir = join(commandRoot(), namespace.name);
131
131
  let sessions;
132
132
  try {
133
133
  sessions = readdirSync(namespaceDir, { withFileTypes: true });
@@ -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
  readJsonSafe
6
6
  } from "./chunk-4LL6LG5X.js";
@@ -0,0 +1,43 @@
1
+ // src/diagnostics/service-runtime.ts
2
+ var unescapeXml = (value) => value.replaceAll("&quot;", '"').replaceAll("&apos;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
3
+ var unquoteSystemd = (value) => value.replaceAll('\\"', '"').replaceAll("\\\\", "\\").replaceAll("%%", "%");
4
+ var readServiceExecPath = (contents, platform) => {
5
+ if (platform === "darwin") {
6
+ const program = contents.match(/<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]*)<\/string>/)?.[1];
7
+ return program ? unescapeXml(program) : null;
8
+ }
9
+ if (platform === "linux") {
10
+ const exec = contents.match(/^ExecStart="((?:[^"\\]|\\.)*)"/m)?.[1];
11
+ return exec ? unquoteSystemd(exec) : null;
12
+ }
13
+ if (platform === "win32") {
14
+ const command = contents.match(/<Command>([^<]*)<\/Command>/)?.[1];
15
+ return command ? unescapeXml(command) : null;
16
+ }
17
+ return null;
18
+ };
19
+ var MANAGER_SEGMENTS = [
20
+ [".nvm", "nvm"],
21
+ ["nvm", "nvm"],
22
+ ["fnm", "fnm"],
23
+ [".fnm", "fnm"],
24
+ [".volta", "volta"],
25
+ ["volta", "volta"],
26
+ [".asdf", "asdf"],
27
+ ["asdf", "asdf"]
28
+ ];
29
+ var versionManagerOf = (execPath) => {
30
+ const segments = execPath.split(/[/\\]/);
31
+ for (const segment of segments) {
32
+ const hit = MANAGER_SEGMENTS.find(([name]) => name === segment);
33
+ if (hit) return hit[1];
34
+ }
35
+ return null;
36
+ };
37
+ var describeVersionManagerPin = (execPath, manager) => `The background service is pinned to ${execPath}, which ${manager} manages. Switching Node versions strands it; run pushary daemon install afterwards.`;
38
+
39
+ export {
40
+ readServiceExecPath,
41
+ versionManagerOf,
42
+ describeVersionManagerPin
43
+ };
@@ -13,7 +13,7 @@ import {
13
13
  savePendingCommand,
14
14
  sendNotification,
15
15
  waitForAnswer
16
- } from "./chunk-FXEVRLHC.js";
16
+ } from "./chunk-F7OURW3C.js";
17
17
  import {
18
18
  AGENT_IDENTITIES,
19
19
  compactionAction,
@@ -33,7 +33,7 @@ import {
33
33
  } from "./chunk-L4E2BIJ2.js";
34
34
  import {
35
35
  getMachineId
36
- } from "./chunk-54ELC7D7.js";
36
+ } from "./chunk-AWHCZOZO.js";
37
37
  import {
38
38
  withRetry
39
39
  } from "./chunk-BSZYIAZL.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-H2QZRQR2.js";
3
+ } from "./chunk-YTDH3XCE.js";
4
4
  import {
5
5
  claudeWired,
6
6
  codexWired,
7
7
  geminiWired
8
- } from "./chunk-DZDI62LY.js";
8
+ } from "./chunk-WGKC6PU2.js";
9
9
  import {
10
10
  GEMINI_HOOK_BINARY,
11
11
  addClaudeMcpServer,
@@ -14,7 +14,7 @@ import {
14
14
  addPusharyHooks,
15
15
  addPusharyToolPermissions,
16
16
  hasGeminiHooks
17
- } from "./chunk-GL3FFM7A.js";
17
+ } from "./chunk-ELHCIEMM.js";
18
18
  import {
19
19
  guardBinary
20
20
  } from "./chunk-EZIEXHYM.js";
@@ -32,7 +32,7 @@ import {
32
32
  codexHooksJson,
33
33
  hasCodexHooks,
34
34
  vscodePluginMcp
35
- } from "./chunk-KUZWXPUS.js";
35
+ } from "./chunk-BVNYTQ5I.js";
36
36
  import {
37
37
  readJsonSafe
38
38
  } from "./chunk-4LL6LG5X.js";
@@ -2,11 +2,11 @@ import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  GEMINI_HOOK_EVENTS,
4
4
  hasGeminiHooks
5
- } from "./chunk-GL3FFM7A.js";
5
+ } from "./chunk-ELHCIEMM.js";
6
6
  import {
7
7
  CODEX_HOOK_EVENTS,
8
8
  hasCodexHooks
9
- } from "./chunk-KUZWXPUS.js";
9
+ } from "./chunk-BVNYTQ5I.js";
10
10
  import {
11
11
  CURSOR_GATE_EVENTS,
12
12
  HOOK_LOCATOR_BINARY
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getMachineId
3
- } from "./chunk-54ELC7D7.js";
3
+ } from "./chunk-AWHCZOZO.js";
4
4
  import {
5
5
  getPackageVersion
6
6
  } from "./chunk-DINDL2CF.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-GL3FFM7A.js";
3
+ } from "./chunk-ELHCIEMM.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-L4ZCFMWW.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-54ELC7D7.js";
10
+ } from "./chunk-AWHCZOZO.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-KUZWXPUS.js";
13
+ } from "./chunk-BVNYTQ5I.js";
14
14
  import {
15
15
  getPackageVersion
16
16
  } from "./chunk-DINDL2CF.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-AJYV3VGU.js";
4
+ } from "./chunk-BXTJVM7O.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
package/dist/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-B7UZHEBL.js";
3
+ } from "../chunk-4IEW4ZRF.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-TDZFPMSZ.js";
19
+ } from "../chunk-TVZQMPB7.js";
20
20
  import {
21
21
  askUser,
22
22
  cancelQuestion,
23
23
  waitForAnswer
24
- } from "../chunk-FXEVRLHC.js";
24
+ } from "../chunk-F7OURW3C.js";
25
25
  import "../chunk-L4E2BIJ2.js";
26
- import "../chunk-54ELC7D7.js";
27
- import "../chunk-KUZWXPUS.js";
26
+ import "../chunk-AWHCZOZO.js";
27
+ import "../chunk-BVNYTQ5I.js";
28
28
  import "../chunk-BSZYIAZL.js";
29
29
  import "../chunk-SAF6HGAA.js";
30
30
  import "../chunk-7QLSKOSU.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.90.0",
3
+ "version": "0.90.2",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",