@pushary/agent-hooks 0.4.3 → 0.4.5

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.
@@ -14,6 +14,7 @@ var check = green("\u2713");
14
14
  var skip = yellow("\u2013");
15
15
  var CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
16
16
  var CLAUDE_SETTINGS_LOCAL = join(homedir(), ".claude", "settings.local.json");
17
+ var CLAUDE_JSON = join(homedir(), ".claude.json");
17
18
  var SKILL_DIR = join(homedir(), ".claude", "skills", "pushary");
18
19
  var CURSOR_MCP = join(".cursor", "mcp.json");
19
20
  var SHELL_FILES = [".zshrc", ".bashrc"].map((f) => join(homedir(), f));
@@ -97,6 +98,18 @@ var main = async () => {
97
98
  console.log();
98
99
  cleanSettingsFile(CLAUDE_SETTINGS, "Claude Code settings");
99
100
  cleanSettingsFile(CLAUDE_SETTINGS_LOCAL, "Claude Code settings.local");
101
+ const claudeJson = readJson(CLAUDE_JSON);
102
+ if (claudeJson) {
103
+ const mcpServers = claudeJson.mcpServers;
104
+ if (mcpServers?.pushary) {
105
+ delete mcpServers.pushary;
106
+ if (Object.keys(mcpServers).length === 0) delete claudeJson.mcpServers;
107
+ writeJson(CLAUDE_JSON, claudeJson);
108
+ console.log(` ${check} Claude Code MCP server ${dim("(removed from ~/.claude.json)")}`);
109
+ } else {
110
+ console.log(` ${skip} Claude Code MCP server ${dim("(not in ~/.claude.json)")}`);
111
+ }
112
+ }
100
113
  const cursorData = readJson(CURSOR_MCP);
101
114
  if (cursorData) {
102
115
  const mcpServers = cursorData.mcpServers;
@@ -119,8 +132,24 @@ var main = async () => {
119
132
  const codexConfig = join(homedir(), ".codex", "config.toml");
120
133
  try {
121
134
  let config = readFileSync(codexConfig, "utf-8");
122
- if (config.includes("pushary-codex")) {
123
- config = config.split("\n").filter((l) => !l.includes("pushary-codex")).join("\n");
135
+ const hadPushary = config.includes("pushary");
136
+ if (hadPushary) {
137
+ const lines = config.split("\n");
138
+ const cleaned = [];
139
+ let skipping = false;
140
+ for (const line of lines) {
141
+ if (line.trim() === "[mcp_servers.pushary]") {
142
+ skipping = true;
143
+ continue;
144
+ }
145
+ if (skipping && (line.startsWith("[") || line.trim() === "")) {
146
+ skipping = false;
147
+ }
148
+ if (skipping) continue;
149
+ if (line.includes("pushary-codex")) continue;
150
+ cleaned.push(line);
151
+ }
152
+ config = cleaned.join("\n").replace(/\n{3,}/g, "\n\n");
124
153
  writeFileSync(codexConfig, config, "utf-8");
125
154
  console.log(` ${check} Codex config ${dim("(cleaned)")}`);
126
155
  } else {
@@ -38,14 +38,16 @@ var main = async () => {
38
38
  console.log(` ${dim("Configuration")}`);
39
39
  const apiKey = process.env.PUSHARY_API_KEY;
40
40
  check(!!apiKey, "API key in environment", apiKey ? `pk_${apiKey.split(".")[0]?.slice(3, 7)}...` : "PUSHARY_API_KEY not set");
41
+ const CLAUDE_JSON = join(homedir(), ".claude.json");
42
+ const claudeJson = readJson(CLAUDE_JSON);
43
+ const mcpServers = claudeJson?.mcpServers ?? {};
44
+ const pusharyServer = mcpServers?.pushary;
45
+ check(!!pusharyServer, "Claude Code: MCP server configured");
46
+ if (pusharyServer) {
47
+ check(pusharyServer.type === "http", "Claude Code: MCP server type", pusharyServer.type ? String(pusharyServer.type) : 'missing \u2014 add type: "http"');
48
+ }
41
49
  const settings = readJson(CLAUDE_SETTINGS);
42
50
  if (settings) {
43
- const mcpServers = settings.mcpServers;
44
- const pusharyServer = mcpServers?.pushary;
45
- check(!!pusharyServer, "Claude Code: MCP server configured");
46
- if (pusharyServer) {
47
- check(pusharyServer.type === "http", "Claude Code: MCP server type", pusharyServer.type ? String(pusharyServer.type) : 'missing \u2014 add type: "http"');
48
- }
49
51
  const hooks = settings.hooks;
50
52
  const hasPreHook = JSON.stringify(hooks?.PreToolUse ?? []).includes("pushary-hook");
51
53
  const hasPostHook = JSON.stringify(hooks?.PostToolUse ?? []).includes("pushary-post-hook");
@@ -54,8 +56,8 @@ var main = async () => {
54
56
  check(hasPostHook, "Claude Code: PostToolUse hook");
55
57
  check(hasStopHook, "Claude Code: Stop hook");
56
58
  const permissions = settings.permissions;
57
- const hasWildcard = permissions?.allow?.some((r) => r === "MCP(pushary:*)") ?? false;
58
- check(hasWildcard, "Claude Code: Pushary tools auto-allowed", hasWildcard ? "MCP(pushary:*)" : "missing");
59
+ const hasWildcard = permissions?.allow?.some((r) => r === "mcp__pushary__*" || r === "MCP(pushary:*)") ?? false;
60
+ check(hasWildcard, "Claude Code: Pushary tools auto-allowed", hasWildcard ? "mcp__pushary__*" : "missing");
59
61
  const hasLegacyPerms = permissions?.allow?.some((r) => r.startsWith("mcp__pushary__")) ?? false;
60
62
  if (hasLegacyPerms) {
61
63
  console.log(` ${warn} Legacy individual permissions detected ${dim("(run pushary clean, then setup again)")}`);
@@ -8,6 +8,7 @@ import { execSync } from "child_process";
8
8
  import { checkbox, input, confirm } from "@inquirer/prompts";
9
9
  import { fileURLToPath } from "url";
10
10
  var CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
11
+ var CLAUDE_JSON = join(homedir(), ".claude.json");
11
12
  var CURSOR_MCP = join(".cursor", "mcp.json");
12
13
  var SKILL_DIR = join(homedir(), ".claude", "skills", "pushary");
13
14
  var SHELL_FILES = [".zshrc", ".bashrc"].map((f) => join(homedir(), f));
@@ -75,14 +76,16 @@ var installGlobally = async () => {
75
76
  execSync("npm install -g @pushary/agent-hooks@latest", { stdio: "ignore" });
76
77
  });
77
78
  };
78
- var addMcpServer = (settings, apiKey) => {
79
- const mcpServers = settings.mcpServers ?? {};
79
+ var addMcpServer = (apiKey) => {
80
+ const data = readJson(CLAUDE_JSON);
81
+ const mcpServers = data.mcpServers ?? {};
80
82
  mcpServers.pushary = {
81
83
  type: "http",
82
84
  url: "https://pushary.com/api/mcp/mcp",
83
85
  headers: { Authorization: `Bearer ${apiKey}` }
84
86
  };
85
- settings.mcpServers = mcpServers;
87
+ data.mcpServers = mcpServers;
88
+ writeJson(CLAUDE_JSON, data);
86
89
  };
87
90
  var addPermissionHooks = (settings) => {
88
91
  const hooks = settings.hooks ?? {};
@@ -127,7 +130,7 @@ var addToolPermissions = (settings) => {
127
130
  const permissions = settings.permissions ?? {};
128
131
  const allow = permissions.allow ?? [];
129
132
  const filtered = allow.filter((r) => !r.includes("pushary"));
130
- const rule = "MCP(pushary:*)";
133
+ const rule = "mcp__pushary__*";
131
134
  if (!filtered.includes(rule)) filtered.push(rule);
132
135
  permissions.allow = filtered;
133
136
  settings.permissions = permissions;
@@ -149,7 +152,7 @@ var setupClaudeCode = async (apiKey) => {
149
152
  `);
150
153
  const settings = readJson(CLAUDE_SETTINGS);
151
154
  await spinner("Adding MCP server (type: http)", async () => {
152
- addMcpServer(settings, apiKey);
155
+ addMcpServer(apiKey);
153
156
  });
154
157
  await spinner("Auto-allowing Pushary tools", async () => {
155
158
  addToolPermissions(settings);
@@ -173,17 +176,25 @@ var setupHermes = async (_apiKey) => {
173
176
  console.log(`
174
177
  ${bold("Setting up Hermes Agent")}
175
178
  `);
179
+ let pipInstalled = false;
176
180
  await spinner("Installing hermes-plugin-pushary", async () => {
177
- try {
178
- execSync("pip install hermes-plugin-pushary", { stdio: "ignore" });
179
- } catch {
181
+ for (const pip of ["pip3", "pip"]) {
180
182
  try {
181
- execSync("pip3 install hermes-plugin-pushary", { stdio: "ignore" });
182
- } catch {
183
- throw new Error("pip not found");
183
+ execSync(`${pip} install hermes-plugin-pushary`, { stdio: "pipe" });
184
+ pipInstalled = true;
185
+ return;
186
+ } catch (err) {
187
+ const msg = err instanceof Error ? err.stderr?.toString() ?? "" : "";
188
+ if (msg.includes("No matching distribution")) {
189
+ throw new Error("requires Python 3.10+ (your pip uses an older version)");
190
+ }
184
191
  }
185
192
  }
193
+ throw new Error("pip not found");
186
194
  });
195
+ if (!pipInstalled) {
196
+ console.log(` ${dim(" Install Python 3.10+ and re-run setup to fix.")}`);
197
+ }
187
198
  await spinner("Enabling plugin", async () => {
188
199
  try {
189
200
  execSync("hermes plugins enable pushary", { stdio: "ignore" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
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",