@pushary/agent-hooks 0.9.0 → 0.9.1

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.
@@ -3,12 +3,14 @@ import {
3
3
  removeClaudeMcpServers,
4
4
  removePusharySettings
5
5
  } from "../chunk-5GFUI5N6.js";
6
+ import {
7
+ execNpm
8
+ } from "../chunk-RSHN2AQ7.js";
6
9
 
7
10
  // bin/pushary-clean.ts
8
11
  import { existsSync, readFileSync, writeFileSync, rmSync } from "fs";
9
12
  import { join } from "path";
10
13
  import { homedir } from "os";
11
- import { execSync } from "child_process";
12
14
  import { confirm } from "@inquirer/prompts";
13
15
  import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
14
16
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
@@ -124,7 +126,7 @@ var main = async () => {
124
126
  }
125
127
  }
126
128
  try {
127
- execSync("npm uninstall -g @pushary/agent-hooks", { stdio: "ignore", timeout: 3e4 });
129
+ execNpm("uninstall -g --no-workspaces @pushary/agent-hooks", { stdio: "ignore", timeout: 3e4 });
128
130
  console.log(` ${check} Global package ${dim("(uninstalled)")}`);
129
131
  } catch {
130
132
  console.log(` ${skip} Global package ${dim("(not installed)")}`);
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ execNpm
4
+ } from "../chunk-RSHN2AQ7.js";
2
5
  import {
3
6
  callMcpTool,
4
7
  sendMcpRequest
@@ -143,7 +146,7 @@ var main = async () => {
143
146
  check(existsSync(SKILL_PATH), "Skill installed", existsSync(SKILL_PATH) ? SKILL_PATH : "not found");
144
147
  let globalVersion = "";
145
148
  try {
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] ?? "";
149
+ globalVersion = execNpm("list -g --no-workspaces @pushary/agent-hooks --depth=0", { timeout: 1e4 }).toString().match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
147
150
  } catch {
148
151
  globalVersion = "";
149
152
  }
@@ -4,6 +4,10 @@ import {
4
4
  addPusharyHooks,
5
5
  addPusharyToolPermissions
6
6
  } from "../chunk-5GFUI5N6.js";
7
+ import {
8
+ execNpm,
9
+ npmErrorMessage
10
+ } from "../chunk-RSHN2AQ7.js";
7
11
  import {
8
12
  isValidApiKey
9
13
  } from "../chunk-IBWCHA5M.js";
@@ -112,7 +116,11 @@ var checkForUpdates = async (current) => {
112
116
  };
113
117
  var installGlobally = async () => {
114
118
  await spinner("Installing pushary-hook globally", async () => {
115
- execSync("npm install -g @pushary/agent-hooks@latest", { stdio: "ignore", timeout: 12e4 });
119
+ try {
120
+ execNpm("install -g --no-workspaces @pushary/agent-hooks@latest", { timeout: 12e4 });
121
+ } catch (err) {
122
+ throw new Error(npmErrorMessage(err));
123
+ }
116
124
  });
117
125
  };
118
126
  var _cachedSkillContent = null;
@@ -165,7 +173,7 @@ var setupClaudeCode = async (apiKey) => {
165
173
  await spinner("Adding hooks (PreToolUse, PostToolUse, Stop)", async () => {
166
174
  let binDir;
167
175
  try {
168
- binDir = join(execSync("npm prefix -g", { encoding: "utf-8", timeout: 5e3 }).trim(), "bin");
176
+ binDir = join(execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim(), "bin");
169
177
  } catch {
170
178
  binDir = void 0;
171
179
  }
@@ -197,7 +205,16 @@ var findPython310Plus = () => {
197
205
  return null;
198
206
  };
199
207
  var installPythonPlugin = (pythonBin) => {
200
- execSync(`${pythonBin} -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
208
+ try {
209
+ execSync(`${pythonBin} -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
210
+ } catch (err) {
211
+ const msg = npmErrorMessage(err);
212
+ if (!msg.includes("externally-managed-environment")) throw err;
213
+ execSync(
214
+ `${pythonBin} -m pip install --upgrade --user --break-system-packages hermes-plugin-pushary`,
215
+ { stdio: "pipe", timeout: 12e4 }
216
+ );
217
+ }
201
218
  };
202
219
  var setupHermes = async (_apiKey) => {
203
220
  console.log(`
@@ -214,6 +231,13 @@ var setupHermes = async (_apiKey) => {
214
231
  return;
215
232
  } catch {
216
233
  }
234
+ if (isInstalled("pipx")) {
235
+ try {
236
+ execSync("pipx inject hermes hermes-plugin-pushary", { stdio: "pipe", timeout: 12e4 });
237
+ return;
238
+ } catch {
239
+ }
240
+ }
217
241
  let python = findPython310Plus();
218
242
  if (!python) {
219
243
  if (process.platform === "darwin") {
@@ -248,7 +272,14 @@ var setupHermes = async (_apiKey) => {
248
272
  try {
249
273
  execSync(`${pip} install hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
250
274
  return;
251
- } catch {
275
+ } catch (err) {
276
+ if (npmErrorMessage(err).includes("externally-managed-environment")) {
277
+ try {
278
+ execSync(`${pip} install --user --break-system-packages hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
279
+ return;
280
+ } catch {
281
+ }
282
+ }
252
283
  }
253
284
  }
254
285
  throw new Error("Python 3.10+ not found and could not be installed");
@@ -297,7 +328,7 @@ var setupCodex = async (_apiKey) => {
297
328
  writeFileSync(codexConfig, stringifyTOML(config), "utf-8");
298
329
  });
299
330
  await spinner("Adding notify handler for Codex events", async () => {
300
- const globalPrefix = execSync("npm prefix -g", { encoding: "utf-8", timeout: 5e3 }).trim();
331
+ const globalPrefix = execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim();
301
332
  const pusharyCodexPath = join(globalPrefix, "bin", "pushary-codex");
302
333
  if (!existsSync(pusharyCodexPath)) throw new Error("pushary-codex not found at " + pusharyCodexPath);
303
334
  let raw = "";
@@ -0,0 +1,29 @@
1
+ // src/npm.ts
2
+ import { execSync } from "child_process";
3
+ var cleanNpmEnv = () => {
4
+ const env = {};
5
+ for (const [key, value] of Object.entries(process.env)) {
6
+ if (key.toLowerCase().startsWith("npm_config_workspace")) continue;
7
+ env[key] = value;
8
+ }
9
+ return env;
10
+ };
11
+ var npmErrorMessage = (err) => {
12
+ const e = err;
13
+ const text = [e?.stderr, e?.stdout].map((part) => part ? part.toString() : "").join("\n");
14
+ const line = text.split("\n").map((l) => l.replace(/^npm error\s*/i, "").trim()).find((l) => l && !l.startsWith("A complete log") && !/^code\s/i.test(l));
15
+ return line || e?.message || String(err);
16
+ };
17
+ var execNpm = (args, options = {}) => {
18
+ return execSync(`npm ${args}`, {
19
+ timeout: 12e4,
20
+ stdio: "pipe",
21
+ ...options,
22
+ env: { ...cleanNpmEnv(), ...options.env ?? {} }
23
+ });
24
+ };
25
+
26
+ export {
27
+ npmErrorMessage,
28
+ execNpm
29
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
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",
@@ -32,7 +32,7 @@
32
32
  "scripts": {
33
33
  "build": "tsup",
34
34
  "dev": "tsup --watch",
35
- "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/hook.test.ts"
35
+ "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.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/hook.test.ts"
36
36
  },
37
37
  "dependencies": {
38
38
  "@inquirer/prompts": "^8.4.2",