@pushary/agent-hooks 1.7.0 → 1.7.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 (97) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/bin/pushary-bell-hook.js +23 -23
  3. package/dist/bin/pushary-bell.js +10 -10
  4. package/dist/bin/pushary-claude.js +25 -25
  5. package/dist/bin/pushary-clean.js +116 -71
  6. package/dist/bin/pushary-codex-bridge.js +17 -17
  7. package/dist/bin/pushary-codex-hook.js +30 -30
  8. package/dist/bin/pushary-codex.js +12 -12
  9. package/dist/bin/pushary-connect.js +14 -14
  10. package/dist/bin/pushary-cowork.js +5 -5
  11. package/dist/bin/pushary-daemon.js +27 -27
  12. package/dist/bin/pushary-disconnect.js +17 -17
  13. package/dist/bin/pushary-doctor.js +68 -40
  14. package/dist/bin/pushary-elicitation-hook.js +24 -24
  15. package/dist/bin/pushary-gemini-bridge.js +12 -12
  16. package/dist/bin/pushary-gemini-hook.js +27 -27
  17. package/dist/bin/pushary-hook.js +31 -31
  18. package/dist/bin/pushary-login.js +13 -13
  19. package/dist/bin/pushary-logout.js +8 -8
  20. package/dist/bin/pushary-mcp.js +8 -8
  21. package/dist/bin/pushary-mode.js +10 -10
  22. package/dist/bin/pushary-notification-hook.js +27 -27
  23. package/dist/bin/pushary-opencode-hook.js +26 -26
  24. package/dist/bin/pushary-permission-denied-hook.js +31 -31
  25. package/dist/bin/pushary-permission-hook.js +30 -30
  26. package/dist/bin/pushary-post-hook.js +27 -27
  27. package/dist/bin/pushary-prompt-hook.js +27 -27
  28. package/dist/bin/pushary-session-end-hook.js +27 -27
  29. package/dist/bin/pushary-session-start-hook.js +31 -31
  30. package/dist/bin/pushary-setup.js +114 -89
  31. package/dist/bin/pushary-stats.js +5 -5
  32. package/dist/bin/pushary-status.js +16 -16
  33. package/dist/bin/pushary-stop-hook.js +27 -27
  34. package/dist/bin/pushary-stopfailure-hook.js +27 -27
  35. package/dist/bin/pushary-suggestions.js +5 -5
  36. package/dist/bin/pushary-transcript-register.js +19 -19
  37. package/dist/bin/pushary-transcripts.js +7 -7
  38. package/dist/bin/pushary-upgrade.js +20 -20
  39. package/dist/bin/pushary-wait.js +10 -10
  40. package/dist/bin/pushary.js +6 -6
  41. package/dist/{chunk-YYINL576.js → chunk-2FU43B5Q.js} +2 -2
  42. package/dist/{chunk-D6BULEJW.js → chunk-3QE3BHBW.js} +2 -2
  43. package/dist/{chunk-52NVBNJT.js → chunk-3QORH5EJ.js} +3 -3
  44. package/dist/{chunk-PUDBG3GM.js → chunk-3T35OE7C.js} +2 -2
  45. package/dist/{chunk-DVQDPI3T.js → chunk-42XR73I2.js} +8 -8
  46. package/dist/{chunk-X6KDKOV5.js → chunk-43SSQCWO.js} +3 -3
  47. package/dist/{chunk-DNJUSFBL.js → chunk-454KMJHF.js} +7 -5
  48. package/dist/{chunk-DZURML2D.js → chunk-4C4NRDIC.js} +1 -1
  49. package/dist/{chunk-6ADZQFVW.js → chunk-55RO62FV.js} +1 -1
  50. package/dist/{chunk-QXAK5RFU.js → chunk-62TEPRWQ.js} +1 -1
  51. package/dist/{chunk-2PS6QWDW.js → chunk-6PV2JQ64.js} +2 -2
  52. package/dist/{chunk-7OS4IHGI.js → chunk-6SJQ442W.js} +2 -2
  53. package/dist/{chunk-UC65FJJT.js → chunk-7GI6QC3M.js} +2 -2
  54. package/dist/{chunk-EAQMWJPG.js → chunk-7GYGVQBU.js} +8 -8
  55. package/dist/{chunk-73T6MSU5.js → chunk-AAGLDXH4.js} +5 -5
  56. package/dist/{chunk-HDRNWNVP.js → chunk-AHWJHLG5.js} +7 -28
  57. package/dist/{chunk-BWYJ6AAF.js → chunk-BYVB3RX3.js} +2 -2
  58. package/dist/{chunk-B3UXMSMW.js → chunk-EIZW73P5.js} +1 -1
  59. package/dist/{chunk-B234346F.js → chunk-H6JV44TE.js} +1 -1
  60. package/dist/{chunk-J2355HR7.js → chunk-IGBDOZUO.js} +1 -1
  61. package/dist/{chunk-DANOZVZK.js → chunk-JMBG47WV.js} +30 -1
  62. package/dist/{chunk-KW2RRR7D.js → chunk-JTQDP4ZL.js} +3 -3
  63. package/dist/{chunk-WUNZAE7I.js → chunk-KBLIVEFJ.js} +5 -5
  64. package/dist/{chunk-V6QRSU2G.js → chunk-KSNAAMU5.js} +4 -4
  65. package/dist/{chunk-2C7CCWVX.js → chunk-MCNS4725.js} +1 -1
  66. package/dist/{chunk-5HLTWFJ6.js → chunk-MWVCDP64.js} +1 -1
  67. package/dist/{chunk-L4H2Q2S4.js → chunk-NAAZCNKI.js} +153 -13
  68. package/dist/{chunk-B3YQANFN.js → chunk-NID4UZDN.js} +1 -1
  69. package/dist/{chunk-JNXVOU6Y.js → chunk-NQFGVAT6.js} +2 -2
  70. package/dist/{chunk-BH2SKSSK.js → chunk-O5JD7UI5.js} +1 -1
  71. package/dist/{chunk-5RUIFDTP.js → chunk-O5PRIYFZ.js} +30 -2
  72. package/dist/{chunk-V4AQBAVG.js → chunk-OAL44FM2.js} +1 -1
  73. package/dist/{chunk-KXMZMZD3.js → chunk-OTZHT34O.js} +1 -1
  74. package/dist/{chunk-XNCMT7YY.js → chunk-P5CBNSYL.js} +1 -1
  75. package/dist/{chunk-JXUAXO74.js → chunk-Q7QRR6YL.js} +2 -2
  76. package/dist/{chunk-BBKNJJVX.js → chunk-QPR33SHS.js} +6 -6
  77. package/dist/{chunk-LEZFOTMI.js → chunk-SNWNA3MQ.js} +1 -1
  78. package/dist/{chunk-R2XCLT6I.js → chunk-SVEMF2VO.js} +4 -4
  79. package/dist/{chunk-ROQK7IN3.js → chunk-UPKP67TA.js} +2 -2
  80. package/dist/{chunk-SQRWHALT.js → chunk-VCMFKZON.js} +2 -2
  81. package/dist/{chunk-IJ6ECPA6.js → chunk-WM3U2RUK.js} +8 -8
  82. package/dist/{chunk-KYQXUGH7.js → chunk-WSC53O7I.js} +3 -3
  83. package/dist/{chunk-ZQ7IDOKY.js → chunk-WZWXBTRF.js} +3 -3
  84. package/dist/{chunk-PQ4F64M2.js → chunk-XDASEYRS.js} +3 -3
  85. package/dist/{chunk-YROGUPOZ.js → chunk-XFNVG3OV.js} +1 -1
  86. package/dist/{chunk-CVALVGA7.js → chunk-XMN37YJH.js} +1 -1
  87. package/dist/{chunk-VKVIAZQO.js → chunk-XPMOCJHN.js} +1 -1
  88. package/dist/{chunk-B76WVEDX.js → chunk-XPTCPU6V.js} +1 -1
  89. package/dist/{chunk-O2LEPIU6.js → chunk-Y6GKOZBT.js} +1 -1
  90. package/dist/{chunk-4F46VIEE.js → chunk-YQN7S57T.js} +6 -6
  91. package/dist/{chunk-UOAQ6QMP.js → chunk-YZKS3ME3.js} +2 -2
  92. package/dist/{chunk-S3UD4V6W.js → chunk-ZEMSCGDD.js} +6 -1
  93. package/dist/{chunk-EGKXSQCY.js → chunk-ZIIYTQHC.js} +4 -4
  94. package/dist/{reapply-6PH3FMPX.js → reapply-66GQIBXN.js} +9 -9
  95. package/dist/src/index.js +19 -19
  96. package/package.json +1 -1
  97. package/dist/chunk-P32LPT2L.js +0 -6
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  MCP_BRIDGE_BINARY
3
- } from "./chunk-YYINL576.js";
3
+ } from "./chunk-2FU43B5Q.js";
4
4
  import {
5
5
  openCodeConfigDirFrom
6
- } from "./chunk-B3YQANFN.js";
6
+ } from "./chunk-NID4UZDN.js";
7
7
  import {
8
8
  codexHomeFrom
9
- } from "./chunk-DNJUSFBL.js";
9
+ } from "./chunk-454KMJHF.js";
10
10
  import {
11
11
  HOOK_COMPATIBLE_PROFILES
12
- } from "./chunk-YROGUPOZ.js";
12
+ } from "./chunk-XFNVG3OV.js";
13
13
 
14
14
  // src/fx-config.ts
15
15
  var FX_SERVER_NAME = "pushary";
@@ -17,7 +17,7 @@ var FX_OPERATION_TIMEOUT_MS = 75e3;
17
17
  var FX_PROFILE_MAX_BYTES = 1024 * 1024 - 1;
18
18
  var FX_SETTINGS_MAX_BYTES = 64 * 1024;
19
19
  var FX_PUSHARY_TOOLS_RULE = "mcp_pushary_*";
20
- var FX_TOOL_DISCOVERY_NOTE = 'In fx, these tools are named mcp_pushary_ask_user, mcp_pushary_send_notification, mcp_pushary_wait_for_answer and mcp_pushary_cancel_question. They are not loaded at the start of a turn: find them with capability_search (query "pushary") or select one with mcp_select_tool. When the user may be away from this terminal, use mcp_pushary_ask_user rather than your built-in question prompt.';
20
+ var FX_TOOL_DISCOVERY_NOTE = 'In fx, these tools are named mcp_pushary_ask_user, mcp_pushary_send_notification, mcp_pushary_wait_for_answer, mcp_pushary_cancel_question and mcp_pushary_propose_scope. fx does not load them at the start of a turn: call mcp_select_tool with the exact name, for example {"name": "mcp_pushary_ask_user"}, then call the tool. For fx this replaces the guidance above to ask the whole round in the terminal while the user is there: ask every question through mcp_pushary_ask_user, even while the user is at this terminal, rather than through ask_user_question. fx cannot show its own question prompt in the Mac notch or on the phone, so a question asked there waits unseen once the user steps away. Ask in your reply instead only when a Pushary handoffAction tells you to ask here, or when mcp_pushary_ask_user is unavailable or fails without one.';
21
21
  var ALLOW_ACTION = "allow";
22
22
  var PERMISSION_KEY = "permission";
23
23
  var WORKSPACES_KEY = "workspaces";
@@ -50,7 +50,9 @@ var FX_PROFILE_REFUSALS = {
50
50
  };
51
51
  var FX_SETTINGS_REFUSALS = {
52
52
  ...FX_JSON_REFUSALS,
53
- "permission-not-object": 'has a "permission" value that is not an object'
53
+ "permission-not-object": 'has a "permission" value that is not an object',
54
+ "invalid-permission-rule": "has a permission rule fx rejects, so fx ignores the whole file",
55
+ "invalid-permission-mode": "has a permission_mode fx does not recognise, so fx ignores the whole file"
54
56
  };
55
57
  var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
56
58
  var stringTokenEnd = (text, start) => {
@@ -182,6 +184,8 @@ var fxSettingsText = (settings) => fxFileText(settings, FX_SETTINGS_MAX_BYTES);
182
184
  var readFxSettings = (text) => {
183
185
  const read = readFxJsonObject(text, FX_SETTINGS_MAX_BYTES);
184
186
  if (read.kind === "refused") return read;
187
+ const invalid = fxSettingsRejection(read.value);
188
+ if (invalid) return { kind: "refused", reason: invalid };
185
189
  const permission = read.value[PERMISSION_KEY];
186
190
  if (permission !== void 0 && !asRecord(permission)) return { kind: "refused", reason: "permission-not-object" };
187
191
  return { kind: "settings", settings: read.value };
@@ -196,15 +200,15 @@ var withPusharyToolsAllowed = (settings) => {
196
200
  change: "added"
197
201
  };
198
202
  };
203
+ var withoutPermissionRule = (settings, permission, rule) => {
204
+ const remaining = Object.fromEntries(Object.entries(permission).filter(([name]) => name !== rule));
205
+ const withoutPermission = Object.fromEntries(Object.entries(settings).filter(([key]) => key !== PERMISSION_KEY));
206
+ return Object.keys(remaining).length === 0 ? withoutPermission : { ...settings, [PERMISSION_KEY]: remaining };
207
+ };
199
208
  var withoutPusharyToolsAllowed = (settings) => {
200
209
  const permission = asRecord(settings[PERMISSION_KEY]);
201
- if (permission?.[FX_PUSHARY_TOOLS_RULE] !== ALLOW_ACTION) return { settings, removed: false };
202
- const remaining = Object.fromEntries(Object.entries(permission).filter(([rule]) => rule !== FX_PUSHARY_TOOLS_RULE));
203
- const withoutPermission = Object.fromEntries(Object.entries(settings).filter(([key]) => key !== PERMISSION_KEY));
204
- return {
205
- settings: Object.keys(remaining).length === 0 ? withoutPermission : { ...settings, [PERMISSION_KEY]: remaining },
206
- removed: true
207
- };
210
+ if (!permission || permission[FX_PUSHARY_TOOLS_RULE] !== ALLOW_ACTION) return { settings, removed: false };
211
+ return { settings: withoutPermissionRule(settings, permission, FX_PUSHARY_TOOLS_RULE), removed: true };
208
212
  };
209
213
  var pusharyToolsAllowed = (settings) => asRecord(settings[PERMISSION_KEY])?.[FX_PUSHARY_TOOLS_RULE] === ALLOW_ACTION;
210
214
  var overridesPusharyRule = (workspace) => {
@@ -212,6 +216,135 @@ var overridesPusharyRule = (workspace) => {
212
216
  return permission !== void 0 && !(FX_PUSHARY_TOOLS_RULE in permission);
213
217
  };
214
218
  var workspacesOverridingPusharyRule = (settings) => Object.entries(asRecord(settings[WORKSPACES_KEY]) ?? {}).filter(([, workspace]) => overridesPusharyRule(workspace)).map(([path]) => path);
219
+ var FX_QUESTION_TOOL = "ask_user_question";
220
+ var DENY_ACTION = "deny";
221
+ var GLOBAL_TARGET_PATTERN = "*";
222
+ var PERMISSION_MODE_KEY = "permission_mode";
223
+ var FX_PERMISSION_MODE_ENV = "FX_PERMISSION_MODE";
224
+ var FX_DEFAULT_PERMISSION_MODE = "auto";
225
+ var FX_PERMISSION_MODE_SPELLINGS = {
226
+ ask: "ask",
227
+ auto: "auto",
228
+ "full-access": "full-access",
229
+ "full access": "full-access",
230
+ yolo: "full-access"
231
+ };
232
+ var FX_RULE_WHITESPACE = /^[ \t\r\n]+|[ \t\r\n]+$/g;
233
+ var FX_QUESTION_RULE_RECEIPT = { rule: FX_QUESTION_TOOL, action: DENY_ACTION };
234
+ var isFxQuestionRuleReceipt = (value) => {
235
+ const receipt = asRecord(value);
236
+ return receipt?.rule === FX_QUESTION_TOOL && receipt.action === DENY_ACTION;
237
+ };
238
+ var isFxRuleAction = (value) => value === "allow" || value === "ask" || value === DENY_ACTION;
239
+ var fxRuleAction = (value) => {
240
+ if (typeof value !== "string") return null;
241
+ const action = value.toLowerCase();
242
+ return isFxRuleAction(action) ? action : null;
243
+ };
244
+ var parseFxPermissionMode = (value) => typeof value === "string" ? FX_PERMISSION_MODE_SPELLINGS[value.toLowerCase()] ?? null : null;
245
+ var trimFxRuleText = (text) => text.replace(FX_RULE_WHITESPACE, "");
246
+ var isValidPatternMap = (value) => {
247
+ const patterns = asRecord(value);
248
+ return patterns !== void 0 && Object.values(patterns).every((action) => fxRuleAction(action) !== null);
249
+ };
250
+ var isValidPermissionValue = (value) => {
251
+ if (fxRuleAction(value) !== null) return true;
252
+ const permission = asRecord(value);
253
+ return permission !== void 0 && Object.entries(permission).every(([name, rule]) => trimFxRuleText(name).length > 0 && (fxRuleAction(rule) !== null || isValidPatternMap(rule)));
254
+ };
255
+ var permissionScopeRejection = (scope) => {
256
+ if (scope[PERMISSION_KEY] !== void 0 && !isValidPermissionValue(scope[PERMISSION_KEY])) return "invalid-permission-rule";
257
+ if (scope[PERMISSION_MODE_KEY] !== void 0 && parseFxPermissionMode(scope[PERMISSION_MODE_KEY]) === null) return "invalid-permission-mode";
258
+ return null;
259
+ };
260
+ var workspaceScopes = (settings) => Object.values(asRecord(settings[WORKSPACES_KEY]) ?? {}).flatMap((workspace) => {
261
+ const scope = asRecord(workspace);
262
+ return scope ? [scope] : [];
263
+ });
264
+ var fxSettingsRejection = (settings) => [settings, ...workspaceScopes(settings)].map(permissionScopeRejection).find((rejection) => rejection !== null) ?? null;
265
+ var rulesOfPermissionEntry = ([name, value]) => {
266
+ const permission = trimFxRuleText(name);
267
+ const action = fxRuleAction(value);
268
+ if (action) return [{ permission, pattern: GLOBAL_TARGET_PATTERN, action }];
269
+ return Object.entries(asRecord(value) ?? {}).flatMap(([pattern, target]) => {
270
+ const targetAction = fxRuleAction(target);
271
+ return targetAction ? [{ permission, pattern: trimFxRuleText(pattern), action: targetAction }] : [];
272
+ });
273
+ };
274
+ var fxPermissionRules = (permission) => {
275
+ const everything = fxRuleAction(permission);
276
+ if (everything) return [{ permission: GLOBAL_TARGET_PATTERN, pattern: GLOBAL_TARGET_PATTERN, action: everything }];
277
+ return Object.entries(asRecord(permission) ?? {}).flatMap(rulesOfPermissionEntry);
278
+ };
279
+ var fxWildcardMatch = (pattern, candidate) => {
280
+ let patternIndex = 0;
281
+ let candidateIndex = 0;
282
+ let starIndex = null;
283
+ let starCandidateIndex = 0;
284
+ while (candidateIndex < candidate.length) {
285
+ if (patternIndex < pattern.length && (pattern[patternIndex] === "?" || pattern[patternIndex] === candidate[candidateIndex])) {
286
+ patternIndex += 1;
287
+ candidateIndex += 1;
288
+ } else if (patternIndex < pattern.length && pattern[patternIndex] === "*") {
289
+ starIndex = patternIndex;
290
+ patternIndex += 1;
291
+ starCandidateIndex = candidateIndex;
292
+ } else if (starIndex === null) {
293
+ return false;
294
+ } else {
295
+ starCandidateIndex += 1;
296
+ candidateIndex = starCandidateIndex;
297
+ patternIndex = starIndex + 1;
298
+ }
299
+ }
300
+ while (pattern[patternIndex] === "*") patternIndex += 1;
301
+ return patternIndex === pattern.length;
302
+ };
303
+ var DIRECTORY_TREE_SUFFIX = "/**";
304
+ var directoryTreeMatches = (pattern, candidate) => {
305
+ if (!pattern.endsWith(DIRECTORY_TREE_SUFFIX)) return false;
306
+ const directory = pattern.slice(0, -DIRECTORY_TREE_SUFFIX.length);
307
+ return candidate === directory || candidate.startsWith(directory) && candidate[directory.length] === "/";
308
+ };
309
+ var targetMatches = (pattern, candidate) => directoryTreeMatches(pattern, candidate) || fxWildcardMatch(pattern, candidate);
310
+ var isGlobalTargetPattern = (pattern) => /^\*+$/.test(pattern);
311
+ var decisionForTarget = (rules, target) => rules.filter((rule) => targetMatches(rule.pattern, target)).at(-1)?.action ?? null;
312
+ var reopensTool = (rules, rule) => rule.action !== DENY_ACTION && decisionForTarget(rules, rule.pattern) !== DENY_ACTION && decisionForTarget(rules, rule.pattern) !== null;
313
+ var fxRulesHideTool = (rules, toolName) => {
314
+ const matching = rules.filter((rule) => fxWildcardMatch(rule.permission, toolName));
315
+ const lastGlobalRule = matching.filter((rule) => isGlobalTargetPattern(rule.pattern)).at(-1);
316
+ if (lastGlobalRule?.action !== DENY_ACTION) return false;
317
+ return !matching.slice(matching.lastIndexOf(lastGlobalRule) + 1).some((rule) => reopensTool(matching, rule));
318
+ };
319
+ var effectivePermissionMode = (scope, environment) => parseFxPermissionMode(environment[FX_PERMISSION_MODE_ENV]) ?? parseFxPermissionMode(scope[PERMISSION_MODE_KEY]) ?? FX_DEFAULT_PERMISSION_MODE;
320
+ var fxRunsWithoutRules = (settings, environment) => effectivePermissionMode(settings, environment) === "full-access";
321
+ var fxQuestionToolHidden = (permission) => fxRulesHideTool(fxPermissionRules(permission), FX_QUESTION_TOOL);
322
+ var fxQuestionRouting = (settings, environment) => {
323
+ if (fxRunsWithoutRules(settings, environment)) return "full-access";
324
+ return fxQuestionToolHidden(settings[PERMISSION_KEY]) ? "routed" : "overridden";
325
+ };
326
+ var withQuestionsRoutedToPushary = (settings) => {
327
+ const permission = asRecord(settings[PERMISSION_KEY]) ?? {};
328
+ const existing = permission[FX_QUESTION_TOOL];
329
+ if (existing === void 0) {
330
+ return { settings: { ...settings, [PERMISSION_KEY]: { ...permission, [FX_QUESTION_TOOL]: DENY_ACTION } }, change: "added" };
331
+ }
332
+ return { settings, change: fxRuleAction(existing) === DENY_ACTION ? "present" : "user-rule-kept" };
333
+ };
334
+ var withoutQuestionsRoutedToPushary = (settings, receipt) => {
335
+ const permission = asRecord(settings[PERMISSION_KEY]);
336
+ if (!permission || permission[FX_QUESTION_TOOL] === void 0) return { settings, outcome: "absent" };
337
+ if (!receipt) return { settings, outcome: "not-ours" };
338
+ if (fxRuleAction(permission[FX_QUESTION_TOOL]) !== DENY_ACTION) return { settings, outcome: "changed-after-setup" };
339
+ return { settings: withoutPermissionRule(settings, permission, FX_QUESTION_TOOL), outcome: "removed" };
340
+ };
341
+ var workspaceShowsQuestionTool = (workspace) => {
342
+ const scope = asRecord(workspace);
343
+ if (!scope) return false;
344
+ if (parseFxPermissionMode(scope[PERMISSION_MODE_KEY]) === "full-access") return true;
345
+ return scope[PERMISSION_KEY] !== void 0 && !fxQuestionToolHidden(scope[PERMISSION_KEY]);
346
+ };
347
+ var workspacesShowingQuestionTool = (settings) => Object.entries(asRecord(settings[WORKSPACES_KEY]) ?? {}).filter(([, workspace]) => workspaceShowsQuestionTool(workspace)).map(([path]) => path);
215
348
 
216
349
  // src/fx-files.ts
217
350
  import { existsSync, lstatSync, readFileSync, realpathSync } from "fs";
@@ -596,6 +729,13 @@ export {
596
729
  withoutPusharyToolsAllowed,
597
730
  pusharyToolsAllowed,
598
731
  workspacesOverridingPusharyRule,
732
+ FX_QUESTION_TOOL,
733
+ FX_QUESTION_RULE_RECEIPT,
734
+ isFxQuestionRuleReceipt,
735
+ fxQuestionRouting,
736
+ withQuestionsRoutedToPushary,
737
+ withoutQuestionsRoutedToPushary,
738
+ workspacesShowingQuestionTool,
599
739
  readFxFile,
600
740
  fxIgnoresInstructionsFile,
601
741
  registerPluginLocation,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
3
  OPENCODE_APPROVAL_EVENT
4
- } from "./chunk-YROGUPOZ.js";
4
+ } from "./chunk-XFNVG3OV.js";
5
5
 
6
6
  // src/opencode-plugin.ts
7
7
  var OPENCODE_PLUGIN_MARKER = "pushary-opencode-plugin w1";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  installNetworkDefaults
3
- } from "./chunk-KW2RRR7D.js";
3
+ } from "./chunk-JTQDP4ZL.js";
4
4
  import {
5
5
  getBaseUrl
6
- } from "./chunk-V4AQBAVG.js";
6
+ } from "./chunk-OAL44FM2.js";
7
7
 
8
8
  // src/retry.ts
9
9
  var isRetryable = (err) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  redactSecrets
3
- } from "./chunk-YROGUPOZ.js";
3
+ } from "./chunk-XFNVG3OV.js";
4
4
 
5
5
  // src/crypto.ts
6
6
  import nacl from "tweetnacl";
@@ -1,3 +1,28 @@
1
+ // src/cli/terminal-row.ts
2
+ var visibleLength = (text) => text.replace(/\x1b\[[0-9;]*m/g, "").length;
3
+ var clipToRow = (text, columns) => {
4
+ const budget = columns - 4;
5
+ if (budget <= 1 || visibleLength(text) <= budget) return text;
6
+ let visible = 0;
7
+ let out = "";
8
+ for (let i = 0; i < text.length; i += 1) {
9
+ const escape = /^\x1b\[[0-9;]*m/.exec(text.slice(i));
10
+ if (escape) {
11
+ out += escape[0];
12
+ i += escape[0].length - 1;
13
+ continue;
14
+ }
15
+ if (visible >= budget - 1) break;
16
+ out += text[i];
17
+ visible += 1;
18
+ }
19
+ return `${out}\u2026`;
20
+ };
21
+ var terminalColumns = (stream = process.stdout) => {
22
+ const columns = stream.columns;
23
+ return typeof columns === "number" && columns > 0 ? columns : 80;
24
+ };
25
+
1
26
  // src/cli/io.ts
2
27
  var truthyLevel = (value) => {
3
28
  if (value === void 0) return void 0;
@@ -15,6 +40,7 @@ var useColor = (context = {}, stream = process.stdout) => {
15
40
  return stream.isTTY === true;
16
41
  };
17
42
  var SPINNER_FRAMES = [" ", ". ", ".. ", "..."];
43
+ var SPINNER_FRAME_WIDTH = SPINNER_FRAMES[0].length + 1;
18
44
  var SPINNER_INTERVAL_MS = 200;
19
45
  var describeError = (err) => {
20
46
  if (err instanceof Error) {
@@ -45,7 +71,7 @@ var createIo = (stream = process.stdout, context = {}) => {
45
71
  `);
46
72
  };
47
73
  const spinner = async (label, fn, options = {}) => {
48
- if (!color) {
74
+ if (stream.isTTY !== true) {
49
75
  try {
50
76
  await fn();
51
77
  line(` ${check()} ${label}`);
@@ -57,7 +83,7 @@ var createIo = (stream = process.stdout, context = {}) => {
57
83
  }
58
84
  let frame = 0;
59
85
  const timer = setInterval(() => {
60
- stream.write(`\r ${dim(SPINNER_FRAMES[frame++ % SPINNER_FRAMES.length])} ${label}`);
86
+ stream.write(`\r ${dim(SPINNER_FRAMES[frame++ % SPINNER_FRAMES.length])} ${clipToRow(label, terminalColumns(stream) - SPINNER_FRAME_WIDTH)}\x1B[K`);
61
87
  }, SPINNER_INTERVAL_MS);
62
88
  try {
63
89
  await fn();
@@ -75,6 +101,8 @@ var createIo = (stream = process.stdout, context = {}) => {
75
101
  };
76
102
 
77
103
  export {
104
+ clipToRow,
105
+ terminalColumns,
78
106
  useColor,
79
107
  createIo
80
108
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-B3UXMSMW.js";
3
+ } from "./chunk-EIZW73P5.js";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getBaseUrl
3
- } from "./chunk-V4AQBAVG.js";
3
+ } from "./chunk-OAL44FM2.js";
4
4
 
5
5
  // src/transcript-api.ts
6
6
  var fetchTranscriptRecipients = async (apiKey, baseUrl = getBaseUrl()) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-DNJUSFBL.js";
3
+ } from "./chunk-454KMJHF.js";
4
4
  import {
5
5
  writeFileAtomic
6
6
  } from "./chunk-T7E75EKA.js";
@@ -2,10 +2,10 @@ import {
2
2
  resolveBinary,
3
3
  resolveGlobalPackageVersion,
4
4
  resolvePackageVersionFromBinary
5
- } from "./chunk-DNJUSFBL.js";
5
+ } from "./chunk-454KMJHF.js";
6
6
  import {
7
7
  MANAGED_PROVIDERS
8
- } from "./chunk-YROGUPOZ.js";
8
+ } from "./chunk-XFNVG3OV.js";
9
9
 
10
10
  // src/live-capabilities.ts
11
11
  var CLAUDE_BRIDGE_CAPABILITY = "claude-bridge";
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-O2LEPIU6.js";
3
+ } from "./chunk-Y6GKOZBT.js";
4
4
  import {
5
5
  ensureCodexTrustsPusharyHooks
6
- } from "./chunk-7OS4IHGI.js";
6
+ } from "./chunk-6SJQ442W.js";
7
7
  import {
8
8
  addClaudeMcpServer,
9
9
  addPusharyHooks,
10
10
  addPusharyToolPermissions
11
- } from "./chunk-KYQXUGH7.js";
11
+ } from "./chunk-WSC53O7I.js";
12
12
  import {
13
13
  claudeUnprovenHolds,
14
14
  claudeWired,
15
15
  codexUnprovenHolds,
16
16
  codexWired,
17
17
  geminiWired
18
- } from "./chunk-PQ4F64M2.js";
18
+ } from "./chunk-XDASEYRS.js";
19
19
  import {
20
20
  CLAUDE_HOOK_BINARY,
21
21
  CLAUDE_HOOK_EVENTS,
@@ -27,7 +27,7 @@ import {
27
27
  needsReregistration,
28
28
  registrationSignatures,
29
29
  supportedClaudeEvents
30
- } from "./chunk-ZQ7IDOKY.js";
30
+ } from "./chunk-WZWXBTRF.js";
31
31
  import {
32
32
  CODEX_HOOK_BINARY,
33
33
  addCodexHookTrust,
@@ -47,7 +47,7 @@ import {
47
47
  registrationHolderOf,
48
48
  resolveGlobalBinary,
49
49
  vscodePluginMcp
50
- } from "./chunk-DNJUSFBL.js";
50
+ } from "./chunk-454KMJHF.js";
51
51
  import {
52
52
  readJsonSafe,
53
53
  writeFileAtomic,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-DNJUSFBL.js";
3
+ } from "./chunk-454KMJHF.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-T7E75EKA.js";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  AGENT_CHOICES
3
- } from "./chunk-CVALVGA7.js";
3
+ } from "./chunk-XMN37YJH.js";
4
4
  import {
5
5
  isBridgeLocator
6
- } from "./chunk-5HLTWFJ6.js";
6
+ } from "./chunk-MWVCDP64.js";
7
7
  import {
8
8
  pusharyHookExecutable
9
- } from "./chunk-PQ4F64M2.js";
9
+ } from "./chunk-XDASEYRS.js";
10
10
  import {
11
11
  BELL_HOOK_BINARY
12
- } from "./chunk-VKVIAZQO.js";
12
+ } from "./chunk-XPMOCJHN.js";
13
13
 
14
14
  // src/setup/hook-ownership.ts
15
15
  var commandsIn = (value) => {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  configFilePath
3
- } from "./chunk-V4AQBAVG.js";
3
+ } from "./chunk-OAL44FM2.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-YROGUPOZ.js";
6
+ } from "./chunk-XFNVG3OV.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -3,11 +3,11 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-BH2SKSSK.js";
6
+ } from "./chunk-O5JD7UI5.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
10
- } from "./chunk-KXMZMZD3.js";
10
+ } from "./chunk-OTZHT34O.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -1,30 +1,30 @@
1
1
  import {
2
2
  NOTIFICATION_PUSH_TYPES,
3
3
  POSTTOOLUSE_MIRROR_ONLY_TOOLS
4
- } from "./chunk-DZURML2D.js";
4
+ } from "./chunk-4C4NRDIC.js";
5
5
  import {
6
6
  cancelQuestion,
7
7
  isPolicyConfig,
8
8
  sendNotification,
9
9
  waitForAnswer
10
- } from "./chunk-EGKXSQCY.js";
10
+ } from "./chunk-ZIIYTQHC.js";
11
11
  import {
12
12
  clientErrorKindOf,
13
13
  reportClientError
14
- } from "./chunk-V6QRSU2G.js";
14
+ } from "./chunk-KSNAAMU5.js";
15
15
  import {
16
16
  withRetry
17
- } from "./chunk-JNXVOU6Y.js";
17
+ } from "./chunk-NQFGVAT6.js";
18
18
  import {
19
19
  getMachineId
20
- } from "./chunk-XNCMT7YY.js";
20
+ } from "./chunk-P5CBNSYL.js";
21
21
  import {
22
22
  reportedInstallSource
23
- } from "./chunk-ROQK7IN3.js";
23
+ } from "./chunk-UPKP67TA.js";
24
24
  import {
25
25
  getApiKey,
26
26
  getBaseUrl
27
- } from "./chunk-V4AQBAVG.js";
27
+ } from "./chunk-OAL44FM2.js";
28
28
  import {
29
29
  ACTION_BODY_MAX,
30
30
  AGENT_LABELS_BY_TYPE,
@@ -48,7 +48,7 @@ import {
48
48
  resolveAutoResolveOrigin,
49
49
  resolvePolicy,
50
50
  toToolLookup
51
- } from "./chunk-YROGUPOZ.js";
51
+ } from "./chunk-XFNVG3OV.js";
52
52
 
53
53
  // src/policy.ts
54
54
  import { createHash } from "crypto";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-ZQ7IDOKY.js";
4
+ } from "./chunk-WZWXBTRF.js";
5
5
  import {
6
6
  guardBinary,
7
7
  holdBudgetOf,
8
8
  isProtectedByScope,
9
9
  registerHookEntry
10
- } from "./chunk-DNJUSFBL.js";
10
+ } from "./chunk-454KMJHF.js";
11
11
  import {
12
12
  HOOK_LOCATOR_BINARY,
13
13
  withHookHoldBudget
14
- } from "./chunk-YROGUPOZ.js";
14
+ } from "./chunk-XFNVG3OV.js";
15
15
 
16
16
  // src/claude-config.ts
17
17
  import { join } from "path";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  NOTIFICATION_MATCHED_TYPES,
3
3
  POSTTOOLUSE_MIRRORED_TOOLS
4
- } from "./chunk-DZURML2D.js";
4
+ } from "./chunk-4C4NRDIC.js";
5
5
  import {
6
6
  isProtectedByScope,
7
7
  registerHookEntry
8
- } from "./chunk-DNJUSFBL.js";
8
+ } from "./chunk-454KMJHF.js";
9
9
  import {
10
10
  CLAUDE_PRETOOLUSE_MATCHER,
11
11
  HOOK_BUDGETS,
12
12
  HOOK_LOCATOR_BINARY
13
- } from "./chunk-YROGUPOZ.js";
13
+ } from "./chunk-XFNVG3OV.js";
14
14
 
15
15
  // src/gemini-config.ts
16
16
  var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
@@ -2,17 +2,17 @@ import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  GEMINI_HOOK_EVENTS,
4
4
  hasGeminiHooks
5
- } from "./chunk-ZQ7IDOKY.js";
5
+ } from "./chunk-WZWXBTRF.js";
6
6
  import {
7
7
  CODEX_HOOK_EVENTS,
8
8
  hasCodexHooks,
9
9
  holdBudgetOf
10
- } from "./chunk-DNJUSFBL.js";
10
+ } from "./chunk-454KMJHF.js";
11
11
  import {
12
12
  CURSOR_GATE_EVENTS,
13
13
  HOOK_LOCATOR_BINARY,
14
14
  hookCommandHoldBudget
15
- } from "./chunk-YROGUPOZ.js";
15
+ } from "./chunk-XFNVG3OV.js";
16
16
 
17
17
  // src/diagnostics/wiring.ts
18
18
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -156,7 +156,7 @@ var AGENTS = {
156
156
  label: "fx",
157
157
  connectVia: "Local MCP bridge",
158
158
  setupCommand: AGENT_HOOKS_SETUP_COMMAND,
159
- capabilityNote: "fx has no hooks that other tools can register, so Pushary cannot answer fx's own approval prompts. fx's agent reaches Pushary through its MCP tools when it chooses to ask."
159
+ 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."
160
160
  },
161
161
  qwen_code: {
162
162
  ...defaults,
@@ -9,7 +9,7 @@ import {
9
9
  SETUP_CONNECT_MODES,
10
10
  isSetupConnectMode,
11
11
  isValidApiKey
12
- } from "./chunk-YROGUPOZ.js";
12
+ } from "./chunk-XFNVG3OV.js";
13
13
 
14
14
  // src/setup/options.ts
15
15
  var AGENT_CHOICES = SETUP_AGENT_CHOICES;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-DNJUSFBL.js";
3
+ } from "./chunk-454KMJHF.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-UOAQ6QMP.js";
3
+ } from "./chunk-YZKS3ME3.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var transcriptEnvironmentPreference = () => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-ZQ7IDOKY.js";
3
+ } from "./chunk-WZWXBTRF.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";
@@ -2,27 +2,27 @@ import {
2
2
  detectProviderLiveCapabilities,
3
3
  hasSpawnProvider,
4
4
  resolveControlPolicy
5
- } from "./chunk-JXUAXO74.js";
5
+ } from "./chunk-Q7QRR6YL.js";
6
6
  import {
7
7
  daemonHasRemoteProof,
8
8
  daemonServiceManagerAvailable,
9
9
  hasDaemonServiceDefinition,
10
10
  readDaemonNeedsLogin,
11
11
  readDaemonStatus
12
- } from "./chunk-73T6MSU5.js";
12
+ } from "./chunk-AAGLDXH4.js";
13
13
  import {
14
14
  readControlMode,
15
15
  readKeySource
16
- } from "./chunk-UOAQ6QMP.js";
16
+ } from "./chunk-YZKS3ME3.js";
17
17
  import {
18
18
  getMachineId
19
- } from "./chunk-XNCMT7YY.js";
19
+ } from "./chunk-P5CBNSYL.js";
20
20
  import {
21
21
  getBaseUrl
22
- } from "./chunk-V4AQBAVG.js";
22
+ } from "./chunk-OAL44FM2.js";
23
23
  import {
24
24
  getPackageVersion
25
- } from "./chunk-YROGUPOZ.js";
25
+ } from "./chunk-XFNVG3OV.js";
26
26
 
27
27
  // src/machines.ts
28
28
  import { hostname } from "os";