@pushary/agent-hooks 0.28.2 → 0.29.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/README.md +5 -3
- package/dist/bin/pushary-clean.js +4 -2
- package/dist/bin/pushary-doctor.js +4 -2
- package/dist/bin/pushary-hook.js +3 -2
- package/dist/bin/pushary-setup.js +6 -4
- package/dist/bin/pushary-stats.d.ts +1 -0
- package/dist/bin/pushary-stats.js +38 -0
- package/dist/bin/pushary-upgrade.d.ts +1 -0
- package/dist/bin/pushary-upgrade.js +58 -0
- package/dist/bin/pushary.js +6 -0
- package/dist/{chunk-P6QBZS27.js → chunk-GPLKEAFG.js} +40 -2
- package/dist/chunk-RSHN2AQ7.js +29 -0
- package/dist/chunk-WLF3NCHS.js +62 -0
- package/dist/{chunk-X3YCHQQK.js → chunk-XY6OKUQ4.js} +1 -28
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +2 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ There are three things.
|
|
|
22
22
|
|
|
23
23
|
**Ask.** The agent asks you questions through push: yes/no, multiple choice, or free text. It waits for your answer. When Pushary is connected, the agent sends its questions to your phone instead of stalling in the terminal.
|
|
24
24
|
|
|
25
|
-
**Gate.** Risky shell commands (`rm -rf`, force pushes, history rewrites, database drops, deploys) are checked before they run. What happens is set by your dashboard policy: auto approve trusted read-only commands, push to your phone for approval, or hold for the phone and never auto-deny. If the check cannot run, the
|
|
25
|
+
**Gate.** Risky shell commands (`rm -rf`, force pushes, history rewrites, database drops, deploys) are checked before they run. What happens is set by your dashboard policy: auto approve trusted read-only commands, push to your phone for approval, or hold for the phone and never auto-deny. If the check cannot run, the decision falls back to the agent's own permission prompt in the terminal; nothing is silently allowed. Without an API key configured, the hooks stay out of the way entirely and only count what they would have gated.
|
|
26
26
|
|
|
27
27
|
## Setup
|
|
28
28
|
|
|
@@ -62,8 +62,10 @@ Any other MCP client can use notify and ask by pointing at the Pushary MCP endpo
|
|
|
62
62
|
setup Configure Claude Code, Codex, Gemini CLI, Hermes, or Cursor with Pushary
|
|
63
63
|
doctor Verify your Pushary installation is working
|
|
64
64
|
clean Remove all Pushary configuration
|
|
65
|
-
mode Switch approval mode (push_only, push_first, terminal_only)
|
|
65
|
+
mode Switch approval mode (push_only, push_first, terminal_only, notify_only)
|
|
66
66
|
wait Show or set the "wait for your phone" ladder (pushary wait 45)
|
|
67
|
+
stats Show the approval moments your agents hit while not connected
|
|
68
|
+
upgrade Update the globally installed hooks to the latest version
|
|
67
69
|
hook Run as a PreToolUse hook (reads stdin, writes stdout)
|
|
68
70
|
```
|
|
69
71
|
|
|
@@ -97,7 +99,7 @@ The MCP server exposes `send_notification`, `ask_user`, `wait_for_answer`, and `
|
|
|
97
99
|
|
|
98
100
|
## Security
|
|
99
101
|
|
|
100
|
-
|
|
102
|
+
If a policy cannot be fetched or a hook errors, the decision falls back to the agent's native permission prompt, never a silent allow. The Cursor gate is the strictest surface: it blocks matched risky commands outright when its check cannot run. Secrets in command arguments and environment are redacted before anything is sent to a phone or stored. Read-only commands can be allowlisted so trusted work never interrupts you, while writes and destructive commands still route through approval.
|
|
101
103
|
|
|
102
104
|
## Links
|
|
103
105
|
|
|
@@ -4,11 +4,13 @@ import {
|
|
|
4
4
|
removePusharySettings
|
|
5
5
|
} from "../chunk-QNEYHDKR.js";
|
|
6
6
|
import {
|
|
7
|
-
execNpm,
|
|
8
7
|
removeCodexHooks,
|
|
9
8
|
removeGeminiSettings,
|
|
10
9
|
removeInstructionBlock
|
|
11
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-XY6OKUQ4.js";
|
|
11
|
+
import {
|
|
12
|
+
execNpm
|
|
13
|
+
} from "../chunk-RSHN2AQ7.js";
|
|
12
14
|
import "../chunk-Z5PL3K7C.js";
|
|
13
15
|
|
|
14
16
|
// bin/pushary-clean.ts
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
} from "../chunk-IIENZE2J.js";
|
|
5
5
|
import {
|
|
6
6
|
GEMINI_HOOK_BINARY,
|
|
7
|
-
execNpm,
|
|
8
7
|
hasCodexHooks,
|
|
9
8
|
hasGeminiHooks,
|
|
10
9
|
hasInstructionBlock,
|
|
@@ -12,7 +11,10 @@ import {
|
|
|
12
11
|
missingGeminiHookEvents,
|
|
13
12
|
readCodexMcpAuth,
|
|
14
13
|
untrustedCodexHookEvents
|
|
15
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-XY6OKUQ4.js";
|
|
15
|
+
import {
|
|
16
|
+
execNpm
|
|
17
|
+
} from "../chunk-RSHN2AQ7.js";
|
|
16
18
|
import {
|
|
17
19
|
callMcpTool,
|
|
18
20
|
sendMcpRequest
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-GPLKEAFG.js";
|
|
5
|
+
import "../chunk-WLF3NCHS.js";
|
|
5
6
|
import "../chunk-KQYIHZ5E.js";
|
|
6
7
|
import "../chunk-IAOXM7X5.js";
|
|
7
8
|
import "../chunk-DWED7BS3.js";
|
|
@@ -28,7 +29,7 @@ var main = async () => {
|
|
|
28
29
|
}
|
|
29
30
|
try {
|
|
30
31
|
const output = await handlePreToolUse(input);
|
|
31
|
-
process.stdout.write(JSON.stringify(output));
|
|
32
|
+
if (output) process.stdout.write(JSON.stringify(output));
|
|
32
33
|
} catch (err) {
|
|
33
34
|
process.stderr.write(
|
|
34
35
|
`[pushary-hook] Error: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
addCodexMcpServer,
|
|
12
12
|
addGeminiHooks,
|
|
13
13
|
addGeminiMcpServer,
|
|
14
|
-
execNpm,
|
|
15
|
-
npmErrorMessage,
|
|
16
14
|
renderAgentInstructions,
|
|
17
15
|
renderProjectAgentInstructions,
|
|
18
16
|
writeInstructionBlock
|
|
19
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-XY6OKUQ4.js";
|
|
18
|
+
import {
|
|
19
|
+
execNpm,
|
|
20
|
+
npmErrorMessage
|
|
21
|
+
} from "../chunk-RSHN2AQ7.js";
|
|
20
22
|
import {
|
|
21
23
|
reportEvent
|
|
22
24
|
} from "../chunk-IAOXM7X5.js";
|
|
@@ -411,7 +413,7 @@ var checkForUpdates = async (current) => {
|
|
|
411
413
|
const latest = data.version;
|
|
412
414
|
if (latest && latest !== current) {
|
|
413
415
|
console.log(` ${yellow2("!")} Update available: ${dim2(current)} \u2192 ${green2(latest)}`);
|
|
414
|
-
console.log(` ${dim2("Run:")} npx @pushary/agent-hooks
|
|
416
|
+
console.log(` ${dim2("Run:")} npx @pushary/agent-hooks@latest upgrade`);
|
|
415
417
|
console.log();
|
|
416
418
|
}
|
|
417
419
|
} catch {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
readLedgerSummary
|
|
4
|
+
} from "../chunk-WLF3NCHS.js";
|
|
5
|
+
import "../chunk-Z5PL3K7C.js";
|
|
6
|
+
import {
|
|
7
|
+
getApiKey
|
|
8
|
+
} from "../chunk-NKXSILEW.js";
|
|
9
|
+
|
|
10
|
+
// bin/pushary-stats.ts
|
|
11
|
+
var DAYS = 7;
|
|
12
|
+
var summary = readLedgerSummary(DAYS);
|
|
13
|
+
var connected = true;
|
|
14
|
+
try {
|
|
15
|
+
getApiKey();
|
|
16
|
+
} catch {
|
|
17
|
+
connected = false;
|
|
18
|
+
}
|
|
19
|
+
var plural = (count, word) => count === 1 ? word : `${word}s`;
|
|
20
|
+
if (summary.count === 0) {
|
|
21
|
+
if (connected) {
|
|
22
|
+
console.log("No locally counted approval moments in the last 7 days. Connected sessions land in your dashboard: https://pushary.com/dashboard/agent/activity");
|
|
23
|
+
} else {
|
|
24
|
+
console.log("No approval moments counted yet. Run your agent with the Pushary hooks installed and check back after a session or two.");
|
|
25
|
+
}
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
var toolLines = Object.entries(summary.byTool).sort((a, b) => b[1] - a[1]).map(([tool, count]) => ` ${tool}: ${count}`).join("\n");
|
|
29
|
+
console.log(`In the last ${DAYS} days, your agents hit ${summary.count} approval ${plural(summary.count, "moment")} across ${summary.sessions} ${plural(summary.sessions, "session")} while Pushary was not connected:
|
|
30
|
+
`);
|
|
31
|
+
console.log(toolLines);
|
|
32
|
+
console.log();
|
|
33
|
+
if (connected) {
|
|
34
|
+
console.log("A key is configured now, so new moments reach your phone instead of waiting in the terminal.");
|
|
35
|
+
} else {
|
|
36
|
+
console.log("Each of those sat in the terminal until you noticed. With Pushary they land on your lock screen as a yes or no.");
|
|
37
|
+
console.log("7-day trial: https://pushary.com/sign-up?utm_source=cli&utm_medium=stats");
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
execNpm,
|
|
4
|
+
npmErrorMessage
|
|
5
|
+
} from "../chunk-RSHN2AQ7.js";
|
|
6
|
+
|
|
7
|
+
// bin/pushary-upgrade.ts
|
|
8
|
+
var getInstalledVersion = () => {
|
|
9
|
+
try {
|
|
10
|
+
return execNpm("list -g --no-workspaces @pushary/agent-hooks --depth=0", { timeout: 1e4 }).toString().match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
|
|
11
|
+
} catch {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var fetchLatestVersion = async () => {
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch("https://registry.npmjs.org/@pushary/agent-hooks/latest", {
|
|
18
|
+
signal: AbortSignal.timeout(5e3)
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) return void 0;
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
return data.version;
|
|
23
|
+
} catch {
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var isNewer = (a, b) => {
|
|
28
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
29
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
30
|
+
for (let i = 0; i < 3; i++) {
|
|
31
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true;
|
|
32
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
};
|
|
36
|
+
var main = async () => {
|
|
37
|
+
const installed = getInstalledVersion();
|
|
38
|
+
if (!installed) {
|
|
39
|
+
console.log("No global install of @pushary/agent-hooks found, so there is nothing to upgrade.");
|
|
40
|
+
console.log("Plugin installs update on their own. To set up the CLI hooks: npx @pushary/agent-hooks@latest setup");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const latest = await fetchLatestVersion();
|
|
44
|
+
if (latest && !isNewer(latest, installed)) {
|
|
45
|
+
console.log(`Already on the latest version (${installed}).`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
console.log(`Upgrading @pushary/agent-hooks ${installed} -> ${latest ?? "latest"}`);
|
|
49
|
+
try {
|
|
50
|
+
execNpm("install -g --no-workspaces @pushary/agent-hooks@latest", { timeout: 12e4 });
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(`Upgrade failed: ${npmErrorMessage(err)}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
console.log("Done. Your hooks point at the global install, so agents pick this up on their next tool call.");
|
|
56
|
+
console.log("Verify with: pushary doctor");
|
|
57
|
+
};
|
|
58
|
+
main();
|
package/dist/bin/pushary.js
CHANGED
|
@@ -14,6 +14,10 @@ if (command === "setup") {
|
|
|
14
14
|
await import("./pushary-mode.js");
|
|
15
15
|
} else if (command === "wait") {
|
|
16
16
|
await import("./pushary-wait.js");
|
|
17
|
+
} else if (command === "stats") {
|
|
18
|
+
await import("./pushary-stats.js");
|
|
19
|
+
} else if (command === "upgrade") {
|
|
20
|
+
await import("./pushary-upgrade.js");
|
|
17
21
|
} else {
|
|
18
22
|
console.log(`
|
|
19
23
|
Pushary Agent Hooks
|
|
@@ -24,6 +28,8 @@ Commands:
|
|
|
24
28
|
clean Remove all Pushary configuration
|
|
25
29
|
mode Switch approval mode (push_only, push_first, terminal_only)
|
|
26
30
|
wait Show or set the "wait for your phone" ladder (pushary wait 45)
|
|
31
|
+
stats Show the approval moments your agents hit while not connected
|
|
32
|
+
upgrade Update the globally installed hooks to the latest version
|
|
27
33
|
hook Run as a PreToolUse hook (reads stdin, writes stdout)
|
|
28
34
|
|
|
29
35
|
Setup options:
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isGatingMoment,
|
|
3
|
+
recordKeylessMoment
|
|
4
|
+
} from "./chunk-WLF3NCHS.js";
|
|
1
5
|
import {
|
|
2
6
|
denyReasonFrom,
|
|
3
7
|
isDeferAnswer
|
|
@@ -31,7 +35,9 @@ import {
|
|
|
31
35
|
} from "./chunk-NKXSILEW.js";
|
|
32
36
|
|
|
33
37
|
// src/hook.ts
|
|
34
|
-
import { basename } from "path";
|
|
38
|
+
import { basename, join } from "path";
|
|
39
|
+
import { tmpdir } from "os";
|
|
40
|
+
import { existsSync, writeFileSync } from "fs";
|
|
35
41
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
42
|
var START_MS = Date.now();
|
|
37
43
|
var allow = () => ({
|
|
@@ -182,9 +188,41 @@ var handleNotifyOnly = async (apiKey, description, projectName, sessionId, machi
|
|
|
182
188
|
}
|
|
183
189
|
return ask();
|
|
184
190
|
};
|
|
191
|
+
var KEYLESS_NOTICE = "[pushary] Not connected (no API key), so your agent runs untouched. Approval moments are counted locally; see them with: npx @pushary/agent-hooks@latest stats. Connect: https://pushary.com/sign-up?utm_source=cli&utm_medium=keyless-hook";
|
|
192
|
+
var keylessNoticeOnce = (sessionId) => {
|
|
193
|
+
try {
|
|
194
|
+
const key = (sessionId || DEFAULT_SESSION).replace(/[^a-zA-Z0-9_-]/g, "");
|
|
195
|
+
const marker = join(tmpdir(), `pushary-keyless-${key}`);
|
|
196
|
+
if (existsSync(marker)) return;
|
|
197
|
+
writeFileSync(marker, "1", "utf-8");
|
|
198
|
+
process.stderr.write(`${KEYLESS_NOTICE}
|
|
199
|
+
`);
|
|
200
|
+
} catch {
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
var handleKeyless = (input) => {
|
|
204
|
+
try {
|
|
205
|
+
if (isGatingMoment(input.tool_name, input.tool_input ?? {})) {
|
|
206
|
+
recordKeylessMoment({
|
|
207
|
+
ts: Date.now(),
|
|
208
|
+
tool: input.tool_name,
|
|
209
|
+
project: basename(input.cwd ?? process.cwd()),
|
|
210
|
+
sessionId: input.session_id
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
keylessNoticeOnce(input.session_id);
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
216
|
+
return void 0;
|
|
217
|
+
};
|
|
185
218
|
var handlePreToolUse = async (input) => {
|
|
219
|
+
let apiKey;
|
|
220
|
+
try {
|
|
221
|
+
apiKey = getApiKey();
|
|
222
|
+
} catch {
|
|
223
|
+
return handleKeyless(input);
|
|
224
|
+
}
|
|
186
225
|
try {
|
|
187
|
-
const apiKey = getApiKey();
|
|
188
226
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
189
227
|
const policy = await getPolicy(apiKey, modeState.policyVersion);
|
|
190
228
|
if (modeState.kill) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/npm.ts
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
var cleanNpmEnv = () => {
|
|
4
|
+
const env = {};
|
|
5
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
6
|
+
if (key.toLowerCase().startsWith("npm_config_workspace")) continue;
|
|
7
|
+
env[key] = value;
|
|
8
|
+
}
|
|
9
|
+
return env;
|
|
10
|
+
};
|
|
11
|
+
var npmErrorMessage = (err) => {
|
|
12
|
+
const e = err;
|
|
13
|
+
const text = [e?.stderr, e?.stdout].map((part) => part ? part.toString() : "").join("\n");
|
|
14
|
+
const line = text.split("\n").map((l) => l.replace(/^npm error\s*/i, "").trim()).find((l) => l && !l.startsWith("A complete log") && !/^code\s/i.test(l));
|
|
15
|
+
return line || e?.message || String(err);
|
|
16
|
+
};
|
|
17
|
+
var execNpm = (args, options = {}) => {
|
|
18
|
+
return execSync(`npm ${args}`, {
|
|
19
|
+
timeout: 12e4,
|
|
20
|
+
stdio: "pipe",
|
|
21
|
+
...options,
|
|
22
|
+
env: { ...cleanNpmEnv(), ...options.env ?? {} }
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
npmErrorMessage,
|
|
28
|
+
execNpm
|
|
29
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isSafeReadOnlyCommand
|
|
3
|
+
} from "./chunk-Z5PL3K7C.js";
|
|
4
|
+
|
|
5
|
+
// src/ledger.ts
|
|
6
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
|
|
7
|
+
import { join, dirname } from "path";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
var ledgerPath = () => process.env.PUSHARY_LEDGER_FILE?.trim() || join(homedir(), ".pushary", "local-ledger.jsonl");
|
|
10
|
+
var MAX_LEDGER_BYTES = 1e6;
|
|
11
|
+
var isGatingMoment = (toolName, toolInput) => {
|
|
12
|
+
const command = typeof toolInput?.command === "string" ? toolInput.command : void 0;
|
|
13
|
+
if (toolName === "Bash" && command && isSafeReadOnlyCommand(command)) return false;
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
16
|
+
var recordKeylessMoment = (entry) => {
|
|
17
|
+
try {
|
|
18
|
+
const path = ledgerPath();
|
|
19
|
+
const dir = dirname(path);
|
|
20
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
21
|
+
if (existsSync(path) && statSync(path).size > MAX_LEDGER_BYTES) {
|
|
22
|
+
rmSync(`${path}.1`, { force: true });
|
|
23
|
+
renameSync(path, `${path}.1`);
|
|
24
|
+
}
|
|
25
|
+
appendFileSync(path, `${JSON.stringify(entry)}
|
|
26
|
+
`, "utf-8");
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var readLedgerSummary = (days = 7) => {
|
|
31
|
+
const sinceMs = Date.now() - days * 864e5;
|
|
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
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
40
|
+
for (const line of raw.split("\n")) {
|
|
41
|
+
if (!line.trim()) continue;
|
|
42
|
+
let entry;
|
|
43
|
+
try {
|
|
44
|
+
entry = JSON.parse(line);
|
|
45
|
+
} catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (typeof entry.ts !== "number" || entry.ts < sinceMs) continue;
|
|
49
|
+
summary.count += 1;
|
|
50
|
+
const tool = typeof entry.tool === "string" && entry.tool ? entry.tool : "Other";
|
|
51
|
+
summary.byTool[tool] = (summary.byTool[tool] ?? 0) + 1;
|
|
52
|
+
sessions.add(entry.sessionId || `${entry.project ?? "unknown"}:no-session`);
|
|
53
|
+
}
|
|
54
|
+
summary.sessions = sessions.size;
|
|
55
|
+
return summary;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
isGatingMoment,
|
|
60
|
+
recordKeylessMoment,
|
|
61
|
+
readLedgerSummary
|
|
62
|
+
};
|
|
@@ -307,31 +307,6 @@ var hasInstructionBlock = (filePath) => {
|
|
|
307
307
|
}
|
|
308
308
|
};
|
|
309
309
|
|
|
310
|
-
// src/npm.ts
|
|
311
|
-
import { execSync } from "child_process";
|
|
312
|
-
var cleanNpmEnv = () => {
|
|
313
|
-
const env = {};
|
|
314
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
315
|
-
if (key.toLowerCase().startsWith("npm_config_workspace")) continue;
|
|
316
|
-
env[key] = value;
|
|
317
|
-
}
|
|
318
|
-
return env;
|
|
319
|
-
};
|
|
320
|
-
var npmErrorMessage = (err) => {
|
|
321
|
-
const e = err;
|
|
322
|
-
const text = [e?.stderr, e?.stdout].map((part) => part ? part.toString() : "").join("\n");
|
|
323
|
-
const line = text.split("\n").map((l) => l.replace(/^npm error\s*/i, "").trim()).find((l) => l && !l.startsWith("A complete log") && !/^code\s/i.test(l));
|
|
324
|
-
return line || e?.message || String(err);
|
|
325
|
-
};
|
|
326
|
-
var execNpm = (args, options = {}) => {
|
|
327
|
-
return execSync(`npm ${args}`, {
|
|
328
|
-
timeout: 12e4,
|
|
329
|
-
stdio: "pipe",
|
|
330
|
-
...options,
|
|
331
|
-
env: { ...cleanNpmEnv(), ...options.env ?? {} }
|
|
332
|
-
});
|
|
333
|
-
};
|
|
334
|
-
|
|
335
310
|
export {
|
|
336
311
|
addCodexMcpServer,
|
|
337
312
|
readCodexMcpAuth,
|
|
@@ -351,7 +326,5 @@ export {
|
|
|
351
326
|
renderProjectAgentInstructions,
|
|
352
327
|
writeInstructionBlock,
|
|
353
328
|
removeInstructionBlock,
|
|
354
|
-
hasInstructionBlock
|
|
355
|
-
npmErrorMessage,
|
|
356
|
-
execNpm
|
|
329
|
+
hasInstructionBlock
|
|
357
330
|
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ interface HookOutput {
|
|
|
15
15
|
permissionDecisionReason?: string;
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
declare const handlePreToolUse: (input: ToolInput) => Promise<HookOutput>;
|
|
18
|
+
declare const handlePreToolUse: (input: ToolInput) => Promise<HookOutput | undefined>;
|
|
19
19
|
|
|
20
20
|
type ReceiptKind = 'edit' | 'write' | 'bash' | 'commit';
|
|
21
21
|
interface ReceiptMeta {
|
package/dist/src/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushary/agent-hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pushary",
|
|
@@ -54,7 +54,9 @@
|
|
|
54
54
|
"pushary-clean": "./dist/bin/pushary-clean.js",
|
|
55
55
|
"pushary-doctor": "./dist/bin/pushary-doctor.js",
|
|
56
56
|
"pushary-mode": "./dist/bin/pushary-mode.js",
|
|
57
|
-
"pushary-wait": "./dist/bin/pushary-wait.js"
|
|
57
|
+
"pushary-wait": "./dist/bin/pushary-wait.js",
|
|
58
|
+
"pushary-stats": "./dist/bin/pushary-stats.js",
|
|
59
|
+
"pushary-upgrade": "./dist/bin/pushary-upgrade.js"
|
|
58
60
|
},
|
|
59
61
|
"files": [
|
|
60
62
|
"dist",
|
|
@@ -63,7 +65,7 @@
|
|
|
63
65
|
"scripts": {
|
|
64
66
|
"build": "node scripts/bundle-plugin.mjs && tsup",
|
|
65
67
|
"dev": "tsup --watch",
|
|
66
|
-
"test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts"
|
|
68
|
+
"test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts"
|
|
67
69
|
},
|
|
68
70
|
"dependencies": {
|
|
69
71
|
"@inquirer/prompts": "^8.4.2",
|