@pushary/agent-hooks 0.89.11 → 0.89.13

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 (80) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +4 -2
  3. package/dist/bin/pushary-bell-hook.js +5 -5
  4. package/dist/bin/pushary-bell.js +11 -11
  5. package/dist/bin/pushary-claude.js +225 -48
  6. package/dist/bin/pushary-clean.js +49 -29
  7. package/dist/bin/pushary-codex-bridge.js +17 -16
  8. package/dist/bin/pushary-codex-hook.js +11 -10
  9. package/dist/bin/pushary-codex.js +7 -7
  10. package/dist/bin/pushary-connect.js +12 -12
  11. package/dist/bin/pushary-daemon-supervisor.d.ts +1 -0
  12. package/dist/bin/pushary-daemon-supervisor.js +27 -0
  13. package/dist/bin/pushary-daemon.js +22 -22
  14. package/dist/bin/pushary-disconnect.js +9 -9
  15. package/dist/bin/pushary-doctor.js +59 -49
  16. package/dist/bin/pushary-elicitation-hook.js +4 -4
  17. package/dist/bin/pushary-gemini-bridge.js +16 -15
  18. package/dist/bin/pushary-gemini-hook.js +10 -9
  19. package/dist/bin/pushary-hook.js +13 -13
  20. package/dist/bin/pushary-login.js +11 -11
  21. package/dist/bin/pushary-logout.js +11 -11
  22. package/dist/bin/pushary-mode.js +5 -5
  23. package/dist/bin/pushary-notification-hook.js +7 -7
  24. package/dist/bin/pushary-opencode-hook.js +10 -9
  25. package/dist/bin/pushary-permission-denied-hook.js +11 -11
  26. package/dist/bin/pushary-permission-hook.js +11 -11
  27. package/dist/bin/pushary-post-hook.js +7 -7
  28. package/dist/bin/pushary-prompt-hook.js +7 -7
  29. package/dist/bin/pushary-session-end-hook.js +7 -7
  30. package/dist/bin/pushary-session-start-hook.js +7 -7
  31. package/dist/bin/pushary-setup.js +87 -59
  32. package/dist/bin/pushary-stats.js +4 -4
  33. package/dist/bin/pushary-status.js +14 -14
  34. package/dist/bin/pushary-stop-hook.js +7 -7
  35. package/dist/bin/pushary-stopfailure-hook.js +7 -7
  36. package/dist/bin/pushary-suggestions.js +2 -2
  37. package/dist/bin/pushary-transcripts.d.ts +1 -0
  38. package/dist/bin/pushary-transcripts.js +79 -0
  39. package/dist/bin/pushary-upgrade.js +21 -21
  40. package/dist/bin/pushary-wait.js +5 -5
  41. package/dist/bin/pushary.js +3 -2
  42. package/dist/{chunk-6XUABEX2.js → chunk-3OXHYPZG.js} +1 -1
  43. package/dist/{chunk-QA72MKIT.js → chunk-3YGGEDJK.js} +5 -5
  44. package/dist/{chunk-IUN6VOKV.js → chunk-4IPSMEGT.js} +68 -46
  45. package/dist/{chunk-XX2V4QS5.js → chunk-4LL6LG5X.js} +2 -2
  46. package/dist/{chunk-J4K75V4D.js → chunk-5MNTWAAJ.js} +10 -9
  47. package/dist/{chunk-KVE5RCQL.js → chunk-5YEF55AX.js} +1 -1
  48. package/dist/{chunk-3SQYKLBW.js → chunk-7JZCADBR.js} +46 -18
  49. package/dist/{chunk-IUWRVTQK.js → chunk-ANMHIZZY.js} +1 -1
  50. package/dist/{chunk-6EKQZA6R.js → chunk-B2HND22S.js} +1 -1
  51. package/dist/{chunk-TRVIRETT.js → chunk-C56VHSXU.js} +1 -1
  52. package/dist/{chunk-TEZ2FZD6.js → chunk-CBGGUEKE.js} +2 -2
  53. package/dist/{chunk-3HFK3UXM.js → chunk-FOGW6O2Y.js} +1 -1
  54. package/dist/{chunk-3XTFX4CL.js → chunk-GMJDQDXU.js} +17 -2
  55. package/dist/{chunk-DKWM5T3P.js → chunk-JTBDBKHU.js} +2 -2
  56. package/dist/{chunk-Q223OM5J.js → chunk-JVLB5CAJ.js} +4 -4
  57. package/dist/{chunk-2LPEZYMC.js → chunk-KNLKHJZR.js} +1 -1
  58. package/dist/{chunk-X4ZUUFKV.js → chunk-L4ZCFMWW.js} +63 -1
  59. package/dist/chunk-M3UABYRT.js +20 -0
  60. package/dist/{chunk-PZRWFWSR.js → chunk-MNTZKFZI.js} +7 -7
  61. package/dist/{chunk-FZ73G2RO.js → chunk-MSTHT2M7.js} +25 -6
  62. package/dist/{chunk-TG3T3M4K.js → chunk-N4QJ3CLX.js} +6 -2
  63. package/dist/{chunk-3CUDMTL4.js → chunk-NBDU3R3G.js} +10 -3
  64. package/dist/{chunk-FXIL7RCL.js → chunk-OX35BSPV.js} +4 -4
  65. package/dist/{chunk-UDBJHQMG.js → chunk-QFJZHVVL.js} +1 -1
  66. package/dist/chunk-QZCFVWA7.js +58 -0
  67. package/dist/{chunk-TY5HVMXT.js → chunk-RAEPAUNJ.js} +1 -1
  68. package/dist/{chunk-NCVIWGVA.js → chunk-S4BLNILH.js} +1 -1
  69. package/dist/{chunk-MZGWKIUZ.js → chunk-VASZ6TWW.js} +7 -3
  70. package/dist/{chunk-SYUIXXQW.js → chunk-VF3RTVWY.js} +1 -1
  71. package/dist/{chunk-LURY4WNF.js → chunk-WHDSJ6PD.js} +1 -1
  72. package/dist/chunk-YKLCWWEI.js +76 -0
  73. package/dist/{chunk-253WC6O5.js → chunk-YXT55PYF.js} +1 -1
  74. package/dist/{chunk-BLODMQKJ.js → chunk-ZO43JTT3.js} +1 -1
  75. package/dist/{chunk-OLAGKKSR.js → chunk-ZP3SNUP7.js} +16 -6
  76. package/dist/{chunk-34GN3IPX.js → chunk-ZWEKONU6.js} +1 -1
  77. package/dist/src/index.d.ts +3 -1
  78. package/dist/src/index.js +15 -11
  79. package/package.json +4 -2
  80. /package/dist/{chunk-X6RXX2NF.js → chunk-QINYXLA5.js} +0 -0
@@ -4,13 +4,13 @@ import {
4
4
  } from "./chunk-XHKBHWLX.js";
5
5
  import {
6
6
  resolveGlobalBinary
7
- } from "./chunk-X4ZUUFKV.js";
7
+ } from "./chunk-L4ZCFMWW.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-DKWM5T3P.js";
10
+ } from "./chunk-JTBDBKHU.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-QA72MKIT.js";
13
+ } from "./chunk-3YGGEDJK.js";
14
14
  import {
15
15
  getPackageVersion
16
16
  } from "./chunk-DINDL2CF.js";
@@ -19,7 +19,7 @@ import {
19
19
  writeFileAtomic,
20
20
  writeJsonAtomic,
21
21
  writeJsonAtomicDurable
22
- } from "./chunk-XX2V4QS5.js";
22
+ } from "./chunk-4LL6LG5X.js";
23
23
  import {
24
24
  getApiKey
25
25
  } from "./chunk-2UMNXADU.js";
@@ -28,7 +28,7 @@ import {
28
28
  import { dirname, join, win32 as win32Path } from "path";
29
29
  import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "fs";
30
30
  import { createHash, randomUUID } from "crypto";
31
- import { homedir } from "os";
31
+ import { homedir, userInfo } from "os";
32
32
  import { execFileSync } from "child_process";
33
33
  var daemonNeedsHandoff = (status) => status.kind === "version-mismatch" || status.kind === "configuration-mismatch" || status.kind === "unresponsive" && status.processAlive === true;
34
34
  var DAEMON_REMOTE_PROOF_MAX_AGE_MS = 18e4;
@@ -39,9 +39,14 @@ var remoteProofIsFresh = (timestamp, now) => {
39
39
  return Number.isFinite(age) && age >= -DAEMON_REMOTE_PROOF_CLOCK_SKEW_MS && age < DAEMON_REMOTE_PROOF_MAX_AGE_MS;
40
40
  };
41
41
  var daemonHasRemoteProof = (status, now = Date.now()) => status.kind === "running" && remoteProofIsFresh(status.lastSuccessfulHeartbeatAt, now) && remoteProofIsFresh(status.lastSuccessfulControlAt, now);
42
+ var readServiceDefinitionFile = (path) => {
43
+ const bytes = readFileSync(path);
44
+ const encoding = bytes.length >= 2 && bytes[0] === 255 && bytes[1] === 254 ? "utf16le" : "utf-8";
45
+ return { contents: bytes.toString(encoding), encoding };
46
+ };
42
47
  var runDaemonServiceCommand = (command, args) => execFileSync(command, [...args], {
43
48
  encoding: "utf-8",
44
- stdio: ["ignore", "pipe", "ignore"],
49
+ stdio: ["ignore", "pipe", "pipe"],
45
50
  windowsHide: true
46
51
  });
47
52
  var commandFailureOutput = (error) => {
@@ -163,7 +168,6 @@ After=network-online.target
163
168
  [Service]
164
169
  Type=simple
165
170
  ExecStart=${systemdQuote(input.execPath)} ${systemdQuote(input.daemonEntry)}
166
- WorkingDirectory=${systemdQuote(input.homeDir)}
167
171
  Environment=${systemdQuote(`PATH=${input.pathEnv}`)}
168
172
  Restart=on-failure
169
173
  RestartSec=10
@@ -199,12 +203,18 @@ WantedBy=default.target
199
203
  }
200
204
  if (input.platform === "win32") {
201
205
  const taskName = "Pushary Daemon";
206
+ const username = process.env.USERNAME?.trim() || userInfo().username;
207
+ const domain = process.env.USERDOMAIN?.trim();
208
+ const userId = input.userId?.trim() || (domain ? `${domain}\\${username}` : username);
202
209
  const definitionPath = win32Path.join(input.stateDir, "pushary-daemon-task.xml");
203
- const contents = `<?xml version="1.0" encoding="UTF-8"?>
210
+ const supervisorPath = win32Path.join(win32Path.dirname(input.daemonEntry), "pushary-daemon-supervisor.js");
211
+ const supervised = (input.entryExists ?? existsSync)(supervisorPath);
212
+ const args = supervised ? `&quot;${xml(supervisorPath)}&quot; &quot;${xml(input.daemonEntry)}&quot;` : `&quot;${xml(input.daemonEntry)}&quot;`;
213
+ const contents = `\uFEFF<?xml version="1.0" encoding="UTF-16"?>
204
214
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
205
215
  <RegistrationInfo><Description>Pushary phone-to-agent daemon</Description></RegistrationInfo>
206
- <Triggers><LogonTrigger><Enabled>true</Enabled></LogonTrigger></Triggers>
207
- <Principals><Principal id="Author"><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>
216
+ <Triggers><LogonTrigger><UserId>${xml(userId)}</UserId><Enabled>true</Enabled></LogonTrigger></Triggers>
217
+ <Principals><Principal id="Author"><UserId>${xml(userId)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>
208
218
  <Settings>
209
219
  <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
210
220
  <StartWhenAvailable>true</StartWhenAvailable>
@@ -216,7 +226,7 @@ WantedBy=default.target
216
226
  <Actions Context="Author">
217
227
  <Exec>
218
228
  <Command>${xml(input.execPath)}</Command>
219
- <Arguments>&quot;${xml(input.daemonEntry)}&quot;</Arguments>
229
+ <Arguments>${args}</Arguments>
220
230
  <WorkingDirectory>${xml(input.homeDir)}</WorkingDirectory>
221
231
  </Exec>
222
232
  </Actions>
@@ -225,6 +235,7 @@ WantedBy=default.target
225
235
  return {
226
236
  definitionPath,
227
237
  contents,
238
+ encoding: "utf16le",
228
239
  install: [
229
240
  { command: "schtasks.exe", args: ["/Create", "/TN", taskName, "/XML", definitionPath, "/F"] },
230
241
  { command: "schtasks.exe", args: ["/Run", "/TN", taskName] }
@@ -302,7 +313,8 @@ var resolveDaemonServiceDefinition = (options = {}) => {
302
313
  execPath: options.execPath ?? process.execPath,
303
314
  daemonEntry,
304
315
  pathEnv: options.pathEnv ?? process.env.PATH ?? "",
305
- uid: options.uid ?? process.getuid?.()
316
+ uid: options.uid ?? process.getuid?.(),
317
+ entryExists: exists
306
318
  });
307
319
  };
308
320
  var runDaemonServiceSteps = (steps, run) => {
@@ -317,7 +329,9 @@ var runDaemonServiceSteps = (steps, run) => {
317
329
  var applyDaemonService = (action, definition, options = {}) => {
318
330
  const run = options.run ?? runDaemonServiceCommand;
319
331
  try {
320
- if (action === "install") writeFileAtomic(definition.definitionPath, definition.contents, 384);
332
+ if (action === "install") {
333
+ writeFileAtomic(definition.definitionPath, definition.contents, 384, definition.encoding);
334
+ }
321
335
  const steps = action === "uninstall" ? [...definition.stop, ...definition.uninstall] : definition[action];
322
336
  runDaemonServiceSteps(steps, run);
323
337
  if (action === "uninstall") {
@@ -674,7 +688,7 @@ var disableDaemonService = async (options = {}) => deactivateDaemonService("unin
674
688
  var parseDaemonServiceTransaction = (value) => {
675
689
  if (typeof value !== "object" || value === null) return null;
676
690
  const transaction = value;
677
- if (transaction.version !== 1 || transaction.phase !== "prepared" && transaction.phase !== "committed" || typeof transaction.definitionPath !== "string" || !(transaction.previousContents === null || typeof transaction.previousContents === "string") || typeof transaction.previousRegistered !== "boolean" || !(transaction.previousEnabled === void 0 || typeof transaction.previousEnabled === "boolean") || typeof transaction.previousRunning !== "boolean") return null;
691
+ if (transaction.version !== 1 || transaction.phase !== "prepared" && transaction.phase !== "committed" || typeof transaction.definitionPath !== "string" || !(transaction.previousContents === null || typeof transaction.previousContents === "string") || !(transaction.previousEncoding === void 0 || typeof transaction.previousEncoding === "string") || typeof transaction.previousRegistered !== "boolean" || !(transaction.previousEnabled === void 0 || typeof transaction.previousEnabled === "boolean") || typeof transaction.previousRunning !== "boolean") return null;
678
692
  return {
679
693
  ...transaction,
680
694
  previousEnabled: typeof transaction.previousEnabled === "boolean" ? transaction.previousEnabled : transaction.previousRegistered
@@ -723,12 +737,13 @@ var probeDaemonServiceState = (definition, run) => {
723
737
  return { kind: "ok", registered, enabled, running: false };
724
738
  }
725
739
  };
740
+ var previousEncoding = (snapshot) => snapshot.previousEncoding ?? "utf-8";
726
741
  var restoreDaemonServiceSnapshot = (definition, snapshot, run) => {
727
742
  const removed = applyDaemonService("uninstall", definition, { run });
728
743
  if (removed.kind !== "ok") return removed;
729
744
  try {
730
745
  if (snapshot.previousContents === null) safeUnlink(definition.definitionPath);
731
- else writeFileAtomic(definition.definitionPath, snapshot.previousContents, 384);
746
+ else writeFileAtomic(definition.definitionPath, snapshot.previousContents, 384, previousEncoding(snapshot));
732
747
  } catch (err) {
733
748
  return {
734
749
  kind: "failed",
@@ -767,7 +782,8 @@ var restoreDaemonServiceSnapshot = (definition, snapshot, run) => {
767
782
  }
768
783
  const restored = applyDaemonService("install", {
769
784
  ...definition,
770
- contents: snapshot.previousContents
785
+ contents: snapshot.previousContents,
786
+ encoding: previousEncoding(snapshot)
771
787
  }, { run });
772
788
  if (restored.kind !== "ok") return restored;
773
789
  if (!snapshot.previousRunning) applyDaemonService("stop", definition, { run });
@@ -829,9 +845,12 @@ var replaceDaemonService = async (options = {}) => {
829
845
  return { kind: "failed", detail: `Could not recover interrupted service replacement: ${recovered.detail}` };
830
846
  }
831
847
  let previousContents = null;
848
+ let previousDefinitionEncoding = "utf-8";
832
849
  try {
833
850
  if (existsSync(definition.definitionPath)) {
834
- previousContents = readFileSync(definition.definitionPath, "utf-8");
851
+ const previous = readServiceDefinitionFile(definition.definitionPath);
852
+ previousContents = previous.contents;
853
+ previousDefinitionEncoding = previous.encoding;
835
854
  }
836
855
  } catch (err) {
837
856
  return { kind: "failed", detail: `Could not snapshot the existing service: ${err instanceof Error ? err.message : String(err)}` };
@@ -846,6 +865,7 @@ var replaceDaemonService = async (options = {}) => {
846
865
  phase: "prepared",
847
866
  definitionPath: definition.definitionPath,
848
867
  previousContents,
868
+ previousEncoding: previousDefinitionEncoding,
849
869
  previousRegistered: previousState.registered,
850
870
  previousEnabled: previousState.enabled,
851
871
  previousRunning: previousState.running
@@ -869,7 +889,15 @@ var replaceDaemonService = async (options = {}) => {
869
889
  }
870
890
  let installed = applyDaemonService("install", definition, { run });
871
891
  if (installed.kind === "ok") {
872
- const state = probeDaemonServiceState(definition, run);
892
+ const sleep = options.sleep ?? wait;
893
+ const pollIntervalMs = Math.max(1, options.pollIntervalMs ?? 50);
894
+ const now = options.now ?? Date.now;
895
+ const deadline = now() + Math.max(0, options.waitTimeoutMs ?? 5e3);
896
+ let state = probeDaemonServiceState(definition, run);
897
+ while (state.kind === "ok" && (!state.registered || !state.enabled || !state.running) && now() < deadline) {
898
+ await sleep(pollIntervalMs);
899
+ state = probeDaemonServiceState(definition, run);
900
+ }
873
901
  if (state.kind !== "ok") {
874
902
  installed = { kind: "failed", detail: state.detail };
875
903
  } else if (!state.registered || !state.enabled || !state.running) {
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-X6RXX2NF.js";
6
+ } from "./chunk-QINYXLA5.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ASKED_IN_EVERY_MODE_TOOLS
3
- } from "./chunk-X6RXX2NF.js";
3
+ } from "./chunk-QINYXLA5.js";
4
4
 
5
5
  // src/claude-tools.ts
6
6
  var PRETOOLUSE_GATED_TOOLS = [
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readConfigFileKey
3
- } from "./chunk-3CUDMTL4.js";
3
+ } from "./chunk-NBDU3R3G.js";
4
4
 
5
5
  // src/bell/fleet.ts
6
6
  import { closeSync, constants, futimesSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-QA72MKIT.js";
3
+ } from "./chunk-3YGGEDJK.js";
4
4
  import {
5
5
  readJsonSafe
6
- } from "./chunk-XX2V4QS5.js";
6
+ } from "./chunk-4LL6LG5X.js";
7
7
 
8
8
  // src/process-ownership.ts
9
9
  import { createHash, randomUUID } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-X6RXX2NF.js";
3
+ } from "./chunk-QINYXLA5.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -3,7 +3,7 @@ var COMMAND_LIST = [
3
3
  {
4
4
  name: "setup",
5
5
  module: "pushary-setup",
6
- summary: "Configure Claude Code, Codex, Gemini CLI, Hermes, Cursor, or VS Code with Pushary"
6
+ summary: "Configure Claude Code, Codex, Gemini CLI, Hermes, Cursor, VS Code, or OpenCode with Pushary"
7
7
  },
8
8
  {
9
9
  name: "claude",
@@ -69,6 +69,16 @@ var COMMAND_LIST = [
69
69
  name: "hook",
70
70
  module: "pushary-hook",
71
71
  summary: "Run as a PreToolUse hook (reads stdin, writes stdout)"
72
+ },
73
+ {
74
+ name: "transcripts",
75
+ module: "pushary-transcripts",
76
+ summary: "Show or re-trust the keys this machine seals transcripts to",
77
+ detail: [
78
+ "Sync stops if the phone key or the compliance recovery key changes, because a",
79
+ "change is either your reinstall or a server handing itself a copy. Compare the",
80
+ "fingerprint with the app under Settings, then accept it with --rotate."
81
+ ]
72
82
  }
73
83
  ];
74
84
  var COMMANDS = COMMAND_LIST;
@@ -79,6 +89,10 @@ var COMMAND_OPTIONS = {
79
89
  ["--dry-run", "List everything it would remove and change nothing"],
80
90
  ["--yes", "Remove everything without asking"]
81
91
  ],
92
+ transcripts: [
93
+ ["status", "Show the pinned keys and their fingerprints (default)"],
94
+ ["trust --rotate", "Accept the keys the server is offering now and resume syncing"]
95
+ ],
82
96
  mode: [
83
97
  ["<mode>", "One of: push_only, push_first, terminal_only, notify_only"],
84
98
  ["status", "Show the current override"],
@@ -133,6 +147,7 @@ var SETUP_OPTIONS = [
133
147
  ["--connect web", "Legacy browser subscribe page"],
134
148
  ["--connect none", "Skip the phone connect step, same as --skip-phone"],
135
149
  ["--control auto|on|off", "Manage phone-start automatically (default), require it, or disable its background service"],
150
+ ["--transcripts on|off", "Enable or disable encrypted transcript sync on this machine"],
136
151
  ["--skip-phone", "Skip the phone connect step"],
137
152
  ["--verify roundtrip", "Send a real question and wait for you to answer it (default when a human is present)"],
138
153
  ["--verify push", "Only prove a notification was delivered. Automatic when nobody is at the terminal"],
@@ -140,7 +155,7 @@ var SETUP_OPTIONS = [
140
155
  ["--dry-run", "Print what would be written and change nothing"],
141
156
  ["--json", "Emit one machine-readable object instead of the human report"]
142
157
  ];
143
- var AGENT_IDS = "claude_code, codex, gemini_cli, hermes, cursor, custom";
158
+ var AGENT_IDS = "claude_code, codex, gemini_cli, hermes, cursor, vscode, opencode, custom";
144
159
  var USAGE_EXAMPLES = [
145
160
  "npx @pushary/agent-hooks@latest setup",
146
161
  "npx @pushary/agent-hooks@latest setup --agents auto --yes --skip-phone",
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-QA72MKIT.js";
3
+ } from "./chunk-3YGGEDJK.js";
4
4
  import {
5
5
  writeFileAtomic
6
- } from "./chunk-XX2V4QS5.js";
6
+ } from "./chunk-4LL6LG5X.js";
7
7
 
8
8
  // src/identity.ts
9
9
  import { randomUUID } from "crypto";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  PRETOOLUSE_GATED_TOOLS,
3
3
  PRETOOLUSE_PHONE_ANSWERED_TOOLS
4
- } from "./chunk-6EKQZA6R.js";
4
+ } from "./chunk-B2HND22S.js";
5
5
  import {
6
6
  guardBinary
7
- } from "./chunk-SYUIXXQW.js";
7
+ } from "./chunk-VF3RTVWY.js";
8
8
  import {
9
9
  isProtectedByScope
10
- } from "./chunk-QA72MKIT.js";
10
+ } from "./chunk-3YGGEDJK.js";
11
11
  import {
12
12
  HOOK_BUDGETS,
13
13
  HOOK_LOCATOR_BINARY
14
- } from "./chunk-X6RXX2NF.js";
14
+ } from "./chunk-QINYXLA5.js";
15
15
 
16
16
  // src/claude-config.ts
17
17
  import { join } from "path";
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-BSZYIAZL.js";
12
12
  import {
13
13
  reportedInstallSource
14
- } from "./chunk-IUWRVTQK.js";
14
+ } from "./chunk-ANMHIZZY.js";
15
15
 
16
16
  // src/reach.ts
17
17
  var reachVerdict = (channels) => {
@@ -1,7 +1,7 @@
1
1
  // src/npm.ts
2
2
  import { execFileSync, execSync } from "child_process";
3
3
  import { existsSync, readFileSync, realpathSync } from "fs";
4
- import { dirname, join } from "path";
4
+ import { dirname, isAbsolute, join, relative, resolve } from "path";
5
5
  var cleanNpmEnv = () => {
6
6
  const env = {};
7
7
  for (const [key, value] of Object.entries(process.env)) {
@@ -77,12 +77,67 @@ var resolveGlobalPackageVersion = (name) => {
77
77
  return void 0;
78
78
  }
79
79
  };
80
+ var localImportsPresent = (entry, packageDir, seen = /* @__PURE__ */ new Set()) => {
81
+ if (seen.has(entry)) return true;
82
+ seen.add(entry);
83
+ try {
84
+ const source = readFileSync(entry, "utf8");
85
+ for (const match of source.matchAll(/\b(?:import|export)\s+(?:[^"'()]*?\s+from\s+)?["'](\.{1,2}\/[^"']+)["']|\bimport\s*\(\s*["'](\.{1,2}\/[^"']+)["']/g)) {
86
+ const dependency = resolve(dirname(entry), match[1] ?? match[2]);
87
+ const localPath = relative(packageDir, dependency);
88
+ if (localPath.startsWith("..") || isAbsolute(localPath) || !existsSync(dependency)) return false;
89
+ if (/\.[cm]?js$/.test(dependency) && !localImportsPresent(dependency, packageDir, seen)) return false;
90
+ }
91
+ return true;
92
+ } catch {
93
+ return false;
94
+ }
95
+ };
96
+ var packageBinsPresent = (packageDir, prefix, platform = process.platform, requiredFiles = []) => {
97
+ try {
98
+ const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
99
+ if (!manifest.bin || typeof manifest.bin !== "object" || Array.isArray(manifest.bin)) return false;
100
+ const bins = Object.entries(manifest.bin);
101
+ if (bins.length === 0) return false;
102
+ if (!requiredFiles.every((path) => existsSync(join(packageDir, path)))) return false;
103
+ const seen = /* @__PURE__ */ new Set();
104
+ return bins.every(([name, target]) => {
105
+ if (typeof target !== "string") return false;
106
+ const shims = platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
107
+ const entry = join(packageDir, target);
108
+ return existsSync(entry) && shims.some(existsSync) && localImportsPresent(entry, packageDir, seen);
109
+ });
110
+ } catch {
111
+ return false;
112
+ }
113
+ };
114
+ var AGENT_HOOKS_RUNTIME_FILES = [
115
+ "data/SKILL.md",
116
+ "data/cursor-plugin/.cursor-plugin/plugin.json",
117
+ "data/cursor-plugin/hooks/hooks.json",
118
+ "data/cursor-plugin/mcp.json",
119
+ "data/cursor-plugin/scripts/pushary-gate.mjs",
120
+ "data/vscode-plugin/.claude-plugin/plugin.json",
121
+ "data/vscode-plugin/hooks/hooks.json",
122
+ "data/vscode-plugin/.mcp.json",
123
+ "data/vscode-plugin/scripts/pushary-gate.mjs"
124
+ ];
125
+ var globalPackageBinsPresent = (name) => {
126
+ try {
127
+ const root = execNpm("root -g --no-workspaces", { timeout: 5e3 }).toString().trim();
128
+ const prefix = execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim();
129
+ return packageBinsPresent(join(root, ...name.split("/")), prefix, process.platform, AGENT_HOOKS_RUNTIME_FILES);
130
+ } catch {
131
+ return false;
132
+ }
133
+ };
80
134
  var resolveGlobalBinDir = (probe) => {
81
135
  if (process.platform === "win32") return void 0;
82
136
  const resolved = resolveGlobalBinary(probe);
83
137
  return resolved ? resolved.slice(0, resolved.length - probe.length - 1) : void 0;
84
138
  };
85
139
  var quoteIfNeeded = (path) => /\s/.test(path) ? `"${path}"` : path;
140
+ var needsGlobalInstall = (requested, installed, binaryPresent) => requested !== installed || !binaryPresent;
86
141
  var installState;
87
142
  var installGlobally = (options) => {
88
143
  if (installState) {
@@ -90,6 +145,13 @@ var installGlobally = (options) => {
90
145
  }
91
146
  const started = (async () => {
92
147
  const pinned = process.env.PUSHARY_PIN_GLOBAL_VERSION !== "0";
148
+ if (pinned && !needsGlobalInstall(
149
+ options.version,
150
+ resolveGlobalPackageVersion("@pushary/agent-hooks"),
151
+ globalPackageBinsPresent("@pushary/agent-hooks")
152
+ )) {
153
+ return "skipped";
154
+ }
93
155
  const spec = pinned && options.version && options.version !== "0.0.0" ? `@pushary/agent-hooks@${options.version}` : "@pushary/agent-hooks@latest";
94
156
  execNpm(`install -g --no-workspaces ${spec}`, { timeout: options.timeoutMs ?? 12e4 });
95
157
  return "installed";
@@ -0,0 +1,20 @@
1
+ import {
2
+ readTranscriptPreference
3
+ } from "./chunk-NBDU3R3G.js";
4
+
5
+ // src/transcript-setting.ts
6
+ var fromEnv = () => {
7
+ const value = process.env.PUSHARY_TRANSCRIPTS?.trim().toLowerCase();
8
+ if (!value) return null;
9
+ if (["0", "false", "off"].includes(value)) return false;
10
+ if (["1", "true", "on"].includes(value)) return true;
11
+ return null;
12
+ };
13
+ var configuredPreference = () => fromEnv() ?? readTranscriptPreference();
14
+ var transcriptsEnabled = () => configuredPreference() ?? false;
15
+ var transcriptPreferenceForSetup = (explicit = null) => explicit ?? configuredPreference() ?? true;
16
+
17
+ export {
18
+ transcriptsEnabled,
19
+ transcriptPreferenceForSetup
20
+ };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-KVE5RCQL.js";
3
+ } from "./chunk-5YEF55AX.js";
4
4
  import {
5
5
  claudeWired,
6
6
  codexWired,
7
7
  geminiWired
8
- } from "./chunk-FZ73G2RO.js";
8
+ } from "./chunk-MSTHT2M7.js";
9
9
  import {
10
10
  GEMINI_HOOK_BINARY,
11
11
  addClaudeMcpServer,
@@ -14,14 +14,14 @@ import {
14
14
  addPusharyHooks,
15
15
  addPusharyToolPermissions,
16
16
  hasGeminiHooks
17
- } from "./chunk-Q223OM5J.js";
17
+ } from "./chunk-JVLB5CAJ.js";
18
18
  import {
19
19
  guardBinary
20
- } from "./chunk-SYUIXXQW.js";
20
+ } from "./chunk-VF3RTVWY.js";
21
21
  import {
22
22
  resolveGlobalBinDir,
23
23
  resolveGlobalBinary
24
- } from "./chunk-X4ZUUFKV.js";
24
+ } from "./chunk-L4ZCFMWW.js";
25
25
  import {
26
26
  CODEX_HOOK_BINARY,
27
27
  addCodexHookTrust,
@@ -32,10 +32,10 @@ import {
32
32
  codexHooksJson,
33
33
  hasCodexHooks,
34
34
  vscodePluginMcp
35
- } from "./chunk-QA72MKIT.js";
35
+ } from "./chunk-3YGGEDJK.js";
36
36
  import {
37
37
  readJsonSafe
38
- } from "./chunk-XX2V4QS5.js";
38
+ } from "./chunk-4LL6LG5X.js";
39
39
 
40
40
  // src/reapply.ts
41
41
  import { join, dirname } from "path";
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  hasGeminiHooks
3
- } from "./chunk-Q223OM5J.js";
3
+ } from "./chunk-JVLB5CAJ.js";
4
4
  import {
5
5
  hasCodexHooks
6
- } from "./chunk-QA72MKIT.js";
6
+ } from "./chunk-3YGGEDJK.js";
7
7
  import {
8
8
  HOOK_LOCATOR_BINARY
9
- } from "./chunk-X6RXX2NF.js";
9
+ } from "./chunk-QINYXLA5.js";
10
10
 
11
11
  // src/diagnostics/wiring.ts
12
12
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
13
13
  var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
14
- var EXACT_HOOK_BINARY = /^pushary-[a-z-]*hook$/;
14
+ var EXACT_HOOK_BINARY = /^pushary-[a-z-]*(hook|gate)$/;
15
15
  var pusharyHookExecutable = (command) => {
16
16
  const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
17
17
  for (const raw of tokens.reverse()) {
18
18
  const token = raw[0] === '"' && raw.at(-1) === '"' || raw[0] === "'" && raw.at(-1) === "'" ? raw.slice(1, -1) : raw;
19
- const name = token.split(/[/\\]/).pop()?.replace(/\.(cmd|exe)$/i, "") ?? "";
19
+ const name = token.split(/[/\\]/).pop()?.replace(/\.(cmd|exe|mjs)$/i, "") ?? "";
20
20
  if (name === HOOK_LOCATOR_BINARY || EXACT_HOOK_BINARY.test(name)) return token;
21
21
  }
22
22
  return null;
@@ -44,10 +44,29 @@ var geminiWired = ({ settings }) => {
44
44
  if (record(s.mcpServers)?.pushary) return true;
45
45
  return hasGeminiHooks(s);
46
46
  };
47
+ var cursorWired = ({ userHooks, mcp, pluginDirExists }) => {
48
+ if (pluginDirExists) return true;
49
+ if (record(record(mcp)?.mcpServers)?.pushary) return true;
50
+ const hooks = record(record(userHooks)?.hooks);
51
+ if (!hooks) return false;
52
+ return Object.values(hooks).some((entries) => Array.isArray(entries) && entries.some((entry) => pusharyHookExecutable(String(record(entry)?.command ?? "")) !== null));
53
+ };
54
+ var cursorMcpAuthorization = (pluginMcp, userMcp) => {
55
+ const pluginServers = record(record(pluginMcp)?.mcpServers);
56
+ const usePlugin = pluginServers !== null && Object.hasOwn(pluginServers, "pushary");
57
+ const servers = usePlugin ? pluginServers : record(record(userMcp)?.mcpServers);
58
+ const headers = record(record(servers?.pushary)?.headers);
59
+ return {
60
+ authorization: typeof headers?.Authorization === "string" ? headers.Authorization : null,
61
+ source: usePlugin ? "plugin mcp.json" : "~/.cursor/mcp.json"
62
+ };
63
+ };
47
64
 
48
65
  export {
49
66
  pusharyHookExecutable,
50
67
  claudeWired,
51
68
  codexWired,
52
- geminiWired
69
+ geminiWired,
70
+ cursorWired,
71
+ cursorMcpAuthorization
53
72
  };
@@ -5,7 +5,7 @@ import {
5
5
  FILE_TARGET_TOOLS,
6
6
  redactSecrets,
7
7
  redactSecretsDeep
8
- } from "./chunk-X6RXX2NF.js";
8
+ } from "./chunk-QINYXLA5.js";
9
9
 
10
10
  // ../hook-mapping/src/describe.ts
11
11
  import { isAbsolute, relative, resolve } from "path";
@@ -46,10 +46,13 @@ var eventPrefixes = {
46
46
  };
47
47
  var EVENT_ACTION_MAX = 120;
48
48
  var HOOK_FALLBACK_MAX = 200;
49
+ var UNNAMED_ACTION = "this request";
49
50
  var describeToolCall = (toolName, toolInput, format = "hook") => {
51
+ if (!toolName) return "";
50
52
  const prefixes = format === "hook" ? hookPrefixes : eventPrefixes;
51
53
  const builder = prefixes[toolName];
52
- const raw = builder ? builder(toolInput) : format === "hook" ? `${toolName}: ${JSON.stringify(toolInput)}` : `${toolName}: done`;
54
+ const detail = JSON.stringify(toolInput ?? {});
55
+ const raw = builder ? builder(toolInput) : format === "hook" ? detail === "{}" || detail === void 0 ? toolName : `${toolName}: ${detail}` : `${toolName}: done`;
53
56
  const redacted = redactSecrets(raw);
54
57
  if (format === "event") return redacted.slice(0, EVENT_ACTION_MAX);
55
58
  return builder ? redacted : redacted.slice(0, HOOK_FALLBACK_MAX);
@@ -269,6 +272,7 @@ var taskTitleFrom = (prompt) => typeof prompt === "string" ? prompt.replace(/\s+
269
272
 
270
273
  export {
271
274
  HOOK_FALLBACK_MAX,
275
+ UNNAMED_ACTION,
272
276
  describeToolCall,
273
277
  deriveToolTarget,
274
278
  parseApplyPatchOps,
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-BC3VCZ3E.js";
4
4
  import {
5
5
  resolveInstallSourceFor
6
- } from "./chunk-IUWRVTQK.js";
6
+ } from "./chunk-ANMHIZZY.js";
7
7
  import {
8
8
  backupFile,
9
9
  readJsonSafe,
10
10
  writeFileAtomic,
11
11
  writeJsonAtomic
12
- } from "./chunk-XX2V4QS5.js";
12
+ } from "./chunk-4LL6LG5X.js";
13
13
  import {
14
14
  configFilePath
15
15
  } from "./chunk-2UMNXADU.js";
@@ -125,7 +125,8 @@ var writeKey = (apiKey, options = {}) => {
125
125
  ...current,
126
126
  apiKey,
127
127
  installSource: resolveInstallSourceFor(current),
128
- ...options.controlMode ? { controlMode: options.controlMode } : {}
128
+ ...options.controlMode ? { controlMode: options.controlMode } : {},
129
+ ...options.transcriptsEnabled === void 0 ? {} : { transcriptsEnabled: options.transcriptsEnabled }
129
130
  },
130
131
  KEY_FILE_MODE
131
132
  );
@@ -188,6 +189,11 @@ var readControlMode = () => {
188
189
  const value = result.value.controlMode;
189
190
  return value === "auto" || value === "on" || value === "off" ? value : null;
190
191
  };
192
+ var readTranscriptPreference = () => {
193
+ const result = readJsonSafe(configFilePath());
194
+ if (result.kind !== "ok") return null;
195
+ return typeof result.value.transcriptsEnabled === "boolean" ? result.value.transcriptsEnabled : null;
196
+ };
191
197
  var readKeySource = () => {
192
198
  const configPath = configFilePath();
193
199
  const envKey = process.env.PUSHARY_API_KEY?.trim() || null;
@@ -206,5 +212,6 @@ export {
206
212
  clearKey,
207
213
  readConfigFileKey,
208
214
  readControlMode,
215
+ readTranscriptPreference,
209
216
  readKeySource
210
217
  };
@@ -26,16 +26,16 @@ import {
26
26
  summarizeStop,
27
27
  taskTitleFrom,
28
28
  toolResultErrorText
29
- } from "./chunk-TG3T3M4K.js";
29
+ } from "./chunk-N4QJ3CLX.js";
30
30
  import {
31
31
  getMachineId
32
- } from "./chunk-DKWM5T3P.js";
32
+ } from "./chunk-JTBDBKHU.js";
33
33
  import {
34
34
  withRetry
35
35
  } from "./chunk-BSZYIAZL.js";
36
36
  import {
37
37
  reportedInstallSource
38
- } from "./chunk-IUWRVTQK.js";
38
+ } from "./chunk-ANMHIZZY.js";
39
39
  import {
40
40
  getApiKey,
41
41
  getBaseUrl
@@ -51,7 +51,7 @@ import {
51
51
  pendingCommandEvents,
52
52
  resolveAutoResolveOrigin,
53
53
  resolvePolicy
54
- } from "./chunk-X6RXX2NF.js";
54
+ } from "./chunk-QINYXLA5.js";
55
55
 
56
56
  // src/policy.ts
57
57
  import { createHash } from "crypto";