@pushary/agent-hooks 1.7.3 → 1.8.1

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 (108) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +39 -19
  3. package/data/SKILL.md +8 -8
  4. package/data/cowork/SKILL.md +2 -2
  5. package/data/cursor-plugin/README.md +2 -2
  6. package/data/cursor-plugin/scripts/pushary-gate.mjs +2 -21
  7. package/data/cursor-plugin/scripts/redaction.mjs +53 -0
  8. package/data/cursor-plugin/skills/pushary/SKILL.md +7 -7
  9. package/data/vscode-plugin/README.md +4 -4
  10. package/data/vscode-plugin/scripts/pushary-gate.mjs +3 -23
  11. package/data/vscode-plugin/scripts/redaction.mjs +53 -0
  12. package/data/vscode-plugin/skills/pushary/SKILL.md +7 -7
  13. package/dist/bin/pushary-bell-hook.js +23 -23
  14. package/dist/bin/pushary-bell.js +9 -9
  15. package/dist/bin/pushary-claude.js +26 -26
  16. package/dist/bin/pushary-clean.js +97 -111
  17. package/dist/bin/pushary-codex-bridge.js +18 -18
  18. package/dist/bin/pushary-codex-hook.js +42 -43
  19. package/dist/bin/pushary-codex.js +13 -13
  20. package/dist/bin/pushary-connect.js +13 -13
  21. package/dist/bin/pushary-cowork.js +5 -5
  22. package/dist/bin/pushary-daemon.js +27 -27
  23. package/dist/bin/pushary-disconnect.js +18 -18
  24. package/dist/bin/pushary-doctor.js +92 -86
  25. package/dist/bin/pushary-elicitation-hook.js +25 -25
  26. package/dist/bin/pushary-gemini-bridge.js +12 -12
  27. package/dist/bin/pushary-gemini-hook.js +29 -30
  28. package/dist/bin/pushary-hook.js +32 -32
  29. package/dist/bin/pushary-login.js +12 -12
  30. package/dist/bin/pushary-logout.js +7 -7
  31. package/dist/bin/pushary-mcp.js +85 -28
  32. package/dist/bin/pushary-mode.js +9 -9
  33. package/dist/bin/pushary-notification-hook.js +28 -28
  34. package/dist/bin/pushary-opencode-hook.js +28 -29
  35. package/dist/bin/pushary-permission-denied-hook.js +32 -32
  36. package/dist/bin/pushary-permission-hook.js +31 -31
  37. package/dist/bin/pushary-post-hook.js +28 -28
  38. package/dist/bin/pushary-prompt-hook.js +28 -28
  39. package/dist/bin/pushary-session-end-hook.js +28 -28
  40. package/dist/bin/pushary-session-start-hook.js +32 -32
  41. package/dist/bin/pushary-setup.js +62 -129
  42. package/dist/bin/pushary-stats.js +5 -5
  43. package/dist/bin/pushary-status.js +15 -15
  44. package/dist/bin/pushary-stop-hook.js +28 -28
  45. package/dist/bin/pushary-stopfailure-hook.js +28 -28
  46. package/dist/bin/pushary-suggestions.js +4 -4
  47. package/dist/bin/pushary-transcript-register.js +19 -19
  48. package/dist/bin/pushary-transcripts.js +7 -7
  49. package/dist/bin/pushary-upgrade.js +236 -110
  50. package/dist/bin/pushary-wait.js +9 -9
  51. package/dist/bin/pushary.js +6 -6
  52. package/dist/{chunk-DUQO4C4I.js → chunk-2SLCHCVN.js} +3 -3
  53. package/dist/{chunk-YYCTF4Q5.js → chunk-35X5UDML.js} +7 -7
  54. package/dist/{chunk-OVC5QXN5.js → chunk-4A3UQERT.js} +1 -1
  55. package/dist/chunk-5DGNEUXK.js +73 -0
  56. package/dist/{chunk-62CVUUNU.js → chunk-5Y2DY6X4.js} +2 -2
  57. package/dist/{chunk-G4GLCLKK.js → chunk-5YY3IJT4.js} +159 -36
  58. package/dist/{chunk-KJXUIPS4.js → chunk-64H5IZZD.js} +28 -16
  59. package/dist/{chunk-OTNOANMX.js → chunk-6TK4IY3A.js} +8 -8
  60. package/dist/{chunk-LPNJLKBQ.js → chunk-72WOCX22.js} +1 -1
  61. package/dist/{chunk-IRZG3HEU.js → chunk-7ITOAMCD.js} +1 -1
  62. package/dist/{chunk-G62JL2NT.js → chunk-7U7ZWUTR.js} +1 -1
  63. package/dist/{chunk-IPSRMLCJ.js → chunk-ASWRMBOM.js} +1 -1
  64. package/dist/{chunk-5S2VBSJI.js → chunk-CB23AO4D.js} +2 -2
  65. package/dist/{chunk-VKVSRCBO.js → chunk-CHMOVJ2M.js} +1 -1
  66. package/dist/{chunk-BGBLLHZ4.js → chunk-DLEKCAQL.js} +1 -1
  67. package/dist/{chunk-EEEJ2JIL.js → chunk-DVW2MYCM.js} +3 -3
  68. package/dist/{chunk-AWVNAYGD.js → chunk-DXBS2EL7.js} +1 -1
  69. package/dist/{chunk-ZKJ34T2F.js → chunk-EIGJYCOE.js} +31 -17
  70. package/dist/{chunk-U6RVDUJ5.js → chunk-F6NTWZZC.js} +283 -41
  71. package/dist/{chunk-7RUJKCR4.js → chunk-FLQPM2GQ.js} +2 -2
  72. package/dist/{chunk-TCVMK6GI.js → chunk-GBNQXYN4.js} +4 -4
  73. package/dist/{chunk-G6RRVJOG.js → chunk-GH6KGWMC.js} +6 -6
  74. package/dist/chunk-GU3EJCVV.js +43 -0
  75. package/dist/{chunk-6JQFIHZC.js → chunk-HBWC3JVZ.js} +3 -3
  76. package/dist/{chunk-IWJKSIB4.js → chunk-HMNR7B2F.js} +1 -1
  77. package/dist/{chunk-NOI3XTCY.js → chunk-HOEB6AKG.js} +1 -1
  78. package/dist/{chunk-62VT3BPH.js → chunk-HQRCVEI2.js} +32 -17
  79. package/dist/{chunk-MIXWMA73.js → chunk-HTEGMMT3.js} +3 -3
  80. package/dist/{chunk-BIH2Y2HP.js → chunk-HVDMGDYV.js} +2 -2
  81. package/dist/{chunk-VE7TXLR4.js → chunk-I4HKMUZQ.js} +3 -3
  82. package/dist/{chunk-YPUBA2FL.js → chunk-INSMZNS7.js} +5 -5
  83. package/dist/{chunk-G3OMOTJ4.js → chunk-JRQRNNGA.js} +3 -3
  84. package/dist/{chunk-N5MZH5A6.js → chunk-KB2FTO4O.js} +4 -4
  85. package/dist/{chunk-O3JDWNLU.js → chunk-LK2S5XIV.js} +183 -28
  86. package/dist/{chunk-YC27PROF.js → chunk-MMVCI6DT.js} +20 -5
  87. package/dist/{chunk-3EUBQARL.js → chunk-MYHG7PX2.js} +1 -1
  88. package/dist/{chunk-EADXTSHE.js → chunk-N6JJJTPH.js} +6 -3
  89. package/dist/{chunk-PRNFSORD.js → chunk-NHCUXTEM.js} +1 -1
  90. package/dist/{chunk-KEL5MXRL.js → chunk-NOLPURUI.js} +1 -1
  91. package/dist/{chunk-WTJM7PSL.js → chunk-OCVVSRDA.js} +3 -3
  92. package/dist/{chunk-R6OYCUNK.js → chunk-ON5JW2HP.js} +1 -1
  93. package/dist/{chunk-UOQRM7JQ.js → chunk-QB6QYZ6B.js} +5 -5
  94. package/dist/{chunk-7OA34PNW.js → chunk-T55H6YEP.js} +40 -10
  95. package/dist/{chunk-D6OODZCM.js → chunk-TJMAXQ5Q.js} +2 -2
  96. package/dist/{chunk-WGMW7OXW.js → chunk-TKQ6LZQZ.js} +6 -6
  97. package/dist/{chunk-WPX67AKT.js → chunk-UMMCHS6P.js} +1 -1
  98. package/dist/{chunk-MZBRL5H5.js → chunk-UPLV5AJF.js} +108 -25
  99. package/dist/{chunk-P2VIBGXA.js → chunk-URNXU3FO.js} +1 -1
  100. package/dist/{chunk-EUGNVLMG.js → chunk-VFFHQC7W.js} +3 -1
  101. package/dist/{chunk-Y4ZH64UK.js → chunk-VZMIHERK.js} +2 -2
  102. package/dist/{chunk-RLW3JWDY.js → chunk-W2LJ4O3A.js} +2 -2
  103. package/dist/{chunk-LHC45YUC.js → chunk-ZJYJAD52.js} +1 -1
  104. package/dist/{reapply-PH5T4BGR.js → reapply-ICGTBHVE.js} +9 -9
  105. package/dist/src/index.js +20 -20
  106. package/package.json +1 -1
  107. package/dist/chunk-5GB22QYL.js +0 -255
  108. package/dist/chunk-VYPJHGG6.js +0 -92
@@ -1,19 +1,15 @@
1
1
  import {
2
2
  activeHookSource,
3
3
  activeIdentity
4
- } from "./chunk-UOQRM7JQ.js";
4
+ } from "./chunk-QB6QYZ6B.js";
5
5
  import {
6
- blockedActionFingerprint,
7
6
  defersToNativeMode,
8
- forgetBlockedActionHint,
9
- hasBlockedActionHint,
10
- recordBlockedActionHint,
11
7
  reportMachineDenial
12
- } from "./chunk-5GB22QYL.js";
8
+ } from "./chunk-5DGNEUXK.js";
13
9
  import {
14
10
  isGatingMoment,
15
11
  recordKeylessMoment
16
- } from "./chunk-IRZG3HEU.js";
12
+ } from "./chunk-7ITOAMCD.js";
17
13
  import {
18
14
  announceSubscriptionRequired,
19
15
  denyReasonFrom,
@@ -22,6 +18,8 @@ import {
22
18
  } from "./chunk-5OP5MQI7.js";
23
19
  import {
24
20
  UNNAMED_ACTION,
21
+ approvalAwaitingRetry,
22
+ blockedActionFingerprint,
25
23
  claimThrottle,
26
24
  deriveAction,
27
25
  deriveActionBody,
@@ -29,15 +27,19 @@ import {
29
27
  deriveToolTarget,
30
28
  describeToolCall,
31
29
  fetchModeState,
30
+ forgetBlockedActionHint,
32
31
  getPolicy,
32
+ hasBlockedActionHint,
33
+ markApprovalReminded,
33
34
  permissionDeniedAction,
34
35
  readLastPrompt,
35
36
  readLastUserPrompt,
37
+ recordBlockedActionHint,
36
38
  repoKeyFor,
37
39
  reportEvent,
38
40
  scopePathFor,
39
41
  settleQuestionsForToolUse
40
- } from "./chunk-U6RVDUJ5.js";
42
+ } from "./chunk-F6NTWZZC.js";
41
43
  import {
42
44
  DEFAULT_SESSION
43
45
  } from "./chunk-N6IOSZOE.js";
@@ -53,17 +55,20 @@ import {
53
55
  requestGate,
54
56
  sendNotification,
55
57
  waitForQuestion
56
- } from "./chunk-YC27PROF.js";
58
+ } from "./chunk-MMVCI6DT.js";
57
59
  import {
58
60
  SubscriptionRequiredError
59
- } from "./chunk-VE7TXLR4.js";
61
+ } from "./chunk-I4HKMUZQ.js";
60
62
  import {
61
63
  getMachineId
62
- } from "./chunk-OVC5QXN5.js";
64
+ } from "./chunk-4A3UQERT.js";
63
65
  import {
64
66
  getApiKey,
65
67
  getBaseUrl
66
- } from "./chunk-R6OYCUNK.js";
68
+ } from "./chunk-ON5JW2HP.js";
69
+ import {
70
+ NPX_LATEST_COMMAND
71
+ } from "./chunk-35X5UDML.js";
67
72
  import {
68
73
  AGENT_QUESTION_STRATEGY,
69
74
  GATE_BLOCK_REASON_MAX_LENGTH,
@@ -91,7 +96,7 @@ import {
91
96
  parseAgentQuestions,
92
97
  resolveGate,
93
98
  stoppedOutput
94
- } from "./chunk-G4GLCLKK.js";
99
+ } from "./chunk-5YY3IJT4.js";
95
100
 
96
101
  // src/claude-permission-rules.ts
97
102
  import { readFileSync, statSync } from "fs";
@@ -354,7 +359,7 @@ var announceWait = (waitSeconds, answerUrl, sessionId) => {
354
359
  } catch {
355
360
  }
356
361
  };
357
- var KEYLESS_NOTICE = "[pushary] Not connected (no API key), so your agent runs untouched. Approval moments are counted locally; see them with: npx @pushary/agent-hooks@latest stats. Connect: https://pushary.com/sign-up?utm_source=cli&utm_medium=keyless-hook";
362
+ var KEYLESS_NOTICE = `[pushary] Not connected (no API key), so your agent runs untouched. Approval moments are counted locally; see them with: ${NPX_LATEST_COMMAND} stats. Connect: https://pushary.com/sign-up?utm_source=cli&utm_medium=keyless-hook`;
358
363
  var keylessNoticeOnce = (sessionId) => {
359
364
  try {
360
365
  if (!oncePerSession("keyless", sessionId)) return;
@@ -530,6 +535,7 @@ var batchScopeReason = "This patch exceeds the agreed scope. Split it into singl
530
535
  var BLOCKED_ACTION_GATE_TIMEOUT_MS = 15e3;
531
536
  var BLOCKED_ACTION_CLAIM_TIMEOUT_MS = 4e3;
532
537
  var BLOCKED_ACTION_ALLOW_REASON = "Approved from Pushary after auto mode blocked it";
538
+ var BLOCKED_ACTION_CHANGED_RETRY_REASON = "This is not the call the person allowed from Pushary, so their approval does not cover it. Send the call auto mode blocked again, exactly as it was with nothing changed, to use their approval.";
533
539
  var blockReasonOf = (reason) => typeof reason === "string" ? reason.trim().slice(0, GATE_BLOCK_REASON_MAX_LENGTH).trim() || void 0 : void 0;
534
540
  var blockedActionFingerprintFor = (input) => blockedActionFingerprint({
535
541
  sessionId: input.session_id,
@@ -566,6 +572,13 @@ var claimedBlockedActionRetry = async (apiKey, input) => {
566
572
  return false;
567
573
  }
568
574
  };
575
+ var remindedOfApprovedRetry = (input) => {
576
+ const approval = approvalAwaitingRetry(input.session_id);
577
+ if (!approval || approval.reminded || approval.toolName !== input.tool_name) return false;
578
+ if (approval.fingerprint === blockedActionFingerprintFor(input)) return false;
579
+ markApprovalReminded(approval);
580
+ return true;
581
+ };
569
582
  var handlePreToolUse = async (input, canonical) => {
570
583
  if (isPusharyOwnTool(input.tool_name)) return void 0;
571
584
  let apiKey;
@@ -597,6 +610,7 @@ var handlePreToolUse = async (input, canonical) => {
597
610
  return handleExitPlanMode(apiKey, input);
598
611
  }
599
612
  if (await claimedBlockedActionRetry(apiKey, input)) return allow(BLOCKED_ACTION_ALLOW_REASON);
613
+ if (remindedOfApprovedRetry(input)) return deny(BLOCKED_ACTION_CHANGED_RETRY_REASON);
600
614
  if (!(verdict.kind === "gate" && verdict.scopeReason) && shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
601
615
  return void 0;
602
616
  }
@@ -709,11 +723,12 @@ var isRetryOutput = (output) => {
709
723
  return false;
710
724
  }
711
725
  };
712
- var retryOnce = (fingerprint) => recordBlockedActionHint(fingerprint) ? claudePermissionDeniedOutput("allow") : void 0;
726
+ var approvedCallOf = (input) => input.session_id ? { sessionId: input.session_id, toolName: input.tool_name } : void 0;
727
+ var retryOnce = (fingerprint, input) => recordBlockedActionHint(fingerprint, approvedCallOf(input)) ? claudePermissionDeniedOutput("allow") : void 0;
713
728
  var askAboutBlockedAction = async (apiKey, input, fingerprint, holdEndsAt) => {
714
729
  const reply = await requestGate(apiKey, blockedActionGateRequest(input, "PermissionDenied"), BLOCKED_ACTION_GATE_TIMEOUT_MS);
715
730
  if (!reply) return void 0;
716
- if (reply.kind === "no_opinion") return isRetryOutput(reply.output) ? retryOnce(fingerprint) : void 0;
731
+ if (reply.kind === "no_opinion") return isRetryOutput(reply.output) ? retryOnce(fingerprint, input) : void 0;
717
732
  if (reply.kind !== "ask" || !reply.correlationId || reply.deadlineSeconds === void 0) return void 0;
718
733
  if (reply.noDevices) {
719
734
  await cancelQuestion(apiKey, reply.correlationId, AbortSignal.timeout(4e3)).catch(() => void 0);
@@ -721,7 +736,7 @@ var askAboutBlockedAction = async (apiKey, input, fingerprint, holdEndsAt) => {
721
736
  }
722
737
  const deadline = Math.min(hookWaitDeadline(START_MS, reply.deadlineSeconds, "claude", Date.now()), holdEndsAt);
723
738
  const answer = await pollForAnswer(apiKey, reply.correlationId, deadline);
724
- return isAffirmativeAnswer(answer) ? retryOnce(fingerprint) : void 0;
739
+ return isAffirmativeAnswer(answer) ? retryOnce(fingerprint, input) : void 0;
725
740
  };
726
741
  var handlePermissionDenied = async (input, holdBudgetSeconds) => {
727
742
  await reportPermissionDenied(input);
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  NOTIFICATION_MATCHED_TYPES,
3
3
  POSTTOOLUSE_MIRRORED_TOOLS
4
- } from "./chunk-EUGNVLMG.js";
4
+ } from "./chunk-VFFHQC7W.js";
5
5
  import {
6
6
  isProtectedByScope,
7
7
  registerHookEntry
8
- } from "./chunk-MZBRL5H5.js";
8
+ } from "./chunk-UPLV5AJF.js";
9
9
  import {
10
10
  CLAUDE_PRETOOLUSE_MATCHER,
11
11
  HOOK_BUDGETS,
12
12
  HOOK_LOCATOR_BINARY
13
- } from "./chunk-G4GLCLKK.js";
13
+ } from "./chunk-5YY3IJT4.js";
14
14
 
15
15
  // src/gemini-config.ts
16
16
  var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
@@ -3,11 +3,11 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-P2VIBGXA.js";
6
+ } from "./chunk-URNXU3FO.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
10
- } from "./chunk-WPX67AKT.js";
10
+ } from "./chunk-UMMCHS6P.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  keyCheckFromResponse
3
- } from "./chunk-EEEJ2JIL.js";
3
+ } from "./chunk-DVW2MYCM.js";
4
4
  import {
5
5
  installNetworkDefaults
6
- } from "./chunk-DUQO4C4I.js";
6
+ } from "./chunk-2SLCHCVN.js";
7
7
  import {
8
8
  getBaseUrl
9
- } from "./chunk-R6OYCUNK.js";
9
+ } from "./chunk-ON5JW2HP.js";
10
10
 
11
11
  // src/retry.ts
12
12
  var isRetryable = (err) => {
@@ -14,17 +14,17 @@ import {
14
14
  } from "./chunk-U4TMAANR.js";
15
15
  import {
16
16
  callMcpTool
17
- } from "./chunk-VE7TXLR4.js";
17
+ } from "./chunk-I4HKMUZQ.js";
18
18
  import {
19
19
  reportedInstallSource
20
- } from "./chunk-62CVUUNU.js";
20
+ } from "./chunk-5Y2DY6X4.js";
21
21
  import {
22
22
  NPX_LATEST_COMMAND
23
- } from "./chunk-YYCTF4Q5.js";
23
+ } from "./chunk-35X5UDML.js";
24
24
  import {
25
25
  PAIR_TTL_SECONDS,
26
26
  isAnswerSource
27
- } from "./chunk-G4GLCLKK.js";
27
+ } from "./chunk-5YY3IJT4.js";
28
28
 
29
29
  // src/reach.ts
30
30
  var reachVerdict = (channels) => {
@@ -71,7 +71,7 @@ var describeAppRepair = (verdict) => {
71
71
  case "nobody":
72
72
  return [
73
73
  "Open the Pushary app, sign in, and allow notifications.",
74
- "If this terminal was never paired with your phone, run: npx @pushary/agent-hooks@latest setup --connect app"
74
+ `If this terminal was never paired with your phone, run: ${NPX_LATEST_COMMAND} setup --connect app`
75
75
  ];
76
76
  case "others":
77
77
  return [
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-MIXWMA73.js";
4
+ } from "./chunk-HTEGMMT3.js";
5
5
  import {
6
6
  guardBinary,
7
7
  holdBudgetOf,
8
8
  isProtectedByScope,
9
9
  registerHookEntry
10
- } from "./chunk-MZBRL5H5.js";
10
+ } from "./chunk-UPLV5AJF.js";
11
11
  import {
12
12
  HOOK_LOCATOR_BINARY,
13
13
  withHookHoldBudget
14
- } from "./chunk-G4GLCLKK.js";
14
+ } from "./chunk-5YY3IJT4.js";
15
15
 
16
16
  // src/claude-config.ts
17
17
  import { join } from "path";
@@ -4,18 +4,18 @@ import {
4
4
  } from "./chunk-POFBE6ZY.js";
5
5
  import {
6
6
  isNetworkFailure
7
- } from "./chunk-DUQO4C4I.js";
7
+ } from "./chunk-2SLCHCVN.js";
8
8
  import {
9
9
  clientReportClaims
10
- } from "./chunk-MZBRL5H5.js";
10
+ } from "./chunk-UPLV5AJF.js";
11
11
  import {
12
12
  getApiKey,
13
13
  getBaseUrl
14
- } from "./chunk-R6OYCUNK.js";
14
+ } from "./chunk-ON5JW2HP.js";
15
15
  import {
16
16
  CLIENT_REPORT_PATH,
17
17
  degradedLine
18
- } from "./chunk-G4GLCLKK.js";
18
+ } from "./chunk-5YY3IJT4.js";
19
19
 
20
20
  // src/client-report-claim.ts
21
21
  import { createHash } from "crypto";
@@ -1,16 +1,28 @@
1
1
  import {
2
2
  MCP_BRIDGE_BINARY
3
- } from "./chunk-7OA34PNW.js";
3
+ } from "./chunk-T55H6YEP.js";
4
4
  import {
5
5
  openCodeConfigDirFrom
6
- } from "./chunk-KEL5MXRL.js";
6
+ } from "./chunk-NOLPURUI.js";
7
7
  import {
8
+ KEY_FILE_MODE
9
+ } from "./chunk-TJMAXQ5Q.js";
10
+ import {
11
+ NpmMissingError,
8
12
  codexHomeFrom,
9
13
  resolvePathBinary
10
- } from "./chunk-MZBRL5H5.js";
14
+ } from "./chunk-UPLV5AJF.js";
15
+ import {
16
+ readJsonSafe,
17
+ writeFileAtomic,
18
+ writeJsonAtomic
19
+ } from "./chunk-T7E75EKA.js";
20
+ import {
21
+ NPX_LATEST_COMMAND
22
+ } from "./chunk-35X5UDML.js";
11
23
  import {
12
24
  HOOK_COMPATIBLE_PROFILES
13
- } from "./chunk-G4GLCLKK.js";
25
+ } from "./chunk-5YY3IJT4.js";
14
26
 
15
27
  // src/hermes-config.ts
16
28
  import { execFileSync } from "child_process";
@@ -680,6 +692,154 @@ var fxIgnoresInstructionsFile = (path, fxDir) => {
680
692
  }
681
693
  };
682
694
 
695
+ // src/setup/agent-failure.ts
696
+ var AgentConfigError = class extends Error {
697
+ kind;
698
+ filePath;
699
+ constructor(kind, filePath, message) {
700
+ super(message);
701
+ this.name = "AgentConfigError";
702
+ this.kind = kind;
703
+ this.filePath = filePath;
704
+ }
705
+ };
706
+ var configFailureKind = (readFailure) => readFailure === "unparseable" ? "config-unparseable" : "config-unreadable";
707
+ var AGENT_FAILURE_DETAILS = {
708
+ "not-wired": "reported success but its config is not there.",
709
+ "app-account-mismatch": "Mac app hooks kept, but their Pushary account could not be matched to this setup."
710
+ };
711
+ var describedAgentFailure = (agent, kind) => ({
712
+ agent,
713
+ kind,
714
+ detail: AGENT_FAILURE_DETAILS[kind],
715
+ remedy: "retry-agent"
716
+ });
717
+ var agentFailureFromError = (agent, error, describeError) => ({
718
+ agent,
719
+ kind: error instanceof AgentConfigError ? error.kind : "installer-error",
720
+ detail: describeError(error),
721
+ remedy: error instanceof NpmMissingError ? "install-node" : "retry-agent"
722
+ });
723
+ var retryAgentCommand = (agent) => `${NPX_LATEST_COMMAND} setup --agents ${agent}`;
724
+ var REMAINING_AGENTS_CONTINUE = "Other agents will continue. Re-run setup to retry.";
725
+ var afterAgentFailureNote = (agentsStillToSetUp) => agentsStillToSetUp > 0 ? REMAINING_AGENTS_CONTINUE : null;
726
+ var failedAgentsContext = (configuredAgentNames) => configuredAgentNames.length > 0 ? `(configured: ${configuredAgentNames.join(", ")})` : null;
727
+ var agentFailureWire = (failures) => failures.map(({ agent, kind }) => ({ agent, kind }));
728
+
729
+ // src/fx-lifecycle.ts
730
+ import { existsSync as existsSync3 } from "fs";
731
+ var fxRetryHint = () => `Fix it, then run ${retryAgentCommand("fx")}.`;
732
+ var readFxProfileForSetup = (filePath) => {
733
+ const read = readFxFile(filePath, readFxProfile);
734
+ if (read.kind === "profile") return read.profile;
735
+ if (read.kind === "missing") return {};
736
+ if (read.kind === "unreadable") {
737
+ throw new AgentConfigError(configFailureKind("unreadable"), filePath, `${filePath} could not be read, so it was left untouched. ${fxRetryHint()}`);
738
+ }
739
+ throw new AgentConfigError(
740
+ configFailureKind("unparseable"),
741
+ filePath,
742
+ `${filePath} ${FX_PROFILE_REFUSALS[read.reason]}, so it was left untouched. ${fxRetryHint()}`
743
+ );
744
+ };
745
+ var writeFxProfile = (filePath, profile) => {
746
+ const rendered = fxProfileText(profile);
747
+ if (rendered.kind === "refused") {
748
+ throw new Error(`${filePath} would grow past the size fx reads, so it was left untouched. ${fxRetryHint()}`);
749
+ }
750
+ writeFileAtomic(filePath, rendered.text, KEY_FILE_MODE);
751
+ };
752
+ var fxSettingsReadRefusal = (filePath, read) => {
753
+ if (read.kind === "unreadable") return `${filePath} could not be read`;
754
+ if (read.kind === "refused") return `${filePath} ${FX_SETTINGS_REFUSALS[read.reason]}`;
755
+ return null;
756
+ };
757
+ var recordFxQuestionRule = (receiptPath) => {
758
+ try {
759
+ if (!existsSync3(receiptPath)) writeJsonAtomic(receiptPath, FX_QUESTION_RULE_RECEIPT, 384);
760
+ return true;
761
+ } catch {
762
+ return false;
763
+ }
764
+ };
765
+ var configureFxSettings = (filePath, receiptPath, environment) => {
766
+ const read = readFxFile(filePath, readFxSettings);
767
+ const refusal = fxSettingsReadRefusal(filePath, read);
768
+ if (refusal) return { kind: "refused", detail: refusal };
769
+ const allowed = withPusharyToolsAllowed(read.kind === "settings" ? read.settings : {});
770
+ const routed = withQuestionsRoutedToPushary(allowed.settings);
771
+ const written = {
772
+ kind: "written",
773
+ toolsChange: allowed.change,
774
+ questionChange: routed.change,
775
+ questionRouting: fxQuestionRouting(routed.settings, environment),
776
+ workspacesWithOwnRules: [.../* @__PURE__ */ new Set([...workspacesOverridingPusharyRule(routed.settings), ...workspacesShowingQuestionTool(routed.settings)])],
777
+ ruleRecorded: true
778
+ };
779
+ if (allowed.change !== "added" && routed.change !== "added") return written;
780
+ const rendered = fxSettingsText(routed.settings);
781
+ if (rendered.kind === "refused") return { kind: "refused", detail: `${filePath} would grow past the 64 KiB fx reads` };
782
+ try {
783
+ writeFileAtomic(filePath, rendered.text, KEY_FILE_MODE);
784
+ } catch (err) {
785
+ return { kind: "refused", detail: `${filePath} could not be written (${err instanceof Error ? err.message : String(err)})` };
786
+ }
787
+ return { ...written, ruleRecorded: routed.change !== "added" || recordFxQuestionRule(receiptPath) };
788
+ };
789
+ var inspectFxInstallation = (profilePath, settingsPath, environment) => {
790
+ const profile = readFxFile(profilePath, readFxProfile);
791
+ const entry = profile.kind === "profile" ? fxPusharyEntryOf(profile.profile) : void 0;
792
+ const settingsRead = readFxFile(settingsPath, readFxSettings);
793
+ const settings = settingsRead.kind === "settings" ? settingsRead.settings : {};
794
+ const settingsProblem = settingsRead.kind === "missing" ? `${settingsPath} is missing, so fx runs with no Pushary rules; re-run ${retryAgentCommand("fx")}` : fxSettingsReadRefusal(settingsPath, settingsRead);
795
+ return {
796
+ profile,
797
+ configured: isFxPusharyEntry(entry),
798
+ bridge: fxBridgeTargetOf(entry),
799
+ settingsProblem,
800
+ toolsAllowed: pusharyToolsAllowed(settings),
801
+ questionRouting: fxQuestionRouting(settings, environment),
802
+ workspacesShowingQuestions: workspacesShowingQuestionTool(settings)
803
+ };
804
+ };
805
+ var removalWrite = (path, rendered, label) => rendered.kind === "refused" ? { kind: "kept", label, detail: `${path} would grow past the size fx reads; left untouched` } : { kind: "write", path, text: rendered.text, label };
806
+ var FX_QUESTION_RULE_KEPT = {
807
+ "not-ours": "no setup record says Pushary added it, so it may be yours; left as it is",
808
+ "changed-after-setup": "changed after setup; left as it is"
809
+ };
810
+ var planFxRemoval = (profilePath, settingsPath, receiptPath) => {
811
+ const actions = [];
812
+ const profile = readFxFile(profilePath, readFxProfile);
813
+ if (profile.kind === "profile") {
814
+ const result = withoutFxPusharyServer(profile.profile);
815
+ actions.push(result.removed ? removalWrite(profilePath, fxProfileText(result.profile), "fx MCP") : { kind: "kept", label: "fx MCP", detail: "no pushary entry" });
816
+ } else {
817
+ let detail = "not found";
818
+ if (profile.kind === "unreadable") detail = `${profilePath} could not be read; left untouched`;
819
+ if (profile.kind === "refused") detail = `${profilePath} ${FX_PROFILE_REFUSALS[profile.reason]}; left untouched`;
820
+ actions.push({ kind: "kept", label: "fx MCP", detail });
821
+ }
822
+ const read = readFxFile(settingsPath, readFxSettings);
823
+ const problem = fxSettingsReadRefusal(settingsPath, read);
824
+ if (problem) return [...actions, { kind: "kept", label: "fx settings", detail: `${problem}; left untouched` }];
825
+ if (read.kind === "settings") {
826
+ const rawReceipt = readJsonSafe(receiptPath);
827
+ const receipt = rawReceipt.kind === "ok" && isFxQuestionRuleReceipt(rawReceipt.value) ? rawReceipt.value : null;
828
+ const tools = withoutPusharyToolsAllowed(read.settings);
829
+ const question = withoutQuestionsRoutedToPushary(tools.settings, receipt);
830
+ if (tools.removed || question.outcome === "removed") {
831
+ const write = removalWrite(settingsPath, fxSettingsText(question.settings), "fx permission rules");
832
+ actions.push(write);
833
+ if (write.kind === "kept") return actions;
834
+ }
835
+ if (question.outcome === "not-ours" || question.outcome === "changed-after-setup") {
836
+ actions.push({ kind: "kept", label: "fx question rule", detail: FX_QUESTION_RULE_KEPT[question.outcome] });
837
+ }
838
+ }
839
+ if (existsSync3(receiptPath)) actions.push({ kind: "remove-receipt", path: receiptPath });
840
+ return actions;
841
+ };
842
+
683
843
  // src/vscode-config.ts
684
844
  import { homedir as homedir2 } from "os";
685
845
  import { join as join2 } from "path";
@@ -828,7 +988,7 @@ var vscodeSettingsTargets = (exists, home, platform) => {
828
988
 
829
989
  // src/setup/detect.ts
830
990
  import { execSync } from "child_process";
831
- import { existsSync as existsSync3 } from "fs";
991
+ import { existsSync as existsSync4 } from "fs";
832
992
  import { homedir as homedir3 } from "os";
833
993
  import { dirname, join as join3 } from "path";
834
994
  var whichCommand = () => process.platform === "win32" ? "where" : "which";
@@ -840,7 +1000,7 @@ var binaryOnPath = (binary) => {
840
1000
  return false;
841
1001
  }
842
1002
  };
843
- var defaultDeps = { onPath: binaryOnPath, exists: existsSync3 };
1003
+ var defaultDeps = { onPath: binaryOnPath, exists: existsSync4 };
844
1004
  var detectAgent = (probe, deps = defaultDeps) => {
845
1005
  const checked = [];
846
1006
  if (probe.binary) {
@@ -943,7 +1103,7 @@ var describeDetection = (result, home) => {
943
1103
  };
944
1104
 
945
1105
  // src/instruction-file.ts
946
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, rmSync as rmSync2 } from "fs";
1106
+ import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, rmSync as rmSync2 } from "fs";
947
1107
  import { dirname as dirname2 } from "path";
948
1108
  var PUSHARY_BEGIN = "<!-- BEGIN PUSHARY (managed by @pushary/agent-hooks; do not edit this block) -->";
949
1109
  var PUSHARY_END = "<!-- END PUSHARY -->";
@@ -963,6 +1123,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
963
1123
  - Respect the user's Pushary delivery mode: it decides where an approval is surfaced (phone, terminal, or awareness only), not whether it is required. In Terminal mode do not expect a phone answer; the approval waits in the terminal. If the Pushary hook already gated an action, a single approval is enough, do not ask again.
964
1124
 
965
1125
  Pass \`agentName\` as "${label} - <project folder name>" so the user knows which session is asking. If a question times out, follow \`handoffAction\` when present, otherwise \`nextAction\`: poll once, then cancel a live phone question before asking in the current client. If cancellation returns \`handoffAction: "stop"\`, stop. Otherwise, if cancellation returns false, poll once for 1 second and honor the answer that won the race.`;
1126
+ var SHARED_AGENTS_MD_LABEL = "<your agent's name, such as Codex or Hermes>";
966
1127
  var renderAgentInstructions = (label) => instructionBody(label);
967
1128
  var KEYLESS_FALLBACK_LINE = "This repo uses Pushary phone approvals. Your teammates get agent questions on their lock screens; you do not have a key yet, so questions will fall back to the terminal. Get your own key (3-day trial): https://pushary.com/sign-up?utm_source=agents-md&utm_medium=teammate";
968
1129
  var renderProjectAgentInstructions = (label) => `${instructionBody(label)}
@@ -973,7 +1134,7 @@ ${body}
973
1134
  ${PUSHARY_END}`;
974
1135
  var writeInstructionBlock = (filePath, body) => {
975
1136
  const dir = dirname2(filePath);
976
- if (!existsSync4(dir)) mkdirSync(dir, { recursive: true });
1137
+ if (!existsSync5(dir)) mkdirSync(dir, { recursive: true });
977
1138
  let existing = "";
978
1139
  try {
979
1140
  existing = readFileSync3(filePath, "utf-8");
@@ -1028,30 +1189,23 @@ export {
1028
1189
  FX_PUSHARY_TOOLS_RULE,
1029
1190
  FX_TOOL_DISCOVERY_NOTE,
1030
1191
  FX_PROFILE_REFUSALS,
1031
- FX_SETTINGS_REFUSALS,
1032
- readFxProfile,
1033
1192
  fxPusharyEntry,
1034
1193
  withFxPusharyServer,
1035
- withoutFxPusharyServer,
1036
- isFxPusharyEntry,
1037
- fxPusharyEntryOf,
1038
- fxBridgeTargetOf,
1039
- fxProfileText,
1040
- fxSettingsText,
1041
- readFxSettings,
1042
- withPusharyToolsAllowed,
1043
- withoutPusharyToolsAllowed,
1044
- pusharyToolsAllowed,
1045
- workspacesOverridingPusharyRule,
1046
1194
  FX_QUESTION_TOOL,
1047
- FX_QUESTION_RULE_RECEIPT,
1048
- isFxQuestionRuleReceipt,
1049
- fxQuestionRouting,
1050
- withQuestionsRoutedToPushary,
1051
- withoutQuestionsRoutedToPushary,
1052
- workspacesShowingQuestionTool,
1053
- readFxFile,
1054
1195
  fxIgnoresInstructionsFile,
1196
+ AgentConfigError,
1197
+ configFailureKind,
1198
+ describedAgentFailure,
1199
+ agentFailureFromError,
1200
+ retryAgentCommand,
1201
+ afterAgentFailureNote,
1202
+ failedAgentsContext,
1203
+ agentFailureWire,
1204
+ readFxProfileForSetup,
1205
+ writeFxProfile,
1206
+ configureFxSettings,
1207
+ inspectFxInstallation,
1208
+ planFxRemoval,
1055
1209
  registerPluginLocation,
1056
1210
  unregisterPluginLocation,
1057
1211
  isPluginRegistered,
@@ -1063,6 +1217,7 @@ export {
1063
1217
  isDetected,
1064
1218
  shortenHome,
1065
1219
  describeDetection,
1220
+ SHARED_AGENTS_MD_LABEL,
1066
1221
  renderAgentInstructions,
1067
1222
  renderProjectAgentInstructions,
1068
1223
  writeInstructionBlock,
@@ -1,17 +1,23 @@
1
1
  import {
2
2
  clientErrorKindOf,
3
3
  reportClientError
4
- } from "./chunk-N5MZH5A6.js";
4
+ } from "./chunk-KB2FTO4O.js";
5
5
  import {
6
6
  SubscriptionRequiredError,
7
7
  callMcpTool
8
- } from "./chunk-VE7TXLR4.js";
8
+ } from "./chunk-I4HKMUZQ.js";
9
9
  import {
10
10
  installNetworkDefaults
11
- } from "./chunk-DUQO4C4I.js";
11
+ } from "./chunk-2SLCHCVN.js";
12
12
  import {
13
13
  getBaseUrl
14
- } from "./chunk-R6OYCUNK.js";
14
+ } from "./chunk-ON5JW2HP.js";
15
+ import {
16
+ ASK_AGENT_NAME_MAX,
17
+ ASK_QUESTION_MAX,
18
+ DECISION_LINE_MAX,
19
+ truncateLine
20
+ } from "./chunk-5YY3IJT4.js";
15
21
 
16
22
  // src/api.ts
17
23
  import { randomUUID } from "crypto";
@@ -59,9 +65,18 @@ var unavailableQuestionUnlessSubscriptionRequired = (error) => {
59
65
  if (error instanceof SubscriptionRequiredError) throw error;
60
66
  return { correlationId: "", status: "unavailable" };
61
67
  };
68
+ var truncateOptional = (text, max) => text === void 0 ? void 0 : truncateLine(text, max);
69
+ var withinAskUserLimits = (params) => ({
70
+ ...params,
71
+ question: truncateLine(params.question, ASK_QUESTION_MAX),
72
+ agentName: truncateOptional(params.agentName, ASK_AGENT_NAME_MAX),
73
+ intent: truncateOptional(params.intent, DECISION_LINE_MAX),
74
+ action: truncateOptional(params.action, DECISION_LINE_MAX),
75
+ blocker: truncateOptional(params.blocker, DECISION_LINE_MAX)
76
+ });
62
77
  var askUser = async (apiKey, params, timeoutMs = 15e3) => {
63
78
  const identified = params.toolUseId || params.requestId ? params : { ...params, requestId: randomUUID() };
64
- const result = await callMcpTool(apiKey, "ask_user", { ...identified, wait: false }, { maxRetries: 3, timeoutMs });
79
+ const result = await callMcpTool(apiKey, "ask_user", { ...withinAskUserLimits(identified), wait: false }, { maxRetries: 3, timeoutMs });
65
80
  if (!isAskUserResponse(result)) throw new Error("Invalid ask_user response");
66
81
  return result;
67
82
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_LOCATOR_BINARY
3
- } from "./chunk-G4GLCLKK.js";
3
+ } from "./chunk-5YY3IJT4.js";
4
4
 
5
5
  // src/diagnostics/bridge.ts
6
6
  import { execFileSync } from "child_process";
@@ -1,12 +1,15 @@
1
1
  import {
2
2
  SETUP_FLAGS
3
- } from "./chunk-BGBLLHZ4.js";
3
+ } from "./chunk-DLEKCAQL.js";
4
4
  import {
5
5
  parseFlags
6
6
  } from "./chunk-GMXKITVA.js";
7
7
  import {
8
8
  UsageError
9
9
  } from "./chunk-7QLSKOSU.js";
10
+ import {
11
+ NPX_LATEST_COMMAND
12
+ } from "./chunk-35X5UDML.js";
10
13
  import {
11
14
  CLAUDE_CONNECTOR_GUIDE,
12
15
  CLAUDE_CONNECTOR_STEPS,
@@ -14,7 +17,7 @@ import {
14
17
  CLAUDE_CONNECTOR_URL,
15
18
  COWORK_CONNECTOR_LIMIT,
16
19
  COWORK_STANDING_INSTRUCTIONS
17
- } from "./chunk-G4GLCLKK.js";
20
+ } from "./chunk-5YY3IJT4.js";
18
21
 
19
22
  // src/cli/cowork.ts
20
23
  import { readFileSync } from "fs";
@@ -63,7 +66,7 @@ var coworkOutput = (format) => {
63
66
  if (error.code !== "ENOENT") throw error;
64
67
  }
65
68
  }
66
- throw new Error("Packaged Cowork skill is missing. Reinstall @pushary/agent-hooks.");
69
+ throw new Error(`Packaged Cowork skill is missing. Run ${NPX_LATEST_COMMAND} cowork --skill to read it from a fresh copy.`);
67
70
  }
68
71
  return renderCoworkSetup();
69
72
  };
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  describeKeyCheck,
6
6
  keyCheckFromResponse
7
- } from "./chunk-EEEJ2JIL.js";
7
+ } from "./chunk-DVW2MYCM.js";
8
8
  import {
9
9
  EXIT
10
10
  } from "./chunk-KZERVKTD.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
3
  OPENCODE_APPROVAL_EVENT
4
- } from "./chunk-G4GLCLKK.js";
4
+ } from "./chunk-5YY3IJT4.js";
5
5
 
6
6
  // src/opencode-plugin.ts
7
7
  var OPENCODE_PLUGIN_MARKER = "pushary-opencode-plugin w1";