@pushary/agent-hooks 1.9.7 → 1.9.9

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 (111) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +3 -3
  3. package/data/cursor-plugin/README.md +3 -7
  4. package/data/vscode-plugin/README.md +1 -1
  5. package/dist/bin/pushary-bell-hook.js +22 -24
  6. package/dist/bin/pushary-bell.js +13 -13
  7. package/dist/bin/pushary-claude.js +28 -30
  8. package/dist/bin/pushary-clean.js +113 -94
  9. package/dist/bin/pushary-codex-bridge.js +20 -20
  10. package/dist/bin/pushary-codex-hook.js +36 -38
  11. package/dist/bin/pushary-codex.js +13 -13
  12. package/dist/bin/pushary-connect.js +48 -22
  13. package/dist/bin/pushary-cowork.js +5 -5
  14. package/dist/bin/pushary-daemon.js +50 -40
  15. package/dist/bin/pushary-disconnect.js +188 -56
  16. package/dist/bin/pushary-doctor.js +105 -85
  17. package/dist/bin/pushary-elicitation-hook.js +25 -27
  18. package/dist/bin/pushary-gemini-bridge.js +13 -13
  19. package/dist/bin/pushary-gemini-hook.js +31 -33
  20. package/dist/bin/pushary-hook.js +36 -39
  21. package/dist/bin/pushary-login.js +12 -12
  22. package/dist/bin/pushary-logout.js +105 -42
  23. package/dist/bin/pushary-mcp.js +9 -9
  24. package/dist/bin/pushary-mode.js +28 -31
  25. package/dist/bin/pushary-notification-hook.js +29 -32
  26. package/dist/bin/pushary-opencode-hook.js +30 -32
  27. package/dist/bin/pushary-permission-denied-hook.js +30 -33
  28. package/dist/bin/pushary-permission-hook.js +33 -36
  29. package/dist/bin/pushary-post-hook.js +30 -33
  30. package/dist/bin/pushary-prompt-hook.js +29 -32
  31. package/dist/bin/pushary-session-end-hook.js +29 -32
  32. package/dist/bin/pushary-session-start-hook.js +33 -36
  33. package/dist/bin/pushary-setup.js +168 -160
  34. package/dist/bin/pushary-stats.js +5 -5
  35. package/dist/bin/pushary-status.js +65 -19
  36. package/dist/bin/pushary-stop-hook.js +29 -32
  37. package/dist/bin/pushary-stopfailure-hook.js +29 -32
  38. package/dist/bin/pushary-suggestions.js +19 -21
  39. package/dist/bin/pushary-transcript-register.js +18 -20
  40. package/dist/bin/pushary-transcripts.js +7 -7
  41. package/dist/bin/pushary-upgrade.js +50 -26
  42. package/dist/bin/pushary-wait.js +23 -19
  43. package/dist/bin/pushary.js +15 -14
  44. package/dist/{chunk-4FK6RZVJ.js → chunk-462EQA6K.js} +1 -1
  45. package/dist/{chunk-STQRUH3S.js → chunk-5DCDQ5VW.js} +59 -15
  46. package/dist/{chunk-JXISQ7DT.js → chunk-5N5L6DNN.js} +3 -3
  47. package/dist/{chunk-QR6VE5ME.js → chunk-5RGA636G.js} +79 -17
  48. package/dist/{chunk-L76HTPU6.js → chunk-6AYSKVLL.js} +3 -3
  49. package/dist/{chunk-NIBFSARF.js → chunk-6EBGOHDJ.js} +33 -13
  50. package/dist/{chunk-QQ4HBXZC.js → chunk-6W5M3MSE.js} +84 -673
  51. package/dist/{chunk-R3YW55MY.js → chunk-AI7RMFVV.js} +29 -26
  52. package/dist/{chunk-AUUXS44H.js → chunk-APP43AW7.js} +1 -1
  53. package/dist/{chunk-RQGODFQN.js → chunk-AZOKPWVI.js} +1 -1
  54. package/dist/{chunk-3GNUXXDZ.js → chunk-B5NMSP3V.js} +2 -2
  55. package/dist/{chunk-4D2AYNSE.js → chunk-BNSKVIAW.js} +2 -2
  56. package/dist/chunk-BYLKCZ2G.js +367 -0
  57. package/dist/{chunk-X5NXPCIC.js → chunk-BZLHTMT6.js} +89 -7
  58. package/dist/{chunk-BTQGAXZ3.js → chunk-COQ64C4F.js} +10 -10
  59. package/dist/{chunk-N4EUSDKX.js → chunk-DCCSULXK.js} +14 -14
  60. package/dist/{chunk-JWKBDSPF.js → chunk-DP3WX7FW.js} +2 -2
  61. package/dist/chunk-E5HOHIUV.js +52 -0
  62. package/dist/{chunk-GU3EJCVV.js → chunk-EA5NWDK7.js} +4 -0
  63. package/dist/chunk-EIAEUG7G.js +46 -0
  64. package/dist/{chunk-EB4XGVBG.js → chunk-EUW7JL2K.js} +0 -32
  65. package/dist/{chunk-T7Y5EADL.js → chunk-F7R3SZ4Q.js} +2 -2
  66. package/dist/{chunk-H37L7TT3.js → chunk-G7RIDXAT.js} +2 -2
  67. package/dist/{chunk-W2KI5KPD.js → chunk-H5T27W7M.js} +2 -2
  68. package/dist/chunk-HBWYMRR5.js +94 -0
  69. package/dist/{chunk-CBQACHKY.js → chunk-IIVZZMSO.js} +1 -1
  70. package/dist/{chunk-Z6HPS67T.js → chunk-IRHABAQT.js} +1 -1
  71. package/dist/{chunk-QTO6HEZD.js → chunk-K545B734.js} +2 -2
  72. package/dist/{chunk-KYO45G5L.js → chunk-KOWFRUIT.js} +1 -1
  73. package/dist/{chunk-QFEM7R6T.js → chunk-KQFTBQ66.js} +11 -11
  74. package/dist/chunk-LENBKYRB.js +50 -0
  75. package/dist/{chunk-JCWZ23MJ.js → chunk-M3STIAGU.js} +3 -3
  76. package/dist/{chunk-6DJTDHBI.js → chunk-MD4SVNT6.js} +1 -1
  77. package/dist/{chunk-7264S5HZ.js → chunk-MLGIVVSG.js} +1 -1
  78. package/dist/{chunk-H7MMU64M.js → chunk-NGKUXZID.js} +2 -2
  79. package/dist/{chunk-JWQZSYCK.js → chunk-NUVGDBZC.js} +1 -1
  80. package/dist/{chunk-AQ55ZSSS.js → chunk-OAYWO5NP.js} +1 -1
  81. package/dist/chunk-Q6C6ILUS.js +49 -0
  82. package/dist/{chunk-MBK7N46B.js → chunk-QIOIO7VN.js} +6 -6
  83. package/dist/{chunk-KFKL3FHS.js → chunk-QRXPWW3M.js} +4 -3
  84. package/dist/{chunk-7OIWWZAD.js → chunk-QTIUJZRX.js} +3 -3
  85. package/dist/{chunk-XWAYAO3V.js → chunk-R5RIVOPL.js} +2 -2
  86. package/dist/{chunk-ADSQDYYG.js → chunk-REU2VQPH.js} +54 -5
  87. package/dist/{chunk-GKI6LFAV.js → chunk-RQ32ADJP.js} +2 -2
  88. package/dist/{chunk-VXRXFU6Q.js → chunk-SZ4Q6ZVS.js} +14 -7
  89. package/dist/{chunk-KT5E2RLS.js → chunk-T6ZN4TEF.js} +1 -1
  90. package/dist/{chunk-UFVGSEWH.js → chunk-TO3PS646.js} +2 -2
  91. package/dist/chunk-UAP5DS6P.js +599 -0
  92. package/dist/{chunk-MZPXVH4A.js → chunk-UF6RBP5V.js} +204 -28
  93. package/dist/{chunk-AVNKVF7N.js → chunk-UPIZSCYD.js} +4 -4
  94. package/dist/{chunk-3PVDOCFI.js → chunk-VHPFNLRS.js} +5 -5
  95. package/dist/{chunk-HPRZJKRO.js → chunk-VIIHC42X.js} +1 -1
  96. package/dist/{chunk-D6S5EKXA.js → chunk-WRPYCIV2.js} +8 -12
  97. package/dist/{chunk-ZUTU43FT.js → chunk-Z23ZBYDW.js} +6 -4
  98. package/dist/{chunk-SBLPHUEB.js → chunk-ZGADIZDS.js} +8 -6
  99. package/dist/{global-upgrade-E2HUEB4B.js → global-upgrade-H272QOGD.js} +6 -6
  100. package/dist/{paths-QVTEGNZV.js → paths-HMVOTFUN.js} +2 -2
  101. package/dist/reapply-Q5NHDTVU.js +55 -0
  102. package/dist/src/index.d.ts +1 -0
  103. package/dist/src/index.js +19 -20
  104. package/dist/{upgrade-lock-JCIDZMHF.js → upgrade-lock-L3GHFIRW.js} +3 -3
  105. package/package.json +1 -1
  106. package/dist/chunk-4YO4T5FV.js +0 -149
  107. package/dist/chunk-ANEAA37E.js +0 -197
  108. package/dist/chunk-MNWRUGMH.js +0 -86
  109. package/dist/chunk-RQSOEB4S.js +0 -44
  110. package/dist/chunk-SD3272LS.js +0 -143
  111. package/dist/reapply-5QVKM6LA.js +0 -38
@@ -3,34 +3,37 @@ import {
3
3
  describeWaitLadder
4
4
  } from "../chunk-CVWG2N3Y.js";
5
5
  import {
6
- exitOnFailedResponse
7
- } from "../chunk-RQSOEB4S.js";
6
+ exitOnFailedSettingsRequest
7
+ } from "../chunk-HBWYMRR5.js";
8
+ import "../chunk-Q6C6ILUS.js";
8
9
  import {
9
10
  createIo
10
11
  } from "../chunk-U4TMAANR.js";
11
12
  import {
12
13
  exitOnHelpFlag
13
- } from "../chunk-UFVGSEWH.js";
14
+ } from "../chunk-TO3PS646.js";
14
15
  import {
15
16
  rejectUnknownFlags
16
17
  } from "../chunk-GMXKITVA.js";
17
- import "../chunk-KFKL3FHS.js";
18
- import "../chunk-JXISQ7DT.js";
19
- import "../chunk-KYO45G5L.js";
18
+ import "../chunk-REU2VQPH.js";
19
+ import "../chunk-QRXPWW3M.js";
20
+ import "../chunk-5N5L6DNN.js";
21
+ import "../chunk-KOWFRUIT.js";
20
22
  import {
21
23
  UsageError
22
24
  } from "../chunk-7QLSKOSU.js";
23
25
  import {
24
26
  EXIT
25
27
  } from "../chunk-KZERVKTD.js";
26
- import "../chunk-ZUTU43FT.js";
28
+ import "../chunk-BNSKVIAW.js";
29
+ import "../chunk-Z23ZBYDW.js";
27
30
  import "../chunk-T7E75EKA.js";
28
31
  import {
29
32
  getApiKey,
30
33
  getBaseUrl
31
- } from "../chunk-Z6HPS67T.js";
32
- import "../chunk-STQRUH3S.js";
33
- import "../chunk-QR6VE5ME.js";
34
+ } from "../chunk-IRHABAQT.js";
35
+ import "../chunk-5DCDQ5VW.js";
36
+ import "../chunk-5RGA636G.js";
34
37
 
35
38
  // bin/pushary-wait.ts
36
39
  exitOnHelpFlag("wait");
@@ -56,18 +59,18 @@ var printLadder = (data) => {
56
59
  console.log(` ${dim("Reset it:")} ${cyan("pushary wait clear")}`);
57
60
  console.log(` ${dim("Per-tool rules live in the dashboard under Agent \u2192 Policies.")}`);
58
61
  };
59
- var fetchPolicy = async (baseUrl, headers) => {
60
- const res = await fetch(`${baseUrl}/api/mcp/policy`, { headers });
61
- if (!res.ok) await exitOnFailedResponse(res, baseUrl);
62
+ var fetchPolicy = async (settings, headers) => {
63
+ const res = await fetch(`${settings.baseUrl}/api/mcp/policy`, { headers });
64
+ if (!res.ok) await exitOnFailedSettingsRequest(res, settings);
62
65
  return res.json();
63
66
  };
64
- var setWindow = async (baseUrl, headers, seconds) => {
65
- const res = await fetch(`${baseUrl}/api/mcp/policy`, {
67
+ var setWindow = async (settings, headers, seconds) => {
68
+ const res = await fetch(`${settings.baseUrl}/api/mcp/policy`, {
66
69
  method: "PATCH",
67
70
  headers,
68
71
  body: JSON.stringify({ defaultPushFirstSeconds: seconds })
69
72
  });
70
- if (!res.ok) await exitOnFailedResponse(res, baseUrl);
73
+ if (!res.ok) await exitOnFailedSettingsRequest(res, settings);
71
74
  const data = await res.json();
72
75
  return data.defaultPushFirstSeconds;
73
76
  };
@@ -85,6 +88,7 @@ var setWindow = async (baseUrl, headers, seconds) => {
85
88
  var main = async () => {
86
89
  const apiKey = getApiKey();
87
90
  const baseUrl = getBaseUrl();
91
+ const settings = { request: "wait", apiKey, baseUrl };
88
92
  const headers = {
89
93
  "Authorization": `Bearer ${apiKey}`,
90
94
  "Content-Type": "application/json"
@@ -93,11 +97,11 @@ var main = async () => {
93
97
  const args = rest[0] === "wait" ? rest.slice(1) : rest;
94
98
  const arg = args[0];
95
99
  if (!arg || arg === "status") {
96
- printLadder(await fetchPolicy(baseUrl, headers));
100
+ printLadder(await fetchPolicy(settings, headers));
97
101
  return;
98
102
  }
99
103
  if (arg === "clear" || arg === "reset") {
100
- const value2 = await setWindow(baseUrl, headers, 20);
104
+ const value2 = await setWindow(settings, headers, 20);
101
105
  console.log(` ${green("\u2713")} Default push window reset to ${cyan(`${value2}s`)}`);
102
106
  return;
103
107
  }
@@ -107,7 +111,7 @@ var main = async () => {
107
111
  console.log(` ${dim("Usage: pushary wait <seconds> | status | clear")}`);
108
112
  process.exit(EXIT.USAGE);
109
113
  }
110
- const value = await setWindow(baseUrl, headers, Math.trunc(seconds));
114
+ const value = await setWindow(settings, headers, Math.trunc(seconds));
111
115
  const ladder = describeWaitLadder({ tool: "*", mode: "push_first", pushFirstSeconds: value, timeoutSeconds: 0, timeoutAction: "escalate" });
112
116
  console.log(` ${green("\u2713")} Default push window set to ${cyan(`${value}s`)} ${dim(`(${ladder})`)}`);
113
117
  };
@@ -1,23 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  installNetworkDefaults
4
- } from "../chunk-JXISQ7DT.js";
5
- import "../chunk-KYO45G5L.js";
4
+ } from "../chunk-5N5L6DNN.js";
5
+ import "../chunk-KOWFRUIT.js";
6
6
  import "../chunk-7QLSKOSU.js";
7
7
  import {
8
8
  EXIT
9
9
  } from "../chunk-KZERVKTD.js";
10
- import "../chunk-ZUTU43FT.js";
10
+ import "../chunk-Z23ZBYDW.js";
11
11
  import "../chunk-T7E75EKA.js";
12
- import "../chunk-Z6HPS67T.js";
12
+ import "../chunk-IRHABAQT.js";
13
13
  import {
14
14
  COMMANDS,
15
- isCommandName,
16
- renderHelp
17
- } from "../chunk-STQRUH3S.js";
15
+ renderHelp,
16
+ resolveCommandName
17
+ } from "../chunk-5DCDQ5VW.js";
18
18
  import {
19
19
  getPackageVersion
20
- } from "../chunk-QR6VE5ME.js";
20
+ } from "../chunk-5RGA636G.js";
21
21
 
22
22
  // src/cli/runtime.ts
23
23
  var supportsRuntime = (version) => {
@@ -33,8 +33,8 @@ var LOADERS = {
33
33
  daemon: () => import("./pushary-daemon.js"),
34
34
  status: () => import("./pushary-status.js"),
35
35
  login: () => import("./pushary-login.js"),
36
- logout: () => import("./pushary-logout.js"),
37
- connect: () => import("./pushary-connect.js"),
36
+ "sign-out": () => import("./pushary-logout.js"),
37
+ pair: () => import("./pushary-connect.js"),
38
38
  bell: () => import("./pushary-bell.js"),
39
39
  doctor: () => import("./pushary-doctor.js"),
40
40
  disconnect: () => import("./pushary-disconnect.js"),
@@ -54,21 +54,22 @@ if (!supportsRuntime(process.versions.node)) {
54
54
  installNetworkDefaults();
55
55
  var argv = process.argv.slice(2);
56
56
  var command = argv[0] ?? "";
57
+ var resolved = resolveCommandName(command);
57
58
  var wants = (...names) => argv.some((arg) => names.includes(arg));
58
- if (!command || wants("--help", "-h") && !isCommandName(command)) {
59
+ if (!command || wants("--help", "-h") && resolved === null) {
59
60
  process.stdout.write(renderHelp(getPackageVersion()));
60
61
  process.exit(EXIT.OK);
61
62
  }
62
- if (wants("--version", "-v") && !isCommandName(command)) {
63
+ if (wants("--version", "-v") && resolved === null) {
63
64
  process.stdout.write(`${getPackageVersion()}
64
65
  `);
65
66
  process.exit(EXIT.OK);
66
67
  }
67
- if (!isCommandName(command)) {
68
+ if (resolved === null) {
68
69
  process.stderr.write(`Unknown command: ${command}
69
70
  `);
70
71
  process.stderr.write(`Run \`pushary --help\`. Available: ${COMMANDS.map((c) => c.name).join(", ")}
71
72
  `);
72
73
  process.exit(EXIT.USAGE);
73
74
  }
74
- await LOADERS[command]();
75
+ await LOADERS[resolved]();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getBaseUrl
3
- } from "./chunk-Z6HPS67T.js";
3
+ } from "./chunk-IRHABAQT.js";
4
4
 
5
5
  // src/transcript-api.ts
6
6
  var fetchTranscriptRecipients = async (apiKey, baseUrl = getBaseUrl()) => {
@@ -1,6 +1,36 @@
1
1
  import {
2
- SETUP_AGENT_CHOICES
3
- } from "./chunk-QR6VE5ME.js";
2
+ DISCONNECT_AGENT_IDS,
3
+ SETUP_AGENT_CHOICES,
4
+ isApprovalMode
5
+ } from "./chunk-5RGA636G.js";
6
+
7
+ // src/cli/mode-request.ts
8
+ var parseDuration = (value) => {
9
+ const match = value.match(/^(\d+)(m|h)$/);
10
+ if (!match) return null;
11
+ const amount = Number(match[1]);
12
+ return match[2] === "h" ? amount * 3600 : amount * 60;
13
+ };
14
+ var modeRequestBody = (mode, duration) => {
15
+ if (duration === void 0) return { mode, permanent: true };
16
+ const ttlSeconds = parseDuration(duration);
17
+ return ttlSeconds ? { mode, ttlSeconds } : null;
18
+ };
19
+ var UPDATES_ONLY_MODE_INPUTS = ["updates-only", "updates_only"];
20
+ var UPDATES_ONLY_MODE = "notify_only";
21
+ var MODE_PROSE_NAMES = {
22
+ [UPDATES_ONLY_MODE]: "Updates only"
23
+ };
24
+ var isUpdatesOnlyInput = (value) => UPDATES_ONLY_MODE_INPUTS.includes(value);
25
+ var resolveModeInput = (value) => {
26
+ const name = value.trim().toLowerCase();
27
+ if (isUpdatesOnlyInput(name)) return UPDATES_ONLY_MODE;
28
+ return isApprovalMode(name) ? name : null;
29
+ };
30
+ var describeMode = (mode) => {
31
+ const prose = isApprovalMode(mode) ? MODE_PROSE_NAMES[mode] : void 0;
32
+ return prose === void 0 ? mode : `${prose} (${mode})`;
33
+ };
4
34
 
5
35
  // src/cli/commands.ts
6
36
  var NPX_LATEST_COMMAND = "npx pushary@latest";
@@ -44,14 +74,16 @@ var COMMAND_LIST = [
44
74
  summary: "Sign in from this terminal, no key to copy (--with-token to pipe one in)"
45
75
  },
46
76
  {
47
- name: "logout",
77
+ name: "sign-out",
48
78
  module: "pushary-logout",
49
- summary: "Remove the key this machine stores, and say what it does not remove"
79
+ aliases: ["logout"],
80
+ summary: "Sign out: remove the key this machine stores, and say what it does not remove (also: logout)"
50
81
  },
51
82
  {
52
- name: "connect",
83
+ name: "pair",
53
84
  module: "pushary-connect",
54
- summary: "Connect a phone without re-running setup (--app for the Pushary app)"
85
+ aliases: ["connect"],
86
+ summary: "Pair a phone without re-running setup (--app for the Pushary app; also: connect)"
55
87
  },
56
88
  {
57
89
  name: "bell",
@@ -63,7 +95,7 @@ var COMMAND_LIST = [
63
95
  ]
64
96
  },
65
97
  { name: "doctor", module: "pushary-doctor", summary: "Verify your Pushary installation is working" },
66
- { name: "disconnect", module: "pushary-disconnect", summary: "Turn off one agent (claude|codex|gemini|cursor)" },
98
+ { name: "disconnect", module: "pushary-disconnect", summary: `Turn off one agent (${DISCONNECT_AGENT_IDS.join("|")})` },
67
99
  { name: "clean", module: "pushary-clean", summary: "Remove Pushary from this machine (--everything: the Mac app's setup too, --dry-run: preview)" },
68
100
  { name: "mode", module: "pushary-mode", summary: "Choose where approval requests appear" },
69
101
  { name: "wait", module: "pushary-wait", summary: 'Show or set the "wait for your phone" ladder (pushary wait 45)' },
@@ -92,8 +124,14 @@ var COMMAND_LIST = [
92
124
  }
93
125
  ];
94
126
  var COMMANDS = COMMAND_LIST;
95
- var findCommand = (name) => COMMANDS.find((command) => command.name === name);
127
+ var answersTo = (command, token) => command.name === token || (command.aliases ?? []).includes(token);
128
+ var findCommand = (token) => COMMANDS.find((command) => answersTo(command, token));
96
129
  var isCommandName = (name) => COMMANDS.some((command) => command.name === name);
130
+ var resolveCommandName = (token) => {
131
+ const name = findCommand(token)?.name;
132
+ return name !== void 0 && isCommandName(name) ? name : null;
133
+ };
134
+ var isInvocationOf = (token, name) => resolveCommandName(token) === name;
97
135
  var COMMAND_OPTIONS = {
98
136
  cowork: [
99
137
  ["--open", "Also open the setup guide in your browser"],
@@ -111,7 +149,7 @@ var COMMAND_OPTIONS = {
111
149
  ["trust --rotate", "Accept the keys the server is offering now and resume syncing"]
112
150
  ],
113
151
  mode: [
114
- ["<mode>", "One of: push_only, push_first, terminal_only, notify_only"],
152
+ ["<mode>", `One of: push_only, push_first, terminal_only, notify_only (Updates only; also: ${UPDATES_ONLY_MODE_INPUTS.join(", ")})`],
115
153
  ["status", "Show the current override"],
116
154
  ["clear", "Drop the override and go back to per-tool policies"],
117
155
  ["--for <30m|2h>", "Expire the override after this long"]
@@ -125,7 +163,7 @@ var COMMAND_OPTIONS = {
125
163
  ["--json", "Emit the whole report as one object instead of the screen"],
126
164
  ["--devices", "Also list the machines registered to this site"]
127
165
  ],
128
- logout: [
166
+ "sign-out": [
129
167
  ["--revoke", "Also kill the key server-side, so it stops working everywhere"],
130
168
  ["--json", "Emit the result as one object instead of the screen"]
131
169
  ],
@@ -168,10 +206,10 @@ var SETUP_OPTIONS = [
168
206
  ["--connect app", "Pair the native Pushary app, no key to paste (default)"],
169
207
  ["--connect browser", "Approve in a browser tab instead. For a machine with no app"],
170
208
  ["--connect web", "Legacy browser subscribe page"],
171
- ["--connect none", "Skip the phone connect step, same as --skip-phone"],
209
+ ["--connect none", "Skip pairing a phone, same as --skip-phone"],
172
210
  ["--control auto|on|off", "Manage phone-start automatically (default), require it, or disable its background service"],
173
211
  ["--transcripts on|off", "Enable or disable encrypted transcript sync on this machine"],
174
- ["--skip-phone", "Skip the phone connect step"],
212
+ ["--skip-phone", "Skip pairing a phone"],
175
213
  ["--take-over-hooks", "Replace hooks the Pushary Mac app owns with the CLI's. Without it the app's wiring is kept"],
176
214
  ["--verify roundtrip", "Send a real question and wait for you to answer it (default when a human is present)"],
177
215
  ["--verify push", "Only prove a notification was delivered. Automatic when nobody is at the terminal"],
@@ -220,8 +258,9 @@ var renderHelp = (version) => {
220
258
  ""
221
259
  ].join("\n");
222
260
  };
223
- var renderCommandHelp = (name, version) => {
224
- const spec = findCommand(name);
261
+ var renderCommandHelp = (token, version) => {
262
+ const spec = findCommand(token);
263
+ const name = spec?.name ?? token;
225
264
  const options = name === "setup" ? SETUP_OPTIONS : COMMAND_OPTIONS[name] ?? [];
226
265
  const width = options.length > 0 ? Math.max(...options.map(([flag]) => flag.length)) + 2 : 0;
227
266
  return [
@@ -241,9 +280,14 @@ var renderCommandHelp = (name, version) => {
241
280
  };
242
281
 
243
282
  export {
283
+ modeRequestBody,
284
+ UPDATES_ONLY_MODE_INPUTS,
285
+ resolveModeInput,
286
+ describeMode,
244
287
  NPX_LATEST_COMMAND,
245
288
  COMMANDS,
246
- isCommandName,
289
+ resolveCommandName,
290
+ isInvocationOf,
247
291
  renderHelp,
248
292
  renderCommandHelp
249
293
  };
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  readExistingMachineId
3
- } from "./chunk-KYO45G5L.js";
3
+ } from "./chunk-KOWFRUIT.js";
4
4
  import {
5
5
  NetworkError
6
6
  } from "./chunk-7QLSKOSU.js";
7
7
  import {
8
8
  getBaseUrl
9
- } from "./chunk-Z6HPS67T.js";
9
+ } from "./chunk-IRHABAQT.js";
10
10
  import {
11
11
  clientContextHeaders,
12
12
  getPackageVersion
13
- } from "./chunk-QR6VE5ME.js";
13
+ } from "./chunk-5RGA636G.js";
14
14
 
15
15
  // src/cli/net.ts
16
16
  import { createRequire } from "module";
@@ -154,6 +154,7 @@ var AGENTS = {
154
154
  fx: {
155
155
  ...defaults,
156
156
  label: "fx",
157
+ disconnectable: true,
157
158
  connectVia: "Local MCP bridge",
158
159
  setupCommand: PUSHARY_SETUP_COMMAND,
159
160
  capabilityNote: "fx has no hooks that other tools can register, so Pushary cannot answer fx's own approval prompts. Setup turns off fx's terminal question tool so fx's agent asks through Pushary. fx's full-access mode ignores that setting; there the agent is only instructed to ask through Pushary."
@@ -196,7 +197,7 @@ var AGENTS = {
196
197
  connectVia: "Native hooks + MCP",
197
198
  setupCommand: PUSHARY_SETUP_COMMAND,
198
199
  enforcementDetail: "Shell, writes and edits through native PreToolUse hooks, plus PermissionRequest, over Qoder's superset of the Claude Code event list.",
199
- capabilityNote: "Qoder documents no user-level instructions file, so the proactive-ask block is not installed. Hooks and MCP are unaffected. Like every agent in this family, the CLI wires it; the macOS app does not yet."
200
+ capabilityNote: "Qoder documents no user-level instructions file, so the proactive-ask block is not installed. Hooks and MCP are unaffected."
200
201
  },
201
202
  droid: {
202
203
  ...defaults,
@@ -262,10 +263,24 @@ var POLICY_AGENTS = [...Object.values(AGENTS).flatMap((agent) => agent.policyAge
262
263
  var AGENT_LABELS_BY_TYPE = Object.fromEntries(
263
264
  Object.entries(AGENTS).flatMap(([id, agent]) => agent.hasStoredIdentity ? [[id, agent.label]] : [])
264
265
  );
265
- var DISCONNECTABLE_AGENTS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.disconnectable ? [agent.hookSource] : []));
266
+ var DISCONNECTABLE_AGENTS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.disconnectable && agent.hookSource !== null ? [agent.hookSource] : []));
266
267
  var REPO_AWARE_AGENTS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.repoAware ? [agent.policyAgent] : []));
267
268
  var MANAGED_PROVIDERS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.managedProvider ? [agent.hookSource] : []));
268
269
  var BRIDGE_HOOK_SOURCES = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.bridged ? [agent.hookSource] : []));
270
+ var isAgentId = (value) => Object.keys(AGENTS).includes(value);
271
+ var isHooklessDisconnectableAgent = (value) => isAgentId(value) && AGENTS[value].disconnectable && AGENTS[value].hookSource === null;
272
+ var DISCONNECT_AGENT_IDS = Object.freeze([...DISCONNECTABLE_AGENTS, ...Object.keys(AGENTS).filter(isHooklessDisconnectableAgent)]);
273
+ var agentIdCalled = (name) => isAgentId(name) ? name : Object.keys(AGENTS).filter(isAgentId).find((id) => AGENTS[id].hookSource === name) ?? null;
274
+ var namesOfAgent = (id) => {
275
+ const source = AGENTS[id].hookSource;
276
+ return source === null ? [id] : [id, source];
277
+ };
278
+ var normalizeAgentId = (value, accepted) => {
279
+ const name = value.trim().toLowerCase();
280
+ const agent = agentIdCalled(name);
281
+ const names = agent === null ? [name] : namesOfAgent(agent);
282
+ return accepted.find((candidate) => names.includes(candidate)) ?? null;
283
+ };
269
284
 
270
285
  // ../contracts/src/agent-wiring.ts
271
286
  var COMPATIBLE_AGENT_TYPES = ["qwen_code", "codebuddy", "qoder", "droid", "devin"];
@@ -718,6 +733,25 @@ var AUTH_REJECTION_REASONS = [
718
733
  ...AGENT_SITE_REJECTION_REASONS
719
734
  ];
720
735
 
736
+ // ../contracts/src/declined-agents.ts
737
+ var DECLINED_AGENTS_FILE = "declined-agents.json";
738
+ var DECLINED_AGENTS_VERSION = 1;
739
+ var isDeclinableAgent = (value) => typeof value === "string" && HOOK_SOURCES.includes(value);
740
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
741
+ var canonicalAgents = (agents) => [...new Set(agents)].sort();
742
+ var parseDeclinedAgents = (value) => {
743
+ if (!isRecord(value) || value.version !== DECLINED_AGENTS_VERSION || !Array.isArray(value.agents)) return [];
744
+ return canonicalAgents(value.agents.filter(isDeclinableAgent));
745
+ };
746
+ var declinedAgentsDocument = (agents) => ({
747
+ version: DECLINED_AGENTS_VERSION,
748
+ agents: canonicalAgents(agents)
749
+ });
750
+ var serializeDeclinedAgents = (agents) => `${JSON.stringify(declinedAgentsDocument(agents), null, 2)}
751
+ `;
752
+ var withDeclinedAgent = (agents, agent) => canonicalAgents([...agents, agent]);
753
+ var withoutDeclinedAgent = (agents, agent) => agents.filter((candidate) => candidate !== agent);
754
+
721
755
  // ../contracts/src/index.ts
722
756
  var KEY_SCOPES = ["full_access", "agent", "connector", "bound"];
723
757
  var normalizeKeyScope = (value) => typeof value === "string" && KEY_SCOPES.includes(value) ? value : null;
@@ -874,7 +908,7 @@ var CURSOR_GATE_EVENTS = ["beforeShellExecution", "beforeMCPExecution", "preTool
874
908
  var SUPPORT_EMAIL = "business@pushary.com";
875
909
  var SUPPORT_DISCORD = "https://discord.gg/9v3VvUByrr";
876
910
  var HOOK_LOCATOR_BINARY = "pushary-bridge";
877
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
911
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
878
912
  var HOOK_BATCH_MAX_BYTES = 4 * 1024 * 1024;
879
913
  var REPO_KEY_MAX_LENGTH = 200;
880
914
  var WILDCARD_TOOL_PATTERN = "*";
@@ -2152,7 +2186,9 @@ function agentQuestionStrategy(source, event, toolName) {
2152
2186
  if (source !== null && event === "PreToolUse" && UPDATED_INPUT_QUESTION_SOURCES.has(source)) return AGENT_QUESTION_STRATEGY.updatedInput;
2153
2187
  return AGENT_QUESTION_STRATEGY.native;
2154
2188
  }
2155
- var ASKED_IN_EVERY_MODE_TOOLS = [AGENT_QUESTION_TOOL, "ExitPlanMode"];
2189
+ var questionHandedToTheTerminal = (toolName, modeState) => toolName === AGENT_QUESTION_TOOL && modeState.askHandoff === true;
2190
+ var EXIT_PLAN_MODE_TOOL = "ExitPlanMode";
2191
+ var ASKED_IN_EVERY_MODE_TOOLS = [AGENT_QUESTION_TOOL, EXIT_PLAN_MODE_TOOL];
2156
2192
  var ASKED_IN_EVERY_MODE = new Set(ASKED_IN_EVERY_MODE_TOOLS);
2157
2193
  var isAskedInEveryMode = (toolName) => ASKED_IN_EVERY_MODE.has(toolName);
2158
2194
  var NATIVE_BLOCK_EVENTS = /* @__PURE__ */ new Set(["PermissionDenied"]);
@@ -2233,6 +2269,12 @@ var resolveGate = (input) => {
2233
2269
  };
2234
2270
  var editAsksAtTheAgentsPrompt = (input, verdict) => verdict.kind === "gate" && !verdict.scopeReason && (input.modeState.mode ?? input.config.modeOverride ?? null) === null && !aChosenRuleGoverns(input) && verdict.policy.mode === "push_first" && ACCEPT_EDITS_DEFER_TOOLS.has(input.toolName);
2235
2271
  var claudePromptsBeforeAnEdit = (permissionMode) => permissionMode === "default";
2272
+ var runsTheClaudeHook = (source) => source === null || source === "claude" || hookCompatibleProfileBySource(source) !== void 0;
2273
+ var agentPromptsBeforeAnEdit = ({ source, event, permissionMode }) => {
2274
+ if (source === "codex") return event === "PermissionRequest";
2275
+ return event === "PreToolUse" && runsTheClaudeHook(source) && claudePromptsBeforeAnEdit(permissionMode);
2276
+ };
2277
+ var editGoesToTheAgentsPrompt = (call, input, verdict) => agentPromptsBeforeAnEdit(call) && editAsksAtTheAgentsPrompt(input, verdict);
2236
2278
  var MAX_AGENT_PERMISSION_RULES_PER_KIND = 256;
2237
2279
  var MAX_AGENT_PERMISSION_RULE_LENGTH = 512;
2238
2280
  var isAgentPermissionRule = (value) => typeof value === "string" && value.length > 0 && value.length <= MAX_AGENT_PERMISSION_RULE_LENGTH;
@@ -2242,7 +2284,7 @@ var settingsPermissionList = (permissions, kind) => {
2242
2284
  };
2243
2285
  var boundedUnion = (lists) => [...new Set(lists.flat())].slice(0, MAX_AGENT_PERMISSION_RULES_PER_KIND);
2244
2286
  var claudePermissionRulesFromSettings = (documents) => {
2245
- const permissionBlocks = documents.flatMap((document) => isRecord(document) && isRecord(document.permissions) ? [document.permissions] : []);
2287
+ const permissionBlocks = documents.flatMap((document) => isRecord2(document) && isRecord2(document.permissions) ? [document.permissions] : []);
2246
2288
  const collect = (kind) => boundedUnion(permissionBlocks.map((permissions) => settingsPermissionList(permissions, kind)));
2247
2289
  return { allow: collect("allow"), ask: collect("ask"), deny: collect("deny") };
2248
2290
  };
@@ -2403,6 +2445,7 @@ var claudePermissionRequestOutput = (decision, reason, updatedInput) => ({
2403
2445
  decision: decision === "allow" ? { behavior: "allow", ...updatedInput ? { updatedInput } : {} } : { behavior: "deny", ...reason ? { message: reason } : {} }
2404
2446
  }
2405
2447
  });
2448
+ var DENIED_FROM_PUSHARY_REASON = "Denied from Pushary";
2406
2449
  var gateOutputFor = (source, event, decision, reason) => {
2407
2450
  let output;
2408
2451
  if (source === "claude" || source === "vscode") {
@@ -2418,7 +2461,7 @@ var gateOutputFor = (source, event, decision, reason) => {
2418
2461
  output = {
2419
2462
  hookSpecificOutput: {
2420
2463
  hookEventName: "PermissionRequest",
2421
- decision: decision === "allow" ? { behavior: "allow" } : { behavior: "deny", message: reason ?? "Denied from Pushary" }
2464
+ decision: decision === "allow" ? { behavior: "allow" } : { behavior: "deny", message: reason ?? DENIED_FROM_PUSHARY_REASON }
2422
2465
  }
2423
2466
  };
2424
2467
  } else if (event === "PreToolUse" && decision === "deny") {
@@ -2426,19 +2469,19 @@ var gateOutputFor = (source, event, decision, reason) => {
2426
2469
  hookSpecificOutput: {
2427
2470
  hookEventName: "PreToolUse",
2428
2471
  permissionDecision: "deny",
2429
- permissionDecisionReason: reason ?? "Denied from Pushary"
2472
+ permissionDecisionReason: reason ?? DENIED_FROM_PUSHARY_REASON
2430
2473
  }
2431
2474
  };
2432
2475
  }
2433
2476
  } else if (source === "gemini" && event === "BeforeTool" && decision !== "ask") {
2434
- output = decision === "allow" ? { decision: "allow" } : { decision: "deny", reason: reason ?? "Denied from Pushary" };
2477
+ output = decision === "allow" ? { decision: "allow" } : { decision: "deny", reason: reason ?? DENIED_FROM_PUSHARY_REASON };
2435
2478
  } else if (source === "opencode" && event === OPENCODE_APPROVAL_EVENT) {
2436
- output = decision === "deny" ? { status: "deny", reason: reason ?? "Denied from Pushary" } : { status: decision };
2479
+ output = decision === "deny" ? { status: "deny", reason: reason ?? DENIED_FROM_PUSHARY_REASON } : { status: decision };
2437
2480
  } else if (source === "cursor" && CURSOR_GATE_EVENTS.includes(event)) {
2438
2481
  output = decision === "allow" ? { permission: "allow" } : decision === "ask" && event !== "preToolUse" ? { permission: "ask" } : {
2439
2482
  permission: "deny",
2440
2483
  user_message: "Command denied via Pushary.",
2441
- agent_message: reason ?? "Denied from Pushary"
2484
+ agent_message: reason ?? DENIED_FROM_PUSHARY_REASON
2442
2485
  };
2443
2486
  } else {
2444
2487
  const profile = hookCompatibleProfileBySource(source);
@@ -2463,6 +2506,12 @@ var claudeElicitationOutput = (event, action, content) => ({
2463
2506
  }
2464
2507
  });
2465
2508
  var renderElicitationOutput = (event, action, content) => ELICITATION_EVENTS.includes(event) ? JSON.stringify(claudeElicitationOutput(event, action, content)) : void 0;
2509
+ var UNVERIFIABLE_APPROVAL_REASON = "Pushary could not create a verifiable approval. Retry the action.";
2510
+ var DECISION_STOP_REASON = "Approval was cancelled or became unavailable. Retry only after resolving it.";
2511
+ var WITHDRAWN_APPROVAL_REASON = "The phone question was withdrawn. Approve in the current client.";
2512
+ var PLAN_DECLINED_REASON = "Plan declined from the phone. Ask what to change before coding.";
2513
+ var claudeStoppedPreToolUseOutput = (reason) => stoppedOutput(claudePreToolUseOutput("deny", reason), reason);
2514
+ var claudeStoppedPermissionRequestOutput = (reason) => stoppedOutput(claudePermissionRequestOutput("deny", reason), reason);
2466
2515
  var ELICITATION_ANSWERABLE_MODE = "form";
2467
2516
  var ELICITATION_MIN_OPTIONS = 2;
2468
2517
  var ELICITATION_MAX_MULTI_QUESTIONS = 4;
@@ -2655,16 +2704,16 @@ var globalInstallTarget = (owner, ownName) => {
2655
2704
  return LEGACY_PACKAGE;
2656
2705
  }
2657
2706
  };
2658
- var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2659
- var declaresOwnershipProbe = (bin) => isRecord2(bin) && typeof bin[OWNERSHIP_PROBE_COMMAND] === "string";
2707
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2708
+ var declaresOwnershipProbe = (bin) => isRecord3(bin) && typeof bin[OWNERSHIP_PROBE_COMMAND] === "string";
2660
2709
  var cliManifestName = (manifest) => {
2661
- if (!isRecord2(manifest) || !isCliPackageName(manifest.name) || !declaresOwnershipProbe(manifest.bin)) return void 0;
2710
+ if (!isRecord3(manifest) || !isCliPackageName(manifest.name) || !declaresOwnershipProbe(manifest.bin)) return void 0;
2662
2711
  return manifest.name;
2663
2712
  };
2664
2713
  var classifyGlobalFolder = (name, read) => {
2665
2714
  if (read.kind === "unreadable") return { kind: "unseen" };
2666
2715
  const { manifest } = read;
2667
- if (!isRecord2(manifest) || cliManifestName(manifest) !== name) return { kind: "foreign" };
2716
+ if (!isRecord3(manifest) || cliManifestName(manifest) !== name) return { kind: "foreign" };
2668
2717
  return { kind: "ours", installed: { name, version: typeof manifest.version === "string" ? manifest.version : void 0 } };
2669
2718
  };
2670
2719
  var classifyGlobalInstall = (installed, owner) => {
@@ -2755,6 +2804,8 @@ export {
2755
2804
  DISCONNECTABLE_AGENTS,
2756
2805
  REPO_AWARE_AGENTS,
2757
2806
  MANAGED_PROVIDERS,
2807
+ DISCONNECT_AGENT_IDS,
2808
+ normalizeAgentId,
2758
2809
  PHONE_ANSWERED_TOOLS,
2759
2810
  canonicalToolName,
2760
2811
  toToolLookup,
@@ -2780,7 +2831,13 @@ export {
2780
2831
  isSetupConnectMode,
2781
2832
  CLIENT_REPORT_PATH,
2782
2833
  clientContextHeaders,
2834
+ DECLINED_AGENTS_FILE,
2835
+ parseDeclinedAgents,
2836
+ serializeDeclinedAgents,
2837
+ withDeclinedAgent,
2838
+ withoutDeclinedAgent,
2783
2839
  normalizeKeyScope,
2840
+ isApprovalMode,
2784
2841
  parseAgentQuestion,
2785
2842
  parseAgentQuestions,
2786
2843
  parseAgentQuestionAnswers,
@@ -2820,6 +2877,7 @@ export {
2820
2877
  shouldDeferToNativeMode,
2821
2878
  AGENT_QUESTION_STRATEGY,
2822
2879
  agentQuestionStrategy,
2880
+ questionHandedToTheTerminal,
2823
2881
  CODEX_RESCUE_TOOL_NAME,
2824
2882
  isBlockedActionRequest,
2825
2883
  BLOCKED_ACTION_GRANT_TTL_SECONDS,
@@ -2828,8 +2886,7 @@ export {
2828
2886
  isPermissionRequestOwnedByPreToolUse,
2829
2887
  CLAUDE_PRETOOLUSE_MATCHER,
2830
2888
  resolveGate,
2831
- editAsksAtTheAgentsPrompt,
2832
- claudePromptsBeforeAnEdit,
2889
+ editGoesToTheAgentsPrompt,
2833
2890
  claudePermissionRulesFromSettings,
2834
2891
  claudeConsultsItsAllowlist,
2835
2892
  agentRulesAllowEveryInput,
@@ -2842,13 +2899,18 @@ export {
2842
2899
  machineDecisionFor,
2843
2900
  claudePermissionDeniedOutput,
2844
2901
  codexStopRescueOutput,
2845
- stoppedOutput,
2846
2902
  claudePreToolUseOutput,
2847
2903
  PENDING_COMMAND_PREFIX,
2848
2904
  pendingCommandEvents,
2849
2905
  claudePermissionRequestOutput,
2850
2906
  gateOutputFor,
2851
2907
  renderElicitationOutput,
2908
+ UNVERIFIABLE_APPROVAL_REASON,
2909
+ DECISION_STOP_REASON,
2910
+ WITHDRAWN_APPROVAL_REASON,
2911
+ PLAN_DECLINED_REASON,
2912
+ claudeStoppedPreToolUseOutput,
2913
+ claudeStoppedPermissionRequestOutput,
2852
2914
  ELICITATION_ANSWERABLE_MODE,
2853
2915
  readElicitationFields,
2854
2916
  planElicitationAsk,
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  keyCheckFromResponse
3
- } from "./chunk-KFKL3FHS.js";
3
+ } from "./chunk-QRXPWW3M.js";
4
4
  import {
5
5
  installNetworkDefaults
6
- } from "./chunk-JXISQ7DT.js";
6
+ } from "./chunk-5N5L6DNN.js";
7
7
  import {
8
8
  getBaseUrl
9
- } from "./chunk-Z6HPS67T.js";
9
+ } from "./chunk-IRHABAQT.js";
10
10
 
11
11
  // src/retry.ts
12
12
  var isRetryable = (err) => {