@pushary/agent-hooks 0.5.1 → 0.7.0
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/dist/bin/pushary-clean.js +13 -19
- package/dist/bin/pushary-codex.js +4 -3
- package/dist/bin/pushary-doctor.js +27 -5
- package/dist/bin/pushary-hook.js +12 -6
- package/dist/bin/pushary-mode.d.ts +1 -0
- package/dist/bin/pushary-mode.js +84 -0
- package/dist/bin/pushary-post-hook.js +4 -3
- package/dist/bin/pushary-setup.js +48 -57
- package/dist/bin/pushary-stop-hook.js +4 -3
- package/dist/bin/pushary.js +4 -1
- package/dist/chunk-2I6DLXJN.js +219 -0
- package/dist/chunk-3MIR7ODJ.js +112 -0
- package/dist/chunk-4Z4MB37G.js +96 -0
- package/dist/chunk-5JEDLXEC.js +99 -0
- package/dist/chunk-C5TFTNHG.js +244 -0
- package/dist/chunk-EMPL27ZV.js +96 -0
- package/dist/chunk-KYARP7KP.js +97 -0
- package/dist/chunk-O6A5RHWY.js +122 -0
- package/dist/chunk-ODUXELPM.js +219 -0
- package/dist/chunk-PMD5JSV3.js +242 -0
- package/dist/chunk-SDCIKREA.js +241 -0
- package/dist/chunk-VUNL35KE.js +16 -0
- package/dist/chunk-WNXGIEX7.js +219 -0
- package/dist/chunk-YTMKB44I.js +220 -0
- package/dist/pushary-mode-T7XOPI6Z.js +83 -0
- package/dist/src/index.d.ts +5 -3
- package/dist/src/index.js +7 -4
- package/package.json +8 -6
|
@@ -10,6 +10,7 @@ import { join } from "path";
|
|
|
10
10
|
import { homedir } from "os";
|
|
11
11
|
import { execSync } from "child_process";
|
|
12
12
|
import { confirm } from "@inquirer/prompts";
|
|
13
|
+
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
13
14
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
14
15
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
15
16
|
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
@@ -21,7 +22,7 @@ var CLAUDE_SETTINGS_LOCAL = join(homedir(), ".claude", "settings.local.json");
|
|
|
21
22
|
var CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
22
23
|
var SKILL_DIR = join(homedir(), ".claude", "skills", "pushary");
|
|
23
24
|
var CURSOR_MCP = join(".cursor", "mcp.json");
|
|
24
|
-
var SHELL_FILES = [".zshrc", ".bashrc"].map((f) => join(homedir(), f));
|
|
25
|
+
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) => join(homedir(), f));
|
|
25
26
|
var readJson = (path) => {
|
|
26
27
|
try {
|
|
27
28
|
return JSON.parse(readFileSync(path, "utf-8"));
|
|
@@ -91,26 +92,19 @@ var main = async () => {
|
|
|
91
92
|
}
|
|
92
93
|
const codexConfig = join(homedir(), ".codex", "config.toml");
|
|
93
94
|
try {
|
|
94
|
-
|
|
95
|
+
const config = readFileSync(codexConfig, "utf-8");
|
|
95
96
|
const hadPushary = config.includes("pushary");
|
|
96
97
|
if (hadPushary) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (skipping && line.startsWith("[") && !/^\[mcp_servers\.pushary/.test(line.trim())) {
|
|
106
|
-
skipping = false;
|
|
107
|
-
}
|
|
108
|
-
if (skipping) continue;
|
|
109
|
-
if (line.includes("pushary-codex")) continue;
|
|
110
|
-
cleaned.push(line);
|
|
98
|
+
const parsed = parseTOML(config);
|
|
99
|
+
const mcpServers = parsed.mcp_servers ?? {};
|
|
100
|
+
delete mcpServers.pushary;
|
|
101
|
+
if (Object.keys(mcpServers).length === 0) delete parsed.mcp_servers;
|
|
102
|
+
else parsed.mcp_servers = mcpServers;
|
|
103
|
+
if (Array.isArray(parsed.notify)) {
|
|
104
|
+
parsed.notify = parsed.notify.filter((n) => typeof n !== "string" || !n.includes("pushary-codex"));
|
|
105
|
+
if (parsed.notify.length === 0) delete parsed.notify;
|
|
111
106
|
}
|
|
112
|
-
|
|
113
|
-
writeFileSync(codexConfig, config, "utf-8");
|
|
107
|
+
writeFileSync(codexConfig, stringifyTOML(parsed), "utf-8");
|
|
114
108
|
console.log(` ${check} Codex config ${dim("(cleaned)")}`);
|
|
115
109
|
} else {
|
|
116
110
|
console.log(` ${skip} Codex config ${dim("(no pushary entries)")}`);
|
|
@@ -130,7 +124,7 @@ var main = async () => {
|
|
|
130
124
|
}
|
|
131
125
|
}
|
|
132
126
|
try {
|
|
133
|
-
execSync("npm uninstall -g @pushary/agent-hooks", { stdio: "ignore" });
|
|
127
|
+
execSync("npm uninstall -g @pushary/agent-hooks", { stdio: "ignore", timeout: 3e4 });
|
|
134
128
|
console.log(` ${check} Global package ${dim("(uninstalled)")}`);
|
|
135
129
|
} catch {
|
|
136
130
|
console.log(` ${skip} Global package ${dim("(not installed)")}`);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
reportEvent
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-5JEDLXEC.js";
|
|
5
5
|
import {
|
|
6
6
|
askUser,
|
|
7
7
|
waitForAnswer
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-EMPL27ZV.js";
|
|
9
|
+
import "../chunk-3MIR7ODJ.js";
|
|
9
10
|
import {
|
|
10
11
|
getApiKey
|
|
11
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-VUNL35KE.js";
|
|
12
13
|
|
|
13
14
|
// bin/pushary-codex.ts
|
|
14
15
|
import { hostname } from "os";
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
callMcpTool,
|
|
4
4
|
sendMcpRequest
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-3MIR7ODJ.js";
|
|
6
|
+
import "../chunk-VUNL35KE.js";
|
|
6
7
|
|
|
7
8
|
// bin/pushary-doctor.ts
|
|
8
9
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -20,6 +21,7 @@ var fail = red("\u2717");
|
|
|
20
21
|
var warn = yellow("!");
|
|
21
22
|
var CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
|
|
22
23
|
var SKILL_PATH = join(homedir(), ".claude", "skills", "pushary", "SKILL.md");
|
|
24
|
+
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) => join(homedir(), f));
|
|
23
25
|
var readJson = (path) => {
|
|
24
26
|
try {
|
|
25
27
|
return JSON.parse(readFileSync(path, "utf-8"));
|
|
@@ -39,8 +41,28 @@ var main = async () => {
|
|
|
39
41
|
console.log(` ${bold("Pushary Doctor")}`);
|
|
40
42
|
console.log();
|
|
41
43
|
console.log(` ${dim("Configuration")}`);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
let apiKey = process.env.PUSHARY_API_KEY;
|
|
45
|
+
let keyFromProfile = false;
|
|
46
|
+
if (!apiKey) {
|
|
47
|
+
for (const f of SHELL_FILES) {
|
|
48
|
+
try {
|
|
49
|
+
const content = readFileSync(f, "utf-8");
|
|
50
|
+
const match = content.match(/export\s+PUSHARY_API_KEY=['"](pk_[a-f0-9]+\.[a-f0-9]+)['"]/);
|
|
51
|
+
if (match) {
|
|
52
|
+
apiKey = match[1];
|
|
53
|
+
keyFromProfile = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (keyFromProfile) {
|
|
61
|
+
console.log(` ${pass} API key in shell profile ${dim(`(pk_${apiKey.split(".")[0]?.slice(3, 7)}...)`)}`);
|
|
62
|
+
console.log(` ${warn} Not loaded in this shell \u2014 run ${cyan("source ~/.zshrc")} or open a new terminal`);
|
|
63
|
+
} else {
|
|
64
|
+
check(!!apiKey, "API key in environment", apiKey ? `pk_${apiKey.split(".")[0]?.slice(3, 7)}...` : "PUSHARY_API_KEY not set");
|
|
65
|
+
}
|
|
44
66
|
const CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
45
67
|
const claudeJson = readJson(CLAUDE_JSON);
|
|
46
68
|
const mcpServers = claudeJson?.mcpServers ?? {};
|
|
@@ -61,7 +83,7 @@ var main = async () => {
|
|
|
61
83
|
const permissions = settings.permissions;
|
|
62
84
|
const hasWildcard = permissions?.allow?.some((r) => r === "mcp__pushary__*" || r === "MCP(pushary:*)") ?? false;
|
|
63
85
|
check(hasWildcard, "Claude Code: Pushary tools auto-allowed", hasWildcard ? "mcp__pushary__*" : "missing");
|
|
64
|
-
const hasLegacyPerms = permissions?.allow?.some((r) => r.startsWith("mcp__pushary__")) ?? false;
|
|
86
|
+
const hasLegacyPerms = permissions?.allow?.some((r) => r.startsWith("mcp__pushary__") && r !== "mcp__pushary__*") ?? false;
|
|
65
87
|
if (hasLegacyPerms) {
|
|
66
88
|
console.log(` ${warn} Legacy individual permissions detected ${dim("(run pushary clean, then setup again)")}`);
|
|
67
89
|
}
|
|
@@ -89,7 +111,7 @@ var main = async () => {
|
|
|
89
111
|
let globalVersion = "";
|
|
90
112
|
try {
|
|
91
113
|
const { execSync } = await import("child_process");
|
|
92
|
-
globalVersion = execSync("npm list -g @pushary/agent-hooks --depth=0 2>/dev/null", { encoding: "utf-8" }).match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
|
|
114
|
+
globalVersion = execSync("npm list -g @pushary/agent-hooks --depth=0 2>/dev/null", { encoding: "utf-8", timeout: 1e4 }).match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
|
|
93
115
|
} catch {
|
|
94
116
|
}
|
|
95
117
|
check(!!globalVersion, "Global package installed", globalVersion || "not found");
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-C5TFTNHG.js";
|
|
5
|
+
import "../chunk-EMPL27ZV.js";
|
|
6
|
+
import "../chunk-3MIR7ODJ.js";
|
|
7
|
+
import "../chunk-VUNL35KE.js";
|
|
7
8
|
|
|
8
9
|
// bin/pushary-hook.ts
|
|
9
10
|
var main = async () => {
|
|
@@ -25,14 +26,19 @@ var main = async () => {
|
|
|
25
26
|
}
|
|
26
27
|
try {
|
|
27
28
|
const output = await handlePreToolUse(input);
|
|
28
|
-
|
|
29
|
-
process.stdout.write(JSON.stringify(output));
|
|
30
|
-
}
|
|
29
|
+
process.stdout.write(JSON.stringify(output));
|
|
31
30
|
} catch (err) {
|
|
32
31
|
process.stderr.write(
|
|
33
32
|
`[pushary-hook] Error: ${err instanceof Error ? err.message : String(err)}
|
|
34
33
|
`
|
|
35
34
|
);
|
|
35
|
+
process.stdout.write(JSON.stringify({
|
|
36
|
+
hookSpecificOutput: {
|
|
37
|
+
hookEventName: "PreToolUse",
|
|
38
|
+
permissionDecision: "ask",
|
|
39
|
+
permissionDecisionReason: "Pushary hook error, falling back to terminal"
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
36
42
|
}
|
|
37
43
|
};
|
|
38
44
|
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getApiKey,
|
|
4
|
+
getBaseUrl
|
|
5
|
+
} from "../chunk-VUNL35KE.js";
|
|
6
|
+
|
|
7
|
+
// bin/pushary-mode.ts
|
|
8
|
+
var VALID_MODES = ["push_only", "push_first", "terminal_only", "notify_only"];
|
|
9
|
+
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
10
|
+
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
11
|
+
var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
12
|
+
var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
13
|
+
var parseDuration = (value) => {
|
|
14
|
+
const match = value.match(/^(\d+)(m|h)$/);
|
|
15
|
+
if (!match) return null;
|
|
16
|
+
const num = Number(match[1]);
|
|
17
|
+
return match[2] === "h" ? num * 3600 : num * 60;
|
|
18
|
+
};
|
|
19
|
+
var main = async () => {
|
|
20
|
+
const apiKey = getApiKey();
|
|
21
|
+
const baseUrl = getBaseUrl();
|
|
22
|
+
const mode = process.argv[2];
|
|
23
|
+
const forFlag = process.argv[3];
|
|
24
|
+
const forValue = process.argv[4];
|
|
25
|
+
const headers = {
|
|
26
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
27
|
+
"Content-Type": "application/json"
|
|
28
|
+
};
|
|
29
|
+
if (!mode || mode === "status") {
|
|
30
|
+
const res2 = await fetch(`${baseUrl}/api/mcp/mode`, { headers });
|
|
31
|
+
const data2 = await res2.json();
|
|
32
|
+
if (!data2.override) {
|
|
33
|
+
console.log(` Mode: ${cyan("default")} ${dim("(using per-tool policies)")}`);
|
|
34
|
+
} else {
|
|
35
|
+
console.log(` Mode: ${green(data2.override.mode)}`);
|
|
36
|
+
if (data2.override.expiresAt) {
|
|
37
|
+
console.log(` Expires: ${new Date(data2.override.expiresAt).toLocaleString()}`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(` ${dim("Sticky (no expiry)")}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (mode === "clear" || mode === "reset") {
|
|
45
|
+
await fetch(`${baseUrl}/api/mcp/mode`, { method: "DELETE", headers });
|
|
46
|
+
console.log(` ${green("\u2713")} Mode override cleared \u2014 using per-tool policies`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!VALID_MODES.includes(mode)) {
|
|
50
|
+
console.log(` ${yellow("!")} Invalid mode: ${mode}`);
|
|
51
|
+
console.log(` Valid modes: ${VALID_MODES.join(", ")}`);
|
|
52
|
+
console.log(` ${dim("Usage: pushary mode <mode> [--for <duration>]")}`);
|
|
53
|
+
console.log(` ${dim("Example: pushary mode push_only --for 30m")}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let ttlSeconds;
|
|
57
|
+
if (forFlag === "--for" && forValue) {
|
|
58
|
+
const parsed = parseDuration(forValue);
|
|
59
|
+
if (!parsed) {
|
|
60
|
+
console.log(` ${yellow("!")} Invalid duration: ${forValue} ${dim("(use e.g. 30m, 1h)")}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
ttlSeconds = parsed;
|
|
64
|
+
}
|
|
65
|
+
const res = await fetch(`${baseUrl}/api/mcp/mode`, {
|
|
66
|
+
method: "PUT",
|
|
67
|
+
headers,
|
|
68
|
+
body: JSON.stringify({ mode, ttlSeconds })
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
const err = await res.json();
|
|
72
|
+
console.log(` ${yellow("!")} Failed: ${err.error ?? res.statusText}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const data = await res.json();
|
|
76
|
+
console.log(` ${green("\u2713")} Mode set to ${cyan(data.override.mode)}`);
|
|
77
|
+
if (data.override.expiresAt) {
|
|
78
|
+
console.log(` Expires: ${new Date(data.override.expiresAt).toLocaleString()}`);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
main().catch((err) => {
|
|
82
|
+
console.error(` ${yellow("!")} ${err instanceof Error ? err.message : err}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePostToolUse
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-5JEDLXEC.js";
|
|
5
|
+
import "../chunk-EMPL27ZV.js";
|
|
6
|
+
import "../chunk-3MIR7ODJ.js";
|
|
7
|
+
import "../chunk-VUNL35KE.js";
|
|
7
8
|
|
|
8
9
|
// bin/pushary-post-hook.ts
|
|
9
10
|
var main = async () => {
|
|
@@ -12,13 +12,14 @@ import { homedir } from "os";
|
|
|
12
12
|
import { execSync } from "child_process";
|
|
13
13
|
import { checkbox, input, confirm } from "@inquirer/prompts";
|
|
14
14
|
import { fileURLToPath } from "url";
|
|
15
|
+
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
15
16
|
var CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
|
|
16
17
|
var CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
17
18
|
var CURSOR_MCP = join(".cursor", "mcp.json");
|
|
18
19
|
var CURSOR_RULES_DIR = join(".cursor", "rules");
|
|
19
20
|
var CLAUDE_SKILL_DIR = join(homedir(), ".claude", "skills", "pushary");
|
|
20
21
|
var CODEX_SKILL_DIR = join(homedir(), ".codex", "skills", "pushary");
|
|
21
|
-
var SHELL_FILES = [".zshrc", ".bashrc"].map((f) => join(homedir(), f));
|
|
22
|
+
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) => join(homedir(), f));
|
|
22
23
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
23
24
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
24
25
|
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
@@ -37,7 +38,16 @@ var writeJson = (path, data) => {
|
|
|
37
38
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
38
39
|
writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
39
40
|
};
|
|
40
|
-
var formatError = (err) =>
|
|
41
|
+
var formatError = (err) => {
|
|
42
|
+
if (err instanceof Error) {
|
|
43
|
+
const errWithSignal = err;
|
|
44
|
+
if (errWithSignal.killed && errWithSignal.signal === "SIGTERM") {
|
|
45
|
+
return "timed out \u2014 check your network connection";
|
|
46
|
+
}
|
|
47
|
+
return err.message;
|
|
48
|
+
}
|
|
49
|
+
return String(err);
|
|
50
|
+
};
|
|
41
51
|
var spinner = async (label, fn, options = {}) => {
|
|
42
52
|
const frames = [" ", ". ", ".. ", "..."];
|
|
43
53
|
let i = 0;
|
|
@@ -82,7 +92,7 @@ var checkForUpdates = async (current) => {
|
|
|
82
92
|
};
|
|
83
93
|
var installGlobally = async () => {
|
|
84
94
|
await spinner("Installing pushary-hook globally", async () => {
|
|
85
|
-
execSync("npm install -g @pushary/agent-hooks@latest", { stdio: "ignore" });
|
|
95
|
+
execSync("npm install -g @pushary/agent-hooks@latest", { stdio: "ignore", timeout: 12e4 });
|
|
86
96
|
});
|
|
87
97
|
};
|
|
88
98
|
var _cachedSkillContent = null;
|
|
@@ -150,7 +160,7 @@ var findPython310Plus = () => {
|
|
|
150
160
|
const candidates = ["python3.13", "python3.12", "python3.11", "python3.10", "python3", "python"];
|
|
151
161
|
for (const py of candidates) {
|
|
152
162
|
try {
|
|
153
|
-
const version = execSync(`${py} --version 2>&1`, { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
163
|
+
const version = execSync(`${py} --version 2>&1`, { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
|
|
154
164
|
const match = version.match(/Python (\d+)\.(\d+)/);
|
|
155
165
|
if (match && (Number(match[1]) > 3 || Number(match[1]) === 3 && Number(match[2]) >= 10)) {
|
|
156
166
|
return py;
|
|
@@ -161,7 +171,7 @@ var findPython310Plus = () => {
|
|
|
161
171
|
return null;
|
|
162
172
|
};
|
|
163
173
|
var installPythonPlugin = (pythonBin) => {
|
|
164
|
-
execSync(`${pythonBin} -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe" });
|
|
174
|
+
execSync(`${pythonBin} -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
|
|
165
175
|
};
|
|
166
176
|
var setupHermes = async (_apiKey) => {
|
|
167
177
|
console.log(`
|
|
@@ -170,7 +180,7 @@ var setupHermes = async (_apiKey) => {
|
|
|
170
180
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
171
181
|
const hasHermes = (() => {
|
|
172
182
|
try {
|
|
173
|
-
execSync(`${whichCmd} hermes`, { stdio: "ignore" });
|
|
183
|
+
execSync(`${whichCmd} hermes`, { stdio: "ignore", timeout: 5e3 });
|
|
174
184
|
return true;
|
|
175
185
|
} catch {
|
|
176
186
|
return false;
|
|
@@ -183,7 +193,7 @@ var setupHermes = async (_apiKey) => {
|
|
|
183
193
|
}
|
|
184
194
|
await spinner("Installing hermes-plugin-pushary", async () => {
|
|
185
195
|
try {
|
|
186
|
-
execSync("uv pip install hermes-plugin-pushary", { stdio: "pipe" });
|
|
196
|
+
execSync("uv pip install hermes-plugin-pushary", { stdio: "pipe", timeout: 12e4 });
|
|
187
197
|
return;
|
|
188
198
|
} catch {
|
|
189
199
|
}
|
|
@@ -191,8 +201,8 @@ var setupHermes = async (_apiKey) => {
|
|
|
191
201
|
if (!python) {
|
|
192
202
|
if (process.platform === "darwin") {
|
|
193
203
|
try {
|
|
194
|
-
execSync("which brew", { stdio: "ignore" });
|
|
195
|
-
execSync("brew install python@3.12", { stdio: "pipe" });
|
|
204
|
+
execSync("which brew", { stdio: "ignore", timeout: 5e3 });
|
|
205
|
+
execSync("brew install python@3.12", { stdio: "pipe", timeout: 3e5 });
|
|
196
206
|
python = findPython310Plus();
|
|
197
207
|
} catch {
|
|
198
208
|
}
|
|
@@ -204,8 +214,8 @@ var setupHermes = async (_apiKey) => {
|
|
|
204
214
|
["which pacman", "sudo pacman -S --noconfirm python python-pip"]
|
|
205
215
|
]) {
|
|
206
216
|
try {
|
|
207
|
-
execSync(check2, { stdio: "ignore" });
|
|
208
|
-
execSync(install, { stdio: "pipe" });
|
|
217
|
+
execSync(check2, { stdio: "ignore", timeout: 5e3 });
|
|
218
|
+
execSync(install, { stdio: "pipe", timeout: 3e5 });
|
|
209
219
|
python = findPython310Plus();
|
|
210
220
|
if (python) break;
|
|
211
221
|
} catch {
|
|
@@ -219,7 +229,7 @@ var setupHermes = async (_apiKey) => {
|
|
|
219
229
|
}
|
|
220
230
|
for (const pip of ["pip3", "pip"]) {
|
|
221
231
|
try {
|
|
222
|
-
execSync(`${pip} install hermes-plugin-pushary`, { stdio: "pipe" });
|
|
232
|
+
execSync(`${pip} install hermes-plugin-pushary`, { stdio: "pipe", timeout: 12e4 });
|
|
223
233
|
return;
|
|
224
234
|
} catch {
|
|
225
235
|
}
|
|
@@ -227,7 +237,7 @@ var setupHermes = async (_apiKey) => {
|
|
|
227
237
|
throw new Error("Python 3.10+ not found and could not be installed");
|
|
228
238
|
});
|
|
229
239
|
await spinner("Enabling plugin", async () => {
|
|
230
|
-
execSync("hermes plugins enable pushary", { stdio: "ignore" });
|
|
240
|
+
execSync("hermes plugins enable pushary", { stdio: "ignore", timeout: 1e4 });
|
|
231
241
|
});
|
|
232
242
|
console.log();
|
|
233
243
|
console.log(` ${dim("What this configured:")}`);
|
|
@@ -241,7 +251,7 @@ var setupCodex = async (_apiKey) => {
|
|
|
241
251
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
242
252
|
const hasCodex = (() => {
|
|
243
253
|
try {
|
|
244
|
-
execSync(`${whichCmd} codex`, { stdio: "ignore" });
|
|
254
|
+
execSync(`${whichCmd} codex`, { stdio: "ignore", timeout: 5e3 });
|
|
245
255
|
return true;
|
|
246
256
|
} catch {
|
|
247
257
|
return false;
|
|
@@ -258,61 +268,41 @@ var setupCodex = async (_apiKey) => {
|
|
|
258
268
|
try {
|
|
259
269
|
execSync(
|
|
260
270
|
"codex mcp add pushary --url https://pushary.com/api/mcp/mcp --bearer-token-env-var PUSHARY_API_KEY",
|
|
261
|
-
{ stdio: "ignore" }
|
|
271
|
+
{ stdio: "ignore", timeout: 15e3 }
|
|
262
272
|
);
|
|
263
273
|
} catch {
|
|
264
274
|
}
|
|
265
275
|
});
|
|
266
276
|
await spinner("Auto-allowing all Pushary tools", async () => {
|
|
267
|
-
let
|
|
277
|
+
let raw = "";
|
|
268
278
|
try {
|
|
269
|
-
|
|
279
|
+
raw = readFileSync(codexConfig, "utf-8");
|
|
270
280
|
} catch {
|
|
271
281
|
}
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (skippingToolSection) {
|
|
281
|
-
if (line.startsWith("[") || line.trim() === "") {
|
|
282
|
-
skippingToolSection = false;
|
|
283
|
-
if (line.startsWith("[")) {
|
|
284
|
-
cleaned.push(line);
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
} else {
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
cleaned.push(line);
|
|
292
|
-
}
|
|
293
|
-
config = cleaned.join("\n");
|
|
294
|
-
if (!config.includes("default_tools_approval_mode")) {
|
|
295
|
-
config = config.replace(
|
|
296
|
-
/(\[mcp_servers\.pushary\]\n)/,
|
|
297
|
-
'$1default_tools_approval_mode = "approve"\n'
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
writeFileSync(codexConfig, config, "utf-8");
|
|
282
|
+
const config = raw ? parseTOML(raw) : {};
|
|
283
|
+
const mcpServers = config.mcp_servers ?? {};
|
|
284
|
+
const pushary = mcpServers.pushary ?? {};
|
|
285
|
+
pushary.default_tools_approval_mode = "approve";
|
|
286
|
+
delete pushary.tools;
|
|
287
|
+
mcpServers.pushary = pushary;
|
|
288
|
+
config.mcp_servers = mcpServers;
|
|
289
|
+
writeFileSync(codexConfig, stringifyTOML(config), "utf-8");
|
|
301
290
|
});
|
|
302
291
|
await spinner("Adding notify handler for Codex events", async () => {
|
|
303
|
-
const globalPrefix = execSync("npm prefix -g", { encoding: "utf-8" }).trim();
|
|
292
|
+
const globalPrefix = execSync("npm prefix -g", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
304
293
|
const pusharyCodexPath = join(globalPrefix, "bin", "pushary-codex");
|
|
305
294
|
if (!existsSync(pusharyCodexPath)) throw new Error("pushary-codex not found at " + pusharyCodexPath);
|
|
306
|
-
let
|
|
295
|
+
let raw = "";
|
|
307
296
|
try {
|
|
308
|
-
|
|
297
|
+
raw = readFileSync(codexConfig, "utf-8");
|
|
309
298
|
} catch {
|
|
310
299
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
notify
|
|
314
|
-
|
|
315
|
-
|
|
300
|
+
const config = raw ? parseTOML(raw) : {};
|
|
301
|
+
const notify = Array.isArray(config.notify) ? config.notify : [];
|
|
302
|
+
if (!notify.some((n) => typeof n === "string" && n.includes("pushary-codex"))) {
|
|
303
|
+
notify.push(pusharyCodexPath);
|
|
304
|
+
config.notify = notify;
|
|
305
|
+
writeFileSync(codexConfig, stringifyTOML(config), "utf-8");
|
|
316
306
|
}
|
|
317
307
|
});
|
|
318
308
|
await installSkillToDir(CODEX_SKILL_DIR, "Installing Pushary skill");
|
|
@@ -452,9 +442,10 @@ var main = async () => {
|
|
|
452
442
|
console.log(` ${green(bold("Setup complete."))}`);
|
|
453
443
|
console.log();
|
|
454
444
|
console.log(` ${dim("Next:")}`);
|
|
455
|
-
console.log(` ${dim("1.")}
|
|
456
|
-
console.log(` ${dim("2.")}
|
|
457
|
-
console.log(` ${dim("3.")}
|
|
445
|
+
console.log(` ${dim("1.")} Load your API key: ${cyan("source ~/.zshrc")} ${dim("(or open a new terminal)")}`);
|
|
446
|
+
console.log(` ${dim("2.")} Enable notifications on your phone at ${cyan("pushary.com")}`);
|
|
447
|
+
console.log(` ${dim("3.")} Restart your agent to load the new config`);
|
|
448
|
+
console.log(` ${dim("4.")} Run ${cyan("npx @pushary/agent-hooks doctor")} to verify`);
|
|
458
449
|
console.log();
|
|
459
450
|
};
|
|
460
451
|
main().catch((err) => {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleStop
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-5JEDLXEC.js";
|
|
5
|
+
import "../chunk-EMPL27ZV.js";
|
|
6
|
+
import "../chunk-3MIR7ODJ.js";
|
|
7
|
+
import "../chunk-VUNL35KE.js";
|
|
7
8
|
|
|
8
9
|
// bin/pushary-stop-hook.ts
|
|
9
10
|
var main = async () => {
|
package/dist/bin/pushary.js
CHANGED
|
@@ -10,6 +10,8 @@ if (command === "setup") {
|
|
|
10
10
|
await import("./pushary-clean.js");
|
|
11
11
|
} else if (command === "doctor") {
|
|
12
12
|
await import("./pushary-doctor.js");
|
|
13
|
+
} else if (command === "mode") {
|
|
14
|
+
await import("./pushary-mode.js");
|
|
13
15
|
} else {
|
|
14
16
|
console.log(`
|
|
15
17
|
Pushary Agent Hooks
|
|
@@ -18,11 +20,12 @@ Commands:
|
|
|
18
20
|
setup Configure Claude Code, Codex, Hermes, or Cursor with Pushary
|
|
19
21
|
doctor Verify your Pushary installation is working
|
|
20
22
|
clean Remove all Pushary configuration
|
|
23
|
+
mode Switch approval mode (push_only, push_first, terminal_only)
|
|
21
24
|
hook Run as a PreToolUse hook (reads stdin, writes stdout)
|
|
22
25
|
|
|
23
26
|
Usage:
|
|
24
27
|
npx @pushary/agent-hooks@latest setup
|
|
25
28
|
npx @pushary/agent-hooks@latest doctor
|
|
26
|
-
npx @pushary/agent-hooks@latest
|
|
29
|
+
npx @pushary/agent-hooks@latest mode push_only --for 30m
|
|
27
30
|
`);
|
|
28
31
|
}
|