@pushary/agent-hooks 0.2.7 → 0.3.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.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ askUser,
4
+ waitForAnswer
5
+ } from "../chunk-4TWRLEOX.js";
6
+ import {
7
+ reportEvent
8
+ } from "../chunk-EQE6Z4YQ.js";
9
+ import {
10
+ getApiKey
11
+ } from "../chunk-VUNL35KE.js";
12
+
13
+ // bin/pushary-codex.ts
14
+ import { hostname } from "os";
15
+ import { basename } from "path";
16
+ var main = async () => {
17
+ let rawInput = "";
18
+ for await (const chunk of process.stdin) {
19
+ rawInput += chunk;
20
+ }
21
+ if (!rawInput.trim()) {
22
+ process.exit(0);
23
+ }
24
+ let event;
25
+ try {
26
+ event = JSON.parse(rawInput);
27
+ } catch {
28
+ process.exit(0);
29
+ }
30
+ const projectName = basename(event.cwd ?? process.cwd());
31
+ const agentName = `Codex - ${projectName}`;
32
+ try {
33
+ if (event.type === "agent-turn-complete") {
34
+ await reportEvent({
35
+ event: "turn_complete",
36
+ agentType: "codex",
37
+ agentName,
38
+ action: event.message ?? "Turn complete",
39
+ machineId: hostname()
40
+ });
41
+ process.stdout.write(JSON.stringify({ acknowledged: true }));
42
+ }
43
+ if (event.type === "approval-requested") {
44
+ const apiKey = getApiKey();
45
+ const description = event.command ?? event.message ?? "Codex needs approval";
46
+ await reportEvent({
47
+ event: "approval_requested",
48
+ agentType: "codex",
49
+ agentName,
50
+ action: description,
51
+ machineId: hostname()
52
+ });
53
+ const result = await askUser(apiKey, {
54
+ question: `Allow: ${description}?`,
55
+ type: "confirm",
56
+ context: `Codex wants to run this in ${projectName}`,
57
+ agentName
58
+ });
59
+ const deadline = Date.now() + 12e4;
60
+ while (Date.now() < deadline) {
61
+ const remaining = Math.min(Math.max(deadline - Date.now(), 1e3), 3e4);
62
+ const answer = await waitForAnswer(apiKey, result.correlationId, remaining);
63
+ if (answer.answered) {
64
+ const approved = answer.value === "yes";
65
+ process.stdout.write(JSON.stringify({ approved }));
66
+ await reportEvent({
67
+ event: approved ? "approval_granted" : "approval_denied",
68
+ agentType: "codex",
69
+ agentName,
70
+ action: approved ? `Approved: ${description}` : `Denied: ${description}`,
71
+ machineId: hostname()
72
+ });
73
+ process.exit(0);
74
+ }
75
+ if (Date.now() + 2e3 >= deadline) break;
76
+ await new Promise((r) => setTimeout(r, 2e3));
77
+ }
78
+ process.stdout.write(JSON.stringify({ approved: false }));
79
+ process.exit(0);
80
+ }
81
+ } catch (err) {
82
+ process.stderr.write(
83
+ `[pushary-codex] Error: ${err instanceof Error ? err.message : String(err)}
84
+ `
85
+ );
86
+ if (event.type === "approval-requested") {
87
+ process.stdout.write(JSON.stringify({ approved: false }));
88
+ }
89
+ }
90
+ };
91
+ main();
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePreToolUse
4
- } from "../chunk-5ZMTG7GF.js";
4
+ } from "../chunk-KINE5LNQ.js";
5
+ import "../chunk-4TWRLEOX.js";
6
+ import "../chunk-VUNL35KE.js";
5
7
 
6
8
  // bin/pushary-hook.ts
7
9
  var main = async () => {
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ handlePostToolUse
4
+ } from "../chunk-EQE6Z4YQ.js";
5
+ import "../chunk-VUNL35KE.js";
6
+
7
+ // bin/pushary-post-hook.ts
8
+ var main = async () => {
9
+ let rawInput = "";
10
+ for await (const chunk of process.stdin) {
11
+ rawInput += chunk;
12
+ }
13
+ if (!rawInput.trim()) {
14
+ process.exit(0);
15
+ }
16
+ try {
17
+ const input = JSON.parse(rawInput);
18
+ await handlePostToolUse(input);
19
+ } catch {
20
+ }
21
+ };
22
+ main();
@@ -72,25 +72,37 @@ var addPermissionHooks = (settings) => {
72
72
  }]
73
73
  });
74
74
  hooks.PreToolUse = preToolUse;
75
- settings.hooks = hooks;
76
75
  }
76
+ const postToolUse = hooks.PostToolUse ?? [];
77
+ if (!JSON.stringify(postToolUse).includes("pushary-post-hook")) {
78
+ postToolUse.push({
79
+ matcher: "Bash|Write|Edit",
80
+ hooks: [{
81
+ type: "command",
82
+ command: "pushary-post-hook",
83
+ timeout: 10
84
+ }]
85
+ });
86
+ hooks.PostToolUse = postToolUse;
87
+ }
88
+ const stop = hooks.Stop ?? [];
89
+ if (!JSON.stringify(stop).includes("pushary-stop-hook")) {
90
+ stop.push({
91
+ hooks: [{
92
+ type: "command",
93
+ command: "pushary-stop-hook",
94
+ timeout: 10
95
+ }]
96
+ });
97
+ hooks.Stop = stop;
98
+ }
99
+ settings.hooks = hooks;
77
100
  };
78
101
  var addToolPermissions = (settings) => {
79
102
  const permissions = settings.permissions ?? {};
80
103
  const allow = permissions.allow ?? [];
81
- const tools = [
82
- "mcp__pushary__send_notification",
83
- "mcp__pushary__ask_user",
84
- "mcp__pushary__ask_user_yes_no",
85
- "mcp__pushary__wait_for_answer",
86
- "mcp__pushary__cancel_question",
87
- "mcp__pushary__list_subscribers",
88
- "mcp__pushary__get_subscriber",
89
- "mcp__pushary__count_subscribers"
90
- ];
91
- for (const tool of tools) {
92
- if (!allow.includes(tool)) allow.push(tool);
93
- }
104
+ const rule = "MCP(pushary:*)";
105
+ if (!allow.includes(rule)) allow.push(rule);
94
106
  permissions.allow = allow;
95
107
  settings.permissions = permissions;
96
108
  };
@@ -106,7 +118,7 @@ var setupClaudeCode = async (apiKey) => {
106
118
  addToolPermissions(settings);
107
119
  });
108
120
  await installGlobally();
109
- await spinner("Adding permission hooks (Bash, Write, Edit)", async () => {
121
+ await spinner("Adding hooks (PreToolUse, PostToolUse, Stop)", async () => {
110
122
  addPermissionHooks(settings);
111
123
  });
112
124
  await spinner(`Writing ${CLAUDE_SETTINGS}`, async () => {
@@ -115,7 +127,9 @@ var setupClaudeCode = async (apiKey) => {
115
127
  console.log();
116
128
  console.log(` ${dim("What this configured:")}`);
117
129
  console.log(` ${dim("\u2022")} MCP server: your agent can send notifications and ask questions`);
118
- console.log(` ${dim("\u2022")} Permission hooks: approve Bash/Write/Edit from your phone`);
130
+ console.log(` ${dim("\u2022")} PreToolUse: approve Bash/Write/Edit from your phone`);
131
+ console.log(` ${dim("\u2022")} PostToolUse: track when tools finish`);
132
+ console.log(` ${dim("\u2022")} Stop: detect when the agent session ends`);
119
133
  console.log(` ${dim("\u2022")} Auto-allowed tools: no permission prompts for Pushary`);
120
134
  };
121
135
  var setupHermes = async (apiKey) => {
@@ -145,6 +159,55 @@ var setupHermes = async (apiKey) => {
145
159
  console.log(` ${dim("\u2022")} Auto-notifications: push alert when tools return errors`);
146
160
  console.log(` ${dim("\u2022")} Session alerts: opt-in with PUSHARY_AUTO_NOTIFY_SESSION_END=1`);
147
161
  };
162
+ var setupCodex = async (_apiKey) => {
163
+ console.log(`
164
+ ${bold("Setting up Codex")}
165
+ `);
166
+ const hasCodex = (() => {
167
+ try {
168
+ execSync("which codex", { stdio: "ignore" });
169
+ return true;
170
+ } catch {
171
+ return false;
172
+ }
173
+ })();
174
+ if (!hasCodex) {
175
+ console.log(` ${yellow("!")} Codex CLI not found. Skipping.`);
176
+ console.log(` ${dim("Install Codex and re-run setup to configure.")}`);
177
+ return;
178
+ }
179
+ await installGlobally();
180
+ await spinner("Adding Pushary MCP server to Codex", async () => {
181
+ try {
182
+ execSync(
183
+ "codex mcp add pushary --url https://pushary.com/api/mcp/mcp --bearer-token-env-var PUSHARY_API_KEY",
184
+ { stdio: "ignore" }
185
+ );
186
+ } catch {
187
+ }
188
+ });
189
+ const codexConfig = join(homedir(), ".codex", "config.toml");
190
+ await spinner("Adding notify handler for Codex events", async () => {
191
+ const pusharyCodexPath = execSync("which pushary-codex", { encoding: "utf-8" }).trim();
192
+ if (!pusharyCodexPath) throw new Error("pushary-codex not found");
193
+ let config = "";
194
+ try {
195
+ config = readFileSync(codexConfig, "utf-8");
196
+ } catch {
197
+ }
198
+ if (!config.includes("pushary-codex")) {
199
+ const notifyLine = `
200
+ notify = ["${pusharyCodexPath}"]
201
+ `;
202
+ appendFileSync(codexConfig, notifyLine, "utf-8");
203
+ }
204
+ });
205
+ console.log();
206
+ console.log(` ${dim("What this configured:")}`);
207
+ console.log(` ${dim("\u2022")} MCP server: Codex can send notifications and ask questions`);
208
+ console.log(` ${dim("\u2022")} Notify handler: captures turn completions and approval requests`);
209
+ console.log(` ${dim("\u2022")} Uses PUSHARY_API_KEY env var for auth`);
210
+ };
148
211
  var setupCursor = async (apiKey) => {
149
212
  console.log(`
150
213
  ${bold("Setting up Cursor")}
@@ -224,29 +287,34 @@ var main = async () => {
224
287
  console.log(` ${bold("Which agent do you use?")}`);
225
288
  console.log();
226
289
  console.log(` ${cyan("1.")} Claude Code ${dim("MCP + permission hooks + auto-allowed tools")}`);
227
- console.log(` ${cyan("2.")} Hermes ${dim("native plugin + auto-error notifications")}`);
228
- console.log(` ${cyan("3.")} Cursor ${dim("MCP server")}`);
229
- console.log(` ${cyan("4.")} All ${dim("configure everything")}`);
230
- console.log(` ${cyan("5.")} Other ${dim("just save the API key")}`);
290
+ console.log(` ${cyan("2.")} Codex ${dim("MCP server via codex mcp add")}`);
291
+ console.log(` ${cyan("3.")} Hermes ${dim("native plugin + auto-error notifications")}`);
292
+ console.log(` ${cyan("4.")} Cursor ${dim("MCP server")}`);
293
+ console.log(` ${cyan("5.")} All ${dim("configure everything")}`);
294
+ console.log(` ${cyan("6.")} Other ${dim("just save the API key")}`);
231
295
  console.log();
232
- const choice = await ask(` Choice ${dim("[1-5]")}: `);
296
+ const choice = await ask(` Choice ${dim("[1-6]")}: `);
233
297
  await saveApiKey(trimmedKey);
234
298
  switch (choice.trim()) {
235
299
  case "1":
236
300
  await setupClaudeCode(trimmedKey);
237
301
  break;
238
302
  case "2":
239
- await setupHermes(trimmedKey);
303
+ await setupCodex(trimmedKey);
240
304
  break;
241
305
  case "3":
242
- await setupCursor(trimmedKey);
306
+ await setupHermes(trimmedKey);
243
307
  break;
244
308
  case "4":
309
+ await setupCursor(trimmedKey);
310
+ break;
311
+ case "5":
245
312
  await setupClaudeCode(trimmedKey);
313
+ await setupCodex(trimmedKey);
246
314
  await setupHermes(trimmedKey);
247
315
  await setupCursor(trimmedKey);
248
316
  break;
249
- case "5":
317
+ case "6":
250
318
  default:
251
319
  break;
252
320
  }
@@ -261,7 +329,7 @@ var main = async () => {
261
329
  console.log(` ${dim("Next:")}`);
262
330
  console.log(` ${dim("1.")} Enable notifications on your phone at ${cyan("pushary.com")}`);
263
331
  console.log(` ${dim("2.")} Restart your agent to load the new config`);
264
- console.log(` ${dim("3.")} Start coding \u2014 your agent will notify you automatically`);
332
+ console.log(` ${dim("3.")} Start coding. Your agent will notify you automatically`);
265
333
  console.log();
266
334
  rl.close();
267
335
  };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ handleStop
4
+ } from "../chunk-EQE6Z4YQ.js";
5
+ import "../chunk-VUNL35KE.js";
6
+
7
+ // bin/pushary-stop-hook.ts
8
+ var main = async () => {
9
+ let rawInput = "";
10
+ for await (const chunk of process.stdin) {
11
+ rawInput += chunk;
12
+ }
13
+ try {
14
+ const input = rawInput.trim() ? JSON.parse(rawInput) : {};
15
+ await handleStop(input);
16
+ } catch {
17
+ }
18
+ };
19
+ main();
@@ -0,0 +1,49 @@
1
+ import {
2
+ getBaseUrl
3
+ } from "./chunk-VUNL35KE.js";
4
+
5
+ // src/api.ts
6
+ var mcpToolCall = async (apiKey, toolName, params) => {
7
+ const baseUrl = getBaseUrl();
8
+ const response = await fetch(`${baseUrl}/api/mcp/mcp`, {
9
+ method: "POST",
10
+ headers: {
11
+ "Content-Type": "application/json",
12
+ "Authorization": `Bearer ${apiKey}`
13
+ },
14
+ body: JSON.stringify({
15
+ jsonrpc: "2.0",
16
+ id: Date.now(),
17
+ method: "tools/call",
18
+ params: { name: toolName, arguments: params }
19
+ })
20
+ });
21
+ if (!response.ok) {
22
+ throw new Error(`Pushary API error: ${response.status} ${response.statusText}`);
23
+ }
24
+ const json = await response.json();
25
+ if (json.error) throw new Error(json.error.message);
26
+ const text = json.result?.content?.[0]?.text;
27
+ if (!text) throw new Error("Empty response from Pushary");
28
+ return JSON.parse(text);
29
+ };
30
+ var askUser = async (apiKey, params) => {
31
+ const result = await mcpToolCall(apiKey, "ask_user", { ...params });
32
+ return result;
33
+ };
34
+ var waitForAnswer = async (apiKey, correlationId, timeoutMs = 3e4) => {
35
+ const result = await mcpToolCall(apiKey, "wait_for_answer", {
36
+ correlationId,
37
+ timeoutMs
38
+ });
39
+ return result;
40
+ };
41
+ var cancelQuestion = async (apiKey, correlationId) => {
42
+ await mcpToolCall(apiKey, "cancel_question", { correlationId });
43
+ };
44
+
45
+ export {
46
+ askUser,
47
+ waitForAnswer,
48
+ cancelQuestion
49
+ };
@@ -0,0 +1,77 @@
1
+ import {
2
+ getApiKey,
3
+ getBaseUrl
4
+ } from "./chunk-VUNL35KE.js";
5
+
6
+ // src/events.ts
7
+ import { hostname } from "os";
8
+ import { basename } from "path";
9
+ var reportEvent = async (event) => {
10
+ const apiKey = getApiKey();
11
+ const baseUrl = getBaseUrl();
12
+ await fetch(`${baseUrl}/api/agent/event`, {
13
+ method: "POST",
14
+ headers: {
15
+ "Content-Type": "application/json",
16
+ "Authorization": `Bearer ${apiKey}`
17
+ },
18
+ body: JSON.stringify({
19
+ ...event,
20
+ machineId: event.machineId ?? hostname()
21
+ })
22
+ });
23
+ };
24
+ var handlePostToolUse = async (input) => {
25
+ const projectName = basename(input.cwd ?? process.cwd());
26
+ let action;
27
+ switch (input.tool_name) {
28
+ case "Bash":
29
+ action = `ran: ${String(input.tool_input.command ?? "").slice(0, 120)}`;
30
+ break;
31
+ case "Write":
32
+ action = `wrote: ${input.tool_input.file_path ?? "unknown"}`;
33
+ break;
34
+ case "Edit":
35
+ action = `edited: ${input.tool_input.file_path ?? "unknown"}`;
36
+ break;
37
+ case "Read":
38
+ action = `read: ${input.tool_input.file_path ?? "unknown"}`;
39
+ break;
40
+ default:
41
+ action = `${input.tool_name}: done`;
42
+ }
43
+ const isError = input.tool_result && ("error" in input.tool_result || "is_error" in input.tool_result);
44
+ await reportEvent({
45
+ event: isError ? "tool_error" : "tool_complete",
46
+ agentType: "claude_code",
47
+ agentName: `Claude Code - ${projectName}`,
48
+ action,
49
+ error: isError ? String(input.tool_result?.error ?? input.tool_result?.stderr ?? "").slice(0, 500) : void 0
50
+ });
51
+ };
52
+ var handleStop = async (input) => {
53
+ const projectName = basename(input.cwd ?? process.cwd());
54
+ await reportEvent({
55
+ event: "session_end",
56
+ agentType: "claude_code",
57
+ agentName: `Claude Code - ${projectName}`,
58
+ action: "Session ended"
59
+ });
60
+ };
61
+ var handleNotification = async (input) => {
62
+ const projectName = basename(input.cwd ?? process.cwd());
63
+ await reportEvent({
64
+ event: input.type === "error" ? "error" : "notification",
65
+ agentType: "claude_code",
66
+ agentName: `Claude Code - ${projectName}`,
67
+ action: input.title ?? input.message ?? "Notification",
68
+ error: input.type === "error" ? input.message : void 0
69
+ });
70
+ };
71
+
72
+ export {
73
+ reportEvent,
74
+ handlePostToolUse,
75
+ handleStop,
76
+ handleNotification
77
+ };
@@ -0,0 +1,136 @@
1
+ import {
2
+ askUser,
3
+ waitForAnswer
4
+ } from "./chunk-4TWRLEOX.js";
5
+ import {
6
+ getApiKey,
7
+ getBaseUrl
8
+ } from "./chunk-VUNL35KE.js";
9
+
10
+ // src/policy.ts
11
+ import { createHash } from "crypto";
12
+ import { existsSync, readFileSync, writeFileSync } from "fs";
13
+ import { join } from "path";
14
+ import { tmpdir } from "os";
15
+ var cacheFile = (apiKey) => {
16
+ const hash = createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
17
+ return join(tmpdir(), `pushary-policy-${hash}.json`);
18
+ };
19
+ var fetchPolicy = async (apiKey) => {
20
+ const baseUrl = getBaseUrl();
21
+ const response = await fetch(`${baseUrl}/api/mcp/policy`, {
22
+ headers: { "Authorization": `Bearer ${apiKey}` },
23
+ signal: AbortSignal.timeout(1e4)
24
+ });
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch policy: ${response.status}`);
27
+ }
28
+ return response.json();
29
+ };
30
+ var getPolicy = async (apiKey) => {
31
+ const path = cacheFile(apiKey);
32
+ if (existsSync(path)) {
33
+ try {
34
+ return JSON.parse(readFileSync(path, "utf-8"));
35
+ } catch {
36
+ }
37
+ }
38
+ const policy = await fetchPolicy(apiKey);
39
+ writeFileSync(path, JSON.stringify(policy), "utf-8");
40
+ return policy;
41
+ };
42
+ var resolvePolicy = (config, toolName) => {
43
+ const exact = config.policies.find((p) => p.tool === toolName);
44
+ if (exact) return exact;
45
+ const wildcard = config.policies.find((p) => p.tool === "*");
46
+ if (wildcard) return wildcard;
47
+ return {
48
+ tool: toolName,
49
+ timeoutSeconds: config.defaultTimeoutSeconds,
50
+ timeoutAction: config.defaultTimeoutAction
51
+ };
52
+ };
53
+
54
+ // src/hook.ts
55
+ import { basename } from "path";
56
+ var describeToolCall = (input) => {
57
+ const { tool_name, tool_input } = input;
58
+ switch (tool_name) {
59
+ case "Bash":
60
+ return `bash: ${tool_input.command ?? "(no command)"}`;
61
+ case "Write":
62
+ return `write file: ${tool_input.file_path ?? "(unknown path)"}`;
63
+ case "Edit":
64
+ return `edit file: ${tool_input.file_path ?? "(unknown path)"}`;
65
+ case "Read":
66
+ return `read file: ${tool_input.file_path ?? "(unknown path)"}`;
67
+ default:
68
+ return `${tool_name}: ${JSON.stringify(tool_input).slice(0, 200)}`;
69
+ }
70
+ };
71
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
72
+ var allow = () => ({
73
+ hookSpecificOutput: {
74
+ hookEventName: "PreToolUse",
75
+ permissionDecision: "allow"
76
+ }
77
+ });
78
+ var deny = (reason) => ({
79
+ hookSpecificOutput: {
80
+ hookEventName: "PreToolUse",
81
+ permissionDecision: "deny",
82
+ permissionDecisionReason: reason
83
+ }
84
+ });
85
+ var ask = (reason) => ({
86
+ hookSpecificOutput: {
87
+ hookEventName: "PreToolUse",
88
+ permissionDecision: "ask",
89
+ permissionDecisionReason: reason
90
+ }
91
+ });
92
+ var handlePreToolUse = async (input) => {
93
+ const apiKey = getApiKey();
94
+ const policy = await getPolicy(apiKey);
95
+ const toolPolicy = resolvePolicy(policy, input.tool_name);
96
+ if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") {
97
+ return allow();
98
+ }
99
+ const description = describeToolCall(input);
100
+ const projectName = basename(input.cwd ?? process.cwd());
101
+ const result = await askUser(apiKey, {
102
+ question: `Allow ${description}?`,
103
+ type: "confirm",
104
+ context: `Agent wants to run this in ${projectName}`,
105
+ agentName: `Claude Code - ${projectName}`
106
+ });
107
+ const deadline = Date.now() + toolPolicy.timeoutSeconds * 1e3;
108
+ const pollInterval = 2e3;
109
+ while (Date.now() < deadline) {
110
+ const remaining = Math.min(
111
+ Math.max(deadline - Date.now(), 1e3),
112
+ 3e4
113
+ );
114
+ const answer = await waitForAnswer(apiKey, result.correlationId, remaining);
115
+ if (answer.answered) {
116
+ return answer.value === "yes" ? allow() : deny("Denied via Pushary push notification");
117
+ }
118
+ if (Date.now() + pollInterval >= deadline) break;
119
+ await sleep(pollInterval);
120
+ }
121
+ switch (toolPolicy.timeoutAction) {
122
+ case "approve":
123
+ return allow();
124
+ case "deny":
125
+ return deny("No response within timeout");
126
+ case "escalate":
127
+ default:
128
+ return ask("Pushary: no response, asking in terminal");
129
+ }
130
+ };
131
+
132
+ export {
133
+ getPolicy,
134
+ resolvePolicy,
135
+ handlePreToolUse
136
+ };
@@ -0,0 +1,16 @@
1
+ // src/config.ts
2
+ var getApiKey = () => {
3
+ const key = process.env.PUSHARY_API_KEY;
4
+ if (!key) {
5
+ throw new Error(
6
+ "PUSHARY_API_KEY environment variable is not set. Get your API key at https://pushary.com/sign-up?from=ai-coding"
7
+ );
8
+ }
9
+ return key;
10
+ };
11
+ var getBaseUrl = () => process.env.PUSHARY_BASE_URL ?? "https://pushary.com";
12
+
13
+ export {
14
+ getApiKey,
15
+ getBaseUrl
16
+ };
@@ -13,6 +13,35 @@ interface HookOutput {
13
13
  }
14
14
  declare const handlePreToolUse: (input: ToolInput) => Promise<HookOutput | null>;
15
15
 
16
+ interface AgentEvent {
17
+ event: string;
18
+ agentType: string;
19
+ agentName?: string;
20
+ action?: string;
21
+ machineId?: string;
22
+ error?: string;
23
+ }
24
+ declare const reportEvent: (event: AgentEvent) => Promise<void>;
25
+ declare const handlePostToolUse: (input: {
26
+ tool_name: string;
27
+ tool_input: Record<string, unknown>;
28
+ tool_result?: Record<string, unknown>;
29
+ cwd?: string;
30
+ session_id?: string;
31
+ }) => Promise<void>;
32
+ declare const handleStop: (input: {
33
+ cwd?: string;
34
+ session_id?: string;
35
+ stop_hook_active?: boolean;
36
+ }) => Promise<void>;
37
+ declare const handleNotification: (input: {
38
+ message?: string;
39
+ title?: string;
40
+ type?: string;
41
+ cwd?: string;
42
+ session_id?: string;
43
+ }) => Promise<void>;
44
+
16
45
  interface AskUserParams {
17
46
  question: string;
18
47
  type?: 'confirm' | 'select' | 'input';
@@ -48,4 +77,4 @@ declare const resolvePolicy: (config: PolicyConfig, toolName: string) => ToolPol
48
77
  declare const getApiKey: () => string;
49
78
  declare const getBaseUrl: () => string;
50
79
 
51
- export { type PolicyConfig, type ToolPolicy, askUser, cancelQuestion, getApiKey, getBaseUrl, getPolicy, handlePreToolUse, resolvePolicy, waitForAnswer };
80
+ export { type PolicyConfig, type ToolPolicy, askUser, cancelQuestion, getApiKey, getBaseUrl, getPolicy, handleNotification, handlePostToolUse, handlePreToolUse, handleStop, reportEvent, resolvePolicy, waitForAnswer };
package/dist/src/index.js CHANGED
@@ -1,20 +1,34 @@
1
1
  import {
2
- askUser,
3
- cancelQuestion,
4
- getApiKey,
5
- getBaseUrl,
6
2
  getPolicy,
7
3
  handlePreToolUse,
8
- resolvePolicy,
4
+ resolvePolicy
5
+ } from "../chunk-KINE5LNQ.js";
6
+ import {
7
+ askUser,
8
+ cancelQuestion,
9
9
  waitForAnswer
10
- } from "../chunk-5ZMTG7GF.js";
10
+ } from "../chunk-4TWRLEOX.js";
11
+ import {
12
+ handleNotification,
13
+ handlePostToolUse,
14
+ handleStop,
15
+ reportEvent
16
+ } from "../chunk-EQE6Z4YQ.js";
17
+ import {
18
+ getApiKey,
19
+ getBaseUrl
20
+ } from "../chunk-VUNL35KE.js";
11
21
  export {
12
22
  askUser,
13
23
  cancelQuestion,
14
24
  getApiKey,
15
25
  getBaseUrl,
16
26
  getPolicy,
27
+ handleNotification,
28
+ handlePostToolUse,
17
29
  handlePreToolUse,
30
+ handleStop,
31
+ reportEvent,
18
32
  resolvePolicy,
19
33
  waitForAnswer
20
34
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.2.7",
4
- "description": "Permission hooks for AI coding agents route tool approvals through Pushary push notifications",
3
+ "version": "0.3.0",
4
+ "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "author": "Pushary <business@pushary.com>",
6
6
  "homepage": "https://pushary.com",
7
7
  "repository": {
@@ -17,14 +17,17 @@
17
17
  "agent-hooks": "./dist/bin/pushary.js",
18
18
  "pushary": "./dist/bin/pushary.js",
19
19
  "pushary-hook": "./dist/bin/pushary-hook.js",
20
+ "pushary-post-hook": "./dist/bin/pushary-post-hook.js",
21
+ "pushary-stop-hook": "./dist/bin/pushary-stop-hook.js",
22
+ "pushary-codex": "./dist/bin/pushary-codex.js",
20
23
  "pushary-setup": "./dist/bin/pushary-setup.js"
21
24
  },
22
25
  "files": [
23
26
  "dist"
24
27
  ],
25
28
  "scripts": {
26
- "build": "tsup src/index.ts bin/pushary.ts bin/pushary-hook.ts bin/pushary-setup.ts --format esm --dts --outDir dist",
27
- "dev": "tsup src/index.ts bin/pushary.ts bin/pushary-hook.ts bin/pushary-setup.ts --format esm --watch"
29
+ "build": "tsup src/index.ts bin/pushary.ts bin/pushary-hook.ts bin/pushary-post-hook.ts bin/pushary-stop-hook.ts bin/pushary-codex.ts bin/pushary-setup.ts --format esm --dts --outDir dist",
30
+ "dev": "tsup src/index.ts bin/pushary.ts bin/pushary-hook.ts bin/pushary-post-hook.ts bin/pushary-stop-hook.ts bin/pushary-codex.ts bin/pushary-setup.ts --format esm --watch"
28
31
  },
29
32
  "devDependencies": {
30
33
  "tsup": "^8.0.0",