@pushary/agent-hooks 0.8.2 → 0.9.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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  removeClaudeMcpServers,
4
4
  removePusharySettings
5
- } from "../chunk-AC4UYAGX.js";
5
+ } from "../chunk-5GFUI5N6.js";
6
6
 
7
7
  // bin/pushary-clean.ts
8
8
  import { existsSync, readFileSync, writeFileSync, rmSync } from "fs";
@@ -1,23 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reportEvent
4
- } from "../chunk-OP4QAN23.js";
4
+ } from "../chunk-AB4KX4XT.js";
5
5
  import {
6
6
  askUser,
7
+ getMachineId,
7
8
  waitForAnswer
8
- } from "../chunk-KTM4ZQ3W.js";
9
+ } from "../chunk-OF5WIOYS.js";
9
10
  import "../chunk-3MIR7ODJ.js";
10
11
  import {
11
12
  getApiKey
12
13
  } from "../chunk-VUNL35KE.js";
13
14
 
14
15
  // bin/pushary-codex.ts
15
- import { hostname } from "os";
16
16
  import { basename } from "path";
17
+ var readStdin = async () => {
18
+ let raw = "";
19
+ for await (const chunk of process.stdin) raw += chunk;
20
+ return raw;
21
+ };
17
22
  var main = async () => {
18
- let rawInput = "";
19
- for await (const chunk of process.stdin) {
20
- rawInput += chunk;
23
+ const argvPayload = process.argv.slice(2).find((a) => a.trim().startsWith("{"));
24
+ let rawInput = argvPayload ?? "";
25
+ if (!rawInput.trim()) {
26
+ rawInput = await readStdin();
21
27
  }
22
28
  if (!rawInput.trim()) {
23
29
  process.exit(0);
@@ -37,7 +43,7 @@ var main = async () => {
37
43
  agentType: "codex",
38
44
  agentName,
39
45
  action: event.message ?? "Turn complete",
40
- machineId: hostname()
46
+ machineId: getMachineId()
41
47
  });
42
48
  process.stdout.write(JSON.stringify({ acknowledged: true }));
43
49
  }
@@ -49,13 +55,14 @@ var main = async () => {
49
55
  agentType: "codex",
50
56
  agentName,
51
57
  action: description,
52
- machineId: hostname()
58
+ machineId: getMachineId()
53
59
  });
54
60
  const result = await askUser(apiKey, {
55
61
  question: `Allow: ${description}?`,
56
62
  type: "confirm",
57
63
  context: `Codex wants to run this in ${projectName}`,
58
- agentName
64
+ agentName,
65
+ machineId: getMachineId()
59
66
  });
60
67
  const deadline = Date.now() + 12e4;
61
68
  while (Date.now() < deadline) {
@@ -69,7 +76,7 @@ var main = async () => {
69
76
  agentType: "codex",
70
77
  agentName,
71
78
  action: approved ? `Approved: ${description}` : `Denied: ${description}`,
72
- machineId: hostname()
79
+ machineId: getMachineId()
73
80
  });
74
81
  process.exit(0);
75
82
  }
@@ -9,6 +9,7 @@ import "../chunk-VUNL35KE.js";
9
9
  import { existsSync, readFileSync } from "fs";
10
10
  import { join } from "path";
11
11
  import { homedir } from "os";
12
+ import { execSync } from "child_process";
12
13
  import { confirm } from "@inquirer/prompts";
13
14
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
14
15
  var bold = (s) => `\x1B[1m${s}\x1B[0m`;
@@ -29,6 +30,28 @@ var readJson = (path) => {
29
30
  return null;
30
31
  }
31
32
  };
33
+ var commandResolves = (command) => {
34
+ if (command.includes("/") || command.includes("\\")) return existsSync(command);
35
+ try {
36
+ const whichCmd = process.platform === "win32" ? "where" : "which";
37
+ execSync(`${whichCmd} ${command}`, { stdio: "ignore", timeout: 5e3 });
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ };
43
+ var extractHookCommand = (entries, needle) => {
44
+ if (!Array.isArray(entries)) return null;
45
+ for (const entry of entries) {
46
+ const hookList = entry.hooks;
47
+ if (!Array.isArray(hookList)) continue;
48
+ for (const candidate of hookList) {
49
+ const command = candidate.command;
50
+ if (typeof command === "string" && command.includes(needle)) return command;
51
+ }
52
+ }
53
+ return null;
54
+ };
32
55
  var results = [];
33
56
  var check = (passed, label, detail) => {
34
57
  results.push({ passed, label, detail });
@@ -80,6 +103,11 @@ var main = async () => {
80
103
  check(hasPreHook, "Claude Code: PreToolUse hook");
81
104
  check(hasPostHook, "Claude Code: PostToolUse hook");
82
105
  check(hasStopHook, "Claude Code: Stop hook");
106
+ const preHookCommand = extractHookCommand(hooks?.PreToolUse, "pushary-hook");
107
+ if (preHookCommand) {
108
+ const resolves = commandResolves(preHookCommand);
109
+ check(resolves, "Claude Code: hook command resolves", resolves ? preHookCommand : `not on PATH \u2014 ${preHookCommand}`);
110
+ }
83
111
  const permissions = settings.permissions;
84
112
  const hasWildcard = permissions?.allow?.some((r) => r === "mcp__pushary__*" || r === "MCP(pushary:*)") ?? false;
85
113
  check(hasWildcard, "Claude Code: Pushary tools auto-allowed", hasWildcard ? "mcp__pushary__*" : "missing");
@@ -103,16 +131,21 @@ var main = async () => {
103
131
  console.log(` ${warn} Codex: per-tool approval overrides detected ${dim("(redundant with default_tools_approval_mode)")}`);
104
132
  }
105
133
  }
106
- check(codexConfig.includes("pushary-codex"), "Codex: notify handler configured");
134
+ const codexNotifyPath = codexConfig.match(/["']([^"']*pushary-codex[^"']*)["']/)?.[1] ?? null;
135
+ check(!!codexNotifyPath, "Codex: notify handler configured");
136
+ if (codexNotifyPath) {
137
+ const resolves = commandResolves(codexNotifyPath);
138
+ check(resolves, "Codex: notify handler resolves", resolves ? codexNotifyPath : `not found \u2014 ${codexNotifyPath}`);
139
+ }
107
140
  const codexSkillPath = join(homedir(), ".codex", "skills", "pushary", "SKILL.md");
108
141
  check(existsSync(codexSkillPath), "Codex: skill installed");
109
142
  }
110
143
  check(existsSync(SKILL_PATH), "Skill installed", existsSync(SKILL_PATH) ? SKILL_PATH : "not found");
111
144
  let globalVersion = "";
112
145
  try {
113
- const { execSync } = await import("child_process");
114
146
  globalVersion = execSync("npm list -g @pushary/agent-hooks --depth=0 2>/dev/null", { encoding: "utf-8", timeout: 1e4 }).match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
115
147
  } catch {
148
+ globalVersion = "";
116
149
  }
117
150
  check(!!globalVersion, "Global package installed", globalVersion || "not found");
118
151
  console.log();
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePreToolUse
4
- } from "../chunk-2CRLBMOX.js";
5
- import "../chunk-KTM4ZQ3W.js";
4
+ } from "../chunk-W5KRWUNE.js";
5
+ import "../chunk-IBWCHA5M.js";
6
+ import "../chunk-OF5WIOYS.js";
6
7
  import "../chunk-3MIR7ODJ.js";
7
8
  import "../chunk-VUNL35KE.js";
8
9
 
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePostToolUse
4
- } from "../chunk-OP4QAN23.js";
5
- import "../chunk-KTM4ZQ3W.js";
4
+ } from "../chunk-AB4KX4XT.js";
5
+ import "../chunk-OF5WIOYS.js";
6
6
  import "../chunk-3MIR7ODJ.js";
7
7
  import "../chunk-VUNL35KE.js";
8
8
 
@@ -3,7 +3,10 @@ import {
3
3
  addClaudeMcpServer,
4
4
  addPusharyHooks,
5
5
  addPusharyToolPermissions
6
- } from "../chunk-AC4UYAGX.js";
6
+ } from "../chunk-5GFUI5N6.js";
7
+ import {
8
+ isValidApiKey
9
+ } from "../chunk-IBWCHA5M.js";
7
10
 
8
11
  // bin/pushary-setup.ts
9
12
  import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from "fs";
@@ -34,6 +37,15 @@ var parseKeyFlag = () => {
34
37
  }
35
38
  return void 0;
36
39
  };
40
+ var isInstalled = (command) => {
41
+ const whichCmd = process.platform === "win32" ? "where" : "which";
42
+ try {
43
+ execSync(`${whichCmd} ${command}`, { stdio: "ignore", timeout: 5e3 });
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ };
37
49
  var readJson = (path) => {
38
50
  try {
39
51
  return JSON.parse(readFileSync(path, "utf-8"));
@@ -151,7 +163,13 @@ var setupClaudeCode = async (apiKey) => {
151
163
  });
152
164
  await installGlobally();
153
165
  await spinner("Adding hooks (PreToolUse, PostToolUse, Stop)", async () => {
154
- addPusharyHooks(settings);
166
+ let binDir;
167
+ try {
168
+ binDir = join(execSync("npm prefix -g", { encoding: "utf-8", timeout: 5e3 }).trim(), "bin");
169
+ } catch {
170
+ binDir = void 0;
171
+ }
172
+ addPusharyHooks(settings, binDir);
155
173
  });
156
174
  await spinner(`Writing ${CLAUDE_SETTINGS}`, async () => {
157
175
  writeJson(CLAUDE_SETTINGS, settings);
@@ -185,16 +203,7 @@ var setupHermes = async (_apiKey) => {
185
203
  console.log(`
186
204
  ${bold("Setting up Hermes Agent")}
187
205
  `);
188
- const whichCmd = process.platform === "win32" ? "where" : "which";
189
- const hasHermes = (() => {
190
- try {
191
- execSync(`${whichCmd} hermes`, { stdio: "ignore", timeout: 5e3 });
192
- return true;
193
- } catch {
194
- return false;
195
- }
196
- })();
197
- if (!hasHermes) {
206
+ if (!isInstalled("hermes")) {
198
207
  console.log(` ${yellow("!")} Hermes CLI not found. Skipping.`);
199
208
  console.log(` ${dim("Install Hermes and re-run setup to configure.")}`);
200
209
  return;
@@ -256,16 +265,7 @@ var setupCodex = async (_apiKey) => {
256
265
  console.log(`
257
266
  ${bold("Setting up Codex")}
258
267
  `);
259
- const whichCmd = process.platform === "win32" ? "where" : "which";
260
- const hasCodex = (() => {
261
- try {
262
- execSync(`${whichCmd} codex`, { stdio: "ignore", timeout: 5e3 });
263
- return true;
264
- } catch {
265
- return false;
266
- }
267
- })();
268
- if (!hasCodex) {
268
+ if (!isInstalled("codex")) {
269
269
  console.log(` ${yellow("!")} Codex CLI not found. Skipping.`);
270
270
  console.log(` ${dim("Install Codex and re-run setup to configure.")}`);
271
271
  return;
@@ -402,16 +402,15 @@ var main = async () => {
402
402
  console.log(` ${dim("Push notifications for AI coding agents")}`);
403
403
  console.log();
404
404
  await checkForUpdates(version);
405
- const keyPattern = /^pk_[a-f0-9]+\.[a-f0-9]+$/;
406
405
  const flagKey = parseKeyFlag();
407
406
  const envKey = process.env.PUSHARY_API_KEY?.trim();
408
407
  let trimmedKey;
409
- if (flagKey && keyPattern.test(flagKey)) {
408
+ if (flagKey && isValidApiKey(flagKey)) {
410
409
  const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
411
410
  console.log(` ${check} Using API key: ${dim(masked)}`);
412
411
  console.log();
413
412
  trimmedKey = flagKey;
414
- } else if (envKey && keyPattern.test(envKey)) {
413
+ } else if (envKey && isValidApiKey(envKey)) {
415
414
  const masked = `${envKey.slice(0, 8)}...${envKey.slice(-4)}`;
416
415
  console.log(` ${check} Found API key in environment: ${dim(masked)}`);
417
416
  console.log();
@@ -429,7 +428,7 @@ var main = async () => {
429
428
  const apiKey = await input({ message: "API key:" });
430
429
  trimmedKey = apiKey.trim();
431
430
  }
432
- if (!trimmedKey || !keyPattern.test(trimmedKey)) {
431
+ if (!trimmedKey || !isValidApiKey(trimmedKey)) {
433
432
  console.log(`
434
433
  ${yellow("!")} Invalid key format. Expected: ${dim("pk_xxx.xxx")}`);
435
434
  console.log(` ${dim("Copy your key from")} ${cyan("https://pushary.com/dashboard/agent/settings")}`);
@@ -437,13 +436,20 @@ var main = async () => {
437
436
  `);
438
437
  process.exit(1);
439
438
  }
439
+ const detected = {
440
+ claude_code: isInstalled("claude"),
441
+ codex: isInstalled("codex"),
442
+ hermes: isInstalled("hermes"),
443
+ cursor: isInstalled("cursor")
444
+ };
445
+ const hint = Object.values(detected).some(Boolean) ? "(detected agents pre-selected)" : "(space = toggle, enter = confirm)";
440
446
  const agents = await checkbox({
441
- message: "Which agents do you use? " + dim("(space = toggle, enter = confirm)"),
447
+ message: "Which agents do you use? " + dim(hint),
442
448
  choices: [
443
- { name: `Claude Code ${dim("MCP + hooks + auto-allowed tools")}`, value: "claude_code" },
444
- { name: `Codex ${dim("MCP + notify handler + auto-allowed tools")}`, value: "codex" },
445
- { name: `Hermes ${dim("native plugin + auto-error notifications")}`, value: "hermes" },
446
- { name: `Cursor ${dim("MCP server")}`, value: "cursor" }
449
+ { name: `Claude Code ${dim("MCP + hooks + auto-allowed tools")}`, value: "claude_code", checked: detected.claude_code },
450
+ { name: `Codex ${dim("MCP + notify handler + auto-allowed tools")}`, value: "codex", checked: detected.codex },
451
+ { name: `Hermes ${dim("native plugin + auto-error notifications")}`, value: "hermes", checked: detected.hermes },
452
+ { name: `Cursor ${dim("MCP server")}`, value: "cursor", checked: detected.cursor }
447
453
  ]
448
454
  });
449
455
  await saveApiKey(trimmedKey);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStop
4
- } from "../chunk-OP4QAN23.js";
5
- import "../chunk-KTM4ZQ3W.js";
4
+ } from "../chunk-AB4KX4XT.js";
5
+ import "../chunk-OF5WIOYS.js";
6
6
  import "../chunk-3MIR7ODJ.js";
7
7
  import "../chunk-VUNL35KE.js";
8
8
 
@@ -0,0 +1,132 @@
1
+ // src/claude-config.ts
2
+ import { join } from "path";
3
+ var PUSHARY_MCP_URL = "https://pushary.com/api/mcp/mcp";
4
+ var PUSHARY_PERMISSION_RULE = "mcp__pushary__*";
5
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
6
+ var ensureRecord = (target, key) => {
7
+ const existing = asRecord(target[key]);
8
+ if (existing) return existing;
9
+ const created = {};
10
+ target[key] = created;
11
+ return created;
12
+ };
13
+ var isPusharyPermission = (rule) => typeof rule === "string" && (rule.includes("pushary") || rule.includes("MCP(pushary"));
14
+ var isPusharyHook = (entry) => {
15
+ const hooks = asRecord(entry)?.hooks;
16
+ if (!Array.isArray(hooks)) return false;
17
+ return hooks.some((hook) => {
18
+ const command = String(asRecord(hook)?.command ?? "");
19
+ return command.includes("pushary-hook") || command.includes("pushary-post-hook") || command.includes("pushary-stop-hook");
20
+ });
21
+ };
22
+ var addClaudeMcpServer = (config, apiKey) => {
23
+ const mcpServers = ensureRecord(config, "mcpServers");
24
+ mcpServers.pushary = {
25
+ type: "http",
26
+ url: PUSHARY_MCP_URL,
27
+ headers: { Authorization: `Bearer ${apiKey}` }
28
+ };
29
+ };
30
+ var removeClaudeMcpServers = (config) => {
31
+ let changed = false;
32
+ const removeFrom = (target) => {
33
+ const mcpServers = asRecord(target.mcpServers);
34
+ if (!mcpServers?.pushary) return;
35
+ delete mcpServers.pushary;
36
+ if (Object.keys(mcpServers).length === 0) delete target.mcpServers;
37
+ changed = true;
38
+ };
39
+ removeFrom(config);
40
+ const projects = asRecord(config.projects);
41
+ if (projects) {
42
+ for (const project of Object.values(projects)) {
43
+ const projectConfig = asRecord(project);
44
+ if (projectConfig) removeFrom(projectConfig);
45
+ }
46
+ }
47
+ return changed;
48
+ };
49
+ var addPusharyToolPermissions = (settings) => {
50
+ const permissions = ensureRecord(settings, "permissions");
51
+ const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
52
+ const filtered = allow.filter((rule) => !isPusharyPermission(rule));
53
+ if (!filtered.includes(PUSHARY_PERMISSION_RULE)) {
54
+ filtered.push(PUSHARY_PERMISSION_RULE);
55
+ }
56
+ permissions.allow = filtered;
57
+ };
58
+ var addPusharyHooks = (settings, binDir) => {
59
+ const resolve = (name) => binDir ? join(binDir, name) : name;
60
+ const hooks = ensureRecord(settings, "hooks");
61
+ const preToolUse = (Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : []).filter((entry) => !isPusharyHook(entry));
62
+ preToolUse.push({
63
+ matcher: "Bash|Write|Edit",
64
+ hooks: [{
65
+ type: "command",
66
+ command: resolve("pushary-hook"),
67
+ timeout: 120
68
+ }]
69
+ });
70
+ hooks.PreToolUse = preToolUse;
71
+ const postToolUse = (Array.isArray(hooks.PostToolUse) ? hooks.PostToolUse : []).filter((entry) => !isPusharyHook(entry));
72
+ postToolUse.push({
73
+ matcher: "Bash|Write|Edit",
74
+ hooks: [{
75
+ type: "command",
76
+ command: resolve("pushary-post-hook"),
77
+ timeout: 10
78
+ }]
79
+ });
80
+ hooks.PostToolUse = postToolUse;
81
+ const stop = (Array.isArray(hooks.Stop) ? hooks.Stop : []).filter((entry) => !isPusharyHook(entry));
82
+ stop.push({
83
+ hooks: [{
84
+ type: "command",
85
+ command: resolve("pushary-stop-hook"),
86
+ timeout: 10
87
+ }]
88
+ });
89
+ hooks.Stop = stop;
90
+ };
91
+ var removePusharySettings = (settings) => {
92
+ let changed = removeClaudeMcpServers(settings);
93
+ const permissions = asRecord(settings.permissions);
94
+ if (permissions && Array.isArray(permissions.allow)) {
95
+ const filtered = permissions.allow.filter((rule) => !isPusharyPermission(rule));
96
+ if (filtered.length !== permissions.allow.length) {
97
+ if (filtered.length === 0) {
98
+ delete permissions.allow;
99
+ } else {
100
+ permissions.allow = filtered;
101
+ }
102
+ if (Object.keys(permissions).length === 0) delete settings.permissions;
103
+ changed = true;
104
+ }
105
+ }
106
+ const hooks = asRecord(settings.hooks);
107
+ if (hooks) {
108
+ for (const key of ["PreToolUse", "PostToolUse", "Stop"]) {
109
+ const entries = hooks[key];
110
+ if (!Array.isArray(entries)) continue;
111
+ const filtered = entries.filter((entry) => !isPusharyHook(entry));
112
+ if (filtered.length !== entries.length) {
113
+ if (filtered.length === 0) {
114
+ delete hooks[key];
115
+ } else {
116
+ hooks[key] = filtered;
117
+ }
118
+ changed = true;
119
+ }
120
+ }
121
+ if (Object.keys(hooks).length === 0) delete settings.hooks;
122
+ }
123
+ return changed;
124
+ };
125
+
126
+ export {
127
+ addClaudeMcpServer,
128
+ removeClaudeMcpServers,
129
+ addPusharyToolPermissions,
130
+ addPusharyHooks,
131
+ removePusharySettings
132
+ };
@@ -0,0 +1,99 @@
1
+ import {
2
+ cancelQuestion,
3
+ describeToolCall,
4
+ listPendingQuestions,
5
+ removePendingQuestion
6
+ } from "./chunk-7PTU7TGE.js";
7
+ import {
8
+ withRetry
9
+ } from "./chunk-3MIR7ODJ.js";
10
+ import {
11
+ getApiKey,
12
+ getBaseUrl
13
+ } from "./chunk-VUNL35KE.js";
14
+
15
+ // src/events.ts
16
+ import { hostname } from "os";
17
+ import { basename } from "path";
18
+ var cleanupPendingQuestions = async () => {
19
+ try {
20
+ const files = listPendingQuestions();
21
+ const apiKey = getApiKey();
22
+ for (const correlationId of files) {
23
+ try {
24
+ await cancelQuestion(apiKey, correlationId);
25
+ } catch {
26
+ }
27
+ removePendingQuestion(correlationId);
28
+ }
29
+ } catch {
30
+ }
31
+ };
32
+ var reportEvent = async (event) => {
33
+ const apiKey = getApiKey();
34
+ const baseUrl = getBaseUrl();
35
+ await withRetry(async () => {
36
+ await fetch(`${baseUrl}/api/agent/event`, {
37
+ method: "POST",
38
+ headers: {
39
+ "Content-Type": "application/json",
40
+ "Authorization": `Bearer ${apiKey}`
41
+ },
42
+ body: JSON.stringify({
43
+ ...event,
44
+ machineId: event.machineId ?? hostname()
45
+ }),
46
+ signal: AbortSignal.timeout(1e4)
47
+ });
48
+ }, { maxAttempts: 2, baseDelayMs: 300 });
49
+ };
50
+ var handlePostToolUse = async (input) => {
51
+ try {
52
+ const projectName = basename(input.cwd ?? process.cwd());
53
+ const action = describeToolCall(input.tool_name, input.tool_input, "event");
54
+ const isError = input.tool_result && ("error" in input.tool_result || "is_error" in input.tool_result);
55
+ await Promise.allSettled([
56
+ cleanupPendingQuestions(),
57
+ reportEvent({
58
+ event: isError ? "tool_error" : "tool_complete",
59
+ agentType: "claude_code",
60
+ agentName: `Claude Code - ${projectName}`,
61
+ action,
62
+ error: isError ? String(input.tool_result?.error ?? input.tool_result?.stderr ?? "").slice(0, 500) : void 0
63
+ })
64
+ ]);
65
+ } catch {
66
+ }
67
+ };
68
+ var handleStop = async (input) => {
69
+ try {
70
+ const projectName = basename(input.cwd ?? process.cwd());
71
+ await reportEvent({
72
+ event: "session_end",
73
+ agentType: "claude_code",
74
+ agentName: `Claude Code - ${projectName}`,
75
+ action: "Session ended"
76
+ });
77
+ } catch {
78
+ }
79
+ };
80
+ var handleNotification = async (input) => {
81
+ try {
82
+ const projectName = basename(input.cwd ?? process.cwd());
83
+ await reportEvent({
84
+ event: input.type === "error" ? "error" : "notification",
85
+ agentType: "claude_code",
86
+ agentName: `Claude Code - ${projectName}`,
87
+ action: input.title ?? input.message ?? "Notification",
88
+ error: input.type === "error" ? input.message : void 0
89
+ });
90
+ } catch {
91
+ }
92
+ };
93
+
94
+ export {
95
+ reportEvent,
96
+ handlePostToolUse,
97
+ handleStop,
98
+ handleNotification
99
+ };
@@ -0,0 +1,96 @@
1
+ import {
2
+ callMcpTool
3
+ } from "./chunk-3MIR7ODJ.js";
4
+
5
+ // src/validate.ts
6
+ var isPolicyConfig = (data) => {
7
+ if (!data || typeof data !== "object") return false;
8
+ const d = data;
9
+ return Array.isArray(d.policies) && typeof d.defaultTimeoutSeconds === "number" && typeof d.defaultTimeoutAction === "string";
10
+ };
11
+ var isAskUserResponse = (data) => {
12
+ if (!data || typeof data !== "object") return false;
13
+ const d = data;
14
+ return typeof d.correlationId === "string" && typeof d.status === "string";
15
+ };
16
+ var isWaitForAnswerResponse = (data) => {
17
+ if (!data || typeof data !== "object") return false;
18
+ const d = data;
19
+ return typeof d.answered === "boolean";
20
+ };
21
+
22
+ // src/api.ts
23
+ var askUser = async (apiKey, params) => {
24
+ const result = await callMcpTool(apiKey, "ask_user", { ...params, wait: false }, { maxRetries: 3 });
25
+ if (!isAskUserResponse(result)) throw new Error("Invalid ask_user response");
26
+ return result;
27
+ };
28
+ var waitForAnswer = async (apiKey, correlationId, timeoutMs = 3e4) => {
29
+ const result = await callMcpTool(apiKey, "wait_for_answer", {
30
+ correlationId,
31
+ timeoutMs
32
+ });
33
+ if (!isWaitForAnswerResponse(result)) throw new Error("Invalid wait_for_answer response");
34
+ return result;
35
+ };
36
+ var cancelQuestion = async (apiKey, correlationId) => {
37
+ await callMcpTool(apiKey, "cancel_question", { correlationId });
38
+ };
39
+ var sendNotification = async (apiKey, params) => {
40
+ await callMcpTool(apiKey, "send_notification", { ...params }, { maxRetries: 3 });
41
+ };
42
+
43
+ // src/describe.ts
44
+ var hookPrefixes = {
45
+ Bash: (input) => `bash: ${input.command ?? "(no command)"}`,
46
+ Write: (input) => `write file: ${input.file_path ?? "(unknown path)"}`,
47
+ Edit: (input) => `edit file: ${input.file_path ?? "(unknown path)"}`,
48
+ Read: (input) => `read file: ${input.file_path ?? "(unknown path)"}`
49
+ };
50
+ var eventPrefixes = {
51
+ Bash: (input) => `ran: ${String(input.command ?? "").slice(0, 120)}`,
52
+ Write: (input) => `wrote: ${input.file_path ?? "unknown"}`,
53
+ Edit: (input) => `edited: ${input.file_path ?? "unknown"}`,
54
+ Read: (input) => `read: ${input.file_path ?? "unknown"}`
55
+ };
56
+ var describeToolCall = (toolName, toolInput, format = "hook") => {
57
+ const prefixes = format === "hook" ? hookPrefixes : eventPrefixes;
58
+ const builder = prefixes[toolName];
59
+ if (builder) return builder(toolInput);
60
+ return format === "hook" ? `${toolName}: ${JSON.stringify(toolInput).slice(0, 200)}` : `${toolName}: done`;
61
+ };
62
+
63
+ // src/pending.ts
64
+ import { join } from "path";
65
+ import { tmpdir } from "os";
66
+ import { existsSync, mkdirSync, writeFileSync, readdirSync, unlinkSync } from "fs";
67
+ var PENDING_DIR = join(tmpdir(), "pushary-pending");
68
+ var savePendingQuestion = (correlationId) => {
69
+ if (!existsSync(PENDING_DIR)) mkdirSync(PENDING_DIR, { recursive: true });
70
+ writeFileSync(join(PENDING_DIR, correlationId), "", "utf-8");
71
+ };
72
+ var listPendingQuestions = () => {
73
+ try {
74
+ return readdirSync(PENDING_DIR);
75
+ } catch {
76
+ return [];
77
+ }
78
+ };
79
+ var removePendingQuestion = (correlationId) => {
80
+ try {
81
+ unlinkSync(join(PENDING_DIR, correlationId));
82
+ } catch {
83
+ }
84
+ };
85
+
86
+ export {
87
+ isPolicyConfig,
88
+ askUser,
89
+ waitForAnswer,
90
+ cancelQuestion,
91
+ sendNotification,
92
+ describeToolCall,
93
+ savePendingQuestion,
94
+ listPendingQuestions,
95
+ removePendingQuestion
96
+ };