@pushary/agent-hooks 0.32.1 → 0.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getApiKey
4
+ } from "../chunk-NKXSILEW.js";
5
+
6
+ // src/wrapper/claudeBinary.ts
7
+ import { statSync } from "fs";
8
+ import { join, delimiter } from "path";
9
+ var WRAPPER_ACTIVE_ENV = "PUSHARY_WRAPPER_ACTIVE";
10
+ var isFile = (path) => {
11
+ try {
12
+ return statSync(path).isFile();
13
+ } catch {
14
+ return false;
15
+ }
16
+ };
17
+ var findClaudeBinary = () => {
18
+ const override = process.env.PUSHARY_CLAUDE_BIN?.trim();
19
+ if (override) return isFile(override) ? override : null;
20
+ const pathVar = process.env.PATH ?? "";
21
+ const exts = process.platform === "win32" ? [".cmd", ".exe", ".bat", ""] : [""];
22
+ for (const dir of pathVar.split(delimiter)) {
23
+ if (!dir) continue;
24
+ for (const ext of exts) {
25
+ const candidate = join(dir, `claude${ext}`);
26
+ if (isFile(candidate)) return candidate;
27
+ }
28
+ }
29
+ return null;
30
+ };
31
+
32
+ // src/wrapper/localPassthrough.ts
33
+ import { spawn } from "child_process";
34
+ var SIGNAL_NUMBERS = {
35
+ SIGHUP: 1,
36
+ SIGINT: 2,
37
+ SIGQUIT: 3,
38
+ SIGKILL: 9,
39
+ SIGTERM: 15
40
+ };
41
+ var FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
42
+ var runLocalPassthrough = (binary, args2) => {
43
+ return new Promise((resolve) => {
44
+ let child;
45
+ try {
46
+ child = spawn(binary, args2, {
47
+ stdio: "inherit",
48
+ env: { ...process.env, [WRAPPER_ACTIVE_ENV]: "1" }
49
+ });
50
+ } catch {
51
+ resolve(127);
52
+ return;
53
+ }
54
+ const forward = (signal) => {
55
+ try {
56
+ child.kill(signal);
57
+ } catch {
58
+ }
59
+ };
60
+ for (const signal of FORWARDED_SIGNALS) process.on(signal, forward);
61
+ const cleanup = () => {
62
+ for (const signal of FORWARDED_SIGNALS) process.off(signal, forward);
63
+ };
64
+ child.on("error", () => {
65
+ cleanup();
66
+ resolve(127);
67
+ });
68
+ child.on("exit", (code, signal) => {
69
+ cleanup();
70
+ if (typeof code === "number") resolve(code);
71
+ else if (signal) resolve(128 + (SIGNAL_NUMBERS[signal] ?? 0));
72
+ else resolve(0);
73
+ });
74
+ });
75
+ };
76
+
77
+ // src/wrapper/commandPoller.ts
78
+ var FAST_POLL_MS = 2e3;
79
+ var SLOW_POLL_MS = 3e4;
80
+ var isEnabled = () => {
81
+ const flag = process.env.PUSHARY_WRAPPER_POLL;
82
+ return flag === "1" || flag === "true";
83
+ };
84
+ var startCommandPoller = (opts) => {
85
+ let stopped = false;
86
+ let timer;
87
+ if (!isEnabled()) return { stop() {
88
+ } };
89
+ try {
90
+ getApiKey();
91
+ } catch {
92
+ return { stop() {
93
+ } };
94
+ }
95
+ const schedule = (delayMs) => {
96
+ if (stopped) return;
97
+ timer = setTimeout(tick, delayMs);
98
+ };
99
+ const tick = async () => {
100
+ if (stopped) return;
101
+ let next = SLOW_POLL_MS;
102
+ try {
103
+ const command = await drainPendingCommand(opts.sessionId);
104
+ if (command) {
105
+ next = FAST_POLL_MS;
106
+ opts.onCommand(command);
107
+ }
108
+ } catch {
109
+ }
110
+ schedule(next);
111
+ };
112
+ opts.log?.("[pushary] wrapper command poller enabled (experimental)");
113
+ schedule(SLOW_POLL_MS);
114
+ return {
115
+ stop() {
116
+ stopped = true;
117
+ if (timer) clearTimeout(timer);
118
+ }
119
+ };
120
+ };
121
+ var drainPendingCommand = async (_sessionId) => {
122
+ return null;
123
+ };
124
+
125
+ // src/wrapper/remoteMode.ts
126
+ var runRemoteMode = async (_binary, _args) => {
127
+ return { implemented: false };
128
+ };
129
+
130
+ // src/wrapper/runClaudeWrapper.ts
131
+ var runClaudeWrapper = async (args2) => {
132
+ const binary = findClaudeBinary();
133
+ if (!binary) {
134
+ process.stderr.write(
135
+ "[pushary] Could not find the `claude` binary on your PATH. Install Claude Code, or set PUSHARY_CLAUDE_BIN to its full path.\n"
136
+ );
137
+ return 127;
138
+ }
139
+ const nested = process.env[WRAPPER_ACTIVE_ENV] === "1";
140
+ if (!nested && process.env.PUSHARY_WRAPPER_REMOTE === "1") {
141
+ try {
142
+ const remote = await runRemoteMode(binary, args2);
143
+ if (remote.implemented) return remote.exitCode ?? 0;
144
+ } catch {
145
+ }
146
+ }
147
+ const poller = nested ? { stop() {
148
+ } } : startCommandPoller({
149
+ onCommand: () => {
150
+ },
151
+ log: (m) => process.stderr.write(`${m}
152
+ `)
153
+ });
154
+ try {
155
+ return await runLocalPassthrough(binary, args2);
156
+ } finally {
157
+ poller.stop();
158
+ }
159
+ };
160
+
161
+ // src/wrapper/args.ts
162
+ var resolveClaudeArgs = (argv) => {
163
+ const rest = argv.slice(2);
164
+ return rest[0] === "claude" ? rest.slice(1) : rest;
165
+ };
166
+
167
+ // bin/pushary-claude.ts
168
+ var args = resolveClaudeArgs(process.argv);
169
+ runClaudeWrapper(args).then((code) => process.exit(code)).catch((err) => {
170
+ process.stderr.write(
171
+ `[pushary] wrapper error: ${err instanceof Error ? err.message : String(err)}
172
+ `
173
+ );
174
+ process.exit(1);
175
+ });
@@ -3,10 +3,6 @@ import {
3
3
  denyReasonFrom,
4
4
  isDeferAnswer
5
5
  } from "../chunk-KQYIHZ5E.js";
6
- import {
7
- isGatingMoment,
8
- recordKeylessMoment
9
- } from "../chunk-R5AJNXZS.js";
10
6
  import {
11
7
  CODEX_AGENT,
12
8
  DEFAULT_SESSION,
@@ -36,7 +32,11 @@ import {
36
32
  toCodexWire,
37
33
  toPolicyLookup,
38
34
  waitForAnswer
39
- } from "../chunk-USRIQPWI.js";
35
+ } from "../chunk-P7B3U6DR.js";
36
+ import {
37
+ isGatingMoment,
38
+ recordKeylessMoment
39
+ } from "../chunk-R5AJNXZS.js";
40
40
  import "../chunk-DWED7BS3.js";
41
41
  import {
42
42
  DECISION_LINE_MAX,
@@ -4,7 +4,7 @@ import {
4
4
  getMachineId,
5
5
  reportEvent,
6
6
  waitForAnswer
7
- } from "../chunk-USRIQPWI.js";
7
+ } from "../chunk-P7B3U6DR.js";
8
8
  import "../chunk-DWED7BS3.js";
9
9
  import "../chunk-Z5PL3K7C.js";
10
10
  import {
@@ -3,10 +3,6 @@ import {
3
3
  denyReasonFrom,
4
4
  isDeferAnswer
5
5
  } from "../chunk-KQYIHZ5E.js";
6
- import {
7
- isGatingMoment,
8
- recordKeylessMoment
9
- } from "../chunk-R5AJNXZS.js";
10
6
  import {
11
7
  DEFAULT_SESSION,
12
8
  askUser,
@@ -28,7 +24,11 @@ import {
28
24
  savePendingQuestion,
29
25
  sendNotification,
30
26
  waitForAnswer
31
- } from "../chunk-USRIQPWI.js";
27
+ } from "../chunk-P7B3U6DR.js";
28
+ import {
29
+ isGatingMoment,
30
+ recordKeylessMoment
31
+ } from "../chunk-R5AJNXZS.js";
32
32
  import "../chunk-DWED7BS3.js";
33
33
  import {
34
34
  DECISION_LINE_MAX,
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePreToolUse
4
- } from "../chunk-FD4NV6BO.js";
4
+ } from "../chunk-2AMV2BDM.js";
5
5
  import "../chunk-KQYIHZ5E.js";
6
+ import "../chunk-P7B3U6DR.js";
6
7
  import "../chunk-R5AJNXZS.js";
7
- import "../chunk-USRIQPWI.js";
8
8
  import "../chunk-DWED7BS3.js";
9
9
  import "../chunk-Z5PL3K7C.js";
10
10
  import "../chunk-NKXSILEW.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleNotification
4
- } from "../chunk-USRIQPWI.js";
4
+ } from "../chunk-P7B3U6DR.js";
5
5
  import "../chunk-DWED7BS3.js";
6
6
  import "../chunk-Z5PL3K7C.js";
7
7
  import "../chunk-NKXSILEW.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePostToolUse
4
- } from "../chunk-USRIQPWI.js";
4
+ } from "../chunk-P7B3U6DR.js";
5
5
  import "../chunk-DWED7BS3.js";
6
6
  import "../chunk-Z5PL3K7C.js";
7
7
  import "../chunk-NKXSILEW.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleUserPrompt
4
- } from "../chunk-USRIQPWI.js";
4
+ } from "../chunk-P7B3U6DR.js";
5
5
  import "../chunk-DWED7BS3.js";
6
6
  import "../chunk-Z5PL3K7C.js";
7
7
  import "../chunk-NKXSILEW.js";
@@ -24,7 +24,7 @@ import {
24
24
  } from "../chunk-J7JWI3KU.js";
25
25
  import {
26
26
  reportEvent
27
- } from "../chunk-USRIQPWI.js";
27
+ } from "../chunk-P7B3U6DR.js";
28
28
  import "../chunk-DWED7BS3.js";
29
29
  import {
30
30
  isValidApiKey
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStop
4
- } from "../chunk-USRIQPWI.js";
4
+ } from "../chunk-P7B3U6DR.js";
5
5
  import "../chunk-DWED7BS3.js";
6
6
  import "../chunk-Z5PL3K7C.js";
7
7
  import "../chunk-NKXSILEW.js";
@@ -18,12 +18,15 @@ if (command === "setup") {
18
18
  await import("./pushary-stats.js");
19
19
  } else if (command === "upgrade") {
20
20
  await import("./pushary-upgrade.js");
21
+ } else if (command === "claude") {
22
+ await import("./pushary-claude.js");
21
23
  } else {
22
24
  console.log(`
23
25
  Pushary Agent Hooks
24
26
 
25
27
  Commands:
26
28
  setup Configure Claude Code, Codex, Gemini CLI, Hermes, or Cursor with Pushary
29
+ claude Run Claude Code through Pushary (experimental wrapper; today a transparent passthrough)
27
30
  doctor Verify your Pushary installation is working
28
31
  clean Remove all Pushary configuration (--yes for non-interactive)
29
32
  mode Switch approval mode (push_only, push_first, terminal_only)
@@ -2,10 +2,6 @@ import {
2
2
  denyReasonFrom,
3
3
  isDeferAnswer
4
4
  } from "./chunk-KQYIHZ5E.js";
5
- import {
6
- isGatingMoment,
7
- recordKeylessMoment
8
- } from "./chunk-R5AJNXZS.js";
9
5
  import {
10
6
  DEFAULT_SESSION,
11
7
  askUser,
@@ -24,7 +20,11 @@ import {
24
20
  savePendingQuestion,
25
21
  sendNotification,
26
22
  waitForAnswer
27
- } from "./chunk-USRIQPWI.js";
23
+ } from "./chunk-P7B3U6DR.js";
24
+ import {
25
+ isGatingMoment,
26
+ recordKeylessMoment
27
+ } from "./chunk-R5AJNXZS.js";
28
28
  import {
29
29
  effectiveWaitSeconds,
30
30
  hookWaitClamped,
@@ -876,6 +876,13 @@ var handleUserPrompt = async (input, agent = CLAUDE_CODE_AGENT) => {
876
876
  return void 0;
877
877
  }
878
878
  };
879
+ var STOP_SUMMARY_MAX_LENGTH = 200;
880
+ var summarizeFinalMessage = (message) => {
881
+ if (typeof message !== "string") return "Session ended";
882
+ const collapsed = message.replace(/\s+/g, " ").trim();
883
+ if (!collapsed) return "Session ended";
884
+ return collapsed.slice(0, STOP_SUMMARY_MAX_LENGTH);
885
+ };
879
886
  var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
880
887
  try {
881
888
  const projectName = basename(input.cwd ?? process.cwd());
@@ -889,7 +896,7 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
889
896
  event: "session_end",
890
897
  agentType: agent.type,
891
898
  agentName: `${agent.label} - ${projectName}`,
892
- action: "Session ended",
899
+ action: summarizeFinalMessage(input.last_assistant_message),
893
900
  sessionId: input.session_id,
894
901
  usage: deriveUsage(input.transcript_path, input.session_id)
895
902
  }, { maxAttempts: 1 })
@@ -91,6 +91,7 @@ declare const handleStop: (input: {
91
91
  session_id?: string;
92
92
  stop_hook_active?: boolean;
93
93
  transcript_path?: string;
94
+ last_assistant_message?: string;
94
95
  }, agent?: AgentIdentity) => Promise<StopHookOutput | undefined>;
95
96
  declare const handleNotification: (input: {
96
97
  message?: string;
package/dist/src/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-FD4NV6BO.js";
3
+ } from "../chunk-2AMV2BDM.js";
4
4
  import "../chunk-KQYIHZ5E.js";
5
- import "../chunk-R5AJNXZS.js";
6
5
  import {
7
6
  askUser,
8
7
  cancelQuestion,
@@ -15,7 +14,8 @@ import {
15
14
  reportEvent,
16
15
  resolvePolicy,
17
16
  waitForAnswer
18
- } from "../chunk-USRIQPWI.js";
17
+ } from "../chunk-P7B3U6DR.js";
18
+ import "../chunk-R5AJNXZS.js";
19
19
  import "../chunk-DWED7BS3.js";
20
20
  import "../chunk-Z5PL3K7C.js";
21
21
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.32.1",
3
+ "version": "0.32.2",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",
@@ -48,6 +48,7 @@
48
48
  "pushary-stop-hook": "./dist/bin/pushary-stop-hook.js",
49
49
  "pushary-prompt-hook": "./dist/bin/pushary-prompt-hook.js",
50
50
  "pushary-notification-hook": "./dist/bin/pushary-notification-hook.js",
51
+ "pushary-claude": "./dist/bin/pushary-claude.js",
51
52
  "pushary-codex": "./dist/bin/pushary-codex.js",
52
53
  "pushary-codex-hook": "./dist/bin/pushary-codex-hook.js",
53
54
  "pushary-gemini-hook": "./dist/bin/pushary-gemini-hook.js",
@@ -66,7 +67,7 @@
66
67
  "scripts": {
67
68
  "build": "node scripts/bundle-plugin.mjs && tsup",
68
69
  "dev": "tsup --watch",
69
- "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts"
70
+ "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts"
70
71
  },
71
72
  "dependencies": {
72
73
  "@inquirer/prompts": "^8.4.2",