@pushary/agent-hooks 0.29.0 → 0.30.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 +83 -5
- package/dist/bin/pushary-codex-hook.js +22 -1
- package/dist/bin/pushary-doctor.js +28 -4
- package/dist/bin/pushary-gemini-hook.js +22 -1
- package/dist/bin/pushary-hook.js +2 -2
- package/dist/bin/pushary-setup.js +10 -23
- package/dist/bin/pushary-stats.js +1 -1
- package/dist/bin/pushary-upgrade.js +1 -1
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-GPLKEAFG.js → chunk-CY5YP34L.js} +4 -4
- package/dist/{chunk-QNEYHDKR.js → chunk-H3LQRYMW.js} +4 -1
- package/dist/{chunk-RSHN2AQ7.js → chunk-J7JWI3KU.js} +21 -1
- package/dist/{chunk-WLF3NCHS.js → chunk-R5AJNXZS.js} +17 -15
- package/dist/src/index.js +2 -2
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
removeClaudeMcpServers,
|
|
4
4
|
removePusharySettings
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-H3LQRYMW.js";
|
|
6
6
|
import {
|
|
7
7
|
removeCodexHooks,
|
|
8
8
|
removeGeminiSettings,
|
|
@@ -10,13 +10,14 @@ import {
|
|
|
10
10
|
} from "../chunk-XY6OKUQ4.js";
|
|
11
11
|
import {
|
|
12
12
|
execNpm
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-J7JWI3KU.js";
|
|
14
14
|
import "../chunk-Z5PL3K7C.js";
|
|
15
15
|
|
|
16
16
|
// bin/pushary-clean.ts
|
|
17
|
-
import { existsSync, readFileSync, writeFileSync, rmSync } from "fs";
|
|
17
|
+
import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync } from "fs";
|
|
18
18
|
import { join } from "path";
|
|
19
|
-
import { homedir } from "os";
|
|
19
|
+
import { homedir, tmpdir } from "os";
|
|
20
|
+
import { execSync } from "child_process";
|
|
20
21
|
import { confirm } from "@inquirer/prompts";
|
|
21
22
|
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
22
23
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
@@ -31,7 +32,25 @@ var CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
|
31
32
|
var SKILL_DIR = join(homedir(), ".claude", "skills", "pushary");
|
|
32
33
|
var CURSOR_MCP = join(".cursor", "mcp.json");
|
|
33
34
|
var CURSOR_PLUGIN_DIR = join(homedir(), ".cursor", "plugins", "local", "pushary");
|
|
35
|
+
var CURSOR_USER_HOOKS = join(homedir(), ".cursor", "hooks.json");
|
|
36
|
+
var PUSHARY_DIR = join(homedir(), ".pushary");
|
|
34
37
|
var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) => join(homedir(), f));
|
|
38
|
+
var resolveHermesPython = () => {
|
|
39
|
+
try {
|
|
40
|
+
const launcher = execSync("command -v hermes", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
|
|
41
|
+
if (launcher) {
|
|
42
|
+
const shebang = readFileSync(launcher, "utf-8").split("\n", 1)[0];
|
|
43
|
+
if (shebang.startsWith("#!")) {
|
|
44
|
+
const interpreter = shebang.slice(2).trim().split(/\s+/)[0];
|
|
45
|
+
if (interpreter && /python/i.test(interpreter) && existsSync(interpreter)) return interpreter;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
const venvPython = join(homedir(), ".hermes", "hermes-agent", "venv", "bin", "python3");
|
|
51
|
+
if (existsSync(venvPython)) return venvPython;
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
35
54
|
var readJson = (path) => {
|
|
36
55
|
try {
|
|
37
56
|
return JSON.parse(readFileSync(path, "utf-8"));
|
|
@@ -61,7 +80,12 @@ var main = async () => {
|
|
|
61
80
|
console.log(` ${bold("Pushary Clean")}`);
|
|
62
81
|
console.log(` ${dim("Removes all Pushary configuration")}`);
|
|
63
82
|
console.log();
|
|
64
|
-
const
|
|
83
|
+
const assumeYes = process.argv.includes("--yes") || process.argv.includes("-y");
|
|
84
|
+
if (!assumeYes && !process.stdin.isTTY) {
|
|
85
|
+
console.log(` ${yellow("!")} Non-interactive shell. Re-run with --yes to confirm removal.`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
const proceed = assumeYes || await confirm({ message: "Remove all Pushary configuration?", default: false });
|
|
65
89
|
if (!proceed) {
|
|
66
90
|
console.log(` ${dim("Cancelled.")}`);
|
|
67
91
|
process.exit(0);
|
|
@@ -99,6 +123,23 @@ var main = async () => {
|
|
|
99
123
|
} else {
|
|
100
124
|
console.log(` ${skip} Cursor plugin ${dim("(not installed)")}`);
|
|
101
125
|
}
|
|
126
|
+
const cursorHooks = readJson(CURSOR_USER_HOOKS);
|
|
127
|
+
if (cursorHooks) {
|
|
128
|
+
const hooks = cursorHooks.hooks ?? {};
|
|
129
|
+
const existing = Array.isArray(hooks.beforeShellExecution) ? hooks.beforeShellExecution : [];
|
|
130
|
+
const others = existing.filter((h) => !String(h.command ?? "").includes("pushary-gate"));
|
|
131
|
+
if (others.length !== existing.length) {
|
|
132
|
+
if (others.length === 0) delete hooks.beforeShellExecution;
|
|
133
|
+
else hooks.beforeShellExecution = others;
|
|
134
|
+
cursorHooks.hooks = hooks;
|
|
135
|
+
writeJson(CURSOR_USER_HOOKS, cursorHooks);
|
|
136
|
+
console.log(` ${check} Cursor gate ${dim("(removed from ~/.cursor/hooks.json)")}`);
|
|
137
|
+
} else {
|
|
138
|
+
console.log(` ${skip} Cursor gate ${dim("(no pushary entries)")}`);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
console.log(` ${skip} Cursor gate ${dim("(no hooks.json)")}`);
|
|
142
|
+
}
|
|
102
143
|
if (existsSync(SKILL_DIR)) {
|
|
103
144
|
rmSync(SKILL_DIR, { recursive: true });
|
|
104
145
|
console.log(` ${check} Skill directory ${dim("(removed)")}`);
|
|
@@ -170,6 +211,24 @@ var main = async () => {
|
|
|
170
211
|
} else {
|
|
171
212
|
console.log(` ${skip} Gemini GEMINI.md ${dim("(no pushary block)")}`);
|
|
172
213
|
}
|
|
214
|
+
const hermesPython = resolveHermesPython();
|
|
215
|
+
if (hermesPython) {
|
|
216
|
+
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"] = [x for x in e if x != "pushary"]; 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"] = [x for x in d if x != "clarify"]; c["agent"] = a; save_config(c)';
|
|
217
|
+
try {
|
|
218
|
+
execSync(`"${hermesPython}" -c '${snippet}'`, { stdio: "pipe", timeout: 15e3 });
|
|
219
|
+
console.log(` ${check} Hermes config ${dim("(plugin disabled, clarify toolset restored)")}`);
|
|
220
|
+
} catch {
|
|
221
|
+
console.log(` ${skip} Hermes config ${dim("(could not update config.yaml)")}`);
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
execSync(`"${hermesPython}" -m pip uninstall -y hermes-plugin-pushary`, { stdio: "pipe", timeout: 6e4 });
|
|
225
|
+
console.log(` ${check} Hermes plugin ${dim("(pip uninstalled)")}`);
|
|
226
|
+
} catch {
|
|
227
|
+
console.log(` ${skip} Hermes plugin ${dim("(not installed)")}`);
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
console.log(` ${skip} Hermes ${dim("(not found)")}`);
|
|
231
|
+
}
|
|
173
232
|
for (const shellFile of SHELL_FILES) {
|
|
174
233
|
try {
|
|
175
234
|
const content = readFileSync(shellFile, "utf-8");
|
|
@@ -181,6 +240,25 @@ var main = async () => {
|
|
|
181
240
|
} catch {
|
|
182
241
|
}
|
|
183
242
|
}
|
|
243
|
+
if (existsSync(PUSHARY_DIR)) {
|
|
244
|
+
rmSync(PUSHARY_DIR, { recursive: true });
|
|
245
|
+
console.log(` ${check} Local state ${dim("(~/.pushary removed: stored API key, ledger)")}`);
|
|
246
|
+
} else {
|
|
247
|
+
console.log(` ${skip} Local state ${dim("(~/.pushary not found)")}`);
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
let swept = 0;
|
|
251
|
+
for (const f of readdirSync(tmpdir())) {
|
|
252
|
+
if (!f.startsWith("pushary-")) continue;
|
|
253
|
+
try {
|
|
254
|
+
rmSync(join(tmpdir(), f), { recursive: true, force: true });
|
|
255
|
+
swept++;
|
|
256
|
+
} catch {
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (swept > 0) console.log(` ${check} Temp state ${dim(`(${swept} pushary entries swept)`)}`);
|
|
260
|
+
} catch {
|
|
261
|
+
}
|
|
184
262
|
try {
|
|
185
263
|
execNpm("uninstall -g --no-workspaces @pushary/agent-hooks", { stdio: "ignore", timeout: 3e4 });
|
|
186
264
|
console.log(` ${check} Global package ${dim("(uninstalled)")}`);
|
|
@@ -3,6 +3,10 @@ import {
|
|
|
3
3
|
denyReasonFrom,
|
|
4
4
|
isDeferAnswer
|
|
5
5
|
} from "../chunk-KQYIHZ5E.js";
|
|
6
|
+
import {
|
|
7
|
+
isGatingMoment,
|
|
8
|
+
recordKeylessMoment
|
|
9
|
+
} from "../chunk-R5AJNXZS.js";
|
|
6
10
|
import {
|
|
7
11
|
CODEX_AGENT,
|
|
8
12
|
DEFAULT_SESSION,
|
|
@@ -201,8 +205,25 @@ var decidePermissionRequest = async (input) => {
|
|
|
201
205
|
}
|
|
202
206
|
};
|
|
203
207
|
var decidePreToolUse = async (input) => {
|
|
208
|
+
let apiKey;
|
|
209
|
+
try {
|
|
210
|
+
apiKey = getApiKey();
|
|
211
|
+
} catch {
|
|
212
|
+
try {
|
|
213
|
+
const lookup = toPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
|
|
214
|
+
if (isGatingMoment(lookup.tool, lookup.input)) {
|
|
215
|
+
recordKeylessMoment({
|
|
216
|
+
ts: Date.now(),
|
|
217
|
+
tool: lookup.tool,
|
|
218
|
+
project: basename(input.cwd ?? process.cwd()),
|
|
219
|
+
sessionId: input.session_id
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
return codexPass();
|
|
225
|
+
}
|
|
204
226
|
try {
|
|
205
|
-
const apiKey = getApiKey();
|
|
206
227
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
207
228
|
if (modeState.kill) return codexDeny(KILL_REASON);
|
|
208
229
|
const lookup = toPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
|
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
} from "../chunk-XY6OKUQ4.js";
|
|
15
15
|
import {
|
|
16
16
|
execNpm
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-J7JWI3KU.js";
|
|
18
|
+
import {
|
|
19
|
+
readLedgerSummary
|
|
20
|
+
} from "../chunk-R5AJNXZS.js";
|
|
18
21
|
import {
|
|
19
22
|
callMcpTool,
|
|
20
23
|
sendMcpRequest
|
|
@@ -317,12 +320,29 @@ var main = async () => {
|
|
|
317
320
|
globalVersion = "";
|
|
318
321
|
}
|
|
319
322
|
check(!!globalVersion, "Global package installed", globalVersion || "not found");
|
|
323
|
+
if (globalVersion) {
|
|
324
|
+
try {
|
|
325
|
+
const res = await fetch("https://registry.npmjs.org/@pushary/agent-hooks/latest", {
|
|
326
|
+
signal: AbortSignal.timeout(5e3)
|
|
327
|
+
});
|
|
328
|
+
const latest = res.ok ? (await res.json()).version ?? "" : "";
|
|
329
|
+
if (latest) {
|
|
330
|
+
const upToDate = latest === globalVersion;
|
|
331
|
+
check(upToDate, "Global package up to date", upToDate ? globalVersion : `${globalVersion} installed, ${latest} available \u2014 run npx @pushary/agent-hooks@latest upgrade`);
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
320
336
|
console.log();
|
|
321
337
|
console.log(` ${dim("Connectivity")}`);
|
|
322
338
|
if (!apiKey) {
|
|
323
339
|
check(false, "MCP server reachable", "skipped \u2014 no API key");
|
|
324
340
|
check(false, "API key valid", "skipped");
|
|
325
341
|
check(false, "MCP handshake", "skipped");
|
|
342
|
+
const keylessSummary = readLedgerSummary(7);
|
|
343
|
+
if (keylessSummary.count > 0) {
|
|
344
|
+
check(false, "Keyless approval moments (7d)", `${keylessSummary.count} counted locally \u2014 npx @pushary/agent-hooks@latest stats`);
|
|
345
|
+
}
|
|
326
346
|
} else {
|
|
327
347
|
let sessionId = "";
|
|
328
348
|
try {
|
|
@@ -379,7 +399,7 @@ var main = async () => {
|
|
|
379
399
|
const msg = err instanceof Error ? err.message : "network error";
|
|
380
400
|
check(false, "Push notification sent", msg);
|
|
381
401
|
}
|
|
382
|
-
const testQuestion = await confirm({ message: "Test question roundtrip? (sends a push notification)", default: false });
|
|
402
|
+
const testQuestion = process.stdin.isTTY ? await confirm({ message: "Test question roundtrip? (sends a push notification)", default: false }) : false;
|
|
383
403
|
if (testQuestion) {
|
|
384
404
|
console.log();
|
|
385
405
|
console.log(` ${dim("Question Roundtrip")}`);
|
|
@@ -418,7 +438,8 @@ var main = async () => {
|
|
|
418
438
|
if (apiKey) {
|
|
419
439
|
try {
|
|
420
440
|
const res = await fetch(`${getBaseUrl()}/api/mcp/policy`, {
|
|
421
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
441
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
442
|
+
signal: AbortSignal.timeout(1e4)
|
|
422
443
|
});
|
|
423
444
|
if (res.ok) {
|
|
424
445
|
const policy = await res.json();
|
|
@@ -453,4 +474,7 @@ var main = async () => {
|
|
|
453
474
|
}
|
|
454
475
|
console.log();
|
|
455
476
|
};
|
|
456
|
-
main()
|
|
477
|
+
main().catch((err) => {
|
|
478
|
+
console.error(` doctor failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
479
|
+
process.exit(1);
|
|
480
|
+
});
|
|
@@ -3,6 +3,10 @@ import {
|
|
|
3
3
|
denyReasonFrom,
|
|
4
4
|
isDeferAnswer
|
|
5
5
|
} from "../chunk-KQYIHZ5E.js";
|
|
6
|
+
import {
|
|
7
|
+
isGatingMoment,
|
|
8
|
+
recordKeylessMoment
|
|
9
|
+
} from "../chunk-R5AJNXZS.js";
|
|
6
10
|
import {
|
|
7
11
|
DEFAULT_SESSION,
|
|
8
12
|
askUser,
|
|
@@ -181,8 +185,25 @@ var handlePushFirst = async (apiKey, input, lookup, pushFirstSeconds) => {
|
|
|
181
185
|
return geminiPass();
|
|
182
186
|
};
|
|
183
187
|
var decideBeforeTool = async (input) => {
|
|
188
|
+
let apiKey;
|
|
189
|
+
try {
|
|
190
|
+
apiKey = getApiKey();
|
|
191
|
+
} catch {
|
|
192
|
+
try {
|
|
193
|
+
const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {});
|
|
194
|
+
if (isGatingMoment(lookup.tool, lookup.input)) {
|
|
195
|
+
recordKeylessMoment({
|
|
196
|
+
ts: Date.now(),
|
|
197
|
+
tool: lookup.tool,
|
|
198
|
+
project: basename(input.cwd ?? process.cwd()),
|
|
199
|
+
sessionId: input.session_id
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
return geminiPass();
|
|
205
|
+
}
|
|
184
206
|
try {
|
|
185
|
-
const apiKey = getApiKey();
|
|
186
207
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
187
208
|
if (modeState.kill) return geminiDeny(KILL_REASON);
|
|
188
209
|
const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {});
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-WLF3NCHS.js";
|
|
4
|
+
} from "../chunk-CY5YP34L.js";
|
|
6
5
|
import "../chunk-KQYIHZ5E.js";
|
|
6
|
+
import "../chunk-R5AJNXZS.js";
|
|
7
7
|
import "../chunk-IAOXM7X5.js";
|
|
8
8
|
import "../chunk-DWED7BS3.js";
|
|
9
9
|
import "../chunk-Z5PL3K7C.js";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
addClaudeMcpServer,
|
|
4
4
|
addPusharyHooks,
|
|
5
5
|
addPusharyToolPermissions
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-H3LQRYMW.js";
|
|
7
7
|
import {
|
|
8
8
|
GEMINI_HOOK_BINARY,
|
|
9
9
|
addCodexHookTrust,
|
|
@@ -17,8 +17,11 @@ import {
|
|
|
17
17
|
} from "../chunk-XY6OKUQ4.js";
|
|
18
18
|
import {
|
|
19
19
|
execNpm,
|
|
20
|
-
npmErrorMessage
|
|
21
|
-
|
|
20
|
+
npmErrorMessage,
|
|
21
|
+
quoteIfNeeded,
|
|
22
|
+
resolveGlobalBinDir,
|
|
23
|
+
resolveGlobalBinary
|
|
24
|
+
} from "../chunk-J7JWI3KU.js";
|
|
22
25
|
import {
|
|
23
26
|
reportEvent
|
|
24
27
|
} from "../chunk-IAOXM7X5.js";
|
|
@@ -463,13 +466,7 @@ var setupClaudeCode = async (apiKey) => {
|
|
|
463
466
|
});
|
|
464
467
|
await installGlobally();
|
|
465
468
|
await spinner("Adding hooks (PreToolUse, PostToolUse, UserPromptSubmit, Stop)", async () => {
|
|
466
|
-
|
|
467
|
-
try {
|
|
468
|
-
binDir = join(execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim(), "bin");
|
|
469
|
-
} catch {
|
|
470
|
-
binDir = void 0;
|
|
471
|
-
}
|
|
472
|
-
addPusharyHooks(settings, binDir);
|
|
469
|
+
addPusharyHooks(settings, resolveGlobalBinDir("pushary-hook"));
|
|
473
470
|
});
|
|
474
471
|
await spinner(`Writing ${CLAUDE_SETTINGS}`, async () => {
|
|
475
472
|
writeJson(CLAUDE_SETTINGS, settings);
|
|
@@ -591,9 +588,7 @@ var removeCodexNotifyEntry = (codexConfig) => {
|
|
|
591
588
|
writeFileSync(codexConfig, stringifyTOML(config), "utf-8");
|
|
592
589
|
};
|
|
593
590
|
var addCodexNotifyEntry = (codexConfig) => {
|
|
594
|
-
const
|
|
595
|
-
const pusharyCodexPath = join(globalPrefix, "bin", "pushary-codex");
|
|
596
|
-
if (!existsSync(pusharyCodexPath)) throw new Error("pushary-codex not found at " + pusharyCodexPath);
|
|
591
|
+
const pusharyCodexPath = resolveGlobalBinary("pushary-codex") ?? "pushary-codex";
|
|
597
592
|
let raw = "";
|
|
598
593
|
try {
|
|
599
594
|
raw = readFileSync(codexConfig, "utf-8");
|
|
@@ -633,10 +628,8 @@ var setupCodex = async (apiKey) => {
|
|
|
633
628
|
const trustAuto = codexTrustAutoSupported(codexVersion);
|
|
634
629
|
let trusted = false;
|
|
635
630
|
if (hooksSupported) {
|
|
636
|
-
const
|
|
637
|
-
const hookCommand = join(globalPrefix, "bin", "pushary-codex-hook");
|
|
631
|
+
const hookCommand = quoteIfNeeded(resolveGlobalBinary("pushary-codex-hook") ?? "pushary-codex-hook");
|
|
638
632
|
await spinner("Adding native hooks (~/.codex/hooks.json)", async () => {
|
|
639
|
-
if (!existsSync(hookCommand)) throw new Error("pushary-codex-hook not found at " + hookCommand);
|
|
640
633
|
const hooksConfig = readJson(CODEX_HOOKS_JSON);
|
|
641
634
|
addCodexHooks(hooksConfig, hookCommand);
|
|
642
635
|
writeJson(CODEX_HOOKS_JSON, hooksConfig);
|
|
@@ -822,13 +815,7 @@ var setupGemini = async (apiKey) => {
|
|
|
822
815
|
addGeminiMcpServer(settings, apiKey);
|
|
823
816
|
});
|
|
824
817
|
await spinner("Adding hooks (BeforeTool, AfterTool, BeforeAgent, SessionStart, SessionEnd)", async () => {
|
|
825
|
-
|
|
826
|
-
try {
|
|
827
|
-
const candidate = join(execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim(), "bin", GEMINI_HOOK_BINARY);
|
|
828
|
-
if (existsSync(candidate)) hookCommand = candidate;
|
|
829
|
-
} catch {
|
|
830
|
-
}
|
|
831
|
-
addGeminiHooks(settings, hookCommand);
|
|
818
|
+
addGeminiHooks(settings, quoteIfNeeded(resolveGlobalBinary(GEMINI_HOOK_BINARY) ?? GEMINI_HOOK_BINARY));
|
|
832
819
|
});
|
|
833
820
|
await spinner(`Writing ${GEMINI_SETTINGS}`, async () => {
|
|
834
821
|
writeJson(GEMINI_SETTINGS, settings);
|
package/dist/bin/pushary.js
CHANGED
|
@@ -25,7 +25,7 @@ Pushary Agent Hooks
|
|
|
25
25
|
Commands:
|
|
26
26
|
setup Configure Claude Code, Codex, Gemini CLI, Hermes, or Cursor with Pushary
|
|
27
27
|
doctor Verify your Pushary installation is working
|
|
28
|
-
clean Remove all Pushary configuration
|
|
28
|
+
clean Remove all Pushary configuration (--yes for non-interactive)
|
|
29
29
|
mode Switch approval mode (push_only, push_first, terminal_only)
|
|
30
30
|
wait Show or set the "wait for your phone" ladder (pushary wait 45)
|
|
31
31
|
stats Show the approval moments your agents hit while not connected
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isGatingMoment,
|
|
3
|
-
recordKeylessMoment
|
|
4
|
-
} from "./chunk-WLF3NCHS.js";
|
|
5
1
|
import {
|
|
6
2
|
denyReasonFrom,
|
|
7
3
|
isDeferAnswer
|
|
8
4
|
} from "./chunk-KQYIHZ5E.js";
|
|
5
|
+
import {
|
|
6
|
+
isGatingMoment,
|
|
7
|
+
recordKeylessMoment
|
|
8
|
+
} from "./chunk-R5AJNXZS.js";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_SESSION,
|
|
11
11
|
askUser,
|
|
@@ -60,7 +60,10 @@ var addPusharyToolPermissions = (settings) => {
|
|
|
60
60
|
permissions.allow = filtered;
|
|
61
61
|
};
|
|
62
62
|
var addPusharyHooks = (settings, binDir) => {
|
|
63
|
-
const resolve = (name) =>
|
|
63
|
+
const resolve = (name) => {
|
|
64
|
+
const path = binDir ? join(binDir, name) : name;
|
|
65
|
+
return /\s/.test(path) ? `"${path}"` : path;
|
|
66
|
+
};
|
|
64
67
|
const hooks = ensureRecord(settings, "hooks");
|
|
65
68
|
const preToolUse = (Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : []).filter((entry) => !isPusharyHook(entry));
|
|
66
69
|
preToolUse.push({
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/npm.ts
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
3
5
|
var cleanNpmEnv = () => {
|
|
4
6
|
const env = {};
|
|
5
7
|
for (const [key, value] of Object.entries(process.env)) {
|
|
@@ -22,8 +24,26 @@ var execNpm = (args, options = {}) => {
|
|
|
22
24
|
env: { ...cleanNpmEnv(), ...options.env ?? {} }
|
|
23
25
|
});
|
|
24
26
|
};
|
|
27
|
+
var resolveGlobalBinary = (name) => {
|
|
28
|
+
try {
|
|
29
|
+
const prefix = execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim();
|
|
30
|
+
const candidates = process.platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
|
|
31
|
+
return candidates.find(existsSync);
|
|
32
|
+
} catch {
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var resolveGlobalBinDir = (probe) => {
|
|
37
|
+
if (process.platform === "win32") return void 0;
|
|
38
|
+
const resolved = resolveGlobalBinary(probe);
|
|
39
|
+
return resolved ? resolved.slice(0, resolved.length - probe.length - 1) : void 0;
|
|
40
|
+
};
|
|
41
|
+
var quoteIfNeeded = (path) => /\s/.test(path) ? `"${path}"` : path;
|
|
25
42
|
|
|
26
43
|
export {
|
|
27
44
|
npmErrorMessage,
|
|
28
|
-
execNpm
|
|
45
|
+
execNpm,
|
|
46
|
+
resolveGlobalBinary,
|
|
47
|
+
resolveGlobalBinDir,
|
|
48
|
+
quoteIfNeeded
|
|
29
49
|
};
|
|
@@ -30,26 +30,28 @@ var recordKeylessMoment = (entry) => {
|
|
|
30
30
|
var readLedgerSummary = (days = 7) => {
|
|
31
31
|
const sinceMs = Date.now() - days * 864e5;
|
|
32
32
|
const summary = { count: 0, sessions: 0, byTool: {}, sinceMs };
|
|
33
|
-
let raw;
|
|
34
|
-
try {
|
|
35
|
-
raw = readFileSync(ledgerPath(), "utf-8");
|
|
36
|
-
} catch {
|
|
37
|
-
return summary;
|
|
38
|
-
}
|
|
39
33
|
const sessions = /* @__PURE__ */ new Set();
|
|
40
|
-
for (const
|
|
41
|
-
|
|
42
|
-
let entry;
|
|
34
|
+
for (const path of [`${ledgerPath()}.1`, ledgerPath()]) {
|
|
35
|
+
let raw;
|
|
43
36
|
try {
|
|
44
|
-
|
|
37
|
+
raw = readFileSync(path, "utf-8");
|
|
45
38
|
} catch {
|
|
46
39
|
continue;
|
|
47
40
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
for (const line of raw.split("\n")) {
|
|
42
|
+
if (!line.trim()) continue;
|
|
43
|
+
let entry;
|
|
44
|
+
try {
|
|
45
|
+
entry = JSON.parse(line);
|
|
46
|
+
} catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (typeof entry.ts !== "number" || entry.ts < sinceMs) continue;
|
|
50
|
+
summary.count += 1;
|
|
51
|
+
const tool = typeof entry.tool === "string" && entry.tool ? entry.tool : "Other";
|
|
52
|
+
summary.byTool[tool] = (summary.byTool[tool] ?? 0) + 1;
|
|
53
|
+
sessions.add(entry.sessionId || `${entry.project ?? "unknown"}:no-session`);
|
|
54
|
+
}
|
|
53
55
|
}
|
|
54
56
|
summary.sessions = sessions.size;
|
|
55
57
|
return summary;
|
package/dist/src/index.js
CHANGED