@pushary/agent-hooks 0.10.1 → 0.11.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.
@@ -344,31 +344,41 @@ var setupClaudeCode = async (apiKey) => {
344
344
  console.log(` ${dim2("\u2022")} Hooks: route permission approvals through push notifications`);
345
345
  console.log(` ${dim2("\u2022")} Auto-allowed tools: no permission prompts for Pushary MCP calls`);
346
346
  };
347
- var findPython310Plus = () => {
348
- const candidates = ["python3.13", "python3.12", "python3.11", "python3.10", "python3", "python"];
349
- for (const py of candidates) {
350
- try {
351
- const version = execSync(`${py} --version 2>&1`, { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
352
- const match = version.match(/Python (\d+)\.(\d+)/);
353
- if (match && (Number(match[1]) > 3 || Number(match[1]) === 3 && Number(match[2]) >= 10)) {
354
- return py;
347
+ var resolveHermesPython = () => {
348
+ try {
349
+ const launcher = execSync("command -v hermes", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
350
+ if (launcher) {
351
+ const shebang = readFileSync(launcher, "utf-8").split("\n", 1)[0];
352
+ if (shebang.startsWith("#!")) {
353
+ const interpreter = shebang.slice(2).trim().split(/\s+/)[0];
354
+ if (interpreter && /python/i.test(interpreter) && existsSync(interpreter)) return interpreter;
355
355
  }
356
- } catch {
357
356
  }
357
+ } catch {
358
358
  }
359
+ const venvPython = join(homedir(), ".hermes", "hermes-agent", "venv", "bin", "python3");
360
+ if (existsSync(venvPython)) return venvPython;
359
361
  return null;
360
362
  };
361
- var installPythonPlugin = (pythonBin) => {
363
+ var ensurePip = (python) => {
362
364
  try {
363
- execSync(`${pythonBin} -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
364
- } catch (err) {
365
- const msg = npmErrorMessage(err);
366
- if (!msg.includes("externally-managed-environment")) throw err;
367
- execSync(
368
- `${pythonBin} -m pip install --upgrade --user --break-system-packages hermes-plugin-pushary`,
369
- { stdio: "pipe", timeout: 12e4 }
370
- );
365
+ execSync(`"${python}" -m pip --version`, { stdio: "ignore", timeout: 15e3 });
366
+ return;
367
+ } catch {
368
+ }
369
+ try {
370
+ execSync(`"${python}" -m ensurepip --upgrade`, { stdio: "pipe", timeout: 6e4 });
371
+ } catch {
372
+ }
373
+ };
374
+ var enablePusharyPlugin = (python) => {
375
+ const snippet = 'from hermes_cli.config import load_config, save_config; c = load_config(); p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}; e = p.get("enabled") if isinstance(p.get("enabled"), list) else []; p["enabled"] = (e + ["pushary"]) if "pushary" not in e else e; c["plugins"] = p; a = c.get("agent") if isinstance(c.get("agent"), dict) else {}; d = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []; a["disabled_toolsets"] = (d + ["clarify"]) if "clarify" not in d else d; c["agent"] = a; save_config(c)';
376
+ try {
377
+ execSync(`"${python}" -c '${snippet}'`, { stdio: "pipe", timeout: 15e3 });
378
+ return;
379
+ } catch {
371
380
  }
381
+ execSync("hermes plugins enable pushary", { stdio: "ignore", timeout: 1e4 });
372
382
  };
373
383
  var setupHermes = async (_apiKey) => {
374
384
  console.log(`
@@ -379,72 +389,26 @@ var setupHermes = async (_apiKey) => {
379
389
  console.log(` ${dim2("Install Hermes and re-run setup to configure.")}`);
380
390
  return;
381
391
  }
382
- await spinner("Installing hermes-plugin-pushary", async () => {
383
- try {
384
- execSync("uv pip install hermes-plugin-pushary", { stdio: "pipe", timeout: 12e4 });
385
- return;
386
- } catch {
387
- }
388
- if (isInstalled("pipx")) {
389
- try {
390
- execSync("pipx inject hermes hermes-plugin-pushary", { stdio: "pipe", timeout: 12e4 });
391
- return;
392
- } catch {
393
- }
394
- }
395
- let python = findPython310Plus();
396
- if (!python) {
397
- if (process.platform === "darwin") {
398
- try {
399
- execSync("which brew", { stdio: "ignore", timeout: 5e3 });
400
- execSync("brew install python@3.12", { stdio: "pipe", timeout: 3e5 });
401
- python = findPython310Plus();
402
- } catch {
403
- }
404
- } else if (process.platform === "linux") {
405
- for (const [check3, install] of [
406
- ["which apt-get", "sudo apt-get update -qq && sudo apt-get install -y -qq python3 python3-pip"],
407
- ["which dnf", "sudo dnf install -y -q python3 python3-pip"],
408
- ["which yum", "sudo yum install -y -q python3 python3-pip"],
409
- ["which pacman", "sudo pacman -S --noconfirm python python-pip"]
410
- ]) {
411
- try {
412
- execSync(check3, { stdio: "ignore", timeout: 5e3 });
413
- execSync(install, { stdio: "pipe", timeout: 3e5 });
414
- python = findPython310Plus();
415
- if (python) break;
416
- } catch {
417
- }
418
- }
419
- }
420
- }
421
- if (python) {
422
- installPythonPlugin(python);
423
- return;
424
- }
425
- for (const pip of ["pip3", "pip"]) {
426
- try {
427
- execSync(`${pip} install hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
428
- return;
429
- } catch (err) {
430
- if (npmErrorMessage(err).includes("externally-managed-environment")) {
431
- try {
432
- execSync(`${pip} install --user --break-system-packages hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
433
- return;
434
- } catch {
435
- }
436
- }
437
- }
438
- }
439
- throw new Error("Python 3.10+ not found and could not be installed");
392
+ const python = resolveHermesPython();
393
+ if (!python) {
394
+ console.log(` ${yellow2("!")} Could not locate the Python that Hermes runs in. Skipping.`);
395
+ console.log(` ${dim2("Install manually: <hermes-python> -m pip install hermes-plugin-pushary")}`);
396
+ return;
397
+ }
398
+ await spinner("Installing hermes-plugin-pushary into Hermes\u2019 environment", async () => {
399
+ ensurePip(python);
400
+ execSync(`"${python}" -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe", timeout: 18e4 });
440
401
  });
441
- await spinner("Enabling plugin", async () => {
442
- execSync("hermes plugins enable pushary", { stdio: "ignore", timeout: 1e4 });
402
+ await spinner("Enabling plugin + routing questions to push", async () => {
403
+ enablePusharyPlugin(python);
443
404
  });
444
405
  console.log();
445
406
  console.log(` ${dim2("What this configured:")}`);
446
407
  console.log(` ${dim2("\u2022")} Native tools: pushary_notify, pushary_ask, pushary_wait, pushary_cancel`);
408
+ console.log(` ${dim2("\u2022")} Questions go to your phone: Hermes' terminal ${bold2("clarify")} tool is disabled, so pushary_ask handles every question`);
447
409
  console.log(` ${dim2("\u2022")} Auto-notifications: push alert when tools return errors`);
410
+ console.log(` ${dim2("\u2022")} Permission gating: set ${bold2("PUSHARY_GATE_TOOLS")} to require lock-screen approval for risky tools`);
411
+ console.log(` ${dim2("To re-enable terminal prompts:")} remove ${bold2("clarify")} from ${dim2("agent.disabled_toolsets")} in ~/.hermes/config.yaml`);
448
412
  };
449
413
  var setupCodex = async (_apiKey) => {
450
414
  console.log(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.10.1",
3
+ "version": "0.11.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",