@pushary/agent-hooks 0.32.2 → 0.34.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 +1 -1
- package/dist/bin/pushary-codex-hook.js +1 -1
- package/dist/bin/pushary-codex.js +1 -1
- package/dist/bin/pushary-gemini-hook.js +1 -1
- package/dist/bin/pushary-hook.js +2 -2
- package/dist/bin/pushary-notification-hook.js +1 -1
- package/dist/bin/pushary-post-hook.js +1 -1
- package/dist/bin/pushary-prompt-hook.js +1 -1
- package/dist/bin/pushary-session-start-hook.d.ts +1 -0
- package/dist/bin/pushary-session-start-hook.js +21 -0
- package/dist/bin/pushary-setup.js +2 -2
- package/dist/bin/pushary-stop-hook.js +1 -1
- package/dist/{chunk-P7B3U6DR.js → chunk-FIBI2QZC.js} +41 -7
- package/dist/{chunk-2AMV2BDM.js → chunk-M3YN5ZNW.js} +17 -6
- package/dist/{chunk-M6YUEVRV.js → chunk-RQSJVS3L.js} +15 -4
- package/dist/src/index.js +2 -2
- package/package.json +2 -1
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-
|
|
4
|
+
} from "../chunk-M3YN5ZNW.js";
|
|
5
5
|
import "../chunk-KQYIHZ5E.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-FIBI2QZC.js";
|
|
7
7
|
import "../chunk-R5AJNXZS.js";
|
|
8
8
|
import "../chunk-DWED7BS3.js";
|
|
9
9
|
import "../chunk-Z5PL3K7C.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
handleSessionStart
|
|
4
|
+
} from "../chunk-FIBI2QZC.js";
|
|
5
|
+
import "../chunk-DWED7BS3.js";
|
|
6
|
+
import "../chunk-Z5PL3K7C.js";
|
|
7
|
+
import "../chunk-NKXSILEW.js";
|
|
8
|
+
|
|
9
|
+
// bin/pushary-session-start-hook.ts
|
|
10
|
+
var main = async () => {
|
|
11
|
+
let rawInput = "";
|
|
12
|
+
for await (const chunk of process.stdin) {
|
|
13
|
+
rawInput += chunk;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const input = rawInput.trim() ? JSON.parse(rawInput) : {};
|
|
17
|
+
await handleSessionStart(input);
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
main();
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
addClaudeMcpServer,
|
|
4
4
|
addPusharyHooks,
|
|
5
5
|
addPusharyToolPermissions
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-RQSJVS3L.js";
|
|
7
7
|
import {
|
|
8
8
|
GEMINI_HOOK_BINARY,
|
|
9
9
|
addCodexHookTrust,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "../chunk-J7JWI3KU.js";
|
|
25
25
|
import {
|
|
26
26
|
reportEvent
|
|
27
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-FIBI2QZC.js";
|
|
28
28
|
import "../chunk-DWED7BS3.js";
|
|
29
29
|
import {
|
|
30
30
|
isValidApiKey
|
|
@@ -190,15 +190,34 @@ var fetchModeOverride = async (apiKey) => (await fetchModeState(apiKey)).mode;
|
|
|
190
190
|
import { isAbsolute, relative } from "path";
|
|
191
191
|
var hookPrefixes = {
|
|
192
192
|
Bash: (input) => `bash: ${input.command ?? "(no command)"}`,
|
|
193
|
+
PowerShell: (input) => `powershell: ${input.command ?? "(no command)"}`,
|
|
194
|
+
Monitor: (input) => `monitor: ${input.command ?? "(no command)"}`,
|
|
193
195
|
Write: (input) => `write file: ${input.file_path ?? "(unknown path)"}`,
|
|
194
196
|
Edit: (input) => `edit file: ${input.file_path ?? "(unknown path)"}`,
|
|
197
|
+
MultiEdit: (input) => `edit file: ${input.file_path ?? "(unknown path)"}`,
|
|
195
198
|
Read: (input) => `read file: ${input.file_path ?? "(unknown path)"}`
|
|
196
199
|
};
|
|
200
|
+
var describeTodoProgress = (todos) => {
|
|
201
|
+
if (!Array.isArray(todos)) return "updated the plan";
|
|
202
|
+
const items = todos.filter((t) => !!t && typeof t === "object");
|
|
203
|
+
const total = items.length;
|
|
204
|
+
if (total === 0) return "updated the plan";
|
|
205
|
+
const done = items.filter((t) => t.status === "completed").length;
|
|
206
|
+
const current = items.find((t) => t.status === "in_progress");
|
|
207
|
+
const label = current ? [current.activeForm, current.content].find((v) => typeof v === "string" && v.trim().length > 0) : void 0;
|
|
208
|
+
if (done >= total) return `finished all ${total} tasks`;
|
|
209
|
+
if (label) return `${label.trim()} (${done}/${total} done)`;
|
|
210
|
+
return `${done}/${total} tasks done`;
|
|
211
|
+
};
|
|
197
212
|
var eventPrefixes = {
|
|
198
213
|
Bash: (input) => `ran: ${String(input.command ?? "")}`,
|
|
214
|
+
PowerShell: (input) => `ran: ${String(input.command ?? "")}`,
|
|
215
|
+
Monitor: (input) => `ran: ${String(input.command ?? "")}`,
|
|
199
216
|
Write: (input) => `wrote: ${input.file_path ?? "unknown"}`,
|
|
200
217
|
Edit: (input) => `edited: ${input.file_path ?? "unknown"}`,
|
|
201
|
-
|
|
218
|
+
MultiEdit: (input) => `edited: ${input.file_path ?? "unknown"}`,
|
|
219
|
+
Read: (input) => `read: ${input.file_path ?? "unknown"}`,
|
|
220
|
+
TodoWrite: (input) => describeTodoProgress(input.todos)
|
|
202
221
|
};
|
|
203
222
|
var EVENT_ACTION_MAX = 120;
|
|
204
223
|
var HOOK_FALLBACK_MAX = 200;
|
|
@@ -217,10 +236,10 @@ var deriveCommandHead = (command) => {
|
|
|
217
236
|
return head ? head.slice(0, TOOL_TARGET_MAX_LENGTH) : void 0;
|
|
218
237
|
};
|
|
219
238
|
var deriveToolTarget = (toolName, toolInput) => {
|
|
220
|
-
if (toolName === "Bash") {
|
|
239
|
+
if (toolName === "Bash" || toolName === "PowerShell" || toolName === "Monitor") {
|
|
221
240
|
return deriveCommandHead(toolInput.command);
|
|
222
241
|
}
|
|
223
|
-
if (toolName === "Edit" || toolName === "Write") {
|
|
242
|
+
if (toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") {
|
|
224
243
|
const filePath = toolInput.file_path;
|
|
225
244
|
if (typeof filePath !== "string") return void 0;
|
|
226
245
|
const separator = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
|
|
@@ -246,16 +265,16 @@ var relativizeReceiptPath = (filePath, cwd) => {
|
|
|
246
265
|
var deriveReceiptMeta = (toolName, toolInput, toolResult, cwd) => {
|
|
247
266
|
try {
|
|
248
267
|
const ok = !isToolResultError(toolResult);
|
|
249
|
-
if (toolName === "Edit" || toolName === "Write") {
|
|
268
|
+
if (toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") {
|
|
250
269
|
const filePath = toolInput.file_path;
|
|
251
270
|
if (typeof filePath !== "string" || !filePath) return void 0;
|
|
252
271
|
return {
|
|
253
|
-
kind: toolName === "
|
|
272
|
+
kind: toolName === "Write" ? "write" : "edit",
|
|
254
273
|
target: relativizeReceiptPath(filePath, cwd).slice(0, RECEIPT_TARGET_MAX_LENGTH),
|
|
255
274
|
ok
|
|
256
275
|
};
|
|
257
276
|
}
|
|
258
|
-
if (toolName === "Bash") {
|
|
277
|
+
if (toolName === "Bash" || toolName === "PowerShell" || toolName === "Monitor") {
|
|
259
278
|
const head = deriveCommandHead(toolInput.command);
|
|
260
279
|
if (!head) return void 0;
|
|
261
280
|
return {
|
|
@@ -281,7 +300,7 @@ var rawActionBody = (toolName, toolInput) => {
|
|
|
281
300
|
+ ${after ?? ""}`;
|
|
282
301
|
}
|
|
283
302
|
if (toolName === "Write" || toolName === "write_file") return firstString(toolInput.content);
|
|
284
|
-
if (toolName === "Bash" || toolName === "run_shell_command" || toolName === "shell" || toolName === "apply_patch") {
|
|
303
|
+
if (toolName === "Bash" || toolName === "PowerShell" || toolName === "Monitor" || toolName === "run_shell_command" || toolName === "shell" || toolName === "apply_patch") {
|
|
285
304
|
return firstString(toolInput.command);
|
|
286
305
|
}
|
|
287
306
|
return void 0;
|
|
@@ -919,6 +938,20 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
919
938
|
return void 0;
|
|
920
939
|
}
|
|
921
940
|
};
|
|
941
|
+
var handleSessionStart = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
942
|
+
try {
|
|
943
|
+
const projectName = basename(input.cwd ?? process.cwd());
|
|
944
|
+
const action = input.source === "resume" ? "Session resumed" : "Session started";
|
|
945
|
+
await reportEvent({
|
|
946
|
+
event: "session_start",
|
|
947
|
+
agentType: agent.type,
|
|
948
|
+
agentName: `${agent.label} - ${projectName}`,
|
|
949
|
+
action,
|
|
950
|
+
sessionId: input.session_id
|
|
951
|
+
}, { maxAttempts: 1, timeoutMs: 800 });
|
|
952
|
+
} catch {
|
|
953
|
+
}
|
|
954
|
+
};
|
|
922
955
|
var NOTIFICATION_PUSH_TYPES = /* @__PURE__ */ new Set(["idle_prompt", "agent_needs_input"]);
|
|
923
956
|
var NOTIFICATION_THROTTLE_MS = 60 * 1e3;
|
|
924
957
|
var handleNotification = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
@@ -990,5 +1023,6 @@ export {
|
|
|
990
1023
|
handlePostToolUse,
|
|
991
1024
|
handleUserPrompt,
|
|
992
1025
|
handleStop,
|
|
1026
|
+
handleSessionStart,
|
|
993
1027
|
handleNotification
|
|
994
1028
|
};
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
savePendingQuestion,
|
|
21
21
|
sendNotification,
|
|
22
22
|
waitForAnswer
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-FIBI2QZC.js";
|
|
24
24
|
import {
|
|
25
25
|
isGatingMoment,
|
|
26
26
|
recordKeylessMoment
|
|
@@ -200,11 +200,22 @@ var keylessNoticeOnce = (sessionId) => {
|
|
|
200
200
|
} catch {
|
|
201
201
|
}
|
|
202
202
|
};
|
|
203
|
-
var
|
|
204
|
-
var
|
|
203
|
+
var FULL_DEFER_MODES = /* @__PURE__ */ new Set(["plan", "auto", "dontAsk"]);
|
|
204
|
+
var ACCEPT_EDITS_DEFER_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
205
|
+
var nativeModeGatingDisabled = () => {
|
|
205
206
|
const flag = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
return flag === "0" || flag === "false";
|
|
208
|
+
};
|
|
209
|
+
var shouldDeferToNativeMode = (permissionMode, toolName) => {
|
|
210
|
+
if (nativeModeGatingDisabled()) return false;
|
|
211
|
+
if (typeof permissionMode !== "string") return false;
|
|
212
|
+
if (FULL_DEFER_MODES.has(permissionMode)) return true;
|
|
213
|
+
if (permissionMode === "acceptEdits" && ACCEPT_EDITS_DEFER_TOOLS.has(toolName)) return true;
|
|
214
|
+
if (permissionMode === "bypassPermissions") {
|
|
215
|
+
const flag = process.env.PUSHARY_DEFER_BYPASS;
|
|
216
|
+
return flag === "1" || flag === "true";
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
208
219
|
};
|
|
209
220
|
var handleKeyless = (input) => {
|
|
210
221
|
try {
|
|
@@ -235,7 +246,7 @@ var handlePreToolUse = async (input) => {
|
|
|
235
246
|
if (modeState.kill) {
|
|
236
247
|
return deny("Stopped by user \u2014 this agent was halted from Pushary");
|
|
237
248
|
}
|
|
238
|
-
if (shouldDeferToNativeMode(input.permission_mode)) {
|
|
249
|
+
if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
|
|
239
250
|
return void 0;
|
|
240
251
|
}
|
|
241
252
|
const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input);
|
|
@@ -20,7 +20,7 @@ var isPusharyHook = (entry) => {
|
|
|
20
20
|
if (!Array.isArray(hooks)) return false;
|
|
21
21
|
return hooks.some((hook) => {
|
|
22
22
|
const command = String(asRecord(hook)?.command ?? "");
|
|
23
|
-
return command.includes("pushary-hook") || command.includes("pushary-post-hook") || command.includes("pushary-stop-hook") || command.includes("pushary-prompt-hook") || command.includes("pushary-notification-hook");
|
|
23
|
+
return command.includes("pushary-hook") || command.includes("pushary-post-hook") || command.includes("pushary-stop-hook") || command.includes("pushary-prompt-hook") || command.includes("pushary-notification-hook") || command.includes("pushary-session-start-hook");
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
26
|
var addClaudeMcpServer = (config, apiKey) => {
|
|
@@ -65,9 +65,10 @@ var addPusharyHooks = (settings, binDir) => {
|
|
|
65
65
|
return /\s/.test(path) ? `"${path}"` : path;
|
|
66
66
|
};
|
|
67
67
|
const hooks = ensureRecord(settings, "hooks");
|
|
68
|
+
const SHELL_AND_FILE_TOOLS = "Bash|Write|Edit|MultiEdit|NotebookEdit|PowerShell|Monitor";
|
|
68
69
|
const preToolUse = (Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : []).filter((entry) => !isPusharyHook(entry));
|
|
69
70
|
preToolUse.push({
|
|
70
|
-
matcher:
|
|
71
|
+
matcher: SHELL_AND_FILE_TOOLS,
|
|
71
72
|
hooks: [{
|
|
72
73
|
type: "command",
|
|
73
74
|
command: resolve("pushary-hook"),
|
|
@@ -77,7 +78,7 @@ var addPusharyHooks = (settings, binDir) => {
|
|
|
77
78
|
hooks.PreToolUse = preToolUse;
|
|
78
79
|
const postToolUse = (Array.isArray(hooks.PostToolUse) ? hooks.PostToolUse : []).filter((entry) => !isPusharyHook(entry));
|
|
79
80
|
postToolUse.push({
|
|
80
|
-
matcher:
|
|
81
|
+
matcher: `${SHELL_AND_FILE_TOOLS}|TodoWrite`,
|
|
81
82
|
hooks: [{
|
|
82
83
|
type: "command",
|
|
83
84
|
command: resolve("pushary-post-hook"),
|
|
@@ -113,6 +114,16 @@ var addPusharyHooks = (settings, binDir) => {
|
|
|
113
114
|
}]
|
|
114
115
|
});
|
|
115
116
|
hooks.Notification = notification;
|
|
117
|
+
const sessionStart = (Array.isArray(hooks.SessionStart) ? hooks.SessionStart : []).filter((entry) => !isPusharyHook(entry));
|
|
118
|
+
sessionStart.push({
|
|
119
|
+
matcher: "startup|resume|clear",
|
|
120
|
+
hooks: [{
|
|
121
|
+
type: "command",
|
|
122
|
+
command: resolve("pushary-session-start-hook"),
|
|
123
|
+
timeout: 10
|
|
124
|
+
}]
|
|
125
|
+
});
|
|
126
|
+
hooks.SessionStart = sessionStart;
|
|
116
127
|
};
|
|
117
128
|
var removePusharySettings = (settings) => {
|
|
118
129
|
let changed = removeClaudeMcpServers(settings);
|
|
@@ -131,7 +142,7 @@ var removePusharySettings = (settings) => {
|
|
|
131
142
|
}
|
|
132
143
|
const hooks = asRecord(settings.hooks);
|
|
133
144
|
if (hooks) {
|
|
134
|
-
for (const key of ["PreToolUse", "PostToolUse", "Stop", "UserPromptSubmit", "Notification"]) {
|
|
145
|
+
for (const key of ["PreToolUse", "PostToolUse", "Stop", "UserPromptSubmit", "Notification", "SessionStart"]) {
|
|
135
146
|
const entries = hooks[key];
|
|
136
147
|
if (!Array.isArray(entries)) continue;
|
|
137
148
|
const filtered = entries.filter((entry) => !isPusharyHook(entry));
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-M3YN5ZNW.js";
|
|
4
4
|
import "../chunk-KQYIHZ5E.js";
|
|
5
5
|
import {
|
|
6
6
|
askUser,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
reportEvent,
|
|
15
15
|
resolvePolicy,
|
|
16
16
|
waitForAnswer
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-FIBI2QZC.js";
|
|
18
18
|
import "../chunk-R5AJNXZS.js";
|
|
19
19
|
import "../chunk-DWED7BS3.js";
|
|
20
20
|
import "../chunk-Z5PL3K7C.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushary/agent-hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pushary",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"pushary-stop-hook": "./dist/bin/pushary-stop-hook.js",
|
|
49
49
|
"pushary-prompt-hook": "./dist/bin/pushary-prompt-hook.js",
|
|
50
50
|
"pushary-notification-hook": "./dist/bin/pushary-notification-hook.js",
|
|
51
|
+
"pushary-session-start-hook": "./dist/bin/pushary-session-start-hook.js",
|
|
51
52
|
"pushary-claude": "./dist/bin/pushary-claude.js",
|
|
52
53
|
"pushary-codex": "./dist/bin/pushary-codex.js",
|
|
53
54
|
"pushary-codex-hook": "./dist/bin/pushary-codex-hook.js",
|