@pushary/agent-hooks 0.4.5 → 0.4.6
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.
- package/data/SKILL.md +293 -0
- package/dist/bin/pushary-clean.js +10 -50
- package/dist/bin/pushary-codex.js +2 -1
- package/dist/bin/pushary-doctor.js +46 -121
- package/dist/bin/pushary-hook.js +3 -2
- package/dist/bin/pushary-setup.js +58 -80
- package/dist/{chunk-KINE5LNQ.js → chunk-4TQW4K6T.js} +1 -1
- package/dist/chunk-AC4UYAGX.js +136 -0
- package/dist/chunk-M5SRSBLS.js +23 -0
- package/dist/chunk-RJKW6LLC.js +78 -0
- package/dist/src/index.js +3 -2
- package/package.json +5 -3
- package/dist/chunk-4TWRLEOX.js +0 -49
- package/dist/chunk-5ZMTG7GF.js +0 -184
- package/dist/chunk-I546R6K2.js +0 -165
- package/dist/chunk-YKZWCOVP.js +0 -165
- package/dist/pushary-clean-M5RW2DG6.js +0 -154
- package/dist/pushary-clean-RM6TBJ3H.js +0 -147
- package/dist/pushary-doctor-EHLTPBD3.js +0 -252
- package/dist/pushary-doctor-LYMEFIZN.js +0 -254
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
addClaudeMcpServer,
|
|
4
|
+
addPusharyHooks,
|
|
5
|
+
addPusharyToolPermissions
|
|
6
|
+
} from "../chunk-AC4UYAGX.js";
|
|
2
7
|
|
|
3
8
|
// bin/pushary-setup.ts
|
|
4
9
|
import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from "fs";
|
|
@@ -10,7 +15,9 @@ import { fileURLToPath } from "url";
|
|
|
10
15
|
var CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
|
|
11
16
|
var CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
12
17
|
var CURSOR_MCP = join(".cursor", "mcp.json");
|
|
13
|
-
var
|
|
18
|
+
var CURSOR_RULES_DIR = join(".cursor", "rules");
|
|
19
|
+
var CLAUDE_SKILL_DIR = join(homedir(), ".claude", "skills", "pushary");
|
|
20
|
+
var CODEX_SKILL_DIR = join(homedir(), ".codex", "skills", "pushary");
|
|
14
21
|
var SHELL_FILES = [".zshrc", ".bashrc"].map((f) => join(homedir(), f));
|
|
15
22
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
16
23
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
@@ -30,7 +37,8 @@ var writeJson = (path, data) => {
|
|
|
30
37
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
31
38
|
writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
32
39
|
};
|
|
33
|
-
var
|
|
40
|
+
var formatError = (err) => err instanceof Error ? err.message : String(err);
|
|
41
|
+
var spinner = async (label, fn, options = {}) => {
|
|
34
42
|
const frames = [" ", ". ", ".. ", "..."];
|
|
35
43
|
let i = 0;
|
|
36
44
|
const interval = setInterval(() => {
|
|
@@ -41,10 +49,11 @@ var spinner = async (label, fn) => {
|
|
|
41
49
|
clearInterval(interval);
|
|
42
50
|
process.stdout.write(`\r ${check} ${label}
|
|
43
51
|
`);
|
|
44
|
-
} catch {
|
|
52
|
+
} catch (err) {
|
|
45
53
|
clearInterval(interval);
|
|
46
|
-
process.stdout.write(`\r ${yellow("!")} ${label} ${dim(
|
|
54
|
+
process.stdout.write(`\r ${yellow("!")} ${label} ${dim(`(${formatError(err)})`)}
|
|
47
55
|
`);
|
|
56
|
+
if (!options.optional) throw err;
|
|
48
57
|
}
|
|
49
58
|
};
|
|
50
59
|
var getPackageVersion = () => {
|
|
@@ -76,74 +85,37 @@ var installGlobally = async () => {
|
|
|
76
85
|
execSync("npm install -g @pushary/agent-hooks@latest", { stdio: "ignore" });
|
|
77
86
|
});
|
|
78
87
|
};
|
|
79
|
-
var
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const hooks = settings.hooks ?? {};
|
|
92
|
-
const preToolUse = hooks.PreToolUse ?? [];
|
|
93
|
-
if (!JSON.stringify(preToolUse).includes("pushary-hook")) {
|
|
94
|
-
preToolUse.push({
|
|
95
|
-
matcher: "Bash|Write|Edit",
|
|
96
|
-
hooks: [{
|
|
97
|
-
type: "command",
|
|
98
|
-
command: "pushary-hook",
|
|
99
|
-
timeout: 120
|
|
100
|
-
}]
|
|
101
|
-
});
|
|
102
|
-
hooks.PreToolUse = preToolUse;
|
|
103
|
-
}
|
|
104
|
-
const postToolUse = hooks.PostToolUse ?? [];
|
|
105
|
-
if (!JSON.stringify(postToolUse).includes("pushary-post-hook")) {
|
|
106
|
-
postToolUse.push({
|
|
107
|
-
matcher: "Bash|Write|Edit",
|
|
108
|
-
hooks: [{
|
|
109
|
-
type: "command",
|
|
110
|
-
command: "pushary-post-hook",
|
|
111
|
-
timeout: 10
|
|
112
|
-
}]
|
|
113
|
-
});
|
|
114
|
-
hooks.PostToolUse = postToolUse;
|
|
115
|
-
}
|
|
116
|
-
const stop = hooks.Stop ?? [];
|
|
117
|
-
if (!JSON.stringify(stop).includes("pushary-stop-hook")) {
|
|
118
|
-
stop.push({
|
|
119
|
-
hooks: [{
|
|
120
|
-
type: "command",
|
|
121
|
-
command: "pushary-stop-hook",
|
|
122
|
-
timeout: 10
|
|
123
|
-
}]
|
|
124
|
-
});
|
|
125
|
-
hooks.Stop = stop;
|
|
126
|
-
}
|
|
127
|
-
settings.hooks = hooks;
|
|
88
|
+
var _cachedSkillContent = null;
|
|
89
|
+
var fetchSkillContent = async () => {
|
|
90
|
+
if (_cachedSkillContent) return _cachedSkillContent;
|
|
91
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
92
|
+
const candidates = [
|
|
93
|
+
join(__dirname, "..", "..", "data", "SKILL.md"),
|
|
94
|
+
join(__dirname, "..", "data", "SKILL.md")
|
|
95
|
+
];
|
|
96
|
+
const source = candidates.find((path) => existsSync(path));
|
|
97
|
+
if (!source) throw new Error("packaged skill not found");
|
|
98
|
+
_cachedSkillContent = readFileSync(source, "utf-8");
|
|
99
|
+
return _cachedSkillContent;
|
|
128
100
|
};
|
|
129
|
-
var
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
permissions.allow = filtered;
|
|
136
|
-
settings.permissions = permissions;
|
|
101
|
+
var installSkillToDir = async (dir, label) => {
|
|
102
|
+
await spinner(label, async () => {
|
|
103
|
+
const content = await fetchSkillContent();
|
|
104
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
105
|
+
writeFileSync(join(dir, "SKILL.md"), content, "utf-8");
|
|
106
|
+
});
|
|
137
107
|
};
|
|
138
|
-
var
|
|
139
|
-
await spinner("Installing Pushary
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
108
|
+
var installCursorRule = async () => {
|
|
109
|
+
await spinner("Installing Pushary rules", async () => {
|
|
110
|
+
const content = await fetchSkillContent();
|
|
111
|
+
const body = content.replace(/^---[\s\S]*?---\n*/m, "");
|
|
112
|
+
const mdc = `---
|
|
113
|
+
alwaysApply: true
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
${body}`;
|
|
117
|
+
if (!existsSync(CURSOR_RULES_DIR)) mkdirSync(CURSOR_RULES_DIR, { recursive: true });
|
|
118
|
+
writeFileSync(join(CURSOR_RULES_DIR, "pushary.mdc"), mdc, "utf-8");
|
|
147
119
|
});
|
|
148
120
|
};
|
|
149
121
|
var setupClaudeCode = async (apiKey) => {
|
|
@@ -152,19 +124,21 @@ var setupClaudeCode = async (apiKey) => {
|
|
|
152
124
|
`);
|
|
153
125
|
const settings = readJson(CLAUDE_SETTINGS);
|
|
154
126
|
await spinner("Adding MCP server (type: http)", async () => {
|
|
155
|
-
|
|
127
|
+
const data = readJson(CLAUDE_JSON);
|
|
128
|
+
addClaudeMcpServer(data, apiKey);
|
|
129
|
+
writeJson(CLAUDE_JSON, data);
|
|
156
130
|
});
|
|
157
131
|
await spinner("Auto-allowing Pushary tools", async () => {
|
|
158
|
-
|
|
132
|
+
addPusharyToolPermissions(settings);
|
|
159
133
|
});
|
|
160
134
|
await installGlobally();
|
|
161
135
|
await spinner("Adding hooks (PreToolUse, PostToolUse, Stop)", async () => {
|
|
162
|
-
|
|
136
|
+
addPusharyHooks(settings);
|
|
163
137
|
});
|
|
164
138
|
await spinner(`Writing ${CLAUDE_SETTINGS}`, async () => {
|
|
165
139
|
writeJson(CLAUDE_SETTINGS, settings);
|
|
166
140
|
});
|
|
167
|
-
await
|
|
141
|
+
await installSkillToDir(CLAUDE_SKILL_DIR, "Installing Pushary skill");
|
|
168
142
|
console.log();
|
|
169
143
|
console.log(` ${dim("What this configured:")}`);
|
|
170
144
|
console.log(` ${dim("\u2022")} MCP server: your agent can send notifications and ask questions`);
|
|
@@ -196,10 +170,7 @@ var setupHermes = async (_apiKey) => {
|
|
|
196
170
|
console.log(` ${dim(" Install Python 3.10+ and re-run setup to fix.")}`);
|
|
197
171
|
}
|
|
198
172
|
await spinner("Enabling plugin", async () => {
|
|
199
|
-
|
|
200
|
-
execSync("hermes plugins enable pushary", { stdio: "ignore" });
|
|
201
|
-
} catch {
|
|
202
|
-
}
|
|
173
|
+
execSync("hermes plugins enable pushary", { stdio: "ignore" });
|
|
203
174
|
});
|
|
204
175
|
console.log();
|
|
205
176
|
console.log(` ${dim("What this configured:")}`);
|
|
@@ -250,6 +221,7 @@ notify = ["${pusharyCodexPath}"]
|
|
|
250
221
|
appendFileSync(codexConfig, notifyLine, "utf-8");
|
|
251
222
|
}
|
|
252
223
|
});
|
|
224
|
+
await installSkillToDir(CODEX_SKILL_DIR, "Installing Pushary skill");
|
|
253
225
|
console.log();
|
|
254
226
|
console.log(` ${dim("What this configured:")}`);
|
|
255
227
|
console.log(` ${dim("\u2022")} MCP server: Codex can send notifications and ask questions`);
|
|
@@ -270,7 +242,7 @@ var setupCursor = async (apiKey) => {
|
|
|
270
242
|
config.mcpServers = mcpServers;
|
|
271
243
|
writeJson(CURSOR_MCP, config);
|
|
272
244
|
});
|
|
273
|
-
await
|
|
245
|
+
await installCursorRule();
|
|
274
246
|
};
|
|
275
247
|
var saveApiKey = async (apiKey) => {
|
|
276
248
|
await spinner("Saving API key to shell profile", async () => {
|
|
@@ -379,4 +351,10 @@ var main = async () => {
|
|
|
379
351
|
console.log(` ${dim("3.")} Run ${cyan("npx @pushary/agent-hooks doctor")} to verify`);
|
|
380
352
|
console.log();
|
|
381
353
|
};
|
|
382
|
-
main()
|
|
354
|
+
main().catch((err) => {
|
|
355
|
+
console.log();
|
|
356
|
+
console.log(` ${yellow("!")} Setup failed: ${formatError(err)}`);
|
|
357
|
+
console.log(` ${dim("Run")} ${cyan("npx @pushary/agent-hooks doctor")} ${dim("after fixing the issue, then rerun setup.")}`);
|
|
358
|
+
console.log();
|
|
359
|
+
process.exit(1);
|
|
360
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// src/claude-config.ts
|
|
2
|
+
var PUSHARY_MCP_URL = "https://pushary.com/api/mcp/mcp";
|
|
3
|
+
var PUSHARY_PERMISSION_RULE = "mcp__pushary__*";
|
|
4
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
5
|
+
var ensureRecord = (target, key) => {
|
|
6
|
+
const existing = asRecord(target[key]);
|
|
7
|
+
if (existing) return existing;
|
|
8
|
+
const created = {};
|
|
9
|
+
target[key] = created;
|
|
10
|
+
return created;
|
|
11
|
+
};
|
|
12
|
+
var isPusharyPermission = (rule) => typeof rule === "string" && (rule.includes("pushary") || rule.includes("MCP(pushary"));
|
|
13
|
+
var isPusharyHook = (entry) => {
|
|
14
|
+
const hooks = asRecord(entry)?.hooks;
|
|
15
|
+
if (!Array.isArray(hooks)) return false;
|
|
16
|
+
return hooks.some((hook) => {
|
|
17
|
+
const command = String(asRecord(hook)?.command ?? "");
|
|
18
|
+
return command.includes("pushary-hook") || command.includes("pushary-post-hook") || command.includes("pushary-stop-hook");
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var addClaudeMcpServer = (config, apiKey) => {
|
|
22
|
+
const mcpServers = ensureRecord(config, "mcpServers");
|
|
23
|
+
mcpServers.pushary = {
|
|
24
|
+
type: "http",
|
|
25
|
+
url: PUSHARY_MCP_URL,
|
|
26
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
var removeClaudeMcpServers = (config) => {
|
|
30
|
+
let changed = false;
|
|
31
|
+
const removeFrom = (target) => {
|
|
32
|
+
const mcpServers = asRecord(target.mcpServers);
|
|
33
|
+
if (!mcpServers?.pushary) return;
|
|
34
|
+
delete mcpServers.pushary;
|
|
35
|
+
if (Object.keys(mcpServers).length === 0) delete target.mcpServers;
|
|
36
|
+
changed = true;
|
|
37
|
+
};
|
|
38
|
+
removeFrom(config);
|
|
39
|
+
const projects = asRecord(config.projects);
|
|
40
|
+
if (projects) {
|
|
41
|
+
for (const project of Object.values(projects)) {
|
|
42
|
+
const projectConfig = asRecord(project);
|
|
43
|
+
if (projectConfig) removeFrom(projectConfig);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return changed;
|
|
47
|
+
};
|
|
48
|
+
var addPusharyToolPermissions = (settings) => {
|
|
49
|
+
const permissions = ensureRecord(settings, "permissions");
|
|
50
|
+
const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
51
|
+
const filtered = allow.filter((rule) => !isPusharyPermission(rule));
|
|
52
|
+
if (!filtered.includes(PUSHARY_PERMISSION_RULE)) {
|
|
53
|
+
filtered.push(PUSHARY_PERMISSION_RULE);
|
|
54
|
+
}
|
|
55
|
+
permissions.allow = filtered;
|
|
56
|
+
};
|
|
57
|
+
var addPusharyHooks = (settings) => {
|
|
58
|
+
const hooks = ensureRecord(settings, "hooks");
|
|
59
|
+
const preToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
|
|
60
|
+
if (!preToolUse.some(isPusharyHook)) {
|
|
61
|
+
preToolUse.push({
|
|
62
|
+
matcher: "Bash|Write|Edit",
|
|
63
|
+
hooks: [{
|
|
64
|
+
type: "command",
|
|
65
|
+
command: "pushary-hook",
|
|
66
|
+
timeout: 120
|
|
67
|
+
}]
|
|
68
|
+
});
|
|
69
|
+
hooks.PreToolUse = preToolUse;
|
|
70
|
+
}
|
|
71
|
+
const postToolUse = Array.isArray(hooks.PostToolUse) ? hooks.PostToolUse : [];
|
|
72
|
+
if (!postToolUse.some(isPusharyHook)) {
|
|
73
|
+
postToolUse.push({
|
|
74
|
+
matcher: "Bash|Write|Edit",
|
|
75
|
+
hooks: [{
|
|
76
|
+
type: "command",
|
|
77
|
+
command: "pushary-post-hook",
|
|
78
|
+
timeout: 10
|
|
79
|
+
}]
|
|
80
|
+
});
|
|
81
|
+
hooks.PostToolUse = postToolUse;
|
|
82
|
+
}
|
|
83
|
+
const stop = Array.isArray(hooks.Stop) ? hooks.Stop : [];
|
|
84
|
+
if (!stop.some(isPusharyHook)) {
|
|
85
|
+
stop.push({
|
|
86
|
+
hooks: [{
|
|
87
|
+
type: "command",
|
|
88
|
+
command: "pushary-stop-hook",
|
|
89
|
+
timeout: 10
|
|
90
|
+
}]
|
|
91
|
+
});
|
|
92
|
+
hooks.Stop = stop;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var removePusharySettings = (settings) => {
|
|
96
|
+
let changed = removeClaudeMcpServers(settings);
|
|
97
|
+
const permissions = asRecord(settings.permissions);
|
|
98
|
+
if (permissions && Array.isArray(permissions.allow)) {
|
|
99
|
+
const filtered = permissions.allow.filter((rule) => !isPusharyPermission(rule));
|
|
100
|
+
if (filtered.length !== permissions.allow.length) {
|
|
101
|
+
if (filtered.length === 0) {
|
|
102
|
+
delete permissions.allow;
|
|
103
|
+
} else {
|
|
104
|
+
permissions.allow = filtered;
|
|
105
|
+
}
|
|
106
|
+
if (Object.keys(permissions).length === 0) delete settings.permissions;
|
|
107
|
+
changed = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const hooks = asRecord(settings.hooks);
|
|
111
|
+
if (hooks) {
|
|
112
|
+
for (const key of ["PreToolUse", "PostToolUse", "Stop"]) {
|
|
113
|
+
const entries = hooks[key];
|
|
114
|
+
if (!Array.isArray(entries)) continue;
|
|
115
|
+
const filtered = entries.filter((entry) => !isPusharyHook(entry));
|
|
116
|
+
if (filtered.length !== entries.length) {
|
|
117
|
+
if (filtered.length === 0) {
|
|
118
|
+
delete hooks[key];
|
|
119
|
+
} else {
|
|
120
|
+
hooks[key] = filtered;
|
|
121
|
+
}
|
|
122
|
+
changed = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
126
|
+
}
|
|
127
|
+
return changed;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
addClaudeMcpServer,
|
|
132
|
+
removeClaudeMcpServers,
|
|
133
|
+
addPusharyToolPermissions,
|
|
134
|
+
addPusharyHooks,
|
|
135
|
+
removePusharySettings
|
|
136
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
callMcpTool
|
|
3
|
+
} from "./chunk-RJKW6LLC.js";
|
|
4
|
+
|
|
5
|
+
// src/api.ts
|
|
6
|
+
var askUser = async (apiKey, params) => {
|
|
7
|
+
return callMcpTool(apiKey, "ask_user", { ...params });
|
|
8
|
+
};
|
|
9
|
+
var waitForAnswer = async (apiKey, correlationId, timeoutMs = 3e4) => {
|
|
10
|
+
return callMcpTool(apiKey, "wait_for_answer", {
|
|
11
|
+
correlationId,
|
|
12
|
+
timeoutMs
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
var cancelQuestion = async (apiKey, correlationId) => {
|
|
16
|
+
await callMcpTool(apiKey, "cancel_question", { correlationId });
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
askUser,
|
|
21
|
+
waitForAnswer,
|
|
22
|
+
cancelQuestion
|
|
23
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBaseUrl
|
|
3
|
+
} from "./chunk-VUNL35KE.js";
|
|
4
|
+
|
|
5
|
+
// src/mcp-http.ts
|
|
6
|
+
var parseSseJson = (body) => {
|
|
7
|
+
const messages = [];
|
|
8
|
+
for (const event of body.split(/\r?\n\r?\n/)) {
|
|
9
|
+
const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n").trim();
|
|
10
|
+
if (!data) continue;
|
|
11
|
+
messages.push(JSON.parse(data));
|
|
12
|
+
}
|
|
13
|
+
const message = messages.at(-1);
|
|
14
|
+
if (!message) throw new Error("Empty response from Pushary");
|
|
15
|
+
return message;
|
|
16
|
+
};
|
|
17
|
+
var parseMcpResponse = (body, contentType) => {
|
|
18
|
+
if (contentType?.includes("text/event-stream")) {
|
|
19
|
+
return parseSseJson(body);
|
|
20
|
+
}
|
|
21
|
+
return JSON.parse(body);
|
|
22
|
+
};
|
|
23
|
+
var sendMcpRequest = async (apiKey, message, options = {}) => {
|
|
24
|
+
const baseUrl = options.baseUrl ?? getBaseUrl();
|
|
25
|
+
const fetchFn = options.fetchFn ?? fetch;
|
|
26
|
+
const headers = {
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
"Accept": "application/json, text/event-stream",
|
|
29
|
+
"Authorization": `Bearer ${apiKey}`
|
|
30
|
+
};
|
|
31
|
+
if (options.sessionId) {
|
|
32
|
+
headers["Mcp-Session-Id"] = options.sessionId;
|
|
33
|
+
}
|
|
34
|
+
const response = await fetchFn(`${baseUrl}/api/mcp/mcp`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers,
|
|
37
|
+
body: JSON.stringify(message),
|
|
38
|
+
signal: options.timeoutMs ? AbortSignal.timeout(options.timeoutMs) : void 0
|
|
39
|
+
});
|
|
40
|
+
const body = await response.text();
|
|
41
|
+
const contentType = response.headers.get("content-type");
|
|
42
|
+
let data = null;
|
|
43
|
+
if (body.trim()) {
|
|
44
|
+
try {
|
|
45
|
+
data = parseMcpResponse(body, contentType);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (response.ok) throw err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const message2 = data?.error?.message ?? (body.trim() || response.statusText);
|
|
52
|
+
throw new Error(`Pushary MCP error: ${response.status} ${message2}`);
|
|
53
|
+
}
|
|
54
|
+
if (!data) throw new Error("Empty response from Pushary");
|
|
55
|
+
if (data.error) throw new Error(data.error.message ?? "Pushary MCP error");
|
|
56
|
+
return {
|
|
57
|
+
data,
|
|
58
|
+
sessionId: response.headers.get("mcp-session-id") ?? "",
|
|
59
|
+
status: response.status,
|
|
60
|
+
statusText: response.statusText
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
var callMcpTool = async (apiKey, toolName, params, options = {}) => {
|
|
64
|
+
const { data } = await sendMcpRequest(apiKey, {
|
|
65
|
+
jsonrpc: "2.0",
|
|
66
|
+
id: options.id ?? Date.now(),
|
|
67
|
+
method: "tools/call",
|
|
68
|
+
params: { name: toolName, arguments: params }
|
|
69
|
+
}, options);
|
|
70
|
+
const text = data.result?.content?.[0]?.text;
|
|
71
|
+
if (!text) throw new Error("Empty response from Pushary");
|
|
72
|
+
return JSON.parse(text);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
sendMcpRequest,
|
|
77
|
+
callMcpTool
|
|
78
|
+
};
|
package/dist/src/index.js
CHANGED
|
@@ -2,12 +2,13 @@ import {
|
|
|
2
2
|
getPolicy,
|
|
3
3
|
handlePreToolUse,
|
|
4
4
|
resolvePolicy
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-4TQW4K6T.js";
|
|
6
6
|
import {
|
|
7
7
|
askUser,
|
|
8
8
|
cancelQuestion,
|
|
9
9
|
waitForAnswer
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-M5SRSBLS.js";
|
|
11
|
+
import "../chunk-RJKW6LLC.js";
|
|
11
12
|
import {
|
|
12
13
|
handleNotification,
|
|
13
14
|
handlePostToolUse,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushary/agent-hooks",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
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",
|
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
"pushary-doctor": "./dist/bin/pushary-doctor.js"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"dist"
|
|
28
|
+
"dist",
|
|
29
|
+
"data"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
31
32
|
"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 bin/pushary-clean.ts bin/pushary-doctor.ts --format esm --dts --outDir dist",
|
|
32
|
-
"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 bin/pushary-clean.ts bin/pushary-doctor.ts --format esm --watch"
|
|
33
|
+
"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 bin/pushary-clean.ts bin/pushary-doctor.ts --format esm --watch",
|
|
34
|
+
"test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/mcp-http.test.ts"
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
35
37
|
"@inquirer/prompts": "^8.4.2"
|
package/dist/chunk-4TWRLEOX.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
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
|
-
};
|