@pushary/agent-hooks 1.7.0 → 1.7.2

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 +28 -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 +46 -35
  5. package/dist/bin/pushary-clean.js +194 -102
  6. package/dist/bin/pushary-codex-bridge.js +18 -17
  7. package/dist/bin/pushary-codex-hook.js +53 -39
  8. package/dist/bin/pushary-codex.js +14 -12
  9. package/dist/bin/pushary-connect.js +16 -16
  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 +19 -17
  13. package/dist/bin/pushary-doctor.js +99 -43
  14. package/dist/bin/pushary-elicitation-hook.js +35 -26
  15. package/dist/bin/pushary-gemini-bridge.js +12 -12
  16. package/dist/bin/pushary-gemini-hook.js +47 -32
  17. package/dist/bin/pushary-hook.js +34 -32
  18. package/dist/bin/pushary-login.js +16 -16
  19. package/dist/bin/pushary-logout.js +8 -8
  20. package/dist/bin/pushary-mcp.js +9 -8
  21. package/dist/bin/pushary-mode.js +10 -10
  22. package/dist/bin/pushary-notification-hook.js +29 -27
  23. package/dist/bin/pushary-opencode-hook.js +46 -32
  24. package/dist/bin/pushary-permission-denied-hook.js +34 -32
  25. package/dist/bin/pushary-permission-hook.js +33 -31
  26. package/dist/bin/pushary-post-hook.js +29 -27
  27. package/dist/bin/pushary-prompt-hook.js +29 -27
  28. package/dist/bin/pushary-session-end-hook.js +29 -27
  29. package/dist/bin/pushary-session-start-hook.js +33 -31
  30. package/dist/bin/pushary-setup.js +143 -105
  31. package/dist/bin/pushary-stats.js +5 -5
  32. package/dist/bin/pushary-status.js +19 -19
  33. package/dist/bin/pushary-stop-hook.js +29 -27
  34. package/dist/bin/pushary-stopfailure-hook.js +29 -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-IJ6ECPA6.js → chunk-2B4XCMH6.js} +75 -232
  42. package/dist/{chunk-EGKXSQCY.js → chunk-2XOHXZRC.js} +12 -4
  43. package/dist/{chunk-D6BULEJW.js → chunk-3NSRL5E7.js} +2 -2
  44. package/dist/{chunk-UC65FJJT.js → chunk-3RO2UWJQ.js} +2 -2
  45. package/dist/{chunk-73T6MSU5.js → chunk-4S7CCOXB.js} +5 -5
  46. package/dist/{chunk-7OS4IHGI.js → chunk-5GIBNUBD.js} +2 -2
  47. package/dist/chunk-5OP5MQI7.js +45 -0
  48. package/dist/{chunk-B3UXMSMW.js → chunk-65RN6ZCO.js} +1 -1
  49. package/dist/{chunk-2PS6QWDW.js → chunk-6NHXKTQU.js} +2 -2
  50. package/dist/{chunk-V6QRSU2G.js → chunk-6QZK4O36.js} +4 -4
  51. package/dist/{chunk-ZQ7IDOKY.js → chunk-A6WQF7FQ.js} +3 -3
  52. package/dist/{chunk-QXAK5RFU.js → chunk-AWZNDM3B.js} +1 -1
  53. package/dist/{chunk-JNXVOU6Y.js → chunk-D4OGEWLH.js} +19 -2
  54. package/dist/{chunk-R2XCLT6I.js → chunk-E3PLF7YB.js} +4 -4
  55. package/dist/{chunk-2C7CCWVX.js → chunk-F6BMJOSW.js} +1 -1
  56. package/dist/{chunk-XNCMT7YY.js → chunk-HOIGXMI7.js} +1 -1
  57. package/dist/{chunk-CVALVGA7.js → chunk-I7B2DNVU.js} +1 -1
  58. package/dist/{chunk-S3UD4V6W.js → chunk-INR3RVQ3.js} +13 -3
  59. package/dist/{chunk-52NVBNJT.js → chunk-IZVAVXHQ.js} +6 -4
  60. package/dist/{chunk-5HLTWFJ6.js → chunk-JMSB6CJT.js} +1 -1
  61. package/dist/{chunk-KYQXUGH7.js → chunk-KAJT6HPI.js} +3 -3
  62. package/dist/{chunk-VKVIAZQO.js → chunk-KLXYMPTM.js} +1 -1
  63. package/dist/{chunk-PQ4F64M2.js → chunk-L6IF4JOF.js} +3 -3
  64. package/dist/{chunk-ROQK7IN3.js → chunk-LI3C2IQY.js} +2 -2
  65. package/dist/{chunk-B234346F.js → chunk-M2JGLFFS.js} +1 -1
  66. package/dist/chunk-MY3UZO5U.js +99 -0
  67. package/dist/chunk-N6IOSZOE.js +185 -0
  68. package/dist/{chunk-DNJUSFBL.js → chunk-NYBZJ44G.js} +40 -5
  69. package/dist/{chunk-BBKNJJVX.js → chunk-OJRB5QKN.js} +6 -6
  70. package/dist/{chunk-PUDBG3GM.js → chunk-OXEF6JHD.js} +4 -4
  71. package/dist/{chunk-LEZFOTMI.js → chunk-PWNQGITG.js} +1 -1
  72. package/dist/{chunk-B76WVEDX.js → chunk-Q7CDBJXU.js} +1 -1
  73. package/dist/{chunk-KXMZMZD3.js → chunk-QSZ37ROP.js} +1 -1
  74. package/dist/{chunk-BH2SKSSK.js → chunk-R7ZETVNR.js} +1 -1
  75. package/dist/{chunk-DZURML2D.js → chunk-RLRDPZG4.js} +1 -1
  76. package/dist/{chunk-J2355HR7.js → chunk-SEIZQZEE.js} +1 -1
  77. package/dist/{chunk-V4AQBAVG.js → chunk-SJN64NJX.js} +1 -1
  78. package/dist/{chunk-B3YQANFN.js → chunk-SKEW3TQX.js} +1 -1
  79. package/dist/{chunk-L4H2Q2S4.js → chunk-SKMWPXGV.js} +500 -46
  80. package/dist/{chunk-WUNZAE7I.js → chunk-T3PRPFDY.js} +5 -5
  81. package/dist/{chunk-YYINL576.js → chunk-TVJYGZXB.js} +2 -2
  82. package/dist/{chunk-O2LEPIU6.js → chunk-U3MS6D5X.js} +1 -1
  83. package/dist/{chunk-5RUIFDTP.js → chunk-U4TMAANR.js} +33 -2
  84. package/dist/{chunk-KW2RRR7D.js → chunk-UFNBKYWT.js} +3 -3
  85. package/dist/{chunk-DVQDPI3T.js → chunk-UUTWHVSV.js} +35 -35
  86. package/dist/{chunk-X6KDKOV5.js → chunk-VQDJ6KB3.js} +3 -3
  87. package/dist/{chunk-UOAQ6QMP.js → chunk-VSGKQ3HI.js} +2 -2
  88. package/dist/{chunk-YROGUPOZ.js → chunk-WWTLAZQA.js} +25 -8
  89. package/dist/{chunk-SQRWHALT.js → chunk-WXIVUIXA.js} +2 -2
  90. package/dist/{chunk-HDRNWNVP.js → chunk-YLCSKVQQ.js} +9 -28
  91. package/dist/{chunk-JXUAXO74.js → chunk-YNJ2H2FD.js} +2 -2
  92. package/dist/{chunk-BWYJ6AAF.js → chunk-YU4222G2.js} +2 -2
  93. package/dist/{chunk-4F46VIEE.js → chunk-YU54LNC6.js} +6 -6
  94. package/dist/{chunk-EAQMWJPG.js → chunk-ZMWX5SDA.js} +8 -8
  95. package/dist/{chunk-6ADZQFVW.js → chunk-ZUKZJQGR.js} +1 -1
  96. package/dist/{reapply-6PH3FMPX.js → reapply-D6RSDYCK.js} +9 -9
  97. package/dist/src/index.d.ts +1 -0
  98. package/dist/src/index.js +22 -20
  99. package/package.json +2 -1
  100. package/dist/chunk-DANOZVZK.js +0 -307
  101. package/dist/chunk-P32LPT2L.js +0 -6
  102. package/dist/chunk-YHG74UFF.js +0 -12
@@ -2,59 +2,62 @@
2
2
  import {
3
3
  codexTrustOutcome,
4
4
  describeCodexTrustConfirmation
5
- } from "../chunk-B234346F.js";
5
+ } from "../chunk-M2JGLFFS.js";
6
6
  import {
7
7
  runBrowserLogin
8
- } from "../chunk-QXAK5RFU.js";
8
+ } from "../chunk-AWZNDM3B.js";
9
9
  import {
10
10
  coworkOutput,
11
11
  coworkSelection
12
- } from "../chunk-D6BULEJW.js";
12
+ } from "../chunk-3NSRL5E7.js";
13
13
  import "../chunk-PUZZELE6.js";
14
14
  import {
15
15
  detectClaudeVersion
16
- } from "../chunk-O2LEPIU6.js";
16
+ } from "../chunk-U3MS6D5X.js";
17
17
  import {
18
18
  AgentConfigError,
19
+ afterAgentFailureNote,
19
20
  agentFailureFromError,
20
21
  agentFailureWire,
21
22
  configFailureKind,
22
23
  describeVersionManagerPin,
23
24
  describedAgentFailure,
25
+ failedAgentsContext,
24
26
  retryAgentCommand,
25
27
  versionManagerOf
26
- } from "../chunk-S3UD4V6W.js";
28
+ } from "../chunk-INR3RVQ3.js";
27
29
  import {
28
30
  ensureCodexTrustsPusharyHooks
29
- } from "../chunk-7OS4IHGI.js";
31
+ } from "../chunk-5GIBNUBD.js";
30
32
  import {
31
33
  readManagedControlEvidence,
32
34
  waitForMachineReadiness
33
- } from "../chunk-4F46VIEE.js";
35
+ } from "../chunk-YU54LNC6.js";
34
36
  import {
35
- HERMES_CONFIG_SCRIPT,
36
- HERMES_PIP_PACKAGE,
37
37
  describeSetupSideEffects,
38
38
  installAgentSkill,
39
39
  installSkillViaSkillsCli,
40
- isHermesConfigReceipt,
41
- resolveHermesPython,
42
- runHermesPythonScript
43
- } from "../chunk-DANOZVZK.js";
40
+ registerCursorGate
41
+ } from "../chunk-MY3UZO5U.js";
44
42
  import {
45
43
  FX_PROFILE_REFUSALS,
46
44
  FX_PUSHARY_TOOLS_RULE,
45
+ FX_QUESTION_RULE_RECEIPT,
46
+ FX_QUESTION_TOOL,
47
47
  FX_SETTINGS_REFUSALS,
48
48
  FX_TOOL_DISCOVERY_NOTE,
49
+ HERMES_PIP_PACKAGE,
49
50
  describeDetection,
50
51
  detectAllAgents,
51
52
  fxIgnoresInstructionsFile,
52
53
  fxProfileText,
53
54
  fxPusharyEntry,
54
55
  fxPusharyEntryOf,
56
+ fxQuestionRouting,
55
57
  fxSettingsText,
56
58
  isDetected,
57
59
  isFxPusharyEntry,
60
+ isHermesConfigReceipt,
58
61
  pluginLocationSnippet,
59
62
  readFxFile,
60
63
  readFxProfile,
@@ -62,16 +65,20 @@ import {
62
65
  registerPluginLocation,
63
66
  renderAgentInstructions,
64
67
  renderProjectAgentInstructions,
68
+ resolveHermesPython,
69
+ runHermesConfigScript,
65
70
  shortenHome,
66
71
  vscodeSettingsTargets,
67
72
  withFxPusharyServer,
68
73
  withPusharyToolsAllowed,
74
+ withQuestionsRoutedToPushary,
69
75
  workspacesOverridingPusharyRule,
76
+ workspacesShowingQuestionTool,
70
77
  writeInstructionBlock
71
- } from "../chunk-L4H2Q2S4.js";
78
+ } from "../chunk-SKMWPXGV.js";
72
79
  import {
73
80
  MCP_BRIDGE_BINARY
74
- } from "../chunk-YYINL576.js";
81
+ } from "../chunk-TVJYGZXB.js";
75
82
  import {
76
83
  OPENCODE_HOOK_BINARY,
77
84
  addOpenCodeMcpServer,
@@ -82,7 +89,7 @@ import {
82
89
  openCodePluginDir,
83
90
  openCodePluginFile,
84
91
  openCodeWired
85
- } from "../chunk-B3YQANFN.js";
92
+ } from "../chunk-SKEW3TQX.js";
86
93
  import {
87
94
  addCompatibleHooks,
88
95
  addCompatibleMcpServer,
@@ -93,7 +100,7 @@ import {
93
100
  compatibleMcpFile,
94
101
  compatiblePermissionRuleFile,
95
102
  hasCompatibleHooks
96
- } from "../chunk-WUNZAE7I.js";
103
+ } from "../chunk-T3PRPFDY.js";
97
104
  import {
98
105
  addClaudeMcpServer,
99
106
  addPusharyHooks,
@@ -101,16 +108,16 @@ import {
101
108
  captureClaudeMcpServer,
102
109
  isClaudeMcpSnapshot,
103
110
  updateClaudeMcpSnapshot
104
- } from "../chunk-KYQXUGH7.js";
111
+ } from "../chunk-KAJT6HPI.js";
105
112
  import {
106
113
  detectProviderLiveCapabilities,
107
114
  hasSpawnProvider,
108
115
  resolveControlPolicy
109
- } from "../chunk-JXUAXO74.js";
116
+ } from "../chunk-YNJ2H2FD.js";
110
117
  import {
111
118
  describeAutoUpdateAnnouncement,
112
119
  takeAutoUpdateAnnouncement
113
- } from "../chunk-BWYJ6AAF.js";
120
+ } from "../chunk-YU4222G2.js";
114
121
  import {
115
122
  KEEP_HINT,
116
123
  captureHookTakeover,
@@ -120,23 +127,23 @@ import {
120
127
  hookOwnerOf,
121
128
  mcpBearerKey,
122
129
  planHooks
123
- } from "../chunk-R2XCLT6I.js";
130
+ } from "../chunk-E3PLF7YB.js";
124
131
  import {
125
132
  AGENT_CHOICES,
126
133
  effectiveControlMode,
127
134
  effectiveVerify,
128
135
  parseSetupOptions
129
- } from "../chunk-CVALVGA7.js";
136
+ } from "../chunk-I7B2DNVU.js";
130
137
  import {
131
138
  liveBridgeProbe,
132
139
  resolveBridgeStub
133
- } from "../chunk-5HLTWFJ6.js";
140
+ } from "../chunk-JMSB6CJT.js";
134
141
  import {
135
142
  claudeWired,
136
143
  codexWired,
137
144
  geminiWired,
138
145
  pusharyHookExecutable
139
- } from "../chunk-PQ4F64M2.js";
146
+ } from "../chunk-L6IF4JOF.js";
140
147
  import {
141
148
  clearDaemonNeedsLogin,
142
149
  daemonHasRemoteProof,
@@ -149,25 +156,26 @@ import {
149
156
  replaceDaemonService,
150
157
  stopDaemonService,
151
158
  waitForDaemonRemoteProof
152
- } from "../chunk-73T6MSU5.js";
159
+ } from "../chunk-4S7CCOXB.js";
153
160
  import {
154
161
  GEMINI_HOOK_BINARY,
155
162
  addGeminiHooks,
156
163
  addGeminiMcpServer,
157
164
  compareVersion,
158
165
  parseVersion
159
- } from "../chunk-ZQ7IDOKY.js";
160
- import "../chunk-VKVIAZQO.js";
166
+ } from "../chunk-A6WQF7FQ.js";
167
+ import "../chunk-KLXYMPTM.js";
161
168
  import {
162
169
  transcriptPreferenceForSetup
163
- } from "../chunk-B76WVEDX.js";
170
+ } from "../chunk-Q7CDBJXU.js";
164
171
  import "../chunk-XHKBHWLX.js";
165
172
  import {
166
173
  reportEvent
167
- } from "../chunk-IJ6ECPA6.js";
168
- import "../chunk-DZURML2D.js";
169
- import "../chunk-EGKXSQCY.js";
170
- import "../chunk-V6QRSU2G.js";
174
+ } from "../chunk-2B4XCMH6.js";
175
+ import "../chunk-N6IOSZOE.js";
176
+ import "../chunk-RLRDPZG4.js";
177
+ import "../chunk-2XOHXZRC.js";
178
+ import "../chunk-6QZK4O36.js";
171
179
  import "../chunk-POFBE6ZY.js";
172
180
  import {
173
181
  BROWSER_CONNECT_COMMAND,
@@ -179,19 +187,16 @@ import {
179
187
  printConnectInstructions,
180
188
  roundTripEvidence,
181
189
  verifyRoundTrip
182
- } from "../chunk-HDRNWNVP.js";
190
+ } from "../chunk-YLCSKVQQ.js";
183
191
  import "../chunk-3EGEA4KH.js";
184
- import {
185
- checkApiKey,
186
- describeKeyCheck,
187
- samePusharyAccount
188
- } from "../chunk-X6KDKOV5.js";
189
192
  import {
190
193
  setHumanStream
191
194
  } from "../chunk-QCGNGEGE.js";
192
195
  import {
196
+ ansiPainter,
197
+ createIo,
193
198
  useColor
194
- } from "../chunk-5RUIFDTP.js";
199
+ } from "../chunk-U4TMAANR.js";
195
200
  import "../chunk-GMXKITVA.js";
196
201
  import {
197
202
  KEY_FILE_MODE,
@@ -203,18 +208,24 @@ import {
203
208
  readKeySource,
204
209
  resolveShellRc,
205
210
  writeKey
206
- } from "../chunk-UOAQ6QMP.js";
207
- import "../chunk-JNXVOU6Y.js";
208
- import "../chunk-KW2RRR7D.js";
209
- import "../chunk-XNCMT7YY.js";
211
+ } from "../chunk-VSGKQ3HI.js";
212
+ import "../chunk-D4OGEWLH.js";
213
+ import {
214
+ checkApiKey,
215
+ describeKeyCheck,
216
+ samePusharyAccount
217
+ } from "../chunk-VQDJ6KB3.js";
218
+ import "../chunk-UFNBKYWT.js";
219
+ import "../chunk-HOIGXMI7.js";
210
220
  import {
211
221
  UsageError
212
222
  } from "../chunk-7QLSKOSU.js";
213
223
  import {
214
224
  EXIT
215
225
  } from "../chunk-KZERVKTD.js";
216
- import "../chunk-ROQK7IN3.js";
226
+ import "../chunk-LI3C2IQY.js";
217
227
  import {
228
+ NpmMissingError,
218
229
  addCodexHookTrust,
219
230
  addCodexHooks,
220
231
  addCodexMcpServer,
@@ -238,6 +249,7 @@ import {
238
249
  fxAgentsMd,
239
250
  fxConfigDir,
240
251
  fxMcpProfile,
252
+ fxQuestionRuleReceipt,
241
253
  fxSettings,
242
254
  geminiMd,
243
255
  geminiSettings,
@@ -251,23 +263,22 @@ import {
251
263
  pusharyConfigFile,
252
264
  pusharyDir,
253
265
  readCodexMcpAuth,
254
- registerHookEntry,
255
266
  resolveGlobalBinDir,
256
267
  resolveGlobalBinary,
257
268
  vscodePluginDir,
258
269
  vscodePluginMcp
259
- } from "../chunk-DNJUSFBL.js";
270
+ } from "../chunk-NYBZJ44G.js";
260
271
  import {
261
272
  backupFile,
262
273
  readJsonSafe,
263
274
  writeFileAtomic,
264
275
  writeJsonAtomic
265
276
  } from "../chunk-T7E75EKA.js";
266
- import "../chunk-V4AQBAVG.js";
277
+ import "../chunk-SJN64NJX.js";
267
278
  import {
268
279
  NPX_LATEST_COMMAND,
269
280
  renderCommandHelp
270
- } from "../chunk-B3UXMSMW.js";
281
+ } from "../chunk-65RN6ZCO.js";
271
282
  import {
272
283
  HOOK_COMPATIBLE_PROFILES,
273
284
  HOOK_LOCATOR_BINARY,
@@ -278,7 +289,7 @@ import {
278
289
  hookCompatibleProfile,
279
290
  isNewerVersion,
280
291
  isValidApiKey
281
- } from "../chunk-YROGUPOZ.js";
292
+ } from "../chunk-WWTLAZQA.js";
282
293
 
283
294
  // bin/pushary-setup.ts
284
295
  import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, rmSync, renameSync, realpathSync, readdirSync, statSync, openSync, readSync, closeSync } from "fs";
@@ -292,10 +303,6 @@ import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
292
303
  // src/cli/exit.ts
293
304
  var isExitPromptError = (err) => err instanceof Error && (err.name === "ExitPromptError" || err.message.includes("User force closed") || /force closed the prompt/i.test(err.message));
294
305
 
295
- // src/cursor-config.ts
296
- var isPusharyCursorEntry = (entry) => pusharyHookExecutable(String(entry?.command ?? "")) !== null;
297
- var registerCursorGate = (existing, entry) => registerHookEntry(existing, isPusharyCursorEntry, entry);
298
-
299
306
  // src/setup/phone-check.ts
300
307
  var planPhoneCheck = (context) => {
301
308
  if (context.locked) return "locked";
@@ -447,10 +454,16 @@ var describeClosing = (facts) => {
447
454
  };
448
455
  };
449
456
  var RETRY_AGENT_LABEL = "Retry just this agent:";
457
+ var INSTALL_NODE_STEP = "Install the current Node.js LTS, which includes npm, then run setup again.";
458
+ var nextStepFor = (failure) => failure.remedy === "install-node" ? { kind: "install-node", instruction: INSTALL_NODE_STEP } : { kind: "retry-agent", command: retryAgentCommand(failure.agent) };
450
459
  var describeAgentFailures = (failures, agentName) => failures.map((failure) => ({
451
460
  summary: `${agentName(failure.agent)}: ${failure.detail}`,
452
- retryCommand: retryAgentCommand(failure.agent)
461
+ next: nextStepFor(failure)
453
462
  }));
463
+ var agentFailureFollowUps = (failures) => [
464
+ ...failures.some((failure) => failure.remedy === "install-node") ? ["install-node"] : [],
465
+ ...failures.some((failure) => failure.remedy === "retry-agent") ? ["run-doctor"] : []
466
+ ];
454
467
  var unverifiedKeyClosingReason = (facts) => {
455
468
  if (facts.failedCount > 0) return "agents-failed";
456
469
  if (facts.controlLifecycleFailed) return "control-unready";
@@ -679,7 +692,7 @@ var writeHuman = (text) => {
679
692
  humanStream.write(text);
680
693
  };
681
694
  var jsonMode = false;
682
- var paint = (open) => (s) => useColor({ json: jsonMode }) ? `${open}${s}\x1B[0m` : s;
695
+ var paint = ansiPainter(() => useColor({ json: jsonMode }));
683
696
  var dim = paint("\x1B[2m");
684
697
  var bold = paint("\x1B[1m");
685
698
  var green = paint("\x1B[32m");
@@ -776,24 +789,7 @@ var formatError = (err) => {
776
789
  }
777
790
  return String(err);
778
791
  };
779
- var spinner = async (label, fn, options = {}) => {
780
- const frames = [" ", ". ", ".. ", "..."];
781
- let i = 0;
782
- const interval = setInterval(() => {
783
- writeHuman(`\r ${dim(frames[i++ % frames.length])} ${label}`);
784
- }, 200);
785
- try {
786
- await fn();
787
- clearInterval(interval);
788
- writeHuman(`\r ${check} ${label}
789
- `);
790
- } catch (err) {
791
- clearInterval(interval);
792
- writeHuman(`\r ${yellow("!")} ${label} ${dim(`(${formatError(err)})`)}
793
- `);
794
- if (!options.optional) throw err;
795
- }
796
- };
792
+ var spinner = (label, fn, options = {}) => createIo(humanStream, { json: jsonMode }).spinner(label, fn, options);
797
793
  var checkForUpdates = async (current, markAnnounced) => {
798
794
  const announcement = takeAutoUpdateAnnouncement(autoUpdateState(), markAnnounced);
799
795
  if (announcement) {
@@ -819,7 +815,7 @@ var installGlobally2 = async () => {
819
815
  try {
820
816
  await installGlobally({ version });
821
817
  } catch (err) {
822
- throw new Error(npmErrorMessage(err));
818
+ throw err instanceof NpmMissingError ? err : new Error(npmErrorMessage(err));
823
819
  }
824
820
  });
825
821
  globalInstallReady = true;
@@ -908,7 +904,7 @@ var ensurePip = (python) => {
908
904
  }
909
905
  };
910
906
  var enablePusharyPlugin = (python) => {
911
- return runHermesPythonScript(python, HERMES_CONFIG_SCRIPT, "setup", HERMES_CONFIG_RECEIPT);
907
+ return runHermesConfigScript(python, "setup", HERMES_CONFIG_RECEIPT) !== "failed";
912
908
  };
913
909
  var setupHermes = async (_apiKey) => {
914
910
  console.log(`
@@ -1505,40 +1501,76 @@ var writeFxProfile = (filePath, profile) => {
1505
1501
  }
1506
1502
  writeFileAtomic(filePath, rendered.text, KEY_FILE_MODE);
1507
1503
  };
1508
- var FX_ALLOW_RULE_SNIPPET = `"permission": { "${FX_PUSHARY_TOOLS_RULE}": "allow" }`;
1504
+ var FX_SETTINGS_RULES_SNIPPET = `"permission": { "${FX_PUSHARY_TOOLS_RULE}": "allow", "${FX_QUESTION_TOOL}": "deny" }`;
1509
1505
  var fxSettingsReadRefusal = (filePath, read) => {
1510
1506
  if (read.kind === "unreadable") return `${filePath} could not be read`;
1511
1507
  if (read.kind === "refused") return `${filePath} ${FX_SETTINGS_REFUSALS[read.reason]}`;
1512
1508
  return null;
1513
1509
  };
1514
- var allowPusharyToolsInFx = (filePath) => {
1510
+ var recordFxQuestionRule = () => {
1511
+ try {
1512
+ if (!existsSync(fxQuestionRuleReceipt())) writeJsonAtomic(fxQuestionRuleReceipt(), FX_QUESTION_RULE_RECEIPT, 384);
1513
+ return true;
1514
+ } catch {
1515
+ return false;
1516
+ }
1517
+ };
1518
+ var configureFxSettings = (filePath) => {
1515
1519
  const read = readFxFile(filePath, readFxSettings);
1516
1520
  const refusal = fxSettingsReadRefusal(filePath, read);
1517
1521
  if (refusal) return { kind: "refused", detail: refusal };
1518
- const { settings, change } = withPusharyToolsAllowed(read.kind === "settings" ? read.settings : {});
1519
- if (change !== "added") return { kind: "written", change, overridingWorkspaces: workspacesOverridingPusharyRule(settings) };
1520
- const rendered = fxSettingsText(settings);
1522
+ const allowed = withPusharyToolsAllowed(read.kind === "settings" ? read.settings : {});
1523
+ const routed = withQuestionsRoutedToPushary(allowed.settings);
1524
+ const written = {
1525
+ kind: "written",
1526
+ toolsChange: allowed.change,
1527
+ questionChange: routed.change,
1528
+ questionRouting: fxQuestionRouting(routed.settings, process.env),
1529
+ workspacesWithOwnRules: [.../* @__PURE__ */ new Set([...workspacesOverridingPusharyRule(routed.settings), ...workspacesShowingQuestionTool(routed.settings)])],
1530
+ ruleRecorded: true
1531
+ };
1532
+ if (allowed.change !== "added" && routed.change !== "added") return written;
1533
+ const rendered = fxSettingsText(routed.settings);
1521
1534
  if (rendered.kind === "refused") return { kind: "refused", detail: `${filePath} would grow past the 64 KiB fx reads` };
1522
1535
  try {
1523
1536
  writeFileAtomic(filePath, rendered.text, KEY_FILE_MODE);
1524
1537
  } catch (err) {
1525
1538
  return { kind: "refused", detail: `${filePath} could not be written (${formatError(err)})` };
1526
1539
  }
1527
- return { kind: "written", change, overridingWorkspaces: workspacesOverridingPusharyRule(settings) };
1540
+ return { ...written, ruleRecorded: routed.change !== "added" || recordFxQuestionRule() };
1541
+ };
1542
+ var FX_QUESTION_ROUTING_REPORTS = {
1543
+ "full-access": "fx runs in full-access mode, which ignores permission rules, so its terminal question tool stays on. Its instructions tell it to ask through Pushary instead; leave full-access mode with /permissions to turn the tool off for certain.",
1544
+ overridden: `A rule in ${fxSettings()} keeps fx's terminal question tool on, so fx can still ask there instead of through Pushary.`
1528
1545
  };
1529
- var reportFxToolsRule = (outcome) => {
1546
+ var FX_QUESTION_SUMMARIES = {
1547
+ routed: "fx's agent asks through Pushary, which reaches your phone, or the Mac notch while the Pushary Mac app runs",
1548
+ "full-access": "fx's agent is told to ask through Pushary, but full-access mode keeps fx's terminal prompt available to it",
1549
+ overridden: "fx's terminal prompt stays available, so fx's agent is only told to ask through Pushary"
1550
+ };
1551
+ var reportFxSettings = (outcome) => {
1530
1552
  if (outcome.kind === "refused") {
1531
- console.log(` ${yellow("!")} ${outcome.detail}, so Pushary tools were not auto-allowed. fx will review or prompt for each Pushary call.`);
1532
- noteManual(`Add ${FX_ALLOW_RULE_SNIPPET} to ${fxSettings()} so fx runs Pushary tools without a prompt.`);
1553
+ console.log(` ${yellow("!")} ${outcome.detail}, so Pushary tools were not auto-allowed and fx's terminal question tool stays on.`);
1554
+ noteManual(`Add ${FX_SETTINGS_RULES_SNIPPET} to ${fxSettings()} so fx runs Pushary tools without a prompt and asks through Pushary.`);
1533
1555
  return;
1534
1556
  }
1535
- if (outcome.change === "user-rule-kept") {
1557
+ if (outcome.toolsChange === "user-rule-kept") {
1536
1558
  console.log(` ${yellow("!")} ${fxSettings()} already has a ${FX_PUSHARY_TOOLS_RULE} rule, so it was left as you set it.`);
1537
1559
  } else {
1538
- console.log(` ${dim("\u2022")} Auto-allowed tools: ${FX_PUSHARY_TOOLS_RULE} in ${fxSettings()}, so Pushary calls run without an fx prompt or review`);
1560
+ console.log(` ${check} Auto-allowing Pushary tools (${fxSettings()})`);
1561
+ }
1562
+ if (outcome.questionRouting === "routed") {
1563
+ console.log(` ${check} Turning off fx's terminal question tool, so its questions go through Pushary`);
1564
+ } else if (outcome.questionChange === "user-rule-kept") {
1565
+ console.log(` ${yellow("!")} ${fxSettings()} already has a ${FX_QUESTION_TOOL} rule, so it was left as you set it and fx can still ask in the terminal.`);
1566
+ } else {
1567
+ console.log(` ${yellow("!")} ${FX_QUESTION_ROUTING_REPORTS[outcome.questionRouting]}`);
1539
1568
  }
1540
- if (outcome.overridingWorkspaces.length > 0) {
1541
- console.log(` ${yellow("!")} ${outcome.overridingWorkspaces.length} fx workspace(s) set their own permission rules, which replace the global ones there. Add ${FX_PUSHARY_TOOLS_RULE} to each to auto-allow Pushary tools.`);
1569
+ if (!outcome.ruleRecorded) {
1570
+ console.log(` ${yellow("!")} ${fxQuestionRuleReceipt()} could not be written, so pushary clean will treat the ${FX_QUESTION_TOOL} rule as yours and leave it.`);
1571
+ }
1572
+ if (outcome.workspacesWithOwnRules.length > 0) {
1573
+ console.log(` ${yellow("!")} ${outcome.workspacesWithOwnRules.length} fx workspace(s) set their own permission rules, which replace the global ones there. Add "${FX_PUSHARY_TOOLS_RULE}": "allow" and "${FX_QUESTION_TOOL}": "deny" to each to route them through Pushary.`);
1542
1574
  }
1543
1575
  };
1544
1576
  var teachFxToAskViaPush = () => {
@@ -1572,20 +1604,19 @@ var setupFx = async () => {
1572
1604
  const entry = fxPusharyEntry(guardedArgv({ kind: "binary", path: bridge, runtime: "node" }));
1573
1605
  writeFxProfile(fxMcpProfile(), withFxPusharyServer(profile, entry));
1574
1606
  });
1575
- const toolsRule = allowPusharyToolsInFx(fxSettings());
1576
- const instructionsWritten = teachFxToAskViaPush();
1577
- console.log();
1578
- console.log(` ${dim("What this configured:")}`);
1579
- console.log(` ${dim("\u2022")} MCP server ${bold("pushary")} in ${fxMcpProfile()}`);
1580
- console.log(` ${dim("\u2022")} It runs ${MCP_BRIDGE_BINARY}, which signs in with the key saved in ~/.pushary, so no key is written to fx's config`);
1581
- reportFxToolsRule(toolsRule);
1582
- if (instructionsWritten) {
1583
- console.log(` ${dim("\u2022")} Instructions: ${fxAgentsMd()}`);
1607
+ const settingsOutcome = configureFxSettings(fxSettings());
1608
+ reportFxSettings(settingsOutcome);
1609
+ if (teachFxToAskViaPush()) {
1610
+ console.log(` ${check} Teaching fx to ask via push (${fxAgentsMd()})`);
1584
1611
  } else {
1585
1612
  console.log(` ${yellow("!")} ${fxAgentsMd()} links outside ${fxConfigDir()}, and fx ignores such a file, so no instructions were written.`);
1586
1613
  noteManual(`Replace the ${fxAgentsMd()} symlink with a file so fx loads Pushary's instructions, then run ${retryAgentCommand("fx")}.`);
1587
1614
  }
1588
- console.log(` ${dim("\u2022")} fx cannot hand its own approval prompts to Pushary; its agent asks through Pushary when it chooses to`);
1615
+ console.log();
1616
+ console.log(` ${dim("What this configured:")}`);
1617
+ console.log(` ${dim("\u2022")} MCP server ${bold("pushary")} in ${fxMcpProfile()}, which runs ${MCP_BRIDGE_BINARY} and signs in with the key saved in ~/.pushary, so no key is written to fx's config`);
1618
+ console.log(` ${dim("\u2022")} Questions: ${FX_QUESTION_SUMMARIES[settingsOutcome.kind === "written" ? settingsOutcome.questionRouting : "overridden"]}`);
1619
+ console.log(` ${dim("\u2022")} fx cannot hand its own approval prompts to Pushary, so those still wait in the terminal`);
1589
1620
  console.log(` ${dim("Restart fx, or run /mcp reload in a running session.")}`);
1590
1621
  noteManual("Restart fx, or run /mcp reload in a running session.");
1591
1622
  return "configured";
@@ -1982,15 +2013,16 @@ var AGENT_CAPABILITIES = {
1982
2013
  cursor: "plugin + permission gate",
1983
2014
  vscode: "agent plugin + permission gate",
1984
2015
  opencode: "generated plugin + permission gate",
1985
- fx: "MCP through a local bridge",
2016
+ fx: "MCP through a local bridge + questions via push",
1986
2017
  ...compatibleRecord((profile) => profile.mcp ? "MCP + native hooks" : "native hooks"),
1987
2018
  custom: "any MCP or HTTP agent (Windsurf, n8n, custom)"
1988
2019
  };
1989
2020
  var failedAgentNames = (failures) => failures.map((failure) => AGENT_NAMES[failure.agent]);
2021
+ var agentFailureNextLine = (next) => next.kind === "retry-agent" ? `${dim(RETRY_AGENT_LABEL)} ${cyan(next.command)}` : next.instruction;
1990
2022
  var printAgentFailures = (failures) => {
1991
2023
  for (const failure of describeAgentFailures(failures, (agent) => AGENT_NAMES[agent])) {
1992
2024
  console.log(` ${yellow("!")} ${failure.summary}`);
1993
- console.log(` ${dim(RETRY_AGENT_LABEL)} ${cyan(failure.retryCommand)}`);
2025
+ console.log(` ${agentFailureNextLine(failure.next)}`);
1994
2026
  }
1995
2027
  };
1996
2028
  var NAME_COLUMN = Math.max(...Object.values(AGENT_NAMES).map((name) => name.length)) + 2;
@@ -2042,6 +2074,7 @@ var resolveAgents = async (options) => {
2042
2074
  // visible and correctable, rather than an unexplained pre-tick.
2043
2075
  name: detected[agent].evidence ? `${AGENT_LABELS[agent]} ${dim(`[${detected[agent].evidence}]`)}` : AGENT_LABELS[agent],
2044
2076
  value: agent,
2077
+ short: AGENT_NAMES[agent],
2045
2078
  checked: detected[agent].found
2046
2079
  })),
2047
2080
  // Today an empty selection skips the loop and still prints "Setup complete."
@@ -2288,7 +2321,7 @@ var main = async () => {
2288
2321
  const transcriptSync = transcriptPreferenceForSetup(options.transcripts);
2289
2322
  if (transcriptSync) {
2290
2323
  console.log();
2291
- console.log("Transcript sync: enabling locally redacted, encrypted sync for Pushary-launched sessions. Your phone can decrypt it; authorized Pushary compliance admins can recover it for documented legal obligations. Disable with pushary setup --transcripts off.");
2324
+ console.log(" Transcript sync: enabling locally redacted, encrypted sync for Pushary-launched sessions. Your phone can decrypt it; authorized Pushary compliance admins can recover it for documented legal obligations. Disable with pushary setup --transcripts off.");
2292
2325
  }
2293
2326
  const rc = await saveApiKey(trimmedKey, controlMode, transcriptSync);
2294
2327
  if (keyCheck.kind === "ok") clearDaemonNeedsLogin();
@@ -2332,7 +2365,7 @@ var main = async () => {
2332
2365
  ${check} API key saved. No agents configured.`);
2333
2366
  console.log(` ${dim("Run setup again with --agents <id> when you want one wired up.")}`);
2334
2367
  } else {
2335
- for (const agent of agents) {
2368
+ for (const [index, agent] of agents.entries()) {
2336
2369
  markStep(`configure:${agent}`);
2337
2370
  try {
2338
2371
  const ownerBefore = hookOwnerFor(agent, appBridgeAvailable);
@@ -2372,7 +2405,8 @@ var main = async () => {
2372
2405
  const failure = agentFailureFromError(agent, err, formatError);
2373
2406
  failures.push(failure);
2374
2407
  console.log(` ${yellow("!")} ${AGENT_NAMES[agent]} setup failed: ${failure.detail}`);
2375
- console.log(` ${dim("Other agents will continue. Re-run setup to retry.")}`);
2408
+ const continuation = afterAgentFailureNote(agents.length - index - 1);
2409
+ if (continuation) console.log(` ${dim(continuation)}`);
2376
2410
  }
2377
2411
  }
2378
2412
  if (skipped.length > 0) {
@@ -2381,8 +2415,12 @@ var main = async () => {
2381
2415
  }
2382
2416
  if (failures.length > 0) {
2383
2417
  console.log();
2384
- console.log(` ${yellow("!")} Failed: ${failedAgentNames(failures).join(", ")} ${dim("(others completed successfully)")}`);
2385
- console.log(` ${dim("Run")} npx @pushary/agent-hooks@latest doctor ${dim("for the reason, or send it to")} ${SUPPORT_EMAIL}`);
2418
+ const configured = failedAgentsContext(completed.map((agent) => AGENT_NAMES[agent]));
2419
+ console.log(` ${yellow("!")} Failed: ${failedAgentNames(failures).join(", ")}${configured ? ` ${dim(configured)}` : ""}`);
2420
+ for (const followUp of agentFailureFollowUps(failures)) {
2421
+ if (followUp === "install-node") console.log(` ${INSTALL_NODE_STEP}`);
2422
+ else console.log(` ${dim("Run")} npx @pushary/agent-hooks@latest doctor ${dim("for the reason, or send it to")} ${SUPPORT_EMAIL}`);
2423
+ }
2386
2424
  }
2387
2425
  markStep("project-instructions");
2388
2426
  await offerProjectInstructions(agents, options);
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readLedgerSummary
4
- } from "../chunk-2C7CCWVX.js";
4
+ } from "../chunk-F6BMJOSW.js";
5
5
  import {
6
6
  exitOnHelpFlag
7
- } from "../chunk-2PS6QWDW.js";
7
+ } from "../chunk-6NHXKTQU.js";
8
8
  import "../chunk-KZERVKTD.js";
9
9
  import {
10
10
  getApiKey
11
- } from "../chunk-V4AQBAVG.js";
12
- import "../chunk-B3UXMSMW.js";
13
- import "../chunk-YROGUPOZ.js";
11
+ } from "../chunk-SJN64NJX.js";
12
+ import "../chunk-65RN6ZCO.js";
13
+ import "../chunk-WWTLAZQA.js";
14
14
 
15
15
  // bin/pushary-stats.ts
16
16
  exitOnHelpFlag("stats");
@@ -2,48 +2,48 @@
2
2
  import {
3
3
  describeLastSeen,
4
4
  listMachines
5
- } from "../chunk-4F46VIEE.js";
6
- import "../chunk-JXUAXO74.js";
7
- import "../chunk-73T6MSU5.js";
5
+ } from "../chunk-YU54LNC6.js";
6
+ import "../chunk-YNJ2H2FD.js";
7
+ import "../chunk-4S7CCOXB.js";
8
8
  import "../chunk-XHKBHWLX.js";
9
9
  import {
10
10
  describeReach,
11
11
  fetchChannels,
12
12
  reachVerdict
13
- } from "../chunk-HDRNWNVP.js";
13
+ } from "../chunk-YLCSKVQQ.js";
14
14
  import "../chunk-3EGEA4KH.js";
15
- import {
16
- checkApiKey,
17
- describeKeyCheck
18
- } from "../chunk-X6KDKOV5.js";
19
15
  import "../chunk-QCGNGEGE.js";
20
16
  import {
21
17
  createIo
22
- } from "../chunk-5RUIFDTP.js";
18
+ } from "../chunk-U4TMAANR.js";
23
19
  import {
24
20
  exitOnHelpFlag
25
- } from "../chunk-2PS6QWDW.js";
21
+ } from "../chunk-6NHXKTQU.js";
26
22
  import {
27
23
  rejectUnknownFlags
28
24
  } from "../chunk-GMXKITVA.js";
29
25
  import {
30
26
  readKeySource
31
- } from "../chunk-UOAQ6QMP.js";
32
- import "../chunk-JNXVOU6Y.js";
33
- import "../chunk-KW2RRR7D.js";
34
- import "../chunk-XNCMT7YY.js";
27
+ } from "../chunk-VSGKQ3HI.js";
28
+ import "../chunk-D4OGEWLH.js";
29
+ import {
30
+ checkApiKey,
31
+ describeKeyCheck
32
+ } from "../chunk-VQDJ6KB3.js";
33
+ import "../chunk-UFNBKYWT.js";
34
+ import "../chunk-HOIGXMI7.js";
35
35
  import {
36
36
  UsageError
37
37
  } from "../chunk-7QLSKOSU.js";
38
38
  import {
39
39
  EXIT
40
40
  } from "../chunk-KZERVKTD.js";
41
- import "../chunk-ROQK7IN3.js";
42
- import "../chunk-DNJUSFBL.js";
41
+ import "../chunk-LI3C2IQY.js";
42
+ import "../chunk-NYBZJ44G.js";
43
43
  import "../chunk-T7E75EKA.js";
44
- import "../chunk-V4AQBAVG.js";
45
- import "../chunk-B3UXMSMW.js";
46
- import "../chunk-YROGUPOZ.js";
44
+ import "../chunk-SJN64NJX.js";
45
+ import "../chunk-65RN6ZCO.js";
46
+ import "../chunk-WWTLAZQA.js";
47
47
 
48
48
  // src/status.ts
49
49
  var keySourceLine = (source) => {