@pushary/agent-hooks 0.34.0 → 0.36.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-end-hook.d.ts +1 -0
- package/dist/bin/pushary-session-end-hook.js +21 -0
- package/dist/bin/pushary-session-start-hook.js +1 -1
- package/dist/bin/pushary-setup.js +2 -2
- package/dist/bin/pushary-stop-hook.js +1 -1
- package/dist/{chunk-M3YN5ZNW.js → chunk-JR4H3FND.js} +53 -1
- package/dist/{chunk-RQSJVS3L.js → chunk-SO6ZG5ML.js} +12 -3
- package/dist/{chunk-FIBI2QZC.js → chunk-WH5ASAZK.js} +25 -0
- package/dist/src/index.d.ts +1 -0
- 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-JR4H3FND.js";
|
|
5
5
|
import "../chunk-KQYIHZ5E.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-WH5ASAZK.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
|
+
handleSessionEnd
|
|
4
|
+
} from "../chunk-WH5ASAZK.js";
|
|
5
|
+
import "../chunk-DWED7BS3.js";
|
|
6
|
+
import "../chunk-Z5PL3K7C.js";
|
|
7
|
+
import "../chunk-NKXSILEW.js";
|
|
8
|
+
|
|
9
|
+
// bin/pushary-session-end-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 handleSessionEnd(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-SO6ZG5ML.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-WH5ASAZK.js";
|
|
28
28
|
import "../chunk-DWED7BS3.js";
|
|
29
29
|
import {
|
|
30
30
|
isValidApiKey
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
savePendingQuestion,
|
|
21
21
|
sendNotification,
|
|
22
22
|
waitForAnswer
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-WH5ASAZK.js";
|
|
24
24
|
import {
|
|
25
25
|
isGatingMoment,
|
|
26
26
|
recordKeylessMoment
|
|
@@ -53,6 +53,13 @@ var deny = (reason) => ({
|
|
|
53
53
|
permissionDecisionReason: reason
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
|
+
var allowWithInput = (updatedInput) => ({
|
|
57
|
+
hookSpecificOutput: {
|
|
58
|
+
hookEventName: "PreToolUse",
|
|
59
|
+
permissionDecision: "allow",
|
|
60
|
+
updatedInput
|
|
61
|
+
}
|
|
62
|
+
});
|
|
56
63
|
var ask = (reason) => ({
|
|
57
64
|
hookSpecificOutput: {
|
|
58
65
|
hookEventName: "PreToolUse",
|
|
@@ -217,6 +224,48 @@ var shouldDeferToNativeMode = (permissionMode, toolName) => {
|
|
|
217
224
|
}
|
|
218
225
|
return false;
|
|
219
226
|
};
|
|
227
|
+
var parseSingleQuestion = (toolInput) => {
|
|
228
|
+
const questions = toolInput.questions;
|
|
229
|
+
if (!Array.isArray(questions) || questions.length !== 1) return void 0;
|
|
230
|
+
const q = questions[0];
|
|
231
|
+
if (!q || typeof q.question !== "string" || q.multiSelect === true) return void 0;
|
|
232
|
+
const labels = Array.isArray(q.options) ? q.options.map((o) => o && typeof o === "object" ? o.label : void 0).filter((l) => typeof l === "string" && l.length > 0) : [];
|
|
233
|
+
if (labels.length < 2) return void 0;
|
|
234
|
+
return { question: q.question, header: typeof q.header === "string" ? q.header : void 0, labels };
|
|
235
|
+
};
|
|
236
|
+
var handleAskUserQuestion = async (apiKey, input) => {
|
|
237
|
+
const parsed = parseSingleQuestion(input.tool_input ?? {});
|
|
238
|
+
if (!parsed) return void 0;
|
|
239
|
+
const projectName = basename(input.cwd ?? process.cwd());
|
|
240
|
+
let result;
|
|
241
|
+
try {
|
|
242
|
+
result = await askUser(apiKey, {
|
|
243
|
+
question: parsed.question,
|
|
244
|
+
type: "select",
|
|
245
|
+
options: [...parsed.labels],
|
|
246
|
+
context: parsed.header ? `${parsed.header}: your agent is asking in ${projectName}` : `Your agent is asking in ${projectName}`,
|
|
247
|
+
agentName: `Claude Code - ${projectName}`,
|
|
248
|
+
sessionId: input.session_id,
|
|
249
|
+
machineId: getMachineId(),
|
|
250
|
+
toolName: "AskUserQuestion"
|
|
251
|
+
});
|
|
252
|
+
} catch {
|
|
253
|
+
return void 0;
|
|
254
|
+
}
|
|
255
|
+
if (result.suppressed || result.noDevices) {
|
|
256
|
+
await cancelQuestion(apiKey, result.correlationId).catch(() => {
|
|
257
|
+
});
|
|
258
|
+
return void 0;
|
|
259
|
+
}
|
|
260
|
+
const deadline = hookWaitDeadline(START_MS, effectiveWaitSeconds("wait", 0, "claude"), "claude", Date.now());
|
|
261
|
+
const answer = await pollForAnswer(apiKey, result.correlationId, deadline);
|
|
262
|
+
const value = answer.answered ? (answer.value ?? "").trim() : "";
|
|
263
|
+
if (!value || isDeferAnswer(answer.value)) {
|
|
264
|
+
savePendingQuestion(input.session_id || DEFAULT_SESSION, result.correlationId);
|
|
265
|
+
return void 0;
|
|
266
|
+
}
|
|
267
|
+
return allowWithInput({ ...input.tool_input ?? {}, answers: { [parsed.question]: value } });
|
|
268
|
+
};
|
|
220
269
|
var handleKeyless = (input) => {
|
|
221
270
|
try {
|
|
222
271
|
if (isGatingMoment(input.tool_name, input.tool_input ?? {})) {
|
|
@@ -246,6 +295,9 @@ var handlePreToolUse = async (input) => {
|
|
|
246
295
|
if (modeState.kill) {
|
|
247
296
|
return deny("Stopped by user \u2014 this agent was halted from Pushary");
|
|
248
297
|
}
|
|
298
|
+
if (input.tool_name === "AskUserQuestion") {
|
|
299
|
+
return handleAskUserQuestion(apiKey, input);
|
|
300
|
+
}
|
|
249
301
|
if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
|
|
250
302
|
return void 0;
|
|
251
303
|
}
|
|
@@ -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") || command.includes("pushary-session-start-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") || command.includes("pushary-session-end-hook");
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
26
|
var addClaudeMcpServer = (config, apiKey) => {
|
|
@@ -68,7 +68,7 @@ var addPusharyHooks = (settings, binDir) => {
|
|
|
68
68
|
const SHELL_AND_FILE_TOOLS = "Bash|Write|Edit|MultiEdit|NotebookEdit|PowerShell|Monitor";
|
|
69
69
|
const preToolUse = (Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : []).filter((entry) => !isPusharyHook(entry));
|
|
70
70
|
preToolUse.push({
|
|
71
|
-
matcher: SHELL_AND_FILE_TOOLS
|
|
71
|
+
matcher: `${SHELL_AND_FILE_TOOLS}|AskUserQuestion`,
|
|
72
72
|
hooks: [{
|
|
73
73
|
type: "command",
|
|
74
74
|
command: resolve("pushary-hook"),
|
|
@@ -124,6 +124,15 @@ var addPusharyHooks = (settings, binDir) => {
|
|
|
124
124
|
}]
|
|
125
125
|
});
|
|
126
126
|
hooks.SessionStart = sessionStart;
|
|
127
|
+
const sessionEnd = (Array.isArray(hooks.SessionEnd) ? hooks.SessionEnd : []).filter((entry) => !isPusharyHook(entry));
|
|
128
|
+
sessionEnd.push({
|
|
129
|
+
hooks: [{
|
|
130
|
+
type: "command",
|
|
131
|
+
command: resolve("pushary-session-end-hook"),
|
|
132
|
+
timeout: 10
|
|
133
|
+
}]
|
|
134
|
+
});
|
|
135
|
+
hooks.SessionEnd = sessionEnd;
|
|
127
136
|
};
|
|
128
137
|
var removePusharySettings = (settings) => {
|
|
129
138
|
let changed = removeClaudeMcpServers(settings);
|
|
@@ -142,7 +151,7 @@ var removePusharySettings = (settings) => {
|
|
|
142
151
|
}
|
|
143
152
|
const hooks = asRecord(settings.hooks);
|
|
144
153
|
if (hooks) {
|
|
145
|
-
for (const key of ["PreToolUse", "PostToolUse", "Stop", "UserPromptSubmit", "Notification", "SessionStart"]) {
|
|
154
|
+
for (const key of ["PreToolUse", "PostToolUse", "Stop", "UserPromptSubmit", "Notification", "SessionStart", "SessionEnd"]) {
|
|
146
155
|
const entries = hooks[key];
|
|
147
156
|
if (!Array.isArray(entries)) continue;
|
|
148
157
|
const filtered = entries.filter((entry) => !isPusharyHook(entry));
|
|
@@ -952,6 +952,30 @@ var handleSessionStart = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
952
952
|
} catch {
|
|
953
953
|
}
|
|
954
954
|
};
|
|
955
|
+
var SESSION_END_ACTIONS = {
|
|
956
|
+
clear: "Session cleared",
|
|
957
|
+
logout: "Logged out",
|
|
958
|
+
resume: "Session suspended",
|
|
959
|
+
prompt_input_exit: "Session ended",
|
|
960
|
+
bypass_permissions_disabled: "Session ended",
|
|
961
|
+
other: "Session ended"
|
|
962
|
+
};
|
|
963
|
+
var handleSessionEnd = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
964
|
+
try {
|
|
965
|
+
const projectName = basename(input.cwd ?? process.cwd());
|
|
966
|
+
const action = typeof input.reason === "string" && SESSION_END_ACTIONS[input.reason] || "Session ended";
|
|
967
|
+
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
968
|
+
if (!isDefaultSession(sessionKey)) removePendingSession(sessionKey);
|
|
969
|
+
await reportEvent({
|
|
970
|
+
event: "session_closed",
|
|
971
|
+
agentType: agent.type,
|
|
972
|
+
agentName: `${agent.label} - ${projectName}`,
|
|
973
|
+
action,
|
|
974
|
+
sessionId: input.session_id
|
|
975
|
+
}, { maxAttempts: 1, timeoutMs: 800 });
|
|
976
|
+
} catch {
|
|
977
|
+
}
|
|
978
|
+
};
|
|
955
979
|
var NOTIFICATION_PUSH_TYPES = /* @__PURE__ */ new Set(["idle_prompt", "agent_needs_input"]);
|
|
956
980
|
var NOTIFICATION_THROTTLE_MS = 60 * 1e3;
|
|
957
981
|
var handleNotification = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
@@ -1024,5 +1048,6 @@ export {
|
|
|
1024
1048
|
handleUserPrompt,
|
|
1025
1049
|
handleStop,
|
|
1026
1050
|
handleSessionStart,
|
|
1051
|
+
handleSessionEnd,
|
|
1027
1052
|
handleNotification
|
|
1028
1053
|
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ interface HookOutput {
|
|
|
14
14
|
hookEventName: 'PreToolUse';
|
|
15
15
|
permissionDecision: 'allow' | 'deny' | 'ask';
|
|
16
16
|
permissionDecisionReason?: string;
|
|
17
|
+
updatedInput?: Record<string, unknown>;
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
declare const handlePreToolUse: (input: ToolInput) => Promise<HookOutput | undefined>;
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-JR4H3FND.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-WH5ASAZK.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.36.0",
|
|
4
4
|
"description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pushary",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"pushary-prompt-hook": "./dist/bin/pushary-prompt-hook.js",
|
|
50
50
|
"pushary-notification-hook": "./dist/bin/pushary-notification-hook.js",
|
|
51
51
|
"pushary-session-start-hook": "./dist/bin/pushary-session-start-hook.js",
|
|
52
|
+
"pushary-session-end-hook": "./dist/bin/pushary-session-end-hook.js",
|
|
52
53
|
"pushary-claude": "./dist/bin/pushary-claude.js",
|
|
53
54
|
"pushary-codex": "./dist/bin/pushary-codex.js",
|
|
54
55
|
"pushary-codex-hook": "./dist/bin/pushary-codex-hook.js",
|