@pushary/agent-hooks 0.88.0 → 0.89.0

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 (62) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/data/cursor-plugin/scripts/pushary-gate.mjs +63 -7
  3. package/data/vscode-plugin/scripts/pushary-gate.mjs +56 -18
  4. package/dist/bin/pushary-bell-hook.js +4 -4
  5. package/dist/bin/pushary-bell.js +7 -7
  6. package/dist/bin/pushary-claude.js +9 -9
  7. package/dist/bin/pushary-clean.js +11 -13
  8. package/dist/bin/pushary-codex-bridge.js +6 -6
  9. package/dist/bin/pushary-codex-hook.js +5 -5
  10. package/dist/bin/pushary-codex.js +4 -4
  11. package/dist/bin/pushary-connect.js +6 -6
  12. package/dist/bin/pushary-daemon.js +5 -5
  13. package/dist/bin/pushary-disconnect.js +9 -11
  14. package/dist/bin/pushary-doctor.js +15 -15
  15. package/dist/bin/pushary-elicitation-hook.js +7 -120
  16. package/dist/bin/pushary-gemini-bridge.js +6 -6
  17. package/dist/bin/pushary-gemini-hook.js +5 -5
  18. package/dist/bin/pushary-hook.js +6 -6
  19. package/dist/bin/pushary-login.js +4 -4
  20. package/dist/bin/pushary-logout.js +4 -4
  21. package/dist/bin/pushary-mode.js +3 -3
  22. package/dist/bin/pushary-notification-hook.js +4 -4
  23. package/dist/bin/pushary-opencode-hook.js +5 -5
  24. package/dist/bin/pushary-permission-denied-hook.js +6 -6
  25. package/dist/bin/pushary-permission-hook.js +6 -6
  26. package/dist/bin/pushary-post-hook.js +4 -4
  27. package/dist/bin/pushary-prompt-hook.js +4 -4
  28. package/dist/bin/pushary-session-end-hook.js +4 -4
  29. package/dist/bin/pushary-session-start-hook.js +4 -4
  30. package/dist/bin/pushary-setup.js +32 -29
  31. package/dist/bin/pushary-stats.js +2 -2
  32. package/dist/bin/pushary-status.js +5 -5
  33. package/dist/bin/pushary-stop-hook.js +4 -4
  34. package/dist/bin/pushary-stopfailure-hook.js +4 -4
  35. package/dist/bin/pushary-upgrade.js +10 -12
  36. package/dist/bin/pushary-wait.js +3 -3
  37. package/dist/{chunk-OULRSHFB.js → chunk-4FANZF2L.js} +2 -2
  38. package/dist/{chunk-DSTZ2RBU.js → chunk-4KMWTOBK.js} +4 -1
  39. package/dist/{chunk-LEYXXXRN.js → chunk-6GBQDZ7K.js} +1 -1
  40. package/dist/{chunk-B7ZBHWOW.js → chunk-AIGDBRIJ.js} +1 -1
  41. package/dist/{chunk-VSCSZ2LI.js → chunk-AJ4EMW5D.js} +1 -1
  42. package/dist/{chunk-DXBUFXPL.js → chunk-DLI5IXSD.js} +1 -1
  43. package/dist/{chunk-67TNJVJW.js → chunk-EKNFTIKO.js} +1 -1
  44. package/dist/{chunk-MY5A4EPQ.js → chunk-F3ZDPL6O.js} +187 -60
  45. package/dist/{chunk-VHHVGVFQ.js → chunk-FRH2QIMY.js} +1 -1
  46. package/dist/{chunk-XFHMGVMX.js → chunk-LNAQWWBP.js} +1 -1
  47. package/dist/{chunk-F5W3AY6L.js → chunk-LXTX5CT7.js} +1 -1
  48. package/dist/{chunk-T2XZH7D5.js → chunk-O7P7O4VY.js} +2 -2
  49. package/dist/{chunk-MNXG6IIM.js → chunk-OSE7X44J.js} +3 -3
  50. package/dist/{chunk-4VYELDAH.js → chunk-PXEFENML.js} +1 -1
  51. package/dist/{chunk-3JL64GVE.js → chunk-Q6MK2YKV.js} +120 -1
  52. package/dist/{chunk-ESARAYRI.js → chunk-SHVW5KFR.js} +1 -1
  53. package/dist/{chunk-RSUBLTMP.js → chunk-SUZF64M4.js} +2 -2
  54. package/dist/{chunk-3B3PYTKH.js → chunk-U44C3ULB.js} +1 -1
  55. package/dist/{chunk-UTQCV46U.js → chunk-U65EIMUJ.js} +4 -4
  56. package/dist/{chunk-6BOE5NJZ.js → chunk-WFFPWJN6.js} +1 -1
  57. package/dist/{chunk-WXCHB6BO.js → chunk-WPDMGNQR.js} +1 -1
  58. package/dist/{chunk-3PSEZOC4.js → chunk-ZPRF4YLA.js} +1 -1
  59. package/dist/src/index.js +6 -6
  60. package/package.json +1 -1
  61. package/dist/chunk-U6HRYNHC.js +0 -137
  62. /package/dist/{chunk-VT4IVERX.js → chunk-PZA7G4CN.js} +0 -0
@@ -2,10 +2,177 @@ import {
2
2
  PRETOOLUSE_GATED_TOOLS,
3
3
  PRETOOLUSE_PHONE_ANSWERED_TOOLS
4
4
  } from "./chunk-ATBKJNWS.js";
5
+ import {
6
+ guardBinary
7
+ } from "./chunk-AIGDBRIJ.js";
5
8
  import {
6
9
  HOOK_BUDGETS,
7
10
  HOOK_LOCATOR_BINARY
8
- } from "./chunk-3JL64GVE.js";
11
+ } from "./chunk-Q6MK2YKV.js";
12
+
13
+ // src/claude-config.ts
14
+ import { join } from "path";
15
+
16
+ // src/agent-version.ts
17
+ var parseVersion = (raw) => {
18
+ const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
19
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
20
+ };
21
+ var compareVersion = (a, b) => {
22
+ for (let index = 0; index < 3; index++) {
23
+ if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
24
+ }
25
+ return 0;
26
+ };
27
+ var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
28
+
29
+ // src/claude-events.ts
30
+ var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
31
+ var MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"].join("|");
32
+ var ESTABLISHED = [1, 0, 0];
33
+ var OBSERVED = [2, 0, 0];
34
+ var CLAUDE_HOOK_EVENTS = [
35
+ { event: "PreToolUse", binary: "pushary-hook", matcher: GATED_TOOLS, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
36
+ { event: "PostToolUse", binary: "pushary-post-hook", matcher: MIRRORED_TOOLS, timeout: 10, delivery: "telemetry", since: ESTABLISHED },
37
+ { event: "Stop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
38
+ { event: "UserPromptSubmit", binary: "pushary-prompt-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
39
+ { event: "Notification", binary: "pushary-notification-hook", matcher: "permission_prompt|idle_prompt|agent_needs_input|agent_completed", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
40
+ { event: "SessionStart", binary: "pushary-session-start-hook", matcher: "startup|resume|clear", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
41
+ { event: "SessionEnd", binary: "pushary-session-end-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
42
+ { event: "StopFailure", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
43
+ { event: "PermissionRequest", binary: "pushary-permission-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
44
+ { event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: 60, delivery: "telemetry", since: ESTABLISHED },
45
+ // Observability only. Each carries session shape the island cannot get any other
46
+ // way, and none of them decides anything.
47
+ { event: "SubagentStart", binary: "pushary-session-start-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
48
+ { event: "SubagentStop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
49
+ { event: "PreCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
50
+ { event: "PostCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
51
+ { event: "TeammateIdle", binary: "pushary-notification-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
52
+ // An MCP server asking the person for a value, answered from the phone rather
53
+ // than the terminal. No matcher: Claude Code matches this event on the MCP
54
+ // server's name, and we have no business preferring one server over another.
55
+ { event: "Elicitation", binary: "pushary-elicitation-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: OBSERVED }
56
+ ];
57
+ var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
58
+
59
+ // src/claude-config.ts
60
+ var PUSHARY_MCP_URL = "https://pushary.com/api/mcp/mcp";
61
+ var PUSHARY_PERMISSION_RULE = "mcp__pushary__*";
62
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
63
+ var ensureRecord = (target, key) => {
64
+ const existing = asRecord(target[key]);
65
+ if (existing) return existing;
66
+ const created = {};
67
+ target[key] = created;
68
+ return created;
69
+ };
70
+ var isPusharyPermission = (rule) => typeof rule === "string" && (rule.includes("pushary") || rule.includes("MCP(pushary"));
71
+ var HOOK_BINARIES = /* @__PURE__ */ new Set([
72
+ ...CLAUDE_HOOK_EVENTS.map((hook) => hook.binary),
73
+ HOOK_LOCATOR_BINARY
74
+ ]);
75
+ var namesPusharyBinary = (command) => command.split(/[\s/\\"']+/).some((segment) => HOOK_BINARIES.has(segment.replace(/\.(cmd|exe)$/i, "")));
76
+ var isPusharyHook = (entry2) => {
77
+ const hooks = asRecord(entry2)?.hooks;
78
+ if (!Array.isArray(hooks)) return false;
79
+ return hooks.some((hook) => namesPusharyBinary(String(asRecord(hook)?.command ?? "")));
80
+ };
81
+ var addClaudeMcpServer = (config, apiKey) => {
82
+ const mcpServers = ensureRecord(config, "mcpServers");
83
+ mcpServers.pushary = {
84
+ type: "http",
85
+ url: PUSHARY_MCP_URL,
86
+ headers: { Authorization: `Bearer ${apiKey}` }
87
+ };
88
+ };
89
+ var removeClaudeMcpServers = (config) => {
90
+ let changed = false;
91
+ const removeFrom = (target) => {
92
+ const mcpServers = asRecord(target.mcpServers);
93
+ if (!mcpServers?.pushary) return;
94
+ delete mcpServers.pushary;
95
+ if (Object.keys(mcpServers).length === 0) delete target.mcpServers;
96
+ changed = true;
97
+ };
98
+ removeFrom(config);
99
+ const projects = asRecord(config.projects);
100
+ if (projects) {
101
+ for (const project of Object.values(projects)) {
102
+ const projectConfig = asRecord(project);
103
+ if (projectConfig) removeFrom(projectConfig);
104
+ }
105
+ }
106
+ return changed;
107
+ };
108
+ var addPusharyToolPermissions = (settings) => {
109
+ const permissions = ensureRecord(settings, "permissions");
110
+ const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
111
+ const filtered = allow.filter((rule) => !isPusharyPermission(rule));
112
+ if (!filtered.includes(PUSHARY_PERMISSION_RULE)) {
113
+ filtered.push(PUSHARY_PERMISSION_RULE);
114
+ }
115
+ permissions.allow = filtered;
116
+ };
117
+ var addPusharyHooks = (settings, binDir, version = null) => {
118
+ const resolve = (name) => guardBinary(binDir ? join(binDir, name) : name);
119
+ const hooks = ensureRecord(settings, "hooks");
120
+ const supported = new Map(supportedClaudeEvents(version).map((hook) => [hook.event, hook]));
121
+ for (const known of CLAUDE_HOOK_EVENTS) {
122
+ const current = hooks[known.event];
123
+ const others = (Array.isArray(current) ? current : []).filter((item) => !isPusharyHook(item));
124
+ const definition = supported.get(known.event);
125
+ if (definition) {
126
+ hooks[known.event] = [...others, entry(definition, resolve)];
127
+ } else if (others.length > 0) {
128
+ hooks[known.event] = others;
129
+ } else {
130
+ delete hooks[known.event];
131
+ }
132
+ }
133
+ };
134
+ var entry = (definition, resolve) => ({
135
+ ...definition.matcher ? { matcher: definition.matcher } : {},
136
+ hooks: [{
137
+ type: "command",
138
+ command: resolve(definition.binary),
139
+ timeout: definition.timeout
140
+ }]
141
+ });
142
+ var removePusharySettings = (settings) => {
143
+ let changed = removeClaudeMcpServers(settings);
144
+ const permissions = asRecord(settings.permissions);
145
+ if (permissions && Array.isArray(permissions.allow)) {
146
+ const filtered = permissions.allow.filter((rule) => !isPusharyPermission(rule));
147
+ if (filtered.length !== permissions.allow.length) {
148
+ if (filtered.length === 0) {
149
+ delete permissions.allow;
150
+ } else {
151
+ permissions.allow = filtered;
152
+ }
153
+ if (Object.keys(permissions).length === 0) delete settings.permissions;
154
+ changed = true;
155
+ }
156
+ }
157
+ const hooks = asRecord(settings.hooks);
158
+ if (hooks) {
159
+ for (const { event: key } of CLAUDE_HOOK_EVENTS) {
160
+ const entries = hooks[key];
161
+ if (!Array.isArray(entries)) continue;
162
+ const filtered = entries.filter((entry2) => !isPusharyHook(entry2));
163
+ if (filtered.length !== entries.length) {
164
+ if (filtered.length === 0) {
165
+ delete hooks[key];
166
+ } else {
167
+ hooks[key] = filtered;
168
+ }
169
+ changed = true;
170
+ }
171
+ }
172
+ if (Object.keys(hooks).length === 0) delete settings.hooks;
173
+ }
174
+ return changed;
175
+ };
9
176
 
10
177
  // src/gemini-config.ts
11
178
  var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
@@ -17,24 +184,24 @@ var GEMINI_HOOK_EVENTS = [
17
184
  { event: "SessionStart", delivery: "telemetry", timeoutMs: 1e4 },
18
185
  { event: "SessionEnd", delivery: "telemetry", timeoutMs: 1e4 }
19
186
  ];
20
- var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
21
- var ensureRecord = (target, key) => {
22
- const existing = asRecord(target[key]);
187
+ var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
188
+ var ensureRecord2 = (target, key) => {
189
+ const existing = asRecord2(target[key]);
23
190
  if (existing) return existing;
24
191
  const created = {};
25
192
  target[key] = created;
26
193
  return created;
27
194
  };
28
- var isPusharyGeminiHook = (entry) => {
29
- const hooks = asRecord(entry)?.hooks;
195
+ var isPusharyGeminiHook = (entry2) => {
196
+ const hooks = asRecord2(entry2)?.hooks;
30
197
  if (!Array.isArray(hooks)) return false;
31
198
  return hooks.some((hook) => {
32
- const command = String(asRecord(hook)?.command ?? "");
199
+ const command = String(asRecord2(hook)?.command ?? "");
33
200
  return command.includes(GEMINI_HOOK_BINARY) || command.includes(HOOK_LOCATOR_BINARY);
34
201
  });
35
202
  };
36
203
  var addGeminiMcpServer = (settings, apiKey) => {
37
- const mcpServers = ensureRecord(settings, "mcpServers");
204
+ const mcpServers = ensureRecord2(settings, "mcpServers");
38
205
  mcpServers.pushary = {
39
206
  httpUrl: GEMINI_MCP_URL,
40
207
  headers: { Authorization: `Bearer ${apiKey}` },
@@ -42,10 +209,10 @@ var addGeminiMcpServer = (settings, apiKey) => {
42
209
  };
43
210
  };
44
211
  var addGeminiHooks = (settings, command) => {
45
- const hooks = ensureRecord(settings, "hooks");
212
+ const hooks = ensureRecord2(settings, "hooks");
46
213
  for (const definition of GEMINI_HOOK_EVENTS) {
47
214
  const existing = Array.isArray(hooks[definition.event]) ? hooks[definition.event] : [];
48
- const entries = existing.filter((entry) => !isPusharyGeminiHook(entry));
215
+ const entries = existing.filter((entry2) => !isPusharyGeminiHook(entry2));
49
216
  entries.push({
50
217
  ...definition.matcher ? { matcher: definition.matcher } : {},
51
218
  hooks: [{
@@ -59,13 +226,13 @@ var addGeminiHooks = (settings, command) => {
59
226
  }
60
227
  };
61
228
  var removeGeminiHooks = (settings) => {
62
- const hooks = asRecord(settings.hooks);
229
+ const hooks = asRecord2(settings.hooks);
63
230
  if (!hooks) return false;
64
231
  let changed = false;
65
232
  for (const definition of GEMINI_HOOK_EVENTS) {
66
233
  const entries = hooks[definition.event];
67
234
  if (!Array.isArray(entries)) continue;
68
- const filtered = entries.filter((entry) => !isPusharyGeminiHook(entry));
235
+ const filtered = entries.filter((entry2) => !isPusharyGeminiHook(entry2));
69
236
  if (filtered.length !== entries.length) {
70
237
  if (filtered.length === 0) {
71
238
  delete hooks[definition.event];
@@ -79,7 +246,7 @@ var removeGeminiHooks = (settings) => {
79
246
  return changed;
80
247
  };
81
248
  var removeGeminiMcpServer = (settings) => {
82
- const mcpServers = asRecord(settings.mcpServers);
249
+ const mcpServers = asRecord2(settings.mcpServers);
83
250
  if (!mcpServers?.pushary) return false;
84
251
  delete mcpServers.pushary;
85
252
  if (Object.keys(mcpServers).length === 0) delete settings.mcpServers;
@@ -91,7 +258,7 @@ var removeGeminiSettings = (settings) => {
91
258
  return mcpRemoved || hooksRemoved;
92
259
  };
93
260
  var hasGeminiHooks = (settings) => {
94
- const hooks = asRecord(settings.hooks);
261
+ const hooks = asRecord2(settings.hooks);
95
262
  if (!hooks) return false;
96
263
  return GEMINI_HOOK_EVENTS.some((definition) => {
97
264
  const entries = hooks[definition.event];
@@ -99,61 +266,21 @@ var hasGeminiHooks = (settings) => {
99
266
  });
100
267
  };
101
268
  var missingGeminiHookEvents = (settings) => {
102
- const hooks = asRecord(settings.hooks);
269
+ const hooks = asRecord2(settings.hooks);
103
270
  return GEMINI_HOOK_EVENTS.filter((definition) => {
104
271
  const entries = hooks?.[definition.event];
105
272
  return !Array.isArray(entries) || !entries.some(isPusharyGeminiHook);
106
273
  }).map((definition) => definition.event);
107
274
  };
108
275
 
109
- // src/agent-version.ts
110
- var parseVersion = (raw) => {
111
- const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
112
- return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
113
- };
114
- var compareVersion = (a, b) => {
115
- for (let index = 0; index < 3; index++) {
116
- if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
117
- }
118
- return 0;
119
- };
120
- var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
121
-
122
- // src/claude-events.ts
123
- var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
124
- var MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"].join("|");
125
- var ESTABLISHED = [1, 0, 0];
126
- var OBSERVED = [2, 0, 0];
127
- var CLAUDE_HOOK_EVENTS = [
128
- { event: "PreToolUse", binary: "pushary-hook", matcher: GATED_TOOLS, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
129
- { event: "PostToolUse", binary: "pushary-post-hook", matcher: MIRRORED_TOOLS, timeout: 10, delivery: "telemetry", since: ESTABLISHED },
130
- { event: "Stop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
131
- { event: "UserPromptSubmit", binary: "pushary-prompt-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
132
- { event: "Notification", binary: "pushary-notification-hook", matcher: "permission_prompt|idle_prompt|agent_needs_input|agent_completed", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
133
- { event: "SessionStart", binary: "pushary-session-start-hook", matcher: "startup|resume|clear", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
134
- { event: "SessionEnd", binary: "pushary-session-end-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
135
- { event: "StopFailure", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
136
- { event: "PermissionRequest", binary: "pushary-permission-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
137
- { event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: 60, delivery: "telemetry", since: ESTABLISHED },
138
- // Observability only. Each carries session shape the island cannot get any other
139
- // way, and none of them decides anything.
140
- { event: "SubagentStart", binary: "pushary-session-start-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
141
- { event: "SubagentStop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
142
- { event: "PreCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
143
- { event: "PostCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
144
- { event: "TeammateIdle", binary: "pushary-notification-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
145
- // An MCP server asking the person for a value, answered from the phone rather
146
- // than the terminal. No matcher: Claude Code matches this event on the MCP
147
- // server's name, and we have no business preferring one server over another.
148
- { event: "Elicitation", binary: "pushary-elicitation-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: OBSERVED, cliOnly: true }
149
- ];
150
- var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
151
-
152
276
  export {
153
277
  parseVersion,
154
278
  compareVersion,
155
- CLAUDE_HOOK_EVENTS,
156
- supportedClaudeEvents,
279
+ addClaudeMcpServer,
280
+ removeClaudeMcpServers,
281
+ addPusharyToolPermissions,
282
+ addPusharyHooks,
283
+ removePusharySettings,
157
284
  GEMINI_HOOK_BINARY,
158
285
  addGeminiMcpServer,
159
286
  addGeminiHooks,
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  HOOK_BUDGETS,
6
6
  HOOK_LOCATOR_BINARY
7
- } from "./chunk-3JL64GVE.js";
7
+ } from "./chunk-Q6MK2YKV.js";
8
8
 
9
9
  // src/codex-config.ts
10
10
  import { createHash } from "crypto";
@@ -5,7 +5,7 @@ import {
5
5
  generateSessionKey,
6
6
  uploadTranscriptRecords,
7
7
  wrapSessionKey
8
- } from "./chunk-LEYXXXRN.js";
8
+ } from "./chunk-6GBQDZ7K.js";
9
9
 
10
10
  // src/live-session.ts
11
11
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -5,7 +5,7 @@ import {
5
5
  FILE_TARGET_TOOLS,
6
6
  redactSecrets,
7
7
  redactSecretsDeep
8
- } from "./chunk-3JL64GVE.js";
8
+ } from "./chunk-Q6MK2YKV.js";
9
9
 
10
10
  // ../hook-mapping/src/describe.ts
11
11
  import { isAbsolute, relative, resolve } from "path";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  openCodeConfigDirFrom
3
- } from "./chunk-DSTZ2RBU.js";
3
+ } from "./chunk-4KMWTOBK.js";
4
4
  import {
5
5
  codexHomeFrom
6
- } from "./chunk-VHHVGVFQ.js";
6
+ } from "./chunk-FRH2QIMY.js";
7
7
 
8
8
  // src/vscode-config.ts
9
9
  import { homedir } from "os";
@@ -22,7 +22,7 @@ import {
22
22
  summarizeStop,
23
23
  taskTitleFrom,
24
24
  toolResultErrorText
25
- } from "./chunk-F5W3AY6L.js";
25
+ } from "./chunk-LXTX5CT7.js";
26
26
  import {
27
27
  getMachineId
28
28
  } from "./chunk-RN3NOEJF.js";
@@ -31,7 +31,7 @@ import {
31
31
  } from "./chunk-BSZYIAZL.js";
32
32
  import {
33
33
  reportedInstallSource
34
- } from "./chunk-VSCSZ2LI.js";
34
+ } from "./chunk-AJ4EMW5D.js";
35
35
  import {
36
36
  getApiKey,
37
37
  getBaseUrl
@@ -47,7 +47,7 @@ import {
47
47
  pendingCommandEvents,
48
48
  resolveAutoResolveOrigin,
49
49
  resolvePolicy
50
- } from "./chunk-3JL64GVE.js";
50
+ } from "./chunk-Q6MK2YKV.js";
51
51
 
52
52
  // src/policy.ts
53
53
  import { createHash } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-3JL64GVE.js";
3
+ } from "./chunk-Q6MK2YKV.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -76,6 +76,8 @@ var INSTALL_SOURCES = [
76
76
  /** The browser onboarding wizard. */
77
77
  "dashboard",
78
78
  /** Reported nothing, or reported something we do not recognise. */
79
+ /** The macOS app pairing a phone from its own onboarding. */
80
+ "mac_app",
79
81
  "unknown"
80
82
  ];
81
83
  var isInstallSource = (value) => typeof value === "string" && INSTALL_SOURCES.includes(value);
@@ -1207,6 +1209,118 @@ var claudeElicitationOutput = (event, action, content) => ({
1207
1209
  }
1208
1210
  });
1209
1211
  var renderElicitationOutput = (event, action, content) => ELICITATION_EVENTS.includes(event) ? JSON.stringify(claudeElicitationOutput(event, action, content)) : void 0;
1212
+ var ELICITATION_ANSWERABLE_MODE = "form";
1213
+ var ELICITATION_MIN_OPTIONS = 2;
1214
+ var ELICITATION_MAX_MULTI_QUESTIONS = 4;
1215
+ var ELICITATION_MAX_MULTI_OPTIONS = 4;
1216
+ var elicitationRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
1217
+ var elicitationLabel = (name, property) => typeof property.title === "string" && property.title.trim() ? property.title.trim() : name;
1218
+ var elicitationEnum = (property) => {
1219
+ if (!Array.isArray(property.enum)) return null;
1220
+ const members = property.enum.filter((member) => typeof member === "string");
1221
+ return members.length === property.enum.length && members.length >= ELICITATION_MIN_OPTIONS ? members : null;
1222
+ };
1223
+ var readElicitationFields = (schema) => {
1224
+ const record = elicitationRecord(schema);
1225
+ const properties = elicitationRecord(record?.properties);
1226
+ if (!properties) return null;
1227
+ const entries = Object.entries(properties);
1228
+ if (entries.length === 0) return null;
1229
+ const fields = [];
1230
+ for (const [name, raw] of entries) {
1231
+ const property = elicitationRecord(raw);
1232
+ if (!property) return null;
1233
+ const label = elicitationLabel(name, property);
1234
+ if ("enum" in property) {
1235
+ const options = elicitationEnum(property);
1236
+ if (!options) return null;
1237
+ fields.push({ kind: "choice", name, label, options });
1238
+ continue;
1239
+ }
1240
+ switch (property.type) {
1241
+ case "string":
1242
+ fields.push({ kind: "text", name, label });
1243
+ break;
1244
+ case "number":
1245
+ fields.push({ kind: "number", name, label, integer: false });
1246
+ break;
1247
+ case "integer":
1248
+ fields.push({ kind: "number", name, label, integer: true });
1249
+ break;
1250
+ case "boolean":
1251
+ fields.push({ kind: "boolean", name, label });
1252
+ break;
1253
+ default:
1254
+ return null;
1255
+ }
1256
+ }
1257
+ return fields;
1258
+ };
1259
+ var elicitationQuestionText = (message, field, alone) => alone ? message : `${message} \u2014 ${field.label}`;
1260
+ var planElicitationAsk = (message, fields) => {
1261
+ if (fields.length === 1) {
1262
+ const field = fields[0];
1263
+ const question = elicitationQuestionText(message, field, true);
1264
+ switch (field.kind) {
1265
+ case "choice":
1266
+ return { kind: "select", field, question, options: field.options };
1267
+ case "boolean":
1268
+ return { kind: "confirm", field, question };
1269
+ case "text":
1270
+ case "number":
1271
+ return { kind: "input", field, question };
1272
+ }
1273
+ }
1274
+ if (fields.length > ELICITATION_MAX_MULTI_QUESTIONS) return null;
1275
+ if (!fields.every((field) => field.kind === "choice" && field.options.length <= ELICITATION_MAX_MULTI_OPTIONS)) return null;
1276
+ const questions = fields.map((field) => ({
1277
+ question: elicitationQuestionText(message, field, false),
1278
+ header: field.label.slice(0, 12),
1279
+ multiSelect: false,
1280
+ options: field.options.map((label) => ({ label }))
1281
+ }));
1282
+ return new Set(questions.map((question) => question.question)).size === questions.length ? { kind: "questions", fields, questions } : null;
1283
+ };
1284
+ var ELICITATION_CONFIRMED = /* @__PURE__ */ new Set(["yes", "true", "approve", "approved", "allow"]);
1285
+ var ELICITATION_REFUSED = /* @__PURE__ */ new Set(["no", "false", "deny", "denied", "reject"]);
1286
+ var elicitationNumber = (raw, integer) => {
1287
+ const trimmed = raw.trim();
1288
+ if (!/^-?\d+(\.\d+)?$/.test(trimmed)) return null;
1289
+ const value = Number(trimmed);
1290
+ if (!Number.isFinite(value)) return null;
1291
+ return integer && !Number.isInteger(value) ? null : value;
1292
+ };
1293
+ var elicitationContent = (ask, answer) => {
1294
+ const value = answer.trim();
1295
+ if (!value) return null;
1296
+ if (ask.kind === "input") {
1297
+ if (ask.field.kind === "number") {
1298
+ const parsed = elicitationNumber(value, ask.field.integer);
1299
+ return parsed === null ? null : { [ask.field.name]: parsed };
1300
+ }
1301
+ return { [ask.field.name]: value };
1302
+ }
1303
+ if (ask.kind === "confirm") {
1304
+ const lowered = value.toLowerCase();
1305
+ if (ELICITATION_CONFIRMED.has(lowered)) return { [ask.field.name]: true };
1306
+ if (ELICITATION_REFUSED.has(lowered)) return { [ask.field.name]: false };
1307
+ return null;
1308
+ }
1309
+ if (ask.kind === "select") {
1310
+ return ask.options.includes(value) ? { [ask.field.name]: value } : null;
1311
+ }
1312
+ return null;
1313
+ };
1314
+ var elicitationContentFromAnswers = (ask, answers) => {
1315
+ const content = {};
1316
+ for (const [index, field] of ask.fields.entries()) {
1317
+ if (field.kind !== "choice") return null;
1318
+ const chosen = answers[ask.questions[index].question];
1319
+ if (typeof chosen !== "string" || !field.options.includes(chosen)) return null;
1320
+ content[field.name] = chosen;
1321
+ }
1322
+ return content;
1323
+ };
1210
1324
 
1211
1325
  export {
1212
1326
  parseAgentQuestions,
@@ -1243,5 +1357,10 @@ export {
1243
1357
  pendingCommandEvents,
1244
1358
  claudePermissionRequestOutput,
1245
1359
  gateOutputFor,
1246
- renderElicitationOutput
1360
+ renderElicitationOutput,
1361
+ ELICITATION_ANSWERABLE_MODE,
1362
+ readElicitationFields,
1363
+ planElicitationAsk,
1364
+ elicitationContent,
1365
+ elicitationContentFromAnswers
1247
1366
  };
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-BSZYIAZL.js";
12
12
  import {
13
13
  reportedInstallSource
14
- } from "./chunk-VSCSZ2LI.js";
14
+ } from "./chunk-AJ4EMW5D.js";
15
15
 
16
16
  // src/reach.ts
17
17
  var reachVerdict = (channels) => {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  hasGeminiHooks
3
- } from "./chunk-MY5A4EPQ.js";
3
+ } from "./chunk-F3ZDPL6O.js";
4
4
  import {
5
5
  hasCodexHooks
6
- } from "./chunk-VHHVGVFQ.js";
6
+ } from "./chunk-FRH2QIMY.js";
7
7
 
8
8
  // src/diagnostics/wiring.ts
9
9
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-VHHVGVFQ.js";
3
+ } from "./chunk-FRH2QIMY.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-6MTNS63X.js";
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  isGatingMoment,
7
7
  recordKeylessMoment
8
- } from "./chunk-4VYELDAH.js";
8
+ } from "./chunk-PXEFENML.js";
9
9
  import {
10
10
  denyReasonFrom,
11
11
  isDeferAnswer
@@ -17,7 +17,7 @@ import {
17
17
  readLastUserPrompt,
18
18
  repoKeyFor,
19
19
  throttlePass
20
- } from "./chunk-MNXG6IIM.js";
20
+ } from "./chunk-OSE7X44J.js";
21
21
  import {
22
22
  DEFAULT_SESSION,
23
23
  askUser,
@@ -33,7 +33,7 @@ import {
33
33
  deriveToolTarget,
34
34
  describeToolCall,
35
35
  scopePathFor
36
- } from "./chunk-F5W3AY6L.js";
36
+ } from "./chunk-LXTX5CT7.js";
37
37
  import {
38
38
  getMachineId
39
39
  } from "./chunk-RN3NOEJF.js";
@@ -52,7 +52,7 @@ import {
52
52
  parseAgentQuestions,
53
53
  resolveGate,
54
54
  shouldDeferToNativeMode
55
- } from "./chunk-3JL64GVE.js";
55
+ } from "./chunk-Q6MK2YKV.js";
56
56
 
57
57
  // src/decision-episode.ts
58
58
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-2UMNXADU.js";
13
13
  import {
14
14
  isValidApiKey
15
- } from "./chunk-3JL64GVE.js";
15
+ } from "./chunk-Q6MK2YKV.js";
16
16
 
17
17
  // src/session.ts
18
18
  var IDENTITY_PATH = "/api/v1/server/identity";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readConfigFileKey
3
- } from "./chunk-67TNJVJW.js";
3
+ } from "./chunk-EKNFTIKO.js";
4
4
 
5
5
  // src/bell/fleet.ts
6
6
  import { closeSync, constants, futimesSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-6BOE5NJZ.js";
4
+ } from "./chunk-WFFPWJN6.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";