@pushary/agent-hooks 1.9.11 → 1.9.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 (102) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/data/cursor-plugin/scripts/pushary-gate.mjs +7 -2
  3. package/dist/bin/pushary-bell-hook.js +25 -25
  4. package/dist/bin/pushary-bell.js +9 -9
  5. package/dist/bin/pushary-claude.js +26 -25
  6. package/dist/bin/pushary-clean.js +52 -30
  7. package/dist/bin/pushary-codex-bridge.js +17 -17
  8. package/dist/bin/pushary-codex-hook.js +30 -30
  9. package/dist/bin/pushary-codex.js +12 -12
  10. package/dist/bin/pushary-connect.js +24 -24
  11. package/dist/bin/pushary-cowork.js +5 -5
  12. package/dist/bin/pushary-daemon.js +42 -37
  13. package/dist/bin/pushary-disconnect.js +43 -29
  14. package/dist/bin/pushary-doctor.js +33 -33
  15. package/dist/bin/pushary-elicitation-hook.js +25 -25
  16. package/dist/bin/pushary-gemini-bridge.js +13 -13
  17. package/dist/bin/pushary-gemini-hook.js +27 -27
  18. package/dist/bin/pushary-hook.js +29 -29
  19. package/dist/bin/pushary-login.js +12 -12
  20. package/dist/bin/pushary-logout.js +10 -10
  21. package/dist/bin/pushary-mcp.js +9 -9
  22. package/dist/bin/pushary-mode.js +12 -12
  23. package/dist/bin/pushary-notification-hook.js +24 -24
  24. package/dist/bin/pushary-opencode-hook.js +26 -26
  25. package/dist/bin/pushary-permission-denied-hook.js +29 -29
  26. package/dist/bin/pushary-permission-hook.js +28 -28
  27. package/dist/bin/pushary-post-hook.js +24 -24
  28. package/dist/bin/pushary-prompt-hook.js +24 -24
  29. package/dist/bin/pushary-session-end-hook.js +24 -24
  30. package/dist/bin/pushary-session-start-hook.js +28 -28
  31. package/dist/bin/pushary-setup.js +69 -41
  32. package/dist/bin/pushary-stats.js +5 -5
  33. package/dist/bin/pushary-status.js +26 -26
  34. package/dist/bin/pushary-stop-hook.js +24 -24
  35. package/dist/bin/pushary-stopfailure-hook.js +24 -24
  36. package/dist/bin/pushary-suggestions.js +12 -12
  37. package/dist/bin/pushary-transcript-register.js +17 -17
  38. package/dist/bin/pushary-transcripts.js +7 -7
  39. package/dist/bin/pushary-upgrade.js +30 -30
  40. package/dist/bin/pushary-wait.js +12 -12
  41. package/dist/bin/pushary.js +6 -6
  42. package/dist/{chunk-7RWQZQV2.js → chunk-226PID65.js} +5 -5
  43. package/dist/{chunk-N7HXP4PZ.js → chunk-3EOXZYUE.js} +1 -1
  44. package/dist/{chunk-MJLTABWF.js → chunk-3LSJ7YVM.js} +3 -3
  45. package/dist/{chunk-KEV2MOJH.js → chunk-3VSMTCXV.js} +10 -5
  46. package/dist/{chunk-PTGUJZSD.js → chunk-43IDNI4X.js} +7 -6
  47. package/dist/{chunk-2NAQRPZQ.js → chunk-43ORAXAL.js} +1 -1
  48. package/dist/{chunk-M54ATCOZ.js → chunk-4ATFPZ6X.js} +1 -1
  49. package/dist/{chunk-CZYMYM7X.js → chunk-4EAILOOQ.js} +3 -3
  50. package/dist/{chunk-MX3LPP3K.js → chunk-4ZSWBLVM.js} +6 -6
  51. package/dist/{chunk-XYCTS3WP.js → chunk-5QTQI3HL.js} +1 -1
  52. package/dist/{chunk-YETKAI5Q.js → chunk-6CM3B4PJ.js} +6 -6
  53. package/dist/{chunk-4HR2ICME.js → chunk-6MGD24SH.js} +1 -1
  54. package/dist/{chunk-A6J5Z2HO.js → chunk-6NL3LYUM.js} +8 -8
  55. package/dist/{chunk-KIVFWGG6.js → chunk-6QPQG3Q5.js} +5 -5
  56. package/dist/{chunk-FJCOOFT7.js → chunk-7JM6TCMA.js} +1 -1
  57. package/dist/{chunk-LX6EWNRK.js → chunk-7VWIBIDK.js} +4 -4
  58. package/dist/{chunk-TVVUJTUW.js → chunk-FWAXBC3P.js} +1 -1
  59. package/dist/{chunk-BZI7EBTB.js → chunk-G54VGPRX.js} +1 -1
  60. package/dist/{chunk-AIWZRRSK.js → chunk-G5K656XW.js} +4 -4
  61. package/dist/{chunk-WHRYW4P2.js → chunk-GFAVGG3X.js} +5 -5
  62. package/dist/{chunk-V6IW5WMK.js → chunk-GYR4G4WO.js} +1 -1
  63. package/dist/{chunk-N6GEI3PK.js → chunk-HHMMY6TB.js} +2 -2
  64. package/dist/{chunk-P7N7FJ7W.js → chunk-J2VNQSS6.js} +1 -1
  65. package/dist/{chunk-E6YQW43K.js → chunk-JRKUJOZL.js} +1 -1
  66. package/dist/{chunk-GUNZHSTL.js → chunk-JVUK5YFM.js} +5 -5
  67. package/dist/{chunk-2AQ4VZSA.js → chunk-LBNIISXH.js} +1 -1
  68. package/dist/{chunk-CHDAH4HP.js → chunk-LHLUKFJV.js} +8 -8
  69. package/dist/{chunk-EJVYEOKC.js → chunk-LUYAXSUS.js} +3 -3
  70. package/dist/{chunk-KBN3UP2R.js → chunk-LYDO2SS6.js} +3 -3
  71. package/dist/{chunk-WCELLK4P.js → chunk-MPDJJNGA.js} +2 -2
  72. package/dist/{chunk-C6QM5SJR.js → chunk-MWP65CZ3.js} +2 -2
  73. package/dist/{chunk-BMP2BLIR.js → chunk-N6XFGVNW.js} +3 -3
  74. package/dist/{chunk-CBDJ2X5P.js → chunk-NPRFA7YM.js} +2 -2
  75. package/dist/chunk-OOKRWBCG.js +111 -0
  76. package/dist/{chunk-PRJNUNCV.js → chunk-OOZ5RSEF.js} +2 -2
  77. package/dist/{chunk-MHHXR7DT.js → chunk-QIPDIZC5.js} +4 -4
  78. package/dist/{chunk-2GG77ADC.js → chunk-RI5E5JQS.js} +1 -1
  79. package/dist/{chunk-GA4J6YFN.js → chunk-RJAPOB34.js} +2 -2
  80. package/dist/{chunk-HX5TI6DP.js → chunk-RYKU62ZI.js} +1 -1
  81. package/dist/{chunk-Z4AOR6RZ.js → chunk-S2LDTUHS.js} +2 -2
  82. package/dist/{chunk-ERXY2SIW.js → chunk-TCVFNLMM.js} +20 -14
  83. package/dist/{chunk-MITMDERZ.js → chunk-TDSLVFG6.js} +1 -1
  84. package/dist/{chunk-VOP5U2VX.js → chunk-TVDI7SX2.js} +113 -16
  85. package/dist/{chunk-5WAYSUWV.js → chunk-U5SXTKZV.js} +2 -2
  86. package/dist/{chunk-I4I4UH62.js → chunk-U6QEHPEF.js} +3 -3
  87. package/dist/{chunk-OKSU74QH.js → chunk-UUBEEKT6.js} +2 -2
  88. package/dist/{chunk-STUJXZC2.js → chunk-VUHT6NYV.js} +4 -4
  89. package/dist/{chunk-ZTSMQYST.js → chunk-WIRM7ZLI.js} +2 -2
  90. package/dist/{chunk-BDRXDO25.js → chunk-WL4PVINO.js} +2 -2
  91. package/dist/{chunk-4FWWHCYM.js → chunk-WP57USIT.js} +13 -4
  92. package/dist/{chunk-P2EJN7UB.js → chunk-Y25RH4VF.js} +1 -1
  93. package/dist/{chunk-4RENRBSB.js → chunk-YI37QERZ.js} +4 -4
  94. package/dist/{chunk-LBAMPNDM.js → chunk-ZAH3CCEV.js} +4 -4
  95. package/dist/{global-upgrade-VT2JS7DR.js → global-upgrade-NIFOQFV4.js} +6 -6
  96. package/dist/{paths-WRBEUNEY.js → paths-QPBLO5Q2.js} +2 -2
  97. package/dist/{reapply-BCR6U7HO.js → reapply-TVMBJWJB.js} +20 -20
  98. package/dist/src/index.d.ts +1 -0
  99. package/dist/src/index.js +19 -19
  100. package/dist/{upgrade-lock-VRYMTPZQ.js → upgrade-lock-Z3LYROGK.js} +3 -3
  101. package/package.json +1 -1
  102. package/dist/chunk-LUHNNZSH.js +0 -50
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  defersToNativeMode,
3
3
  reportMachineDenial
4
- } from "./chunk-ZTSMQYST.js";
4
+ } from "./chunk-WIRM7ZLI.js";
5
5
  import {
6
6
  isGatingMoment,
7
7
  recordKeylessMoment
8
- } from "./chunk-2AQ4VZSA.js";
8
+ } from "./chunk-LBNIISXH.js";
9
9
  import {
10
10
  announceSubscriptionRequired,
11
11
  denyReasonFrom,
12
12
  isDeferAnswer,
13
13
  oncePerSession
14
- } from "./chunk-V6IW5WMK.js";
14
+ } from "./chunk-GYR4G4WO.js";
15
15
  import {
16
16
  activeHookSource,
17
17
  activeIdentity
18
- } from "./chunk-KEV2MOJH.js";
18
+ } from "./chunk-3VSMTCXV.js";
19
19
  import {
20
20
  DEFAULT_SESSION,
21
21
  UNNAMED_ACTION,
@@ -40,7 +40,7 @@ import {
40
40
  reportEvent,
41
41
  scopePathFor,
42
42
  settleQuestionsForToolUse
43
- } from "./chunk-A6J5Z2HO.js";
43
+ } from "./chunk-6NL3LYUM.js";
44
44
  import {
45
45
  askUser,
46
46
  cancelQuestion,
@@ -51,26 +51,28 @@ import {
51
51
  requestGate,
52
52
  sendNotification,
53
53
  waitForQuestion
54
- } from "./chunk-GUNZHSTL.js";
54
+ } from "./chunk-JVUK5YFM.js";
55
55
  import {
56
56
  SubscriptionRequiredError
57
- } from "./chunk-KBN3UP2R.js";
57
+ } from "./chunk-LYDO2SS6.js";
58
58
  import {
59
59
  getMachineId
60
- } from "./chunk-2NAQRPZQ.js";
60
+ } from "./chunk-43ORAXAL.js";
61
61
  import {
62
62
  getApiKey,
63
63
  getBaseUrl
64
- } from "./chunk-FJCOOFT7.js";
64
+ } from "./chunk-7JM6TCMA.js";
65
65
  import {
66
66
  NPX_LATEST_COMMAND
67
- } from "./chunk-M54ATCOZ.js";
67
+ } from "./chunk-4ATFPZ6X.js";
68
68
  import {
69
69
  AGENT_QUESTION_STRATEGY,
70
70
  DECISION_STOP_REASON,
71
71
  GATE_BLOCK_REASON_MAX_LENGTH,
72
72
  GATE_WIRE,
73
73
  PLAN_DECLINED_REASON,
74
+ SOLE_GATE_SIGNED_OUT_REASON,
75
+ SOLE_GATE_UNDECIDED_REASON,
74
76
  UNVERIFIABLE_APPROVAL_REASON,
75
77
  WITHDRAWN_APPROVAL_REASON,
76
78
  agentQuestionStrategy,
@@ -87,6 +89,7 @@ import {
87
89
  editGoesToTheAgentsPrompt,
88
90
  effectiveWaitSeconds,
89
91
  hasNativeApprovalPrompt,
92
+ hookCompatibleProfileBySource,
90
93
  hookHoldWindowMs,
91
94
  hookWaitClamped,
92
95
  hookWaitDeadline,
@@ -98,7 +101,7 @@ import {
98
101
  parseAgentQuestions,
99
102
  questionHandedToTheTerminal,
100
103
  resolveGate
101
- } from "./chunk-VOP5U2VX.js";
104
+ } from "./chunk-TVDI7SX2.js";
102
105
 
103
106
  // src/claude-permission-rules.ts
104
107
  import { readFileSync, statSync } from "fs";
@@ -171,12 +174,14 @@ import { basename } from "path";
171
174
  var START_MS = Date.now();
172
175
  var ACTIVE_AGENT = activeIdentity(process.argv);
173
176
  var ACTIVE_SOURCE = activeHookSource(process.argv);
177
+ var PUSHARY_IS_SOLE_GATE = ACTIVE_SOURCE !== null && hookCompatibleProfileBySource(ACTIVE_SOURCE)?.soleGate != null;
174
178
  var agentNameIn = (projectName) => `${ACTIVE_AGENT.label} - ${projectName}`;
175
179
  var allow = (reason) => claudePreToolUseOutput("allow", reason);
176
180
  var deny = (reason) => claudePreToolUseOutput("deny", reason);
177
181
  var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
178
182
  var ask = (reason) => claudePreToolUseOutput("ask", reason);
179
183
  var halted = (reason) => claudeStoppedPreToolUseOutput(reason);
184
+ var undecidedCall = (reason) => PUSHARY_IS_SOLE_GATE ? deny(reason) : void 0;
180
185
  var outcomeOfFailedAsk = (error, sessionId, failure) => {
181
186
  if (!(error instanceof SubscriptionRequiredError)) return failure;
182
187
  announceSubscriptionRequired(error.upgradeUrl, sessionId);
@@ -591,7 +596,8 @@ var handlePreToolUse = async (input, canonical) => {
591
596
  try {
592
597
  apiKey = getApiKey();
593
598
  } catch {
594
- return handleKeyless(input);
599
+ handleKeyless(input);
600
+ return undecidedCall(SOLE_GATE_SIGNED_OUT_REASON);
595
601
  }
596
602
  try {
597
603
  const modeState = await fetchModeState(apiKey, input.session_id);
@@ -631,12 +637,12 @@ var handlePreToolUse = async (input, canonical) => {
631
637
  // State we could not establish, with an auto-approve on the table. No
632
638
  // opinion, so Claude Code's own flow runs as if the hook were absent.
633
639
  case "defer":
634
- return void 0;
640
+ return undecidedCall(SOLE_GATE_UNDECIDED_REASON);
635
641
  default:
636
642
  return dispatchModeHandler(apiKey, input, verdict.policy, getMachineId(), modeState.trainingConsent, verdict.scopeReason, verdict.scopePath);
637
643
  }
638
644
  } catch {
639
- return void 0;
645
+ return undecidedCall(SOLE_GATE_UNDECIDED_REASON);
640
646
  }
641
647
  };
642
648
  var permReqAllow = (updatedInput) => claudePermissionRequestOutput("allow", void 0, updatedInput);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-4FWWHCYM.js";
3
+ } from "./chunk-WP57USIT.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-T7E75EKA.js";
@@ -239,7 +239,7 @@ var AGENTS = {
239
239
  connectVia: "Native hooks + MCP",
240
240
  setupCommand: PUSHARY_SETUP_COMMAND,
241
241
  enforcementDetail: "Shell commands, file writes, edits and MCP calls through native PreToolUse hooks in ~/.gemini/config/hooks.json, answered allow or deny.",
242
- capabilityNote: 'Antigravity CLI ignores a hook\'s allow unless it runs in always-proceed mode (`toolPermission: "always-proceed"` or `--dangerously-skip-permissions`), so approving from the phone needs that mode, with Pushary as the only gate. Pushary answers allow or deny and never ask, because ask and force_ask run the call unprompted in that mode. An unanswered approval is blocked with a reason.'
242
+ capabilityNote: 'Antigravity CLI ignores a hook\'s allow unless it runs in always-proceed mode, so connecting it sets `toolPermission: "always-proceed"` when you never chose a mode, and disconnecting puts it back. Pushary is then the only gate: it answers allow or deny and never ask, and a call it cannot decide, one nobody answers, or one made after Pushary was removed is blocked with a reason.'
243
243
  },
244
244
  windsurf: {
245
245
  ...defaults,
@@ -332,6 +332,8 @@ var toToolLookup = (profile, toolName, toolInput, resolvePath = (path) => path)
332
332
  };
333
333
  var NO_AGENT_PROMPT_REASON = "Pushary needs an answer from the phone before this can run, and this agent has no approval prompt of its own here, so nothing ran. Ask the user, then try again.";
334
334
  var DENIED_BY_PUSHARY_REASON = "Denied from Pushary.";
335
+ var SOLE_GATE_UNDECIDED_REASON = "Pushary could not decide on this call, so it did not run. Pushary is the only approval for this agent. Check that Pushary is signed in and online, then try again.";
336
+ var SOLE_GATE_SIGNED_OUT_REASON = "Pushary is not signed in on this computer, so nothing can approve this call and it did not run. Sign in to Pushary, or disconnect this agent from Pushary.";
335
337
  var UNCARRIED_ANSWER_REASON = "The answer from the phone cannot be passed to this agent, so the call did not run. Ask the question in chat instead.";
336
338
  var renderAllowDenyDecision = (verdict) => {
337
339
  const { permissionDecision, permissionDecisionReason } = verdict.hookSpecificOutput;
@@ -357,6 +359,14 @@ var renderPreToolUseVerdict = (profile, verdict) => {
357
359
  ...stop
358
360
  };
359
361
  };
362
+ var homePath = (segments) => `~/${segments.join("/")}`;
363
+ var pusharyMissingReason = (profile) => {
364
+ const settingBack = profile.soleGate ? ` and remove ${profile.soleGate.key} from ${homePath(profile.soleGate.file)}` : "";
365
+ return `Pushary is not installed on this computer any more, so nothing can approve this call and it did not run. Reinstall Pushary, or remove the pushary hook from ${homePath(profile.configFile)}${settingBack}.`;
366
+ };
367
+ var refusalOutput = (profile, reason) => JSON.stringify(renderPreToolUseVerdict(profile, {
368
+ hookSpecificOutput: { permissionDecision: "deny", permissionDecisionReason: reason }
369
+ }));
360
370
  var ANTIGRAVITY_MCP_TOOL = "call_mcp_tool";
361
371
  var isPayloadRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
362
372
  var nonEmptyString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
@@ -380,13 +390,15 @@ var fromAntigravity = (event, payload) => {
380
390
  const cwd = firstWorkspace(payload) ?? nonEmptyString(call?.tool_input.Cwd);
381
391
  const sessionId = nonEmptyString(payload.conversationId);
382
392
  const transcriptPath = nonEmptyString(payload.transcriptPath);
393
+ const error = event === "PostToolUse" ? nonEmptyString(payload.error) : void 0;
383
394
  return {
384
395
  ...rest,
385
396
  ...event ? { hook_event_name: event } : {},
386
397
  ...sessionId ? { session_id: sessionId } : {},
387
398
  ...transcriptPath ? { transcript_path: transcriptPath } : {},
388
399
  ...cwd ? { cwd } : {},
389
- ...call ?? {}
400
+ ...call ?? {},
401
+ ...error ? { tool_response: { error } } : {}
390
402
  };
391
403
  };
392
404
  var claudeShapedHookInput = (dialect, event, payload) => dialect === "antigravity" ? fromAntigravity(event, payload) : payload;
@@ -440,7 +452,7 @@ var QWEN_CODE = {
440
452
  instructions: { file: [".qwen", "QWEN.md"], label: "Qwen Code" },
441
453
  permissionRuleFile: null,
442
454
  configDirIsShared: false,
443
- setupNote: null
455
+ soleGate: null
444
456
  };
445
457
  var CODEBUDDY = {
446
458
  agentType: "codebuddy",
@@ -463,7 +475,7 @@ var CODEBUDDY = {
463
475
  instructions: { file: [".codebuddy", "CODEBUDDY.md"], label: "CodeBuddy" },
464
476
  permissionRuleFile: [".codebuddy", "settings.json"],
465
477
  configDirIsShared: false,
466
- setupNote: null
478
+ soleGate: null
467
479
  };
468
480
  var QODER = {
469
481
  agentType: "qoder",
@@ -486,7 +498,7 @@ var QODER = {
486
498
  instructions: null,
487
499
  permissionRuleFile: null,
488
500
  configDirIsShared: false,
489
- setupNote: null
501
+ soleGate: null
490
502
  };
491
503
  var DROID = {
492
504
  agentType: "droid",
@@ -514,7 +526,7 @@ var DROID = {
514
526
  instructions: null,
515
527
  permissionRuleFile: null,
516
528
  configDirIsShared: false,
517
- setupNote: null
529
+ soleGate: null
518
530
  };
519
531
  var DEVIN = {
520
532
  agentType: "devin",
@@ -547,7 +559,7 @@ var DEVIN = {
547
559
  instructions: null,
548
560
  permissionRuleFile: null,
549
561
  configDirIsShared: false,
550
- setupNote: null
562
+ soleGate: null
551
563
  };
552
564
  var ANTIGRAVITY_CLI = {
553
565
  agentType: "antigravity_cli",
@@ -582,7 +594,12 @@ var ANTIGRAVITY_CLI = {
582
594
  instructions: null,
583
595
  permissionRuleFile: null,
584
596
  configDirIsShared: true,
585
- setupNote: 'Approvals from your phone take effect only when Antigravity CLI runs in always-proceed mode: start it with --dangerously-skip-permissions, or set "toolPermission": "always-proceed" in ~/.gemini/antigravity-cli/settings.json. Pushary is then the only gate, and an unanswered approval is blocked.'
597
+ soleGate: {
598
+ file: [".gemini", "antigravity-cli", "settings.json"],
599
+ key: "toolPermission",
600
+ value: "always-proceed",
601
+ receipt: [".pushary", "setup", "antigravity-tool-permission.json"]
602
+ }
586
603
  };
587
604
  var HOOK_COMPATIBLE_PROFILES = Object.freeze([QWEN_CODE, CODEBUDDY, QODER, DROID, DEVIN, ANTIGRAVITY_CLI]);
588
605
  var hookCompatibleProfile = (agentType) => HOOK_COMPATIBLE_PROFILES.find((profile) => profile.agentType === agentType);
@@ -862,7 +879,7 @@ var AUTH_REJECTION_REASONS = [
862
879
  // ../contracts/src/declined-agents.ts
863
880
  var DECLINED_AGENTS_FILE = "declined-agents.json";
864
881
  var DECLINED_AGENTS_VERSION = 1;
865
- var isDeclinableAgent = (value) => typeof value === "string" && HOOK_SOURCES.includes(value);
882
+ var isDeclinableAgent = (value) => typeof value === "string" && DISCONNECT_AGENT_IDS.includes(value);
866
883
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
867
884
  var canonicalAgents = (agents) => [...new Set(agents)].sort();
868
885
  var parseDeclinedAgents = (value) => {
@@ -1273,7 +1290,9 @@ var SAFE_SHELL_COMMANDS = /* @__PURE__ */ new Set([
1273
1290
  "du",
1274
1291
  "df",
1275
1292
  "ps",
1276
- "true"
1293
+ "true",
1294
+ "sleep",
1295
+ "pgrep"
1277
1296
  ]);
1278
1297
  var SAFE_GIT_SUBCOMMANDS = /* @__PURE__ */ new Set([
1279
1298
  "status",
@@ -1295,6 +1314,8 @@ var SAFE_GIT_SUBCOMMANDS = /* @__PURE__ */ new Set([
1295
1314
  "count-objects"
1296
1315
  ]);
1297
1316
  var GIT_WRITE_FLAGS = (token) => token === "--output" || token.startsWith("--output=");
1317
+ var GIT_READ_ONLY_BRANCH_ARGUMENTS = ["branch", "--show-current"];
1318
+ var isReadOnlyGitBranch = (args) => args.length === GIT_READ_ONLY_BRANCH_ARGUMENTS.length && args.every((arg, index) => arg === GIT_READ_ONLY_BRANCH_ARGUMENTS[index]);
1298
1319
  var DANGEROUS_FIND_ACTIONS = /* @__PURE__ */ new Set([
1299
1320
  "-exec",
1300
1321
  "-execdir",
@@ -1401,6 +1422,7 @@ var isSafeSimpleCommand = (segment) => {
1401
1422
  if (exe === "git") {
1402
1423
  const sub = tokens[1];
1403
1424
  if (!sub || sub.startsWith("-")) return false;
1425
+ if (isReadOnlyGitBranch(tokens.slice(1))) return true;
1404
1426
  if (!SAFE_GIT_SUBCOMMANDS.has(sub)) return false;
1405
1427
  return !tokens.some(GIT_WRITE_FLAGS);
1406
1428
  }
@@ -1415,8 +1437,61 @@ var isSafeSimpleCommand = (segment) => {
1415
1437
  if (exe === "sed") return isPrintOnlySed(tokens.slice(1));
1416
1438
  if (exe === "date") return isReadOnlyDate(tokens.slice(1));
1417
1439
  if (exe === "printf") return isOutputOnlyPrintf(tokens.slice(1));
1440
+ if (exe === RIPGREP_PROGRAM) return carriesNoRefusedOption(tokens.slice(1), RIPGREP_OPTIONS_THAT_RUN_A_PROGRAM);
1441
+ if (exe === SORT_PROGRAM) return carriesNoRefusedOption(tokens.slice(1), SORT_OPTIONS_THAT_WRITE_OR_RUN);
1442
+ if (exe === UNIQ_PROGRAM) return uniqFileOperandCount(tokens.slice(1)) <= UNIQ_MAX_FILE_OPERANDS;
1418
1443
  return SAFE_SHELL_COMMANDS.has(exe);
1419
1444
  };
1445
+ var RIPGREP_PROGRAM = "rg";
1446
+ var RIPGREP_OPTIONS_THAT_RUN_A_PROGRAM = {
1447
+ long: ["--pre", "--pre-glob", "--hostname-bin", "--search-zip"],
1448
+ short: ["-z"]
1449
+ };
1450
+ var SORT_PROGRAM = "sort";
1451
+ var SORT_OPTIONS_THAT_WRITE_OR_RUN = {
1452
+ long: ["--output", "--compress-program", "--temporary-directory"],
1453
+ short: ["-o", "-T"]
1454
+ };
1455
+ var UNIQ_PROGRAM = "uniq";
1456
+ var UNIQ_MAX_FILE_OPERANDS = 1;
1457
+ var UNIQ_OPTIONS_WITH_A_SEPARATE_VALUE = [
1458
+ "-f",
1459
+ "-s",
1460
+ "-w",
1461
+ "--skip-fields",
1462
+ "--skip-chars",
1463
+ "--check-chars"
1464
+ ];
1465
+ var uniqFileOperandCount = (args) => {
1466
+ let operands = 0;
1467
+ let optionsEnded = false;
1468
+ for (let index = 0; index < args.length; index += 1) {
1469
+ const arg = args[index] ?? "";
1470
+ if (optionsEnded || !isFlagToken(arg) || arg === STDIN_OPERAND) {
1471
+ operands += 1;
1472
+ optionsEnded = true;
1473
+ } else if (arg === END_OF_OPTIONS) {
1474
+ optionsEnded = true;
1475
+ } else if (UNIQ_OPTIONS_WITH_A_SEPARATE_VALUE.includes(arg)) {
1476
+ index += 1;
1477
+ }
1478
+ }
1479
+ return operands;
1480
+ };
1481
+ var STDIN_OPERAND = "-";
1482
+ var LONG_OPTION_PREFIX = "--";
1483
+ var OPTION_VALUE_SEPARATOR = "=";
1484
+ var longOptionName = (token) => token.split(OPTION_VALUE_SEPARATOR, 1)[0] ?? token;
1485
+ var abbreviatesALongOption = (token, longOptions) => {
1486
+ const name = longOptionName(token);
1487
+ return longOptions.some((option) => option.startsWith(name));
1488
+ };
1489
+ var clustersAShortOption = (token, shortOptions) => shortOptions.some((option) => token.slice(1).includes(option.slice(1)));
1490
+ var isRefusedOption = (token, options) => {
1491
+ if (token === END_OF_OPTIONS || !isFlagToken(token)) return false;
1492
+ return token.startsWith(LONG_OPTION_PREFIX) ? abbreviatesALongOption(token, options.long) : clustersAShortOption(token, options.short);
1493
+ };
1494
+ var carriesNoRefusedOption = (args, options) => !args.some((arg) => isRefusedOption(arg, options));
1420
1495
  var SED_PRINT_ONLY_SCRIPT = /^(?:\d+|\$)(?:,(?:\d+|\$))?p$/;
1421
1496
  var SED_QUIET_FLAG = "-n";
1422
1497
  var DATE_FORMAT_PREFIX = "+";
@@ -1429,7 +1504,16 @@ var READ_ONLY_SEGMENT_SEPARATOR = /&&|\|\||[;|]/g;
1429
1504
  var PIPELINE_CHAIN_SEPARATOR = /&&|\|\||;/g;
1430
1505
  var EXPANSION_THAT_CAN_BECOME_A_FLAG = /\$|(?:^|\s)[*?[]/;
1431
1506
  var SHELL_QUOTE_CHARACTERS = /['"]/g;
1432
- var INSPECTED_ARGUMENT_COMMANDS = /* @__PURE__ */ new Set(["git", "find", "sed", "date", "printf"]);
1507
+ var INSPECTED_ARGUMENT_COMMANDS = /* @__PURE__ */ new Set([
1508
+ "git",
1509
+ "find",
1510
+ "sed",
1511
+ "date",
1512
+ "printf",
1513
+ RIPGREP_PROGRAM,
1514
+ SORT_PROGRAM,
1515
+ UNIQ_PROGRAM
1516
+ ]);
1433
1517
  var argumentsMayExpand = (segment) => {
1434
1518
  const visible = shellVisibleText(segment);
1435
1519
  return visible === null || EXPANSION_THAT_CAN_BECOME_A_FLAG.test(visible.replace(SHELL_QUOTE_CHARACTERS, ""));
@@ -2206,12 +2290,21 @@ var autoApprove = (tool) => ({
2206
2290
  mode: "terminal_only",
2207
2291
  pushFirstSeconds: 0
2208
2292
  });
2293
+ var CODEX_COORDINATION_TOOLS = [
2294
+ "mcp__codex_app__wait_threads",
2295
+ "mcp__codex_app__send_message_to_thread"
2296
+ ];
2297
+ var CODEX_COORDINATION_TOOL_NAMES = new Set(CODEX_COORDINATION_TOOLS);
2298
+ var isCodexCoordinationTool = (toolName) => CODEX_COORDINATION_TOOL_NAMES.has(toolName);
2299
+ var isNamedByARule = (rank) => rank !== void 0 && rank !== "wildcard";
2300
+ var isSpecificArgumentRank = (rank) => rank === "exact" || rank === "prefix";
2301
+ var shellReadOnlyFloorApplies = (toolName, arg, rank) => !isSpecificArgumentRank(rank) && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg);
2302
+ var coordinationFloorApplies = (toolName, rank) => isCodexCoordinationTool(toolName) && !isNamedByARule(rank);
2209
2303
  var resolveAutoResolveOrigin = (config, toolName, toolInput, cwd, repoKey, sessionId) => {
2210
2304
  const args = toolInput ? policyArgForms(toolName, toolInput, cwd) : [];
2211
2305
  const arg = args[0];
2212
- const match = selectGoverningRule(config.policies, toolName, args, repoKey, sessionId);
2213
- const governedBySpecificRule = match?.rank === "exact" || match?.rank === "prefix";
2214
- if (!governedBySpecificRule && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg)) {
2306
+ const rank = selectGoverningRule(config.policies, toolName, args, repoKey, sessionId)?.rank;
2307
+ if (shellReadOnlyFloorApplies(toolName, arg, rank) || coordinationFloorApplies(toolName, rank)) {
2215
2308
  return DECISION_ORIGIN.safeReadonly;
2216
2309
  }
2217
2310
  return DECISION_ORIGIN.autopilot;
@@ -2238,10 +2331,10 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, se
2238
2331
  mode: config.defaultMode ?? "push_first",
2239
2332
  pushFirstSeconds: config.defaultPushFirstSeconds ?? 20
2240
2333
  };
2241
- const governedBySpecificRule = match?.rank === "exact" || match?.rank === "prefix";
2334
+ const governedBySpecificRule = isSpecificArgumentRank(match?.rank);
2242
2335
  const governedBySessionToolGrant = match?.rank === "tool" && isSessionToolGrant(match.policy);
2243
2336
  const governingMode = base.mode;
2244
- const approvedByReadOnlyFloor = !governedBySpecificRule && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg);
2337
+ const approvedByReadOnlyFloor = !isAutoDeny(base) && (shellReadOnlyFloorApplies(toolName, arg, match?.rank) || coordinationFloorApplies(toolName, match?.rank));
2245
2338
  if (governedBySessionToolGrant && !approvedByReadOnlyFloor && typeof arg === "string" && commandNeedsAPerson(toolName, arg)) {
2246
2339
  return resolvePolicy(withoutSessionToolGrants(config), toolName, modeOverride, toolInput, cwd, repoKey, sessionId);
2247
2340
  }
@@ -2941,7 +3034,11 @@ export {
2941
3034
  PHONE_ANSWERED_TOOLS,
2942
3035
  canonicalToolName,
2943
3036
  toToolLookup,
3037
+ SOLE_GATE_UNDECIDED_REASON,
3038
+ SOLE_GATE_SIGNED_OUT_REASON,
2944
3039
  renderPreToolUseVerdict,
3040
+ pusharyMissingReason,
3041
+ refusalOutput,
2945
3042
  claudeShapedHookInput,
2946
3043
  HOOK_COMPATIBLE_PROFILES,
2947
3044
  hookCompatibleProfile,
@@ -3,11 +3,11 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-2GG77ADC.js";
6
+ } from "./chunk-RI5E5JQS.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
10
- } from "./chunk-N7HXP4PZ.js";
10
+ } from "./chunk-3EOXZYUE.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyFetch
3
- } from "./chunk-EJVYEOKC.js";
3
+ } from "./chunk-LUYAXSUS.js";
4
4
  import {
5
5
  NetworkError
6
6
  } from "./chunk-7QLSKOSU.js";
@@ -9,11 +9,11 @@ import {
9
9
  } from "./chunk-KZERVKTD.js";
10
10
  import {
11
11
  getBaseUrl
12
- } from "./chunk-FJCOOFT7.js";
12
+ } from "./chunk-7JM6TCMA.js";
13
13
  import {
14
14
  isValidApiKey,
15
15
  normalizeKeyScope
16
- } from "./chunk-VOP5U2VX.js";
16
+ } from "./chunk-TVDI7SX2.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;
@@ -3,10 +3,10 @@ import {
3
3
  keyPrefix,
4
4
  readConfigFileKey,
5
5
  readMacAppKeyPrefix
6
- } from "./chunk-Z4AOR6RZ.js";
6
+ } from "./chunk-S2LDTUHS.js";
7
7
  import {
8
8
  NPX_LATEST_COMMAND
9
- } from "./chunk-M54ATCOZ.js";
9
+ } from "./chunk-4ATFPZ6X.js";
10
10
 
11
11
  // src/key-signed-out.ts
12
12
  var VERDICT_BY_CHECK = {
@@ -14,19 +14,19 @@ import {
14
14
  } from "./chunk-U4TMAANR.js";
15
15
  import {
16
16
  callMcpTool
17
- } from "./chunk-KBN3UP2R.js";
17
+ } from "./chunk-LYDO2SS6.js";
18
18
  import {
19
19
  reportedInstallSource
20
- } from "./chunk-CBDJ2X5P.js";
20
+ } from "./chunk-NPRFA7YM.js";
21
21
  import {
22
22
  NPX_LATEST_COMMAND
23
- } from "./chunk-M54ATCOZ.js";
23
+ } from "./chunk-4ATFPZ6X.js";
24
24
  import {
25
25
  NO_PAIRING_COMPUTER,
26
26
  PAIR_TTL_SECONDS,
27
27
  classifyConfirmResponse,
28
28
  isAnswerSource
29
- } from "./chunk-VOP5U2VX.js";
29
+ } from "./chunk-TVDI7SX2.js";
30
30
 
31
31
  // src/reach.ts
32
32
  var reachVerdict = (channels) => {
@@ -6,12 +6,12 @@ import {
6
6
  permissionDeniedAction,
7
7
  releaseThrottle,
8
8
  reportEvent
9
- } from "./chunk-A6J5Z2HO.js";
9
+ } from "./chunk-6NL3LYUM.js";
10
10
  import {
11
11
  machineDecisionFor,
12
12
  shouldDeferToNativeMode,
13
13
  wireVerdictFor
14
- } from "./chunk-VOP5U2VX.js";
14
+ } from "./chunk-TVDI7SX2.js";
15
15
 
16
16
  // src/machine-decision-audit.ts
17
17
  import { basename } from "path";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseSseMessages
3
- } from "./chunk-KBN3UP2R.js";
3
+ } from "./chunk-LYDO2SS6.js";
4
4
  import {
5
5
  NPX_LATEST_COMMAND
6
- } from "./chunk-M54ATCOZ.js";
6
+ } from "./chunk-4ATFPZ6X.js";
7
7
 
8
8
  // src/mcp-stdio-bridge.ts
9
9
  var MCP_BRIDGE_BINARY = "pushary-mcp";
@@ -13,7 +13,7 @@ import {
13
13
  isReleaseVersion,
14
14
  ownPackageName,
15
15
  withHookHoldBudget
16
- } from "./chunk-VOP5U2VX.js";
16
+ } from "./chunk-TVDI7SX2.js";
17
17
 
18
18
  // src/setup/paths.ts
19
19
  import { homedir as homedir2 } from "os";
@@ -365,9 +365,9 @@ var installGlobally = (options) => {
365
365
  }
366
366
  const started = (async () => {
367
367
  if (!npmIsOnPath()) throw new NpmMissingError();
368
- const { withUpgradeLock } = await import("./global-upgrade-VT2JS7DR.js");
369
- const { installWithUpgradeLock, ownsUpgradeLock } = await import("./upgrade-lock-VRYMTPZQ.js");
370
- const { autoUpdateLock: autoUpdateLock2 } = await import("./paths-WRBEUNEY.js");
368
+ const { withUpgradeLock } = await import("./global-upgrade-NIFOQFV4.js");
369
+ const { installWithUpgradeLock, ownsUpgradeLock } = await import("./upgrade-lock-Z3LYROGK.js");
370
+ const { autoUpdateLock: autoUpdateLock2 } = await import("./paths-QPBLO5Q2.js");
371
371
  let result = "skipped";
372
372
  const install = async () => {
373
373
  const platform = process.platform;
@@ -438,6 +438,14 @@ var guardHookCommand = (target, platform = process.platform) => {
438
438
  return `${HOOK_SHELL} '${HOOK_GUARD_SCRIPTS[guardKindOf(target)]}' ${path}`;
439
439
  };
440
440
  var guardBinary = (path, platform) => guardHookCommand({ kind: "binary", path, runtime: "node" }, platform);
441
+ var SILENT_MISS = "exit 0";
442
+ var REFUSE = "R";
443
+ var definesRefusal = (refusal) => `${REFUSE}() { printf %s ${shellDoubleQuoted(refusal)}; exit 0; }`;
444
+ var guardBinaryRefusing = (path, refusal, platform = process.platform) => {
445
+ if (platform === "win32") return guardBinary(path, platform);
446
+ const script = `${definesRefusal(refusal)}; ${HOOK_GUARD_SCRIPTS.node_binary.replaceAll(SILENT_MISS, REFUSE)}`;
447
+ return `${HOOK_SHELL} '${script}' ${quoteIfNeeded(path)}`;
448
+ };
441
449
  var guardNodeScript = (script, platform) => guardHookCommand({ kind: "node", script }, platform);
442
450
  var guardCursorGate = (script, platform) => guardHookCommand({ kind: "cursor_gate", script }, platform);
443
451
  var guardedArgv = (target, args = [], platform = process.platform) => {
@@ -854,6 +862,7 @@ export {
854
862
  installGlobally,
855
863
  holdBudgetOf,
856
864
  guardBinary,
865
+ guardBinaryRefusing,
857
866
  guardNodeScript,
858
867
  guardCursorGate,
859
868
  guardedArgv,
@@ -2,7 +2,7 @@ import {
2
2
  autoUpdateLock,
3
3
  installPackageGlobally,
4
4
  npmErrorMessage
5
- } from "./chunk-4FWWHCYM.js";
5
+ } from "./chunk-WP57USIT.js";
6
6
  import {
7
7
  writeJsonAtomic
8
8
  } from "./chunk-T7E75EKA.js";
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  macAppBridgeAvailable
3
- } from "./chunk-KIVFWGG6.js";
3
+ } from "./chunk-6QPQG3Q5.js";
4
4
  import {
5
5
  keyPrefix
6
- } from "./chunk-Z4AOR6RZ.js";
6
+ } from "./chunk-S2LDTUHS.js";
7
7
  import {
8
8
  bridgeAgentKey
9
- } from "./chunk-4FWWHCYM.js";
9
+ } from "./chunk-WP57USIT.js";
10
10
  import {
11
11
  readJsonSafe
12
12
  } from "./chunk-T7E75EKA.js";
13
13
  import {
14
14
  isValidApiKey
15
- } from "./chunk-VOP5U2VX.js";
15
+ } from "./chunk-TVDI7SX2.js";
16
16
 
17
17
  // src/shared-mcp-key.ts
18
18
  var asRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
@@ -2,25 +2,25 @@ import {
2
2
  SIGNED_OUT_ON_MAC_MESSAGE,
3
3
  describeSignedOutKey,
4
4
  readSignedOutDiagnosis
5
- } from "./chunk-OKSU74QH.js";
5
+ } from "./chunk-UUBEEKT6.js";
6
6
  import {
7
7
  createIo
8
8
  } from "./chunk-U4TMAANR.js";
9
9
  import {
10
10
  readKeySource
11
- } from "./chunk-Z4AOR6RZ.js";
11
+ } from "./chunk-S2LDTUHS.js";
12
12
  import {
13
13
  absoluteUrl,
14
14
  checkApiKey,
15
15
  describeKeyCheck,
16
16
  keyCheckFromResponse
17
- } from "./chunk-I4I4UH62.js";
17
+ } from "./chunk-U6QEHPEF.js";
18
18
  import {
19
19
  EXIT
20
20
  } from "./chunk-KZERVKTD.js";
21
21
  import {
22
22
  getBaseUrl
23
- } from "./chunk-FJCOOFT7.js";
23
+ } from "./chunk-7JM6TCMA.js";
24
24
 
25
25
  // src/cli/report-key.ts
26
26
  var { dim, yellow } = createIo();
@@ -5,15 +5,15 @@ import {
5
5
  readGlobalUpgradeSubject,
6
6
  upgradeInstalledPackage,
7
7
  withUpgradeLock
8
- } from "./chunk-WHRYW4P2.js";
9
- import "./chunk-P2EJN7UB.js";
10
- import "./chunk-WCELLK4P.js";
8
+ } from "./chunk-GFAVGG3X.js";
9
+ import "./chunk-Y25RH4VF.js";
10
+ import "./chunk-MPDJJNGA.js";
11
11
  import "./chunk-POFBE6ZY.js";
12
12
  import "./chunk-KZERVKTD.js";
13
- import "./chunk-4FWWHCYM.js";
13
+ import "./chunk-WP57USIT.js";
14
14
  import "./chunk-T7E75EKA.js";
15
- import "./chunk-M54ATCOZ.js";
16
- import "./chunk-VOP5U2VX.js";
15
+ import "./chunk-4ATFPZ6X.js";
16
+ import "./chunk-TVDI7SX2.js";
17
17
  export {
18
18
  finishGlobalUpgrade,
19
19
  finishUpgradeWithLock,
@@ -32,8 +32,8 @@ import {
32
32
  pusharyDir,
33
33
  vscodePluginDir,
34
34
  vscodePluginMcp
35
- } from "./chunk-4FWWHCYM.js";
36
- import "./chunk-VOP5U2VX.js";
35
+ } from "./chunk-WP57USIT.js";
36
+ import "./chunk-TVDI7SX2.js";
37
37
  export {
38
38
  autoUpdateLock,
39
39
  autoUpdateLog,