pushary 1.9.35 → 1.9.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/bin/pushary-bell-hook.js +25 -25
  4. package/dist/bin/pushary-bell.js +9 -9
  5. package/dist/bin/pushary-claude.js +24 -24
  6. package/dist/bin/pushary-clean.js +52 -45
  7. package/dist/bin/pushary-codex-bridge.js +17 -17
  8. package/dist/bin/pushary-codex-hook.js +65 -41
  9. package/dist/bin/pushary-codex.js +12 -12
  10. package/dist/bin/pushary-connect.js +24 -24
  11. package/dist/bin/pushary-cowork.js +5 -5
  12. package/dist/bin/pushary-daemon.js +32 -32
  13. package/dist/bin/pushary-disconnect.js +31 -28
  14. package/dist/bin/pushary-doctor.js +35 -34
  15. package/dist/bin/pushary-elicitation-hook.js +25 -25
  16. package/dist/bin/pushary-gemini-bridge.js +13 -13
  17. package/dist/bin/pushary-gemini-hook.js +27 -27
  18. package/dist/bin/pushary-hook.js +34 -30
  19. package/dist/bin/pushary-login.js +12 -12
  20. package/dist/bin/pushary-logout.js +24 -24
  21. package/dist/bin/pushary-mcp.js +9 -9
  22. package/dist/bin/pushary-mode.js +12 -12
  23. package/dist/bin/pushary-notification-hook.js +24 -24
  24. package/dist/bin/pushary-opencode-hook.js +26 -26
  25. package/dist/bin/pushary-permission-denied-hook.js +29 -29
  26. package/dist/bin/pushary-permission-hook.js +28 -28
  27. package/dist/bin/pushary-post-hook.js +24 -24
  28. package/dist/bin/pushary-prompt-hook.js +24 -24
  29. package/dist/bin/pushary-session-end-hook.js +24 -24
  30. package/dist/bin/pushary-session-start-hook.js +28 -28
  31. package/dist/bin/pushary-setup.js +65 -56
  32. package/dist/bin/pushary-stats.js +5 -5
  33. package/dist/bin/pushary-status.js +26 -26
  34. package/dist/bin/pushary-stop-hook.js +24 -24
  35. package/dist/bin/pushary-stopfailure-hook.js +24 -24
  36. package/dist/bin/pushary-suggestions.js +12 -12
  37. package/dist/bin/pushary-transcript-register.js +17 -17
  38. package/dist/bin/pushary-transcripts.js +7 -7
  39. package/dist/bin/pushary-upgrade.js +30 -30
  40. package/dist/bin/pushary-wait.js +12 -12
  41. package/dist/bin/pushary.js +6 -6
  42. package/dist/{chunk-7FQL5NWJ.js → chunk-235LKHLW.js} +301 -90
  43. package/dist/{chunk-LOYSI2CZ.js → chunk-2WZJHJAB.js} +5 -5
  44. package/dist/{chunk-YABMIGR3.js → chunk-3Y73MQPF.js} +1 -1
  45. package/dist/{chunk-GC3SUMYD.js → chunk-4OQHT5IJ.js} +4 -4
  46. package/dist/{chunk-QJ3ENGBT.js → chunk-6SYU5LVK.js} +3 -3
  47. package/dist/{chunk-NKAMF2YJ.js → chunk-6VUVVVVN.js} +1 -1
  48. package/dist/{chunk-3LS3GFJW.js → chunk-AZXPJLKC.js} +1 -1
  49. package/dist/{chunk-A455TSHF.js → chunk-B7SE22M7.js} +2 -2
  50. package/dist/{chunk-XLXG6MA5.js → chunk-DG5QHKJA.js} +3 -3
  51. package/dist/{chunk-7IVV4LLF.js → chunk-DYALNCS6.js} +3 -3
  52. package/dist/{chunk-PEEJP4HN.js → chunk-EK6PRPCY.js} +4 -4
  53. package/dist/{chunk-FPYBX36E.js → chunk-F3J6JFR2.js} +5 -5
  54. package/dist/{chunk-Z6HDYK2A.js → chunk-FFRH7KZP.js} +2 -2
  55. package/dist/{chunk-V4ZIG66K.js → chunk-GKTVIN4F.js} +1 -1
  56. package/dist/{chunk-YRDNKY77.js → chunk-GQSJVYKN.js} +6 -6
  57. package/dist/{chunk-EGTRIM74.js → chunk-GSP7CC52.js} +4 -4
  58. package/dist/{chunk-NMQLY4PE.js → chunk-GUHM2UQO.js} +2 -2
  59. package/dist/{chunk-OZMGO4WF.js → chunk-HA22GGDG.js} +5 -5
  60. package/dist/{chunk-WFPXKCN4.js → chunk-HJF57K2U.js} +5 -5
  61. package/dist/{chunk-VTAC5FBZ.js → chunk-HK2RPWAD.js} +1 -1
  62. package/dist/{chunk-SMSX4TCC.js → chunk-HQR6DYVG.js} +1 -1
  63. package/dist/{chunk-FQQSW7MD.js → chunk-ISNGKZG4.js} +1 -1
  64. package/dist/{chunk-EJWSE7S7.js → chunk-JLQV3XLV.js} +4 -4
  65. package/dist/{chunk-A5FFSXZE.js → chunk-JMLJHKJN.js} +2 -2
  66. package/dist/{chunk-7S7H7V5V.js → chunk-KHLYOIPY.js} +97 -40
  67. package/dist/{chunk-HVUBWLER.js → chunk-KZ3XE3JG.js} +34 -109
  68. package/dist/{chunk-CK3W7Q5S.js → chunk-MAGB6VW6.js} +1 -1
  69. package/dist/{chunk-DCBSD22V.js → chunk-N5WEEYV6.js} +3 -3
  70. package/dist/{chunk-U7CN5HG2.js → chunk-NDQXKLGE.js} +5 -5
  71. package/dist/{chunk-IRU7DKJW.js → chunk-NPE44CX4.js} +7 -3
  72. package/dist/{chunk-22YSHBTI.js → chunk-NWSWP3KY.js} +3 -3
  73. package/dist/{chunk-HQKYWUBF.js → chunk-OSLC7G3D.js} +2 -2
  74. package/dist/{chunk-7UGGCTTW.js → chunk-PDDJKGIW.js} +2 -2
  75. package/dist/chunk-PDRKQSC6.js +184 -0
  76. package/dist/{chunk-4PIAOJAW.js → chunk-PYWLW7PC.js} +1 -1
  77. package/dist/{chunk-6OGDGQTT.js → chunk-Q3TCLMYC.js} +5 -5
  78. package/dist/{chunk-57FFQR46.js → chunk-Q52PBCN7.js} +1 -1
  79. package/dist/{chunk-DQKEGNTI.js → chunk-QUKATQ66.js} +1 -1
  80. package/dist/{chunk-UPRNSD2T.js → chunk-RFIHLLRH.js} +9 -6
  81. package/dist/{chunk-36ZCFJUI.js → chunk-SNI4FGOV.js} +1 -1
  82. package/dist/{chunk-43H2FUNN.js → chunk-SNUNAIKK.js} +4 -4
  83. package/dist/{chunk-WMB3OCI4.js → chunk-T2NKNQPM.js} +2 -2
  84. package/dist/{chunk-WPSI4246.js → chunk-UEIL72RN.js} +1 -1
  85. package/dist/{chunk-UDECNZYI.js → chunk-UK3WY3LE.js} +2 -2
  86. package/dist/{chunk-E2OZZZKP.js → chunk-UKCGR5T2.js} +2 -2
  87. package/dist/{chunk-CPYOJRDN.js → chunk-V7Q7DQXL.js} +8 -8
  88. package/dist/{chunk-ULJFIF5N.js → chunk-VA36Q6BD.js} +1 -1
  89. package/dist/{chunk-FDNMQEU6.js → chunk-VMIDUNOT.js} +2 -2
  90. package/dist/{chunk-SMRMWDFE.js → chunk-W73URGML.js} +43 -17
  91. package/dist/{chunk-45OXG3VR.js → chunk-WXA3CFZR.js} +1 -1
  92. package/dist/{chunk-4TFD3WAA.js → chunk-YNJ372JU.js} +2 -2
  93. package/dist/{chunk-H52HBSOD.js → chunk-Z5ORWTY3.js} +3 -3
  94. package/dist/{chunk-B5RLJ6QD.js → chunk-ZMR3DSMH.js} +9 -5
  95. package/dist/{chunk-HJNCVY5F.js → chunk-ZQD6GVS7.js} +7 -7
  96. package/dist/{global-upgrade-IZCB4WIE.js → global-upgrade-R57HCUTC.js} +6 -6
  97. package/dist/{paths-4WAA57P5.js → paths-G2AYNZCD.js} +2 -2
  98. package/dist/{reapply-GJ73ZHDD.js → reapply-FHCOPXEN.js} +20 -20
  99. package/dist/src/index.js +19 -19
  100. package/dist/{upgrade-lock-J5PIKSYV.js → upgrade-lock-3I7AF7E3.js} +3 -3
  101. package/package.json +1 -1
  102. package/dist/chunk-FYQG2UTK.js +0 -71
@@ -3,28 +3,29 @@ import {
3
3
  addGeminiMcpServer,
4
4
  removeGeminiMcpServer,
5
5
  supportedClaudeEvents
6
- } from "./chunk-QJ3ENGBT.js";
6
+ } from "./chunk-6SYU5LVK.js";
7
7
  import {
8
8
  CLAUDE_CODE_AGENT,
9
- parseApplyPatchFiles
10
- } from "./chunk-SMRMWDFE.js";
9
+ compatibleToolLookup
10
+ } from "./chunk-W73URGML.js";
11
11
  import {
12
12
  guardBinary,
13
13
  guardBinaryRefusing,
14
14
  holdBudgetOf,
15
15
  isProtectedByScope,
16
16
  registerHookEntry
17
- } from "./chunk-UPRNSD2T.js";
17
+ } from "./chunk-RFIHLLRH.js";
18
18
  import {
19
19
  HOOK_LOCATOR_BINARY,
20
20
  PHONE_ANSWERED_TOOLS,
21
+ compatibleTargetFiles,
21
22
  hookCompatibleProfile,
22
23
  hookCompatibleProfileBySource,
24
+ isRecord,
23
25
  pusharyMissingReason,
24
26
  refusalOutput,
25
- toToolLookup,
26
27
  withHookHoldBudget
27
- } from "./chunk-7FQL5NWJ.js";
28
+ } from "./chunk-235LKHLW.js";
28
29
 
29
30
  // src/claude-config.ts
30
31
  import { join } from "path";
@@ -146,14 +147,14 @@ var addPusharyToolPermissions = (settings) => {
146
147
  permissions.allow = filtered;
147
148
  };
148
149
  var addPusharyHooks = (settings, binDir, version = null) => {
149
- const resolve2 = (name) => guardBinary(binDir ? join(binDir, name) : name);
150
+ const resolve = (name) => guardBinary(binDir ? join(binDir, name) : name);
150
151
  const hooks = ensureRecord(settings, "hooks");
151
152
  const supported = new Map(supportedClaudeEvents(version).map((hook) => [hook.event, hook]));
152
153
  for (const known of CLAUDE_HOOK_EVENTS) {
153
154
  const current = Array.isArray(hooks[known.event]) ? hooks[known.event] : [];
154
155
  const definition = supported.get(known.event);
155
156
  if (definition) {
156
- hooks[known.event] = registerHookEntry(current, isPusharyHook, entry(definition, resolve2));
157
+ hooks[known.event] = registerHookEntry(current, isPusharyHook, entry(definition, resolve));
157
158
  continue;
158
159
  }
159
160
  const others = current.filter((item) => !isPusharyHook(item));
@@ -164,11 +165,11 @@ var addPusharyHooks = (settings, binDir, version = null) => {
164
165
  }
165
166
  }
166
167
  };
167
- var entry = (definition, resolve2) => ({
168
+ var entry = (definition, resolve) => ({
168
169
  ...definition.matcher ? { matcher: definition.matcher } : {},
169
170
  hooks: [{
170
171
  type: "command",
171
- command: withHookHoldBudget(resolve2(definition.binary), holdBudgetOf(definition)),
172
+ command: withHookHoldBudget(resolve(definition.binary), holdBudgetOf(definition)),
172
173
  timeout: definition.timeout
173
174
  }]
174
175
  });
@@ -195,9 +196,37 @@ var removePusharySettings = (settings, scope) => {
195
196
  return changed;
196
197
  };
197
198
 
199
+ // src/config-document.ts
200
+ import { parse, stringify } from "smol-toml";
201
+ var parseConfigDocument = (text, format) => {
202
+ if (text === null) return { kind: "missing" };
203
+ try {
204
+ const value = format === "toml" ? parse(text) : JSON.parse(text);
205
+ return isRecord(value) ? { kind: "parsed", value } : { kind: "unreadable" };
206
+ } catch {
207
+ return { kind: "unreadable" };
208
+ }
209
+ };
210
+ var readConfigDocument = (path, format, files) => {
211
+ try {
212
+ return parseConfigDocument(files.read(path), format);
213
+ } catch {
214
+ return { kind: "unreadable" };
215
+ }
216
+ };
217
+ var serializeConfigDocument = (value, format) => format === "toml" ? stringify(value) : `${JSON.stringify(value, null, 2)}
218
+ `;
219
+
198
220
  // src/compatible-config.ts
199
221
  import { homedir } from "os";
200
- import { isAbsolute, join as join2, resolve } from "path";
222
+ import { join as join2 } from "path";
223
+ var hasCompatibleMcpServer = (profile, contents) => {
224
+ if (!profile.mcp) return false;
225
+ const document = parseConfigDocument(contents, profile.mcp.file.format);
226
+ if (document.kind !== "parsed") return false;
227
+ const servers = document.value[profile.mcp.container];
228
+ return isRecord(servers) && isRecord(servers.pushary);
229
+ };
201
230
  var AGENT_FLAG = "--agent";
202
231
  var SOURCE_FLAG = "--source";
203
232
  var EVENT_FLAG = "--event";
@@ -237,25 +266,24 @@ var activeHookSource = (argv) => {
237
266
  if (source === void 0) return "claude";
238
267
  return source === "claude" ? "claude" : hookCompatibleProfileBySource(source)?.source ?? null;
239
268
  };
240
- var PATCH_INPUT_KEYS = ["command", "patch", "input"];
241
- var patchedFiles = (toolInput) => [...new Set(PATCH_INPUT_KEYS.flatMap((key) => parseApplyPatchFiles(toolInput[key])))];
242
- var lookupFor = (profile, input) => {
243
- const absolute = (path) => input.cwd && !isAbsolute(path) ? resolve(input.cwd, path) : path;
244
- const toolInput = input.tool_input ?? {};
245
- const lookup = toToolLookup(profile, input.tool_name ?? "", toolInput, absolute);
246
- if (!lookup.unreadable) return lookup;
247
- const files = patchedFiles(toolInput);
248
- return files.length === 1 ? { ...lookup, input: { ...lookup.input, file_path: absolute(files[0]) }, unreadable: false } : lookup;
249
- };
250
- var normalizeToolCall = (profile, input) => {
269
+ function normalizeToolCall(profile, input) {
251
270
  if (!profile || !input.tool_name) return input;
252
- const lookup = lookupFor(profile, input);
271
+ const lookup = compatibleToolLookup(profile, input.tool_name, input.tool_input ?? {}, input.cwd);
253
272
  return { ...input, tool_name: lookup.tool, tool_input: lookup.input };
273
+ }
274
+ var grokHome = (home = homedir(), environment = process.env) => environment.GROK_HOME?.trim() || join2(home, ".grok");
275
+ var compatibleFilePath = (file, home = homedir()) => join2(file.root === "grok_home" ? grokHome(home) : home, ...file.path);
276
+ var compatibleDocuments = (profile, home) => {
277
+ const documents = /* @__PURE__ */ new Map();
278
+ for (const file of [profile.configFile, profile.mcp?.file, profile.permissionRuleFile]) {
279
+ if (file) documents.set(compatibleFilePath(file, home), file);
280
+ }
281
+ return documents;
254
282
  };
255
- var underHome = (segments, home = homedir()) => join2(home, ...segments);
256
- var compatibleConfigFile = (profile, home) => underHome(profile.configFile, home);
257
- var compatibleMcpFile = (profile, home) => profile.mcp ? underHome(profile.mcp.file, home) : null;
258
- var compatibleInstructionsFile = (profile, home) => profile.instructions ? underHome(profile.instructions.file, home) : null;
283
+ var compatibleTargetPaths = (profile, home) => compatibleTargetFiles(profile).map((file) => compatibleFilePath(file, home));
284
+ var compatibleConfigFile = (profile, home) => compatibleFilePath(profile.configFile, home);
285
+ var compatibleMcpFile = (profile, home) => profile.mcp ? compatibleFilePath(profile.mcp.file, home) : null;
286
+ var compatibleInstructionsFile = (profile, home) => profile.instructions ? compatibleFilePath(profile.instructions.file, home) : null;
259
287
  var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
260
288
  var ensureRecord2 = (target, key) => {
261
289
  const existing = asRecord2(target[key]);
@@ -283,14 +311,22 @@ var translateMatcher = (profile, definition, matcher) => {
283
311
  const spellings = matcher.split("|").filter((canonical) => !carriesPhoneAnswer(profile, canonical)).flatMap((canonical) => [canonical, ...aliasesFor(profile, canonical)]);
284
312
  return [.../* @__PURE__ */ new Set([...spellings, ...soleGateCoverage(profile, definition), ...profile.extraMatchers])].join("|");
285
313
  };
286
- var TOOL_MATCHED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
314
+ var TOOL_MATCHED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse", "PostToolUseFailure"]);
287
315
  var isPusharyHandler = (entry2, scope) => {
288
316
  const record = asRecord2(entry2);
289
317
  return record !== void 0 && !Array.isArray(record.hooks) && isPusharyHook({ hooks: [record] }, scope);
290
318
  };
291
319
  var isPusharyCompatibleEntry = (entry2, scope) => isPusharyHook(entry2, scope) || isPusharyHandler(entry2, scope);
292
320
  var withoutMatcher = ({ matcher, ...hook }) => hook;
293
- var compatibleHookEvents = (profile) => CLAUDE_HOOK_EVENTS.filter((hook) => profile.events.includes(hook.event)).map((hook) => {
321
+ var compatibleDefinitions = (profile) => {
322
+ if (profile.source !== "grok") return CLAUDE_HOOK_EVENTS;
323
+ return [
324
+ ...CLAUDE_HOOK_EVENTS,
325
+ { event: "PostToolUseFailure", binary: "pushary-post-hook", timeout: 10, delivery: "telemetry", since: [1, 0, 0] },
326
+ { event: "StopCancelled", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: [1, 0, 0] }
327
+ ];
328
+ };
329
+ var compatibleHookEvents = (profile) => compatibleDefinitions(profile).filter((hook) => profile.events.includes(hook.event)).map((hook) => {
294
330
  if (!hook.matcher) return hook;
295
331
  if (!TOOL_MATCHED_EVENTS.has(hook.event)) return withoutMatcher(hook);
296
332
  return { ...hook, matcher: translateMatcher(profile, hook, hook.matcher) };
@@ -309,7 +345,7 @@ var hooksShapeConflict = (config, profile) => {
309
345
  walked.push(key);
310
346
  node = nested;
311
347
  }
312
- const clash = CLAUDE_HOOK_EVENTS.find(({ event }) => node[event] !== void 0 && !Array.isArray(node[event]));
348
+ const clash = compatibleDefinitions(profile).find(({ event }) => node[event] !== void 0 && !Array.isArray(node[event]));
313
349
  return clash ? dotted([...walked, clash.event]) : void 0;
314
350
  };
315
351
  var permissionsShapeConflict = (config) => {
@@ -325,15 +361,15 @@ var addCompatibleToolPermissions = (config) => {
325
361
  addPusharyToolPermissions(config);
326
362
  return WRITTEN;
327
363
  };
328
- var commandFor = (profile, definition, resolve2) => {
329
- const named = `${resolve2(definition.binary)} ${agentArgument(profile)}`;
364
+ var commandFor = (profile, definition, resolve) => {
365
+ const named = `${resolve(definition.binary)} ${agentArgument(profile)}`;
330
366
  const command = payloadNamesItsEvent(profile) ? named : `${named} ${eventArgument(definition.event)}`;
331
367
  return withHookHoldBudget(command, holdBudgetOf(definition));
332
368
  };
333
369
  var writesBareHandler = (profile, definition) => profile.nonToolEntries === "handler" && !TOOL_MATCHED_EVENTS.has(definition.event);
334
- var guardFor = (profile, definition, path) => isSoleGateEvent(profile, definition) ? guardBinaryRefusing(path, refusalOutput(profile, pusharyMissingReason(profile))) : guardBinary(path);
335
- var entryFor = (profile, definition, resolve2) => {
336
- const handler = { type: "command", command: commandFor(profile, definition, resolve2), timeout: definition.timeout };
370
+ var guardFor = (profile, definition, path) => isSoleGateEvent(profile, definition) || profile.source === "grok" && definition.delivery === "approval" ? guardBinaryRefusing(path, refusalOutput(profile, pusharyMissingReason(profile))) : guardBinary(path);
371
+ var entryFor = (profile, definition, resolve) => {
372
+ const handler = { type: "command", command: commandFor(profile, definition, resolve), timeout: definition.timeout };
337
373
  if (writesBareHandler(profile, definition)) return handler;
338
374
  return {
339
375
  ...definition.matcher ? { matcher: definition.matcher } : {},
@@ -345,12 +381,12 @@ var addCompatibleHooks = (config, profile, binDir) => {
345
381
  if (conflictAt) return { ok: false, conflictAt };
346
382
  const hooks = descend(config, profile.hooksKeyPath);
347
383
  const supported = new Map(compatibleHookEvents(profile).map((hook) => [hook.event, hook]));
348
- for (const known of CLAUDE_HOOK_EVENTS) {
384
+ for (const known of compatibleDefinitions(profile)) {
349
385
  const current = Array.isArray(hooks[known.event]) ? hooks[known.event] : [];
350
386
  const definition = supported.get(known.event);
351
387
  if (definition) {
352
- const resolve2 = (name) => guardFor(profile, definition, binDir ? join2(binDir, name) : name);
353
- hooks[known.event] = registerHookEntry(current, isPusharyCompatibleEntry, entryFor(profile, definition, resolve2));
388
+ const resolve = (name) => guardFor(profile, definition, binDir ? join2(binDir, name) : name);
389
+ hooks[known.event] = registerHookEntry(current, isPusharyCompatibleEntry, entryFor(profile, definition, resolve));
354
390
  continue;
355
391
  }
356
392
  const others = current.filter((item) => !isPusharyCompatibleEntry(item));
@@ -363,7 +399,7 @@ var removeCompatibleHooks = (config, profile, scope) => {
363
399
  const hooks = findMap(config, profile.hooksKeyPath);
364
400
  if (!hooks) return false;
365
401
  let changed = false;
366
- for (const { event } of CLAUDE_HOOK_EVENTS) {
402
+ for (const { event } of compatibleDefinitions(profile)) {
367
403
  const entries = hooks[event];
368
404
  if (!Array.isArray(entries)) continue;
369
405
  const kept = entries.filter((entry2) => !isPusharyCompatibleEntry(entry2, scope));
@@ -390,9 +426,15 @@ var missingCompatibleHookEvents = (config, profile) => {
390
426
  return !Array.isArray(entries) || !entries.some((entry2) => isPusharyCompatibleEntry(entry2));
391
427
  }).map(({ event }) => event);
392
428
  };
393
- var compatiblePermissionRuleFile = (profile, home) => profile.permissionRuleFile ? underHome(profile.permissionRuleFile, home) : null;
429
+ var compatiblePermissionRuleFile = (profile, home) => profile.permissionRuleFile ? compatibleFilePath(profile.permissionRuleFile, home) : null;
394
430
  var addCompatibleMcpServer = (config, profile, apiKey) => {
395
431
  if (!profile.mcp) return;
432
+ if (profile.mcp.shape === "grok_http") {
433
+ const container = profile.mcp.container;
434
+ if (config[container] !== void 0 && !asRecord2(config[container])) throw new Error(`${profile.label} ${container} is not a table; configuration was left unchanged.`);
435
+ ensureRecord2(config, container).pushary = { url: PUSHARY_MCP_URL, headers: { Authorization: `Bearer ${apiKey}` } };
436
+ return;
437
+ }
396
438
  if (profile.mcp.shape === "gemini_http") {
397
439
  addGeminiMcpServer(config, apiKey);
398
440
  return;
@@ -411,10 +453,20 @@ var addCompatibleMcpServer = (config, profile, apiKey) => {
411
453
  };
412
454
  var removeCompatibleMcpServer = (config, profile) => {
413
455
  if (!profile.mcp) return false;
456
+ if (profile.mcp.shape === "grok_http") {
457
+ const container = profile.mcp.container;
458
+ const servers = asRecord2(config[container]);
459
+ if (!servers || servers.pushary === void 0) return false;
460
+ delete servers.pushary;
461
+ if (Object.keys(servers).length === 0) delete config[container];
462
+ return true;
463
+ }
414
464
  return profile.mcp.shape === "gemini_http" ? removeGeminiMcpServer(config) : removeClaudeMcpServers(config);
415
465
  };
416
466
 
417
467
  export {
468
+ readConfigDocument,
469
+ serializeConfigDocument,
418
470
  addClaudeMcpServer,
419
471
  isClaudeMcpSnapshot,
420
472
  captureClaudeMcpServer,
@@ -425,11 +477,16 @@ export {
425
477
  addPusharyToolPermissions,
426
478
  addPusharyHooks,
427
479
  removePusharySettings,
480
+ hasCompatibleMcpServer,
428
481
  profileFromArgv,
429
482
  activeIdentity,
430
483
  namedEvent,
431
484
  activeHookSource,
432
485
  normalizeToolCall,
486
+ grokHome,
487
+ compatibleFilePath,
488
+ compatibleDocuments,
489
+ compatibleTargetPaths,
433
490
  compatibleConfigFile,
434
491
  compatibleMcpFile,
435
492
  compatibleInstructionsFile,
@@ -1,20 +1,22 @@
1
1
  import {
2
+ askAgentQuestions,
2
3
  defersToNativeMode,
3
- reportMachineDenial
4
- } from "./chunk-FYQG2UTK.js";
4
+ outcomeOfFailedAsk,
5
+ reportMachineDenial,
6
+ storedWaitDeadline
7
+ } from "./chunk-PDRKQSC6.js";
5
8
  import {
6
9
  isGatingMoment,
7
10
  recordKeylessMoment
8
- } from "./chunk-VTAC5FBZ.js";
11
+ } from "./chunk-HK2RPWAD.js";
9
12
  import {
10
- announceSubscriptionRequired,
11
13
  isDeferAnswer,
12
14
  oncePerSession
13
- } from "./chunk-NKAMF2YJ.js";
15
+ } from "./chunk-6VUVVVVN.js";
14
16
  import {
15
17
  activeHookSource,
16
18
  activeIdentity
17
- } from "./chunk-7S7H7V5V.js";
19
+ } from "./chunk-KHLYOIPY.js";
18
20
  import {
19
21
  DEFAULT_SESSION,
20
22
  UNNAMED_ACTION,
@@ -39,7 +41,7 @@ import {
39
41
  reportEvent,
40
42
  scopePathFor,
41
43
  settleQuestionsForToolUse
42
- } from "./chunk-SMRMWDFE.js";
44
+ } from "./chunk-W73URGML.js";
43
45
  import {
44
46
  askUser,
45
47
  cancelQuestion,
@@ -52,22 +54,20 @@ import {
52
54
  sendNotification,
53
55
  waitForQuestion,
54
56
  waitsForThePhone
55
- } from "./chunk-OZMGO4WF.js";
56
- import {
57
- SubscriptionRequiredError
58
- } from "./chunk-H52HBSOD.js";
57
+ } from "./chunk-HA22GGDG.js";
59
58
  import {
60
59
  getMachineId
61
- } from "./chunk-ULJFIF5N.js";
60
+ } from "./chunk-VA36Q6BD.js";
62
61
  import {
63
62
  getApiKey,
64
63
  getBaseUrl
65
- } from "./chunk-3LS3GFJW.js";
64
+ } from "./chunk-AZXPJLKC.js";
66
65
  import {
67
66
  NPX_LATEST_COMMAND
68
- } from "./chunk-45OXG3VR.js";
67
+ } from "./chunk-WXA3CFZR.js";
69
68
  import {
70
69
  AGENT_QUESTION_STRATEGY,
70
+ AGENT_QUESTION_TOOL,
71
71
  DECISION_STOP_REASON,
72
72
  GATE_BLOCK_REASON_MAX_LENGTH,
73
73
  GATE_WIRE,
@@ -104,13 +104,12 @@ import {
104
104
  isBlockedActionRequest,
105
105
  isPermissionRequestOwnedByPreToolUse,
106
106
  isPusharyOwnTool,
107
- parseAgentQuestionAnswers,
108
107
  parseAgentQuestions,
109
108
  questionHandedToTheTerminal,
110
109
  resolveGate,
111
110
  timeoutActionWithoutLocalPrompt,
112
111
  unansweredRefusalReason
113
- } from "./chunk-7FQL5NWJ.js";
112
+ } from "./chunk-235LKHLW.js";
114
113
 
115
114
  // src/claude-permission-rules.ts
116
115
  import { readFileSync, statSync } from "fs";
@@ -191,16 +190,7 @@ var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, u
191
190
  var ask = (reason) => claudePreToolUseOutput("ask", reason);
192
191
  var halted = (reason) => claudeStoppedPreToolUseOutput(reason);
193
192
  var undecidedCall = (reason) => PUSHARY_IS_SOLE_GATE ? deny(reason) : void 0;
194
- var outcomeOfFailedAsk = (error, sessionId, failure) => {
195
- if (!(error instanceof SubscriptionRequiredError)) return failure;
196
- announceSubscriptionRequired(error.upgradeUrl, sessionId);
197
- return void 0;
198
- };
199
193
  var outcomeOfFailedPermissionAsk = (error, sessionId) => outcomeOfFailedAsk(error, sessionId, halted(UNVERIFIABLE_APPROVAL_REASON)) ?? undecidedCall(SOLE_GATE_SUBSCRIPTION_REASON);
200
- var storedWaitDeadline = (result, requestedDeadline) => {
201
- const stored = result.waitEndsAt ? Date.parse(result.waitEndsAt) : Number.NaN;
202
- return Number.isFinite(stored) ? Math.min(stored, requestedDeadline) : requestedDeadline;
203
- };
204
194
  var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, timeoutAction, sessionId, machineId, toolName, toolTarget, decision, signals) => {
205
195
  const effectiveWait = effectiveWaitSeconds(timeoutAction, timeoutSeconds, "claude");
206
196
  const now = Date.now();
@@ -393,94 +383,29 @@ var keylessNoticeOnce = (sessionId) => {
393
383
  }
394
384
  };
395
385
  var shouldDeferToNativeMode = defersToNativeMode;
396
- var withLabels = (question) => ({
397
- ...question,
398
- labels: question.options.map((option) => option.label)
399
- });
400
- var withoutLabels = ({ labels: _, ...question }) => question;
401
- var parseQuestions = (toolInput) => parseAgentQuestions(toolInput)?.map(withLabels);
402
- var questionContext = (question, index, total, project) => {
403
- const subject = question.header ? `${question.header}: your agent is asking in ${project}` : `Your agent is asking in ${project}`;
404
- return total > 1 ? `${subject} (${index + 1} of ${total})` : subject;
405
- };
406
- var askAsOneCard = async (apiKey, input, parsed, projectName, deadline) => {
407
- const only = parsed[0];
408
- let result;
409
- try {
410
- result = await askUser(apiKey, {
411
- question: only.question,
412
- type: "select",
413
- options: [...only.labels],
414
- questions: parsed.map(withoutLabels),
415
- context: questionContext(only, 0, 1, projectName),
416
- agentName: agentNameIn(projectName),
417
- sessionId: input.session_id,
418
- machineId: getMachineId(),
419
- toolName: "AskUserQuestion",
420
- toolUseId: input.tool_use_id,
421
- waitEndsAt: new Date(deadline).toISOString()
422
- });
423
- } catch (error) {
424
- return outcomeOfFailedAsk(error, input.session_id, null);
425
- }
426
- if (isDecisionStopped(result)) return null;
427
- const answer = await waitForQuestion(apiKey, result, storedWaitDeadline(result, deadline));
428
- if (isDecisionStopped(answer)) return null;
429
- const values = answer.answered && answer.value ? parseAgentQuestionAnswers(parsed, answer.value) : null;
430
- if (!values || isDeferAnswer(answer.value)) {
431
- return void 0;
432
- }
433
- return values;
434
- };
435
- var askQuestionByQuestion = async (apiKey, input, parsed, projectName, deadline) => {
436
- const answers = {};
437
- for (const [index, question] of parsed.entries()) {
438
- let result;
439
- try {
440
- result = await askUser(apiKey, {
441
- question: question.question,
442
- type: "select",
443
- options: [...question.labels],
444
- questions: [withoutLabels(question)],
445
- context: questionContext(question, index, parsed.length, projectName),
446
- agentName: agentNameIn(projectName),
447
- sessionId: input.session_id,
448
- machineId: getMachineId(),
449
- toolName: "AskUserQuestion",
450
- // One AskUserQuestion call raises several questions, and their payload
451
- // fingerprints are identical (same tool, no target, no body). Without the
452
- // index they would all collapse onto the first decision.
453
- toolUseId: input.tool_use_id ? `${input.tool_use_id}#q${index}` : void 0,
454
- waitEndsAt: new Date(deadline).toISOString()
455
- });
456
- } catch (error) {
457
- return outcomeOfFailedAsk(error, input.session_id, null);
458
- }
459
- if (isDecisionStopped(result)) return null;
460
- const answer = await waitForQuestion(apiKey, result, storedWaitDeadline(result, deadline));
461
- if (isDecisionStopped(answer)) return null;
462
- const legacyValue = answer.answered ? answer.value?.trim() : void 0;
463
- const values = legacyValue ? parseAgentQuestionAnswers([question], legacyValue) ?? parseAgentQuestionAnswers([question], JSON.stringify({
464
- [question.question]: question.multiSelect ? JSON.stringify([legacyValue]) : legacyValue
465
- })) : null;
466
- const value = values?.[question.question];
467
- if (!value || isDeferAnswer(answer.value)) {
468
- return void 0;
469
- }
470
- answers[question.question] = value;
471
- }
472
- return answers;
473
- };
474
386
  var handleAskUserQuestion = async (apiKey, input, modeState) => {
475
387
  if (questionHandedToTheTerminal(input.tool_name, modeState)) return void 0;
476
- const parsed = parseQuestions(input.tool_input ?? {});
477
- if (!parsed) return void 0;
388
+ const questions = parseAgentQuestions(input.tool_input ?? {});
389
+ if (!questions) return void 0;
478
390
  const projectName = basename(input.cwd ?? process.cwd());
479
391
  const deadline = hookWaitDeadline(START_MS, effectiveWaitSeconds("wait", 0, "claude"), "claude", Date.now());
480
- const answers = parsed.length === 1 ? await askAsOneCard(apiKey, input, parsed, projectName, deadline) : await askQuestionByQuestion(apiKey, input, parsed, projectName, deadline);
481
- if (answers === null) return halted(DECISION_STOP_REASON);
482
- if (!answers) return void 0;
483
- return allowWithInput({ ...input.tool_input ?? {}, answers });
392
+ const outcome = await askAgentQuestions(apiKey, {
393
+ toolName: AGENT_QUESTION_TOOL,
394
+ agentName: agentNameIn(projectName),
395
+ projectName,
396
+ sessionId: input.session_id,
397
+ toolUseId: input.tool_use_id,
398
+ deadline
399
+ }, questions);
400
+ switch (outcome.kind) {
401
+ case "answered":
402
+ return allowWithInput({ ...input.tool_input ?? {}, answers: outcome.answers });
403
+ case "stopped":
404
+ case "unavailable":
405
+ return halted(DECISION_STOP_REASON);
406
+ case "unanswered":
407
+ return void 0;
408
+ }
484
409
  };
485
410
  var PLAN_APPROVE = "Approve, start coding";
486
411
  var PLAN_KEEP_PLANNING = "Keep planning";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  redactSecretsFromContent
3
- } from "./chunk-7FQL5NWJ.js";
3
+ } from "./chunk-235LKHLW.js";
4
4
 
5
5
  // src/crypto.ts
6
6
  import nacl from "tweetnacl";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveInstallSourceFor
3
- } from "./chunk-NMQLY4PE.js";
3
+ } from "./chunk-GUHM2UQO.js";
4
4
  import {
5
5
  backupFile,
6
6
  readJsonSafe,
@@ -9,11 +9,11 @@ import {
9
9
  } from "./chunk-AQFEP65J.js";
10
10
  import {
11
11
  configFilePath
12
- } from "./chunk-3LS3GFJW.js";
12
+ } from "./chunk-AZXPJLKC.js";
13
13
  import {
14
14
  API_KEY_PREFIX_PATTERN,
15
15
  isRecord
16
- } from "./chunk-7FQL5NWJ.js";
16
+ } from "./chunk-235LKHLW.js";
17
17
 
18
18
  // src/shell-alias.ts
19
19
  import { homedir } from "os";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  APPROVAL_SETTINGS_PATH
3
- } from "./chunk-PEEJP4HN.js";
3
+ } from "./chunk-EK6PRPCY.js";
4
4
  import {
5
5
  readMacAppAgentKey
6
- } from "./chunk-EGTRIM74.js";
6
+ } from "./chunk-GSP7CC52.js";
7
7
  import {
8
8
  findExportedKeyLines,
9
9
  isAdoptedMacAppKey,
@@ -13,13 +13,13 @@ import {
13
13
  readConfigFileKey,
14
14
  readKeySource,
15
15
  readMacAppKeyPrefix
16
- } from "./chunk-DCBSD22V.js";
16
+ } from "./chunk-N5WEEYV6.js";
17
17
  import {
18
18
  absoluteUrl
19
- } from "./chunk-7IVV4LLF.js";
19
+ } from "./chunk-DYALNCS6.js";
20
20
  import {
21
21
  isValidApiKey
22
- } from "./chunk-7FQL5NWJ.js";
22
+ } from "./chunk-235LKHLW.js";
23
23
 
24
24
  // src/key-revocation.ts
25
25
  var SELF_REVOKE_PATH = "/api/v1/server/keys/self/revoke";
@@ -1,13 +1,16 @@
1
1
  import {
2
2
  openCodeConfigDirFrom
3
- } from "./chunk-WFPXKCN4.js";
3
+ } from "./chunk-HJF57K2U.js";
4
+ import {
5
+ grokHome
6
+ } from "./chunk-KHLYOIPY.js";
4
7
  import {
5
8
  codexHomeFrom,
6
9
  resolvePathBinary
7
- } from "./chunk-UPRNSD2T.js";
10
+ } from "./chunk-RFIHLLRH.js";
8
11
  import {
9
12
  HOOK_COMPATIBLE_PROFILES
10
- } from "./chunk-7FQL5NWJ.js";
13
+ } from "./chunk-235LKHLW.js";
11
14
 
12
15
  // src/hermes-config.ts
13
16
  import { execFileSync } from "child_process";
@@ -550,6 +553,7 @@ var agentProbes = (home = homedir3()) => ({
550
553
  configMarkers: []
551
554
  },
552
555
  ...compatibleProbes(),
556
+ grok: { binary: "grok", paths: [join3(home, ".local", "bin", "grok")], configMarkers: [grokHome(home)] },
553
557
  // "Other" is a set of printed instructions for any MCP or HTTP client. There is
554
558
  // nothing to detect, and it must never be auto-selected.
555
559
  custom: { binary: null, paths: [], configMarkers: [] }
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  readExistingMachineId
3
- } from "./chunk-ULJFIF5N.js";
3
+ } from "./chunk-VA36Q6BD.js";
4
4
  import {
5
5
  NetworkError
6
6
  } from "./chunk-7QLSKOSU.js";
7
7
  import {
8
8
  getBaseUrl
9
- } from "./chunk-3LS3GFJW.js";
9
+ } from "./chunk-AZXPJLKC.js";
10
10
  import {
11
11
  clientContextHeaders,
12
12
  getPackageVersion
13
- } from "./chunk-7FQL5NWJ.js";
13
+ } from "./chunk-235LKHLW.js";
14
14
 
15
15
  // src/cli/net.ts
16
16
  import { createRequire } from "module";
@@ -3,11 +3,11 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-CK3W7Q5S.js";
6
+ } from "./chunk-MAGB6VW6.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
10
- } from "./chunk-4PIAOJAW.js";
10
+ } from "./chunk-PYWLW7PC.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-POFBE6ZY.js";
4
4
  import {
5
5
  autoUpdateState
6
- } from "./chunk-UPRNSD2T.js";
6
+ } from "./chunk-RFIHLLRH.js";
7
7
  import {
8
8
  readJsonSafe,
9
9
  writeJsonAtomic
@@ -15,7 +15,7 @@ import {
15
15
  globalUpgradeEntry,
16
16
  isAutoUpdateResult,
17
17
  ownPackageRoot
18
- } from "./chunk-7FQL5NWJ.js";
18
+ } from "./chunk-235LKHLW.js";
19
19
 
20
20
  // src/auto-update.ts
21
21
  import { spawn } from "child_process";