@pushary/agent-hooks 0.95.2 → 0.95.5

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 (98) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +1 -1
  3. package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
  4. package/data/cursor-plugin/CHANGELOG.md +5 -0
  5. package/data/cursor-plugin/README.md +6 -8
  6. package/data/cursor-plugin/hooks/hooks.json +56 -1
  7. package/data/cursor-plugin/scripts/pushary-gate.mjs +120 -71
  8. package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
  9. package/data/vscode-plugin/CHANGELOG.md +5 -0
  10. package/data/vscode-plugin/README.md +7 -15
  11. package/data/vscode-plugin/hooks/hooks.json +49 -0
  12. package/data/vscode-plugin/scripts/pushary-gate.mjs +208 -88
  13. package/dist/bin/pushary-bell-hook.js +20 -22
  14. package/dist/bin/pushary-bell.js +9 -11
  15. package/dist/bin/pushary-claude.js +58 -46
  16. package/dist/bin/pushary-clean.js +21 -20
  17. package/dist/bin/pushary-codex-bridge.js +120 -25
  18. package/dist/bin/pushary-codex-hook.js +43 -63
  19. package/dist/bin/pushary-codex.js +11 -10
  20. package/dist/bin/pushary-connect.js +8 -10
  21. package/dist/bin/pushary-cowork.js +5 -5
  22. package/dist/bin/pushary-daemon.js +26 -28
  23. package/dist/bin/pushary-disconnect.js +11 -10
  24. package/dist/bin/pushary-doctor.js +25 -25
  25. package/dist/bin/pushary-elicitation-hook.js +30 -31
  26. package/dist/bin/pushary-gemini-bridge.js +9 -11
  27. package/dist/bin/pushary-gemini-hook.js +45 -79
  28. package/dist/bin/pushary-hook.js +27 -29
  29. package/dist/bin/pushary-login.js +57 -31
  30. package/dist/bin/pushary-logout.js +6 -8
  31. package/dist/bin/pushary-mode.js +5 -5
  32. package/dist/bin/pushary-notification-hook.js +22 -24
  33. package/dist/bin/pushary-opencode-hook.js +38 -74
  34. package/dist/bin/pushary-permission-denied-hook.js +25 -27
  35. package/dist/bin/pushary-permission-hook.js +25 -27
  36. package/dist/bin/pushary-post-hook.js +22 -24
  37. package/dist/bin/pushary-prompt-hook.js +22 -24
  38. package/dist/bin/pushary-session-end-hook.js +22 -24
  39. package/dist/bin/pushary-session-start-hook.js +25 -27
  40. package/dist/bin/pushary-setup.js +78 -55
  41. package/dist/bin/pushary-stats.js +4 -4
  42. package/dist/bin/pushary-status.js +10 -12
  43. package/dist/bin/pushary-stop-hook.js +22 -24
  44. package/dist/bin/pushary-stopfailure-hook.js +22 -24
  45. package/dist/bin/pushary-suggestions.js +2 -2
  46. package/dist/bin/pushary-transcript-register.js +18 -20
  47. package/dist/bin/pushary-transcripts.js +7 -9
  48. package/dist/bin/pushary-upgrade.js +17 -19
  49. package/dist/bin/pushary-wait.js +5 -5
  50. package/dist/bin/pushary.js +1 -1
  51. package/dist/{chunk-7LUFYRH4.js → chunk-2D7BAOMY.js} +7 -7
  52. package/dist/{chunk-3ZUTNTZA.js → chunk-2ZTL3PGN.js} +1 -1
  53. package/dist/{chunk-EPFDN2ZT.js → chunk-32RMQO5Z.js} +1 -1
  54. package/dist/{chunk-EZIEXHYM.js → chunk-3AJIK5AO.js} +5 -2
  55. package/dist/{chunk-IPHOUBTU.js → chunk-4PX4QK2Z.js} +1 -1
  56. package/dist/{chunk-UW4LWAWY.js → chunk-4RF52LSU.js} +8 -8
  57. package/dist/{chunk-UOTSWS5N.js → chunk-67YNQ3QA.js} +43 -98
  58. package/dist/{chunk-G4TL4YWH.js → chunk-6AEM5RCS.js} +1 -1
  59. package/dist/{chunk-G24SEVCI.js → chunk-6VANQN5Q.js} +3 -7
  60. package/dist/{chunk-CKOLDI47.js → chunk-BDI2MXTT.js} +2 -2
  61. package/dist/{chunk-YKLCWWEI.js → chunk-BMANRGJQ.js} +1 -1
  62. package/dist/{chunk-2XUPP4WM.js → chunk-CUEZMPLJ.js} +1 -1
  63. package/dist/{chunk-A4R6BZCK.js → chunk-D62EONG3.js} +1 -1
  64. package/dist/{chunk-ZDAT5KMU.js → chunk-D6PRC3YQ.js} +1 -1
  65. package/dist/{chunk-MOY5X6HJ.js → chunk-DLLAWCQB.js} +1 -1
  66. package/dist/{chunk-DSTLA2TC.js → chunk-GOPIMNVC.js} +1 -1
  67. package/dist/{chunk-CH6DTGQR.js → chunk-HBHEX3JQ.js} +3 -3
  68. package/dist/{chunk-DNVDKHQD.js → chunk-HCMM3BOW.js} +226 -58
  69. package/dist/{chunk-76XICHYT.js → chunk-HGUZOUB2.js} +4 -4
  70. package/dist/{chunk-E3NF5CL2.js → chunk-HQIVR7MM.js} +6 -3
  71. package/dist/{chunk-JDFB3YKG.js → chunk-I6GZX3FT.js} +3 -3
  72. package/dist/{chunk-3OXHYPZG.js → chunk-IM2A5H33.js} +4 -1
  73. package/dist/{chunk-V27AHID3.js → chunk-IVQMNLSA.js} +3 -14
  74. package/dist/{chunk-4LL6LG5X.js → chunk-KAVDAZ7A.js} +43 -1
  75. package/dist/chunk-KBITFQST.js +6 -0
  76. package/dist/{chunk-AMZJ3CKW.js → chunk-KI4PA7MQ.js} +1 -1
  77. package/dist/{chunk-IAEXPTXA.js → chunk-N4VNLISN.js} +2 -2
  78. package/dist/{chunk-VX3BWMC5.js → chunk-OEKFD5BC.js} +1 -1
  79. package/dist/{chunk-QF4RI5CD.js → chunk-OI3DJA7N.js} +1 -1
  80. package/dist/{chunk-WB2C7VPF.js → chunk-OW6AZDY2.js} +3 -3
  81. package/dist/{chunk-4XXUHWIK.js → chunk-QHWXUNMN.js} +1 -1
  82. package/dist/{chunk-M7WWEFG6.js → chunk-QQGNFHIK.js} +2 -2
  83. package/dist/{chunk-YJAQRKH5.js → chunk-RLXVW4AM.js} +1 -1
  84. package/dist/{chunk-GUVCYYDK.js → chunk-S5CMLR35.js} +94 -15
  85. package/dist/{chunk-EUAQ4YAN.js → chunk-UCS65VVR.js} +1 -1
  86. package/dist/{chunk-WM5XMCID.js → chunk-VOSGZ6PH.js} +4 -4
  87. package/dist/{chunk-QUE76J4H.js → chunk-VTO3TNIB.js} +1 -1
  88. package/dist/{chunk-6FAHPGCZ.js → chunk-WDKHZRAS.js} +183 -25
  89. package/dist/{chunk-5GLPYGKS.js → chunk-WRDLHR2F.js} +2 -2
  90. package/dist/{chunk-RVSEGFPD.js → chunk-XLOELLAS.js} +9 -4
  91. package/dist/chunk-XS33QBJA.js +112 -0
  92. package/dist/{chunk-FQUV5KTM.js → chunk-Z2UWFEJW.js} +8 -8
  93. package/dist/src/index.d.ts +8 -1
  94. package/dist/src/index.js +11 -12
  95. package/package.json +1 -1
  96. package/dist/chunk-BC3VCZ3E.js +0 -86
  97. package/dist/chunk-RER3FRTD.js +0 -265
  98. package/dist/chunk-X7YWD6IS.js +0 -44
@@ -1,19 +1,9 @@
1
1
  import {
2
- DEFAULT_SESSION,
3
2
  cancelQuestion,
4
- isDefaultSession,
5
3
  isPolicyConfig,
6
- listPendingQuestions,
7
- listPendingQuestionsForToolUse,
8
- markPendingCommandDelivered,
9
- readPendingCommand,
10
- removePendingCommand,
11
- removePendingQuestion,
12
- removePendingSession,
13
- savePendingCommand,
14
4
  sendNotification,
15
5
  waitForAnswer
16
- } from "./chunk-RER3FRTD.js";
6
+ } from "./chunk-XS33QBJA.js";
17
7
  import {
18
8
  AGENT_IDENTITIES,
19
9
  TEAMMATE_IDLE_ACTION,
@@ -34,20 +24,20 @@ import {
34
24
  summarizeStop,
35
25
  taskTitleFrom,
36
26
  toolResultErrorText
37
- } from "./chunk-CKOLDI47.js";
27
+ } from "./chunk-BDI2MXTT.js";
38
28
  import {
39
29
  NOTIFICATION_PUSH_TYPES,
40
30
  POSTTOOLUSE_MIRROR_ONLY_TOOLS
41
- } from "./chunk-V27AHID3.js";
31
+ } from "./chunk-IVQMNLSA.js";
42
32
  import {
43
33
  getMachineId
44
- } from "./chunk-IAEXPTXA.js";
34
+ } from "./chunk-N4VNLISN.js";
45
35
  import {
46
36
  withRetry
47
37
  } from "./chunk-EYRGBYZG.js";
48
38
  import {
49
39
  reportedInstallSource
50
- } from "./chunk-X7YWD6IS.js";
40
+ } from "./chunk-KAVDAZ7A.js";
51
41
  import {
52
42
  getApiKey,
53
43
  getBaseUrl
@@ -55,6 +45,7 @@ import {
55
45
  import {
56
46
  HOOK_SOURCES,
57
47
  PENDING_COMMAND_PREFIX,
48
+ REPO_AWARE_AGENTS,
58
49
  gateOutputFor,
59
50
  isAutoApprove,
60
51
  localRepoKey,
@@ -63,7 +54,7 @@ import {
63
54
  pendingCommandEvents,
64
55
  resolveAutoResolveOrigin,
65
56
  resolvePolicy
66
- } from "./chunk-6FAHPGCZ.js";
57
+ } from "./chunk-WDKHZRAS.js";
67
58
 
68
59
  // src/policy.ts
69
60
  import { createHash } from "crypto";
@@ -72,7 +63,6 @@ import { join } from "path";
72
63
  import { tmpdir } from "os";
73
64
  var CACHE_TTL_MS = 60 * 1e3;
74
65
  var STALE_CACHE_MAX_MS = 12 * 60 * 60 * 1e3;
75
- var REPO_AWARE_AGENTS = ["claude_code", "codex", "gemini_cli", "opencode"];
76
66
  var policyRequestFor = (agentType) => ({
77
67
  agent: agentType,
78
68
  repoAware: REPO_AWARE_AGENTS.includes(agentType)
@@ -206,19 +196,190 @@ var fetchModeState = async (apiKey, sessionId, reuseWithinMs = 0, signal) => {
206
196
  };
207
197
  var fetchModeOverride = async (apiKey) => (await fetchModeState(apiKey)).mode;
208
198
 
199
+ // src/pending.ts
200
+ import { join as join2 } from "path";
201
+ import { homedir, tmpdir as tmpdir2 } from "os";
202
+ import { createHash as createHash2 } from "crypto";
203
+ import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2, readdirSync, renameSync, unlinkSync, rmSync, statSync } from "fs";
204
+ var PENDING_DIR = join2(tmpdir2(), "pushary-pending");
205
+ var commandRoot = () => process.env.PUSHARY_COMMAND_DIR?.trim() || join2(homedir(), ".pushary", "run", "commands");
206
+ var DEFAULT_SESSION = "_no_session";
207
+ var GRACE_MS = 10 * 60 * 1e3;
208
+ var sanitize = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
209
+ var dirFor = (sessionId) => join2(PENDING_DIR, sanitize(sessionId));
210
+ var commandDirFor = (sessionId, namespace = "") => join2(
211
+ commandRoot(),
212
+ createHash2("sha256").update(namespace).digest("hex").slice(0, 16),
213
+ sanitize(sessionId)
214
+ );
215
+ var legacyCommandFile = (sessionId, namespace = "") => join2(commandDirFor(sessionId, namespace), "command.json");
216
+ var commandFile = (sessionId, id, namespace = "") => join2(commandDirFor(sessionId, namespace), `${createHash2("sha256").update(id).digest("hex")}.json`);
217
+ var readCommandFile = (file) => {
218
+ try {
219
+ const value = JSON.parse(readFileSync2(file, "utf-8"));
220
+ if (typeof value.id !== "string" || typeof value.text !== "string" || typeof value.expiresAtMs !== "number") return null;
221
+ if (value.expiresAtMs <= Date.now()) {
222
+ unlinkSync(file);
223
+ return null;
224
+ }
225
+ return {
226
+ ...value,
227
+ delivered: value.delivered === true,
228
+ ...value.failed === true ? { failed: true } : {}
229
+ };
230
+ } catch {
231
+ return null;
232
+ }
233
+ };
234
+ var readPendingCommandById = (sessionId, id, namespace = "") => {
235
+ const current = readCommandFile(commandFile(sessionId, id, namespace));
236
+ if (current?.id === id) return current;
237
+ const legacy = readCommandFile(legacyCommandFile(sessionId, namespace));
238
+ return legacy?.id === id ? legacy : null;
239
+ };
240
+ var pruneExpiredCommands = (dir) => {
241
+ let files;
242
+ try {
243
+ files = readdirSync(dir).filter((file) => file.endsWith(".json"));
244
+ } catch {
245
+ return [];
246
+ }
247
+ return files.flatMap((file) => {
248
+ const command = readCommandFile(join2(dir, file));
249
+ return command ? [command] : [];
250
+ });
251
+ };
252
+ var pruneExpiredCommandStore = () => {
253
+ let namespaces;
254
+ try {
255
+ namespaces = readdirSync(commandRoot(), { withFileTypes: true });
256
+ } catch {
257
+ return;
258
+ }
259
+ for (const namespace of namespaces.filter((entry) => entry.isDirectory())) {
260
+ const namespaceDir = join2(commandRoot(), namespace.name);
261
+ let sessions;
262
+ try {
263
+ sessions = readdirSync(namespaceDir, { withFileTypes: true });
264
+ } catch {
265
+ continue;
266
+ }
267
+ for (const session of sessions.filter((entry) => entry.isDirectory())) {
268
+ pruneExpiredCommands(join2(namespaceDir, session.name));
269
+ }
270
+ }
271
+ };
272
+ var readPendingCommand = (sessionId, namespace = "") => {
273
+ const commands = pruneExpiredCommands(commandDirFor(sessionId, namespace));
274
+ return commands.find((command) => !command.delivered && !command.failed) ?? commands[0] ?? null;
275
+ };
276
+ var savePendingCommand = (sessionId, command, namespace = "") => {
277
+ const dir = commandDirFor(sessionId, namespace);
278
+ try {
279
+ mkdirSync(dir, { recursive: true, mode: 448 });
280
+ pruneExpiredCommandStore();
281
+ if (readPendingCommandById(sessionId, command.id, namespace)) return false;
282
+ writeFileSync2(commandFile(sessionId, command.id, namespace), JSON.stringify(command), { encoding: "utf-8", mode: 384, flag: "wx" });
283
+ return true;
284
+ } catch {
285
+ return false;
286
+ }
287
+ };
288
+ var updatePendingCommand = (sessionId, id, update, namespace = "") => {
289
+ const command = readPendingCommandById(sessionId, id, namespace);
290
+ if (!command) return false;
291
+ const currentFile = commandFile(sessionId, id, namespace);
292
+ const file = existsSync2(currentFile) ? currentFile : legacyCommandFile(sessionId, namespace);
293
+ const temporary = `${file}.${process.pid}.tmp`;
294
+ try {
295
+ writeFileSync2(temporary, JSON.stringify({ ...command, ...update, text: "" }), { encoding: "utf-8", mode: 384 });
296
+ renameSync(temporary, file);
297
+ return true;
298
+ } catch {
299
+ try {
300
+ unlinkSync(temporary);
301
+ } catch {
302
+ }
303
+ return false;
304
+ }
305
+ };
306
+ var markPendingCommandDelivered = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: true, failed: void 0 }, namespace);
307
+ var markPendingCommandFailed = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: false, failed: true }, namespace);
308
+ var removePendingCommand = (sessionId, id, namespace = "") => {
309
+ if (!readPendingCommandById(sessionId, id, namespace)) return;
310
+ try {
311
+ unlinkSync(commandFile(sessionId, id, namespace));
312
+ } catch {
313
+ }
314
+ try {
315
+ if (readCommandFile(legacyCommandFile(sessionId, namespace))?.id === id) {
316
+ unlinkSync(legacyCommandFile(sessionId, namespace));
317
+ }
318
+ } catch {
319
+ }
320
+ };
321
+ var isDefaultSession = (sessionId) => sanitize(sessionId) === DEFAULT_SESSION;
322
+ var listPendingQuestionsForToolUse = (sessionId, toolUseId) => {
323
+ if (!toolUseId) return [];
324
+ const dir = dirFor(sessionId);
325
+ let files;
326
+ try {
327
+ files = readdirSync(dir);
328
+ } catch {
329
+ return [];
330
+ }
331
+ return files.filter((name) => {
332
+ try {
333
+ return readFileSync2(join2(dir, name), "utf-8") === toolUseId;
334
+ } catch {
335
+ return false;
336
+ }
337
+ });
338
+ };
339
+ var listPendingQuestions = (sessionId) => {
340
+ const dir = dirFor(sessionId);
341
+ let files;
342
+ try {
343
+ files = readdirSync(dir);
344
+ } catch {
345
+ return [];
346
+ }
347
+ if (!isDefaultSession(sessionId)) return files;
348
+ const cutoff = Date.now() - GRACE_MS;
349
+ return files.filter((name) => {
350
+ try {
351
+ return statSync(join2(dir, name)).mtimeMs < cutoff;
352
+ } catch {
353
+ return false;
354
+ }
355
+ });
356
+ };
357
+ var removePendingQuestion = (sessionId, correlationId) => {
358
+ try {
359
+ unlinkSync(join2(dirFor(sessionId), correlationId));
360
+ } catch {
361
+ }
362
+ };
363
+ var removePendingSession = (sessionId) => {
364
+ try {
365
+ rmSync(dirFor(sessionId), { recursive: true, force: true });
366
+ } catch {
367
+ }
368
+ };
369
+
209
370
  // src/repo.ts
210
- import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
211
- import { basename, dirname, isAbsolute, join as join2, resolve } from "path";
371
+ import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
372
+ import { basename, dirname, isAbsolute, join as join3, resolve } from "path";
212
373
  var MAX_PARENT_WALK = 64;
213
374
  var GIT_FILE_PREFIX = "gitdir:";
214
375
  var findGitDir = (startDir) => {
215
376
  let current = resolve(startDir);
216
377
  for (let depth = 0; depth < MAX_PARENT_WALK; depth += 1) {
217
- const candidate = join2(current, ".git");
378
+ const candidate = join3(current, ".git");
218
379
  try {
219
- if (existsSync2(candidate)) {
220
- if (statSync(candidate).isDirectory()) return candidate;
221
- const pointer = readFileSync2(candidate, "utf-8").trim();
380
+ if (existsSync3(candidate)) {
381
+ if (statSync2(candidate).isDirectory()) return candidate;
382
+ const pointer = readFileSync3(candidate, "utf-8").trim();
222
383
  if (pointer.startsWith(GIT_FILE_PREFIX)) {
223
384
  const target = pointer.slice(GIT_FILE_PREFIX.length).trim();
224
385
  return isAbsolute(target) ? target : resolve(current, target);
@@ -234,7 +395,7 @@ var findGitDir = (startDir) => {
234
395
  };
235
396
  var configPathFor = (gitDir) => {
236
397
  const index = gitDir.replace(/\\/g, "/").indexOf("/worktrees/");
237
- return index === -1 ? join2(gitDir, "config") : join2(gitDir.slice(0, index), "config");
398
+ return index === -1 ? join3(gitDir, "config") : join3(gitDir.slice(0, index), "config");
238
399
  };
239
400
  var ORIGIN_SECTION = /^\[remote "origin"\]/;
240
401
  var SECTION_HEADER = /^\[/;
@@ -242,9 +403,9 @@ var URL_LINE = /^url\s*=\s*(.+)$/;
242
403
  var readOriginRemote = (gitDir) => {
243
404
  try {
244
405
  const configPath = configPathFor(gitDir);
245
- if (!existsSync2(configPath)) return void 0;
406
+ if (!existsSync3(configPath)) return void 0;
246
407
  let inOrigin = false;
247
- for (const rawLine of readFileSync2(configPath, "utf-8").split("\n")) {
408
+ for (const rawLine of readFileSync3(configPath, "utf-8").split("\n")) {
248
409
  const line = rawLine.trim();
249
410
  if (SECTION_HEADER.test(line)) {
250
411
  inOrigin = ORIGIN_SECTION.test(line);
@@ -291,10 +452,10 @@ var repoKeyFor = (cwd) => {
291
452
  };
292
453
 
293
454
  // src/usage.ts
294
- import { closeSync, mkdirSync, openSync, readFileSync as readFileSync3, readSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
295
- import { createHash as createHash2 } from "crypto";
296
- import { join as join3 } from "path";
297
- import { tmpdir as tmpdir2 } from "os";
455
+ import { closeSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync4, readSync, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
456
+ import { createHash as createHash3 } from "crypto";
457
+ import { join as join4 } from "path";
458
+ import { tmpdir as tmpdir3 } from "os";
298
459
  var DEFAULT_PRICES = [
299
460
  { match: "opus-4-1", in: 15, out: 75 },
300
461
  { match: "opus-4-0", in: 15, out: 75 },
@@ -329,12 +490,12 @@ var estimateCostUsd = (usage, model) => {
329
490
  const perTokenOut = price.out / 1e6;
330
491
  return usage.inputTokens * perTokenIn + usage.outputTokens * perTokenOut + usage.cacheCreationTokens * perTokenIn * CACHE_WRITE_MULTIPLIER + usage.cacheReadTokens * perTokenIn * CACHE_READ_MULTIPLIER;
331
492
  };
332
- var stateDir = () => process.env.PUSHARY_USAGE_DIR?.trim() || join3(tmpdir2(), "pushary-usage");
333
- var stateFile = (sessionId) => join3(stateDir(), sessionId.replace(/[^a-zA-Z0-9_-]/g, "_"));
493
+ var stateDir = () => process.env.PUSHARY_USAGE_DIR?.trim() || join4(tmpdir3(), "pushary-usage");
494
+ var stateFile = (sessionId) => join4(stateDir(), sessionId.replace(/[^a-zA-Z0-9_-]/g, "_"));
334
495
  var emptyState = () => ({ offset: 0, tokensIn: 0, tokensOut: 0, costUsd: 0, recentIds: [] });
335
496
  var readState = (path) => {
336
497
  try {
337
- const parsed = JSON.parse(readFileSync3(path, "utf-8"));
498
+ const parsed = JSON.parse(readFileSync4(path, "utf-8"));
338
499
  if (typeof parsed.offset === "number" && parsed.offset >= 0 && typeof parsed.tokensIn === "number" && typeof parsed.tokensOut === "number" && typeof parsed.costUsd === "number" && Array.isArray(parsed.recentIds)) {
339
500
  return {
340
501
  offset: parsed.offset,
@@ -409,7 +570,7 @@ var extractUserText = (content) => {
409
570
  };
410
571
  var readLastUserPrompt = (transcriptPath) => {
411
572
  try {
412
- const size = statSync2(transcriptPath).size;
573
+ const size = statSync3(transcriptPath).size;
413
574
  const start = Math.max(0, size - USER_PROMPT_TAIL_BYTES);
414
575
  const lines = readRange(transcriptPath, start, size).toString("utf-8").split("\n");
415
576
  for (let i = lines.length - 1; i >= 0; i -= 1) {
@@ -432,7 +593,7 @@ var readLastUserPrompt = (transcriptPath) => {
432
593
  };
433
594
  var readNewUsage = (transcriptPath, sessionId) => {
434
595
  try {
435
- const file = statSync2(transcriptPath);
596
+ const file = statSync3(transcriptPath);
436
597
  const size = file.size;
437
598
  const path = stateFile(sessionId);
438
599
  let state = readState(path);
@@ -450,14 +611,14 @@ var readNewUsage = (transcriptPath, sessionId) => {
450
611
  state.offset += lastNewline + 1;
451
612
  }
452
613
  }
453
- mkdirSync(stateDir(), { recursive: true });
454
- writeFileSync2(path, JSON.stringify(state), "utf-8");
614
+ mkdirSync2(stateDir(), { recursive: true });
615
+ writeFileSync3(path, JSON.stringify(state), "utf-8");
455
616
  if (state.tokensIn === 0 && state.tokensOut === 0) return null;
456
617
  return {
457
618
  // Identical cursor-range reports share an identity across transport retries.
458
619
  // Native message identity distinguishes an in-place rotation of the range.
459
620
  // This does not serialize readers of different, potentially overlapping ranges.
460
- eventId: createHash2("sha256").update(JSON.stringify([
621
+ eventId: createHash3("sha256").update(JSON.stringify([
461
622
  sessionId,
462
623
  file.dev,
463
624
  file.ino,
@@ -542,18 +703,18 @@ var phoneDrivenEnv = (base) => ({
542
703
  });
543
704
 
544
705
  // src/throttle.ts
545
- import { join as join4 } from "path";
546
- import { tmpdir as tmpdir3 } from "os";
547
- import { createHash as createHash3 } from "crypto";
548
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
549
- var THROTTLE_DIR = join4(tmpdir3(), "pushary-throttle");
550
- var markerPath = (key) => join4(THROTTLE_DIR, createHash3("sha256").update(key).digest("hex").slice(0, 16));
706
+ import { join as join5 } from "path";
707
+ import { tmpdir as tmpdir4 } from "os";
708
+ import { createHash as createHash4 } from "crypto";
709
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
710
+ var THROTTLE_DIR = join5(tmpdir4(), "pushary-throttle");
711
+ var markerPath = (key) => join5(THROTTLE_DIR, createHash4("sha256").update(key).digest("hex").slice(0, 16));
551
712
  var throttlePass = (key, windowMs) => {
552
713
  try {
553
714
  const path = markerPath(key);
554
- if (existsSync3(path) && Date.now() - statSync3(path).mtimeMs < windowMs) return false;
555
- mkdirSync2(THROTTLE_DIR, { recursive: true });
556
- writeFileSync3(path, "", "utf-8");
715
+ if (existsSync4(path) && Date.now() - statSync4(path).mtimeMs < windowMs) return false;
716
+ mkdirSync3(THROTTLE_DIR, { recursive: true });
717
+ writeFileSync4(path, "", "utf-8");
557
718
  return true;
558
719
  } catch {
559
720
  return false;
@@ -561,28 +722,28 @@ var throttlePass = (key, windowMs) => {
561
722
  };
562
723
 
563
724
  // src/events.ts
564
- import { basename as basename2, join as join5 } from "path";
565
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
566
- import { tmpdir as tmpdir4 } from "os";
567
- var INTENT_DIR = join5(tmpdir4(), "pushary-intent");
725
+ import { basename as basename2, join as join6 } from "path";
726
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
727
+ import { tmpdir as tmpdir5 } from "os";
728
+ var INTENT_DIR = join6(tmpdir5(), "pushary-intent");
568
729
  var INTENT_MAX_BYTES = 2048;
569
730
  var INTENT_GRACE_MS = 10 * 60 * 1e3;
570
731
  var sanitizeSession = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
571
- var intentFile = (sessionId) => join5(INTENT_DIR, sanitizeSession(sessionId));
732
+ var intentFile = (sessionId) => join6(INTENT_DIR, sanitizeSession(sessionId));
572
733
  var saveLastPrompt = (sessionId, prompt) => {
573
734
  try {
574
735
  const trimmed = prompt.replace(/\s+/g, " ").trim().slice(0, INTENT_MAX_BYTES);
575
736
  if (!trimmed) return;
576
- mkdirSync3(INTENT_DIR, { recursive: true });
577
- writeFileSync4(intentFile(sessionId), trimmed, "utf-8");
737
+ mkdirSync4(INTENT_DIR, { recursive: true });
738
+ writeFileSync5(intentFile(sessionId), trimmed, "utf-8");
578
739
  } catch {
579
740
  }
580
741
  };
581
742
  var readLastPrompt = (sessionId) => {
582
743
  try {
583
744
  const path = intentFile(sessionId);
584
- if (isDefaultSession(sessionId) && statSync4(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
585
- const value = readFileSync4(path, "utf-8").trim();
745
+ if (isDefaultSession(sessionId) && statSync5(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
746
+ const value = readFileSync5(path, "utf-8").trim();
586
747
  return value.length > 0 ? value : void 0;
587
748
  } catch {
588
749
  return void 0;
@@ -645,8 +806,8 @@ var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
645
806
  var readFreshCachedPolicy = (apiKey, agentType, sessionId) => {
646
807
  const request = policyRequestFor(agentType);
647
808
  const path = policyCacheFile(apiKey, request.agent, request.repoAware);
648
- if (!existsSync4(path)) return null;
649
- const cached = JSON.parse(readFileSync4(path, "utf-8"));
809
+ if (!existsSync5(path)) return null;
810
+ const cached = JSON.parse(readFileSync5(path, "utf-8"));
650
811
  if (!isPolicyConfig(cached)) return null;
651
812
  if ((cached._sessionId ?? null) !== (sessionId ?? null)) return null;
652
813
  if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
@@ -1139,6 +1300,13 @@ export {
1139
1300
  fetchModeState,
1140
1301
  fetchModeOverride,
1141
1302
  phoneDrivenEnv,
1303
+ DEFAULT_SESSION,
1304
+ readPendingCommandById,
1305
+ readPendingCommand,
1306
+ savePendingCommand,
1307
+ markPendingCommandDelivered,
1308
+ markPendingCommandFailed,
1309
+ removePendingCommand,
1142
1310
  repoKeyFor,
1143
1311
  throttlePass,
1144
1312
  readLastUserPrompt,
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-7LUFYRH4.js";
4
+ } from "./chunk-2D7BAOMY.js";
5
5
  import {
6
6
  guardBinary
7
- } from "./chunk-EZIEXHYM.js";
7
+ } from "./chunk-3AJIK5AO.js";
8
8
  import {
9
9
  isProtectedByScope
10
- } from "./chunk-WB2C7VPF.js";
10
+ } from "./chunk-OW6AZDY2.js";
11
11
  import {
12
12
  HOOK_LOCATOR_BINARY
13
- } from "./chunk-6FAHPGCZ.js";
13
+ } from "./chunk-WDKHZRAS.js";
14
14
 
15
15
  // src/claude-config.ts
16
16
  import { join } from "path";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
3
  OPENCODE_APPROVAL_EVENT
4
- } from "./chunk-6FAHPGCZ.js";
4
+ } from "./chunk-WDKHZRAS.js";
5
5
 
6
6
  // src/opencode-plugin.ts
7
7
  var OPENCODE_PLUGIN_MARKER = "pushary-opencode-plugin w1";
@@ -174,15 +174,18 @@ export const PusharyPlugin = async ({ client, directory, worktree }) => {
174
174
  : null
175
175
  const current = client && client.permission && typeof client.permission.reply === "function"
176
176
  ? () => client.permission.reply({
177
+ // Current v2 uses flat parameters; older generated clients read path/body.
178
+ requestID: asked.id, reply: allow ? "once" : "reject",
177
179
  path: { requestID: asked.id },
178
180
  body: { reply: allow ? "once" : "reject" },
179
- })
181
+ }, { throwOnError: true })
180
182
  : null
181
183
 
182
184
  for (const attempt of [legacy, current]) {
183
185
  if (!attempt) continue
184
186
  try {
185
- await attempt()
187
+ const result = await attempt()
188
+ if (result && result.error) continue
186
189
  return
187
190
  } catch {
188
191
  // The person answered first, or this client does not serve that route.
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  isValidApiKey,
15
15
  normalizeKeyScope
16
- } from "./chunk-6FAHPGCZ.js";
16
+ } from "./chunk-WDKHZRAS.js";
17
17
 
18
18
  // src/session.ts
19
19
  var samePusharyAccount = (left, right) => left.kind === "ok" && right.kind === "ok" && left.identity.workspaceId !== "" && left.identity.workspaceId === right.identity.workspaceId && left.identity.siteId === right.identity.siteId;
@@ -148,7 +148,7 @@ var describeKeyCheck = (check, baseUrl = getBaseUrl()) => {
148
148
  return {
149
149
  message: "Your Pushary plan is not active, so approvals will not deliver yet.",
150
150
  next: [
151
- "Your agents are configured and your key itself is fine.",
151
+ "Your key is valid; activating the plan does not require replacing it.",
152
152
  `Activate the plan: ${check.upgradeUrl}`,
153
153
  "Then run: pushary doctor"
154
154
  ],
@@ -158,7 +158,7 @@ var describeKeyCheck = (check, baseUrl = getBaseUrl()) => {
158
158
  case "unreachable":
159
159
  return {
160
160
  message: `Could not reach pushary.com to verify the key (${check.detail}).`,
161
- next: ["Continuing anyway. Run `pushary doctor` once you are online."],
161
+ next: ["Account readiness is unverified. Re-run `pushary setup` once you are online."],
162
162
  exitCode: EXIT.UNREACHABLE,
163
163
  fatal: false
164
164
  };
@@ -3,6 +3,9 @@ import {
3
3
  resolveGlobalPackageVersion,
4
4
  resolvePackageVersionFromBinary
5
5
  } from "./chunk-L4ZCFMWW.js";
6
+ import {
7
+ MANAGED_PROVIDERS
8
+ } from "./chunk-WDKHZRAS.js";
6
9
 
7
10
  // src/live-capabilities.ts
8
11
  var CLAUDE_BRIDGE_CAPABILITY = "claude-bridge";
@@ -39,7 +42,7 @@ var resolveControlPolicy = (input) => {
39
42
  if (input.providerAvailable && input.managerAvailable) return "enabled";
40
43
  return input.servicePresent ? "legacy" : "unsupported";
41
44
  };
42
- var detectProviderLiveCapabilities = (providers = ["claude", "codex", "gemini"]) => {
45
+ var detectProviderLiveCapabilities = (providers = MANAGED_PROVIDERS) => {
43
46
  const enabled = new Set(providers);
44
47
  const geminiBin = enabled.has("gemini") ? resolveBinary("gemini") : void 0;
45
48
  return providerLiveCapabilities({
@@ -1,21 +1,12 @@
1
1
  import {
2
- ASKED_IN_EVERY_MODE_TOOLS
3
- } from "./chunk-6FAHPGCZ.js";
2
+ ASKED_IN_EVERY_MODE_TOOLS,
3
+ PRETOOLUSE_GATED_TOOLS
4
+ } from "./chunk-WDKHZRAS.js";
4
5
 
5
6
  // src/claude-tools.ts
6
- var PRETOOLUSE_GATED_TOOLS = [
7
- "Bash",
8
- "Write",
9
- "Edit",
10
- "MultiEdit",
11
- "NotebookEdit",
12
- "PowerShell",
13
- "Monitor"
14
- ];
15
7
  var PRETOOLUSE_PHONE_ANSWERED_TOOLS = ASKED_IN_EVERY_MODE_TOOLS;
16
8
  var POSTTOOLUSE_MIRROR_ONLY_TOOLS = ["TodoWrite", "Task", "WebFetch", "WebSearch"];
17
9
  var POSTTOOLUSE_MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...POSTTOOLUSE_MIRROR_ONLY_TOOLS];
18
- var PRETOOLUSE_HANDLED_TOOLS = /* @__PURE__ */ new Set([...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS]);
19
10
 
20
11
  // src/claude-notifications.ts
21
12
  var NOTIFICATION_MATCHED_TYPES = [
@@ -43,11 +34,9 @@ var NOTIFICATION_PUSH_TYPES = /* @__PURE__ */ new Set([
43
34
  ]);
44
35
 
45
36
  export {
46
- PRETOOLUSE_GATED_TOOLS,
47
37
  PRETOOLUSE_PHONE_ANSWERED_TOOLS,
48
38
  POSTTOOLUSE_MIRROR_ONLY_TOOLS,
49
39
  POSTTOOLUSE_MIRRORED_TOOLS,
50
- PRETOOLUSE_HANDLED_TOOLS,
51
40
  NOTIFICATION_MATCHED_TYPES,
52
41
  NOTIFICATION_PUSH_TYPES
53
42
  };
@@ -1,3 +1,10 @@
1
+ import {
2
+ configFilePath
3
+ } from "./chunk-2UMNXADU.js";
4
+ import {
5
+ normalizeInstallSource
6
+ } from "./chunk-WDKHZRAS.js";
7
+
1
8
  // src/cli/fsx.ts
2
9
  import { copyFileSync, existsSync, fsyncSync, mkdirSync, openSync, closeSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
3
10
  import { dirname, join } from "path";
@@ -57,10 +64,45 @@ var writeJsonAtomicDurable = (filePath, data, mode) => {
57
64
  }
58
65
  };
59
66
 
67
+ // src/install-source.ts
68
+ import { readFileSync as readFileSync2 } from "fs";
69
+ var ENV_OVERRIDE = "PUSHARY_INSTALL_SOURCE";
70
+ var PLUGIN_ROOT = "CLAUDE_PLUGIN_ROOT";
71
+ var EDITOR_PLUGIN_MARKERS = ["cursor-plugin", "vscode-plugin"];
72
+ var detectInstallSource = (env = process.env, argv = process.argv) => {
73
+ const declared = env[ENV_OVERRIDE]?.trim();
74
+ if (declared) return normalizeInstallSource(declared);
75
+ const pluginRoot = env[PLUGIN_ROOT]?.trim();
76
+ if (pluginRoot) {
77
+ return EDITOR_PLUGIN_MARKERS.some((marker) => pluginRoot.includes(marker)) ? "editor_plugin" : "claude_plugin";
78
+ }
79
+ const entry = argv[1] ?? "";
80
+ if (entry.includes("_npx")) return "npx";
81
+ if (entry) return "npm";
82
+ return "unknown";
83
+ };
84
+ var storedInstallSource = () => {
85
+ try {
86
+ const parsed = JSON.parse(readFileSync2(configFilePath(), "utf-8"));
87
+ const stored = parsed.installSource;
88
+ return typeof stored === "string" && stored ? normalizeInstallSource(stored) : void 0;
89
+ } catch {
90
+ return void 0;
91
+ }
92
+ };
93
+ var resolveInstallSourceFor = (config) => {
94
+ const stored = config.installSource;
95
+ if (typeof stored === "string" && stored) return normalizeInstallSource(stored);
96
+ return detectInstallSource();
97
+ };
98
+ var reportedInstallSource = () => storedInstallSource() ?? detectInstallSource();
99
+
60
100
  export {
61
101
  readJsonSafe,
62
102
  backupFile,
63
103
  writeFileAtomic,
64
104
  writeJsonAtomic,
65
- writeJsonAtomicDurable
105
+ writeJsonAtomicDurable,
106
+ resolveInstallSourceFor,
107
+ reportedInstallSource
66
108
  };
@@ -0,0 +1,6 @@
1
+ // src/hooks-spec.ts
2
+ var CURSOR_GATE_SCRIPT = "pushary-gate.mjs";
3
+
4
+ export {
5
+ CURSOR_GATE_SCRIPT
6
+ };
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-EYRGBYZG.js";
16
16
  import {
17
17
  reportedInstallSource
18
- } from "./chunk-X7YWD6IS.js";
18
+ } from "./chunk-KAVDAZ7A.js";
19
19
 
20
20
  // src/reach.ts
21
21
  var reachVerdict = (channels) => {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-WB2C7VPF.js";
3
+ } from "./chunk-OW6AZDY2.js";
4
4
  import {
5
5
  writeFileAtomic
6
- } from "./chunk-4LL6LG5X.js";
6
+ } from "./chunk-KAVDAZ7A.js";
7
7
 
8
8
  // src/identity.ts
9
9
  import { randomUUID } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  registerTranscript
3
- } from "./chunk-UW4LWAWY.js";
3
+ } from "./chunk-4RF52LSU.js";
4
4
 
5
5
  // src/admission.ts
6
6
  import { readFileSync } from "fs";