pushary 1.8.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/CHANGELOG.md +1943 -0
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/data/SKILL.md +591 -0
- package/data/cowork/SKILL.md +77 -0
- package/data/cursor-plugin/.cursor-plugin/plugin.json +25 -0
- package/data/cursor-plugin/CHANGELOG.md +70 -0
- package/data/cursor-plugin/CONTRIBUTING.md +40 -0
- package/data/cursor-plugin/LICENSE +21 -0
- package/data/cursor-plugin/README.md +113 -0
- package/data/cursor-plugin/SECURITY.md +33 -0
- package/data/cursor-plugin/assets/logo.png +0 -0
- package/data/cursor-plugin/commands/notify-when-done.md +14 -0
- package/data/cursor-plugin/commands/pushary-test.md +13 -0
- package/data/cursor-plugin/hooks/hooks.json +75 -0
- package/data/cursor-plugin/mcp.json +11 -0
- package/data/cursor-plugin/rules/pushary.mdc +40 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +724 -0
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +137 -0
- package/data/cursor-plugin/scripts/redaction.mjs +51 -0
- package/data/cursor-plugin/skills/pushary/SKILL.md +584 -0
- package/data/vscode-plugin/.claude-plugin/plugin.json +28 -0
- package/data/vscode-plugin/.mcp.json +11 -0
- package/data/vscode-plugin/CHANGELOG.md +49 -0
- package/data/vscode-plugin/CONTRIBUTING.md +40 -0
- package/data/vscode-plugin/LICENSE +21 -0
- package/data/vscode-plugin/README.md +128 -0
- package/data/vscode-plugin/SECURITY.md +56 -0
- package/data/vscode-plugin/assets/logo.png +0 -0
- package/data/vscode-plugin/commands/notify-when-done.md +14 -0
- package/data/vscode-plugin/commands/pushary-test.md +13 -0
- package/data/vscode-plugin/hooks/hooks.json +60 -0
- package/data/vscode-plugin/scripts/pushary-gate.mjs +762 -0
- package/data/vscode-plugin/scripts/redaction.mjs +51 -0
- package/data/vscode-plugin/skills/pushary/SKILL.md +584 -0
- package/dist/bin/pushary-bell-hook.d.ts +1 -0
- package/dist/bin/pushary-bell-hook.js +46 -0
- package/dist/bin/pushary-bell.d.ts +1 -0
- package/dist/bin/pushary-bell.js +146 -0
- package/dist/bin/pushary-claude.d.ts +1 -0
- package/dist/bin/pushary-claude.js +1916 -0
- package/dist/bin/pushary-clean.d.ts +1 -0
- package/dist/bin/pushary-clean.js +1300 -0
- package/dist/bin/pushary-codex-bridge.d.ts +1 -0
- package/dist/bin/pushary-codex-bridge.js +599 -0
- package/dist/bin/pushary-codex-hook.d.ts +1 -0
- package/dist/bin/pushary-codex-hook.js +886 -0
- package/dist/bin/pushary-codex.d.ts +1 -0
- package/dist/bin/pushary-codex.js +125 -0
- package/dist/bin/pushary-connect.d.ts +1 -0
- package/dist/bin/pushary-connect.js +100 -0
- package/dist/bin/pushary-cowork.d.ts +1 -0
- package/dist/bin/pushary-cowork.js +36 -0
- package/dist/bin/pushary-daemon-supervisor.d.ts +1 -0
- package/dist/bin/pushary-daemon-supervisor.js +27 -0
- package/dist/bin/pushary-daemon.d.ts +1 -0
- package/dist/bin/pushary-daemon.js +878 -0
- package/dist/bin/pushary-disconnect.d.ts +1 -0
- package/dist/bin/pushary-disconnect.js +264 -0
- package/dist/bin/pushary-doctor.d.ts +1 -0
- package/dist/bin/pushary-doctor.js +1433 -0
- package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
- package/dist/bin/pushary-elicitation-hook.js +149 -0
- package/dist/bin/pushary-gemini-bridge.d.ts +1 -0
- package/dist/bin/pushary-gemini-bridge.js +339 -0
- package/dist/bin/pushary-gemini-hook.d.ts +1 -0
- package/dist/bin/pushary-gemini-hook.js +381 -0
- package/dist/bin/pushary-hook.d.ts +1 -0
- package/dist/bin/pushary-hook.js +95 -0
- package/dist/bin/pushary-login.d.ts +1 -0
- package/dist/bin/pushary-login.js +198 -0
- package/dist/bin/pushary-logout.d.ts +1 -0
- package/dist/bin/pushary-logout.js +147 -0
- package/dist/bin/pushary-mcp.d.ts +1 -0
- package/dist/bin/pushary-mcp.js +114 -0
- package/dist/bin/pushary-mode.d.ts +1 -0
- package/dist/bin/pushary-mode.js +130 -0
- package/dist/bin/pushary-notification-hook.d.ts +1 -0
- package/dist/bin/pushary-notification-hook.js +56 -0
- package/dist/bin/pushary-opencode-hook.d.ts +1 -0
- package/dist/bin/pushary-opencode-hook.js +338 -0
- package/dist/bin/pushary-permission-denied-hook.d.ts +1 -0
- package/dist/bin/pushary-permission-denied-hook.js +60 -0
- package/dist/bin/pushary-permission-hook.d.ts +1 -0
- package/dist/bin/pushary-permission-hook.js +65 -0
- package/dist/bin/pushary-post-hook.d.ts +1 -0
- package/dist/bin/pushary-post-hook.js +70 -0
- package/dist/bin/pushary-prompt-hook.d.ts +1 -0
- package/dist/bin/pushary-prompt-hook.js +61 -0
- package/dist/bin/pushary-session-end-hook.d.ts +1 -0
- package/dist/bin/pushary-session-end-hook.js +59 -0
- package/dist/bin/pushary-session-start-hook.d.ts +1 -0
- package/dist/bin/pushary-session-start-hook.js +72 -0
- package/dist/bin/pushary-setup.d.ts +1 -0
- package/dist/bin/pushary-setup.js +2660 -0
- package/dist/bin/pushary-stats.d.ts +1 -0
- package/dist/bin/pushary-stats.js +44 -0
- package/dist/bin/pushary-status.d.ts +1 -0
- package/dist/bin/pushary-status.js +262 -0
- package/dist/bin/pushary-stop-hook.d.ts +1 -0
- package/dist/bin/pushary-stop-hook.js +63 -0
- package/dist/bin/pushary-stopfailure-hook.d.ts +1 -0
- package/dist/bin/pushary-stopfailure-hook.js +56 -0
- package/dist/bin/pushary-suggestions.d.ts +1 -0
- package/dist/bin/pushary-suggestions.js +100 -0
- package/dist/bin/pushary-transcript-register.d.ts +1 -0
- package/dist/bin/pushary-transcript-register.js +40 -0
- package/dist/bin/pushary-transcripts.d.ts +1 -0
- package/dist/bin/pushary-transcripts.js +77 -0
- package/dist/bin/pushary-upgrade.d.ts +1 -0
- package/dist/bin/pushary-upgrade.js +599 -0
- package/dist/bin/pushary-wait.d.ts +1 -0
- package/dist/bin/pushary-wait.js +122 -0
- package/dist/bin/pushary.d.ts +1 -0
- package/dist/bin/pushary.js +74 -0
- package/dist/chunk-2ABTSGFT.js +173 -0
- package/dist/chunk-2OY3ZZ5N.js +124 -0
- package/dist/chunk-3EGEA4KH.js +44 -0
- package/dist/chunk-3EVPNIBE.js +185 -0
- package/dist/chunk-5OP5MQI7.js +45 -0
- package/dist/chunk-6J2JC2RT.js +149 -0
- package/dist/chunk-7QLSKOSU.js +19 -0
- package/dist/chunk-7Z7Q4GGS.js +88 -0
- package/dist/chunk-A2A2YTMG.js +835 -0
- package/dist/chunk-AHV4LNB5.js +377 -0
- package/dist/chunk-AXPCESYO.js +197 -0
- package/dist/chunk-C2WCPBF7.js +86 -0
- package/dist/chunk-CJVVKOSR.js +73 -0
- package/dist/chunk-CULUZTWQ.js +2117 -0
- package/dist/chunk-CVQYGMSR.js +142 -0
- package/dist/chunk-CVWG2N3Y.js +16 -0
- package/dist/chunk-D2FX3EPW.js +15 -0
- package/dist/chunk-DB3ONZB4.js +44 -0
- package/dist/chunk-DE4Y6TCC.js +160 -0
- package/dist/chunk-E4FJTUH4.js +249 -0
- package/dist/chunk-EPCXUP3P.js +41 -0
- package/dist/chunk-EX3GAPA4.js +44 -0
- package/dist/chunk-EYK3GSRH.js +143 -0
- package/dist/chunk-GMXKITVA.js +70 -0
- package/dist/chunk-GPEGOGRJ.js +49 -0
- package/dist/chunk-GU3EJCVV.js +43 -0
- package/dist/chunk-HQ6G3B5R.js +59 -0
- package/dist/chunk-HZVVNGEW.js +22 -0
- package/dist/chunk-IPVKH2ST.js +231 -0
- package/dist/chunk-J5TIV3J6.js +238 -0
- package/dist/chunk-JU6W3XAU.js +64 -0
- package/dist/chunk-JYG4B33G.js +227 -0
- package/dist/chunk-JZTHGAPA.js +503 -0
- package/dist/chunk-KGDWDSNL.js +102 -0
- package/dist/chunk-KVQBWI5T.js +99 -0
- package/dist/chunk-KZERVKTD.js +16 -0
- package/dist/chunk-LXQ5FBBD.js +47 -0
- package/dist/chunk-M5ICYGHT.js +138 -0
- package/dist/chunk-M5JDZ45W.js +943 -0
- package/dist/chunk-MDEPU45E.js +1226 -0
- package/dist/chunk-MPWXGONV.js +157 -0
- package/dist/chunk-N6IOSZOE.js +185 -0
- package/dist/chunk-NGDD6TXY.js +192 -0
- package/dist/chunk-NQTHFF4W.js +2830 -0
- package/dist/chunk-OKXS7WDZ.js +68 -0
- package/dist/chunk-OLQRRFBX.js +21 -0
- package/dist/chunk-P7WQEVWA.js +307 -0
- package/dist/chunk-POFBE6ZY.js +12 -0
- package/dist/chunk-PQ4K74WL.js +30 -0
- package/dist/chunk-PUZZELE6.js +37 -0
- package/dist/chunk-Q7E4I6UA.js +767 -0
- package/dist/chunk-QCGNGEGE.js +15 -0
- package/dist/chunk-R7437YI3.js +151 -0
- package/dist/chunk-RHR6NARM.js +305 -0
- package/dist/chunk-SAXBC4AZ.js +60 -0
- package/dist/chunk-SGZJ5LYH.js +76 -0
- package/dist/chunk-SIFVFP6T.js +70 -0
- package/dist/chunk-SLFABHW3.js +150 -0
- package/dist/chunk-T7E75EKA.js +67 -0
- package/dist/chunk-TY4JT7W3.js +38 -0
- package/dist/chunk-U4TMAANR.js +111 -0
- package/dist/chunk-UI36QSBI.js +177 -0
- package/dist/chunk-VQKRG3AC.js +339 -0
- package/dist/chunk-WE2J62NC.js +1131 -0
- package/dist/chunk-XHKBHWLX.js +14 -0
- package/dist/chunk-ZSZSSJCV.js +275 -0
- package/dist/chunk-ZVSKMPBZ.js +135 -0
- package/dist/reapply-CW3MA66V.js +34 -0
- package/dist/src/index.d.ts +204 -0
- package/dist/src/index.js +64 -0
- package/package.json +108 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NOTIFICATION_MATCHED_TYPES,
|
|
3
|
+
POSTTOOLUSE_MIRRORED_TOOLS
|
|
4
|
+
} from "./chunk-EPCXUP3P.js";
|
|
5
|
+
import {
|
|
6
|
+
isProtectedByScope,
|
|
7
|
+
registerHookEntry
|
|
8
|
+
} from "./chunk-A2A2YTMG.js";
|
|
9
|
+
import {
|
|
10
|
+
CLAUDE_PRETOOLUSE_MATCHER,
|
|
11
|
+
HOOK_BUDGETS,
|
|
12
|
+
HOOK_LOCATOR_BINARY
|
|
13
|
+
} from "./chunk-NQTHFF4W.js";
|
|
14
|
+
|
|
15
|
+
// src/gemini-config.ts
|
|
16
|
+
var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
|
|
17
|
+
var GEMINI_MCP_URL = "https://pushary.com/api/mcp/mcp";
|
|
18
|
+
var GEMINI_HOOK_EVENTS = [
|
|
19
|
+
{ event: "BeforeTool", matcher: "run_shell_command|write_file|replace|mcp_.*", delivery: "approval", timeoutMs: HOOK_BUDGETS.gemini.budgetSeconds * 1e3 },
|
|
20
|
+
{ event: "AfterTool", matcher: "run_shell_command|write_file|replace|mcp_.*", delivery: "telemetry", timeoutMs: 1e4 },
|
|
21
|
+
{ event: "BeforeAgent", delivery: "telemetry", timeoutMs: 1e4 },
|
|
22
|
+
{ event: "AfterAgent", delivery: "telemetry", timeoutMs: 1e4 },
|
|
23
|
+
{ event: "SessionStart", delivery: "telemetry", timeoutMs: 1e4 },
|
|
24
|
+
{ event: "SessionEnd", delivery: "telemetry", timeoutMs: 1e4 }
|
|
25
|
+
];
|
|
26
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
27
|
+
var ensureRecord = (target, key) => {
|
|
28
|
+
const existing = asRecord(target[key]);
|
|
29
|
+
if (existing) return existing;
|
|
30
|
+
const created = {};
|
|
31
|
+
target[key] = created;
|
|
32
|
+
return created;
|
|
33
|
+
};
|
|
34
|
+
var isPusharyGeminiHook = (entry, scope) => {
|
|
35
|
+
const hooks = asRecord(entry)?.hooks;
|
|
36
|
+
if (!Array.isArray(hooks)) return false;
|
|
37
|
+
return hooks.some((hook) => {
|
|
38
|
+
const command = String(asRecord(hook)?.command ?? "");
|
|
39
|
+
if (isProtectedByScope(command, scope)) return false;
|
|
40
|
+
return command.includes(GEMINI_HOOK_BINARY) || command.includes(HOOK_LOCATOR_BINARY);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
var addGeminiMcpServer = (settings, apiKey) => {
|
|
44
|
+
const mcpServers = ensureRecord(settings, "mcpServers");
|
|
45
|
+
mcpServers.pushary = {
|
|
46
|
+
httpUrl: GEMINI_MCP_URL,
|
|
47
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
48
|
+
trust: true
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
var addGeminiHooks = (settings, command) => {
|
|
52
|
+
const hooks = ensureRecord(settings, "hooks");
|
|
53
|
+
for (const definition of GEMINI_HOOK_EVENTS) {
|
|
54
|
+
const existing = Array.isArray(hooks[definition.event]) ? hooks[definition.event] : [];
|
|
55
|
+
hooks[definition.event] = registerHookEntry(existing, isPusharyGeminiHook, {
|
|
56
|
+
...definition.matcher ? { matcher: definition.matcher } : {},
|
|
57
|
+
hooks: [{
|
|
58
|
+
name: "pushary",
|
|
59
|
+
type: "command",
|
|
60
|
+
command,
|
|
61
|
+
timeout: definition.timeoutMs
|
|
62
|
+
}]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var removeGeminiHooks = (settings, scope) => {
|
|
67
|
+
const hooks = asRecord(settings.hooks);
|
|
68
|
+
if (!hooks) return false;
|
|
69
|
+
let changed = false;
|
|
70
|
+
for (const definition of GEMINI_HOOK_EVENTS) {
|
|
71
|
+
const entries = hooks[definition.event];
|
|
72
|
+
if (!Array.isArray(entries)) continue;
|
|
73
|
+
const filtered = entries.filter((entry) => !isPusharyGeminiHook(entry, scope));
|
|
74
|
+
if (filtered.length !== entries.length) {
|
|
75
|
+
if (filtered.length === 0) {
|
|
76
|
+
delete hooks[definition.event];
|
|
77
|
+
} else {
|
|
78
|
+
hooks[definition.event] = filtered;
|
|
79
|
+
}
|
|
80
|
+
changed = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
84
|
+
return changed;
|
|
85
|
+
};
|
|
86
|
+
var removeGeminiMcpServer = (settings) => {
|
|
87
|
+
const mcpServers = asRecord(settings.mcpServers);
|
|
88
|
+
if (!mcpServers?.pushary) return false;
|
|
89
|
+
delete mcpServers.pushary;
|
|
90
|
+
if (Object.keys(mcpServers).length === 0) delete settings.mcpServers;
|
|
91
|
+
return true;
|
|
92
|
+
};
|
|
93
|
+
var removeGeminiSettings = (settings, scope) => {
|
|
94
|
+
const mcpRemoved = scope?.keepMcpServer === true ? false : removeGeminiMcpServer(settings);
|
|
95
|
+
const hooksRemoved = removeGeminiHooks(settings, scope);
|
|
96
|
+
return mcpRemoved || hooksRemoved;
|
|
97
|
+
};
|
|
98
|
+
var hasGeminiHooks = (settings) => {
|
|
99
|
+
const hooks = asRecord(settings.hooks);
|
|
100
|
+
if (!hooks) return false;
|
|
101
|
+
return GEMINI_HOOK_EVENTS.some((definition) => {
|
|
102
|
+
const entries = hooks[definition.event];
|
|
103
|
+
return Array.isArray(entries) && entries.some((entry) => isPusharyGeminiHook(entry));
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
var missingGeminiHookEvents = (settings) => {
|
|
107
|
+
const hooks = asRecord(settings.hooks);
|
|
108
|
+
return GEMINI_HOOK_EVENTS.filter((definition) => {
|
|
109
|
+
const entries = hooks?.[definition.event];
|
|
110
|
+
return !Array.isArray(entries) || !entries.some((entry) => isPusharyGeminiHook(entry));
|
|
111
|
+
}).map((definition) => definition.event);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/agent-version.ts
|
|
115
|
+
var parseVersion = (raw) => {
|
|
116
|
+
const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
117
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
118
|
+
};
|
|
119
|
+
var compareVersion = (a, b) => {
|
|
120
|
+
for (let index = 0; index < 3; index++) {
|
|
121
|
+
if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
|
|
122
|
+
}
|
|
123
|
+
return 0;
|
|
124
|
+
};
|
|
125
|
+
var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
|
|
126
|
+
var formatVersion = (version) => version.join(".");
|
|
127
|
+
|
|
128
|
+
// src/claude-events.ts
|
|
129
|
+
var MIRRORED_TOOLS = POSTTOOLUSE_MIRRORED_TOOLS.join("|");
|
|
130
|
+
var ESTABLISHED = [1, 0, 0];
|
|
131
|
+
var OBSERVED = [2, 0, 0];
|
|
132
|
+
var CLAUDE_HOOK_BINARY = "pushary-hook";
|
|
133
|
+
var CLAUDE_HOOK_EVENTS = [
|
|
134
|
+
{ event: "PreToolUse", binary: CLAUDE_HOOK_BINARY, matcher: CLAUDE_PRETOOLUSE_MATCHER, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
135
|
+
{ event: "PostToolUse", binary: "pushary-post-hook", matcher: MIRRORED_TOOLS, timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
136
|
+
{ event: "Stop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
137
|
+
{ event: "UserPromptSubmit", binary: "pushary-prompt-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
138
|
+
{ event: "Notification", binary: "pushary-notification-hook", matcher: NOTIFICATION_MATCHED_TYPES.join("|"), timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
139
|
+
{ event: "SessionStart", binary: "pushary-session-start-hook", matcher: "startup|resume|clear", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
140
|
+
{ event: "SessionEnd", binary: "pushary-session-end-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
141
|
+
{ event: "StopFailure", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
142
|
+
{ event: "PermissionRequest", binary: "pushary-permission-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
143
|
+
{ event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", holds: true, since: ESTABLISHED },
|
|
144
|
+
// Observability only. Each carries session shape the island cannot get any other
|
|
145
|
+
// way, and none of them decides anything.
|
|
146
|
+
{ event: "SubagentStart", binary: "pushary-session-start-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
147
|
+
{ event: "SubagentStop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
148
|
+
{ event: "PreCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
149
|
+
{ event: "PostCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
150
|
+
{ event: "TeammateIdle", binary: "pushary-notification-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
151
|
+
// An MCP server asking the person for a value, answered from the phone rather
|
|
152
|
+
// than the terminal. No matcher: Claude Code matches this event on the MCP
|
|
153
|
+
// server's name, and we have no business preferring one server over another.
|
|
154
|
+
{ event: "Elicitation", binary: "pushary-elicitation-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: OBSERVED }
|
|
155
|
+
];
|
|
156
|
+
var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
|
|
157
|
+
var registrationSignatures = (events, version) => Object.fromEntries(
|
|
158
|
+
events.map((hook) => [hook.event, `registered|${version ? formatVersion(version) : "unknown"}`])
|
|
159
|
+
);
|
|
160
|
+
var needsReregistration = (stored, version) => {
|
|
161
|
+
const expected = registrationSignatures(supportedClaudeEvents(version), version);
|
|
162
|
+
const keys = Object.keys(expected);
|
|
163
|
+
if (!stored) return true;
|
|
164
|
+
if (keys.length !== Object.keys(stored).length) return true;
|
|
165
|
+
return keys.some((event) => stored[event] !== expected[event]);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export {
|
|
169
|
+
GEMINI_HOOK_BINARY,
|
|
170
|
+
GEMINI_HOOK_EVENTS,
|
|
171
|
+
addGeminiMcpServer,
|
|
172
|
+
addGeminiHooks,
|
|
173
|
+
removeGeminiMcpServer,
|
|
174
|
+
removeGeminiSettings,
|
|
175
|
+
hasGeminiHooks,
|
|
176
|
+
missingGeminiHookEvents,
|
|
177
|
+
parseVersion,
|
|
178
|
+
compareVersion,
|
|
179
|
+
formatVersion,
|
|
180
|
+
CLAUDE_HOOK_BINARY,
|
|
181
|
+
CLAUDE_HOOK_EVENTS,
|
|
182
|
+
supportedClaudeEvents,
|
|
183
|
+
registrationSignatures,
|
|
184
|
+
needsReregistration
|
|
185
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_SESSION
|
|
3
|
+
} from "./chunk-N6IOSZOE.js";
|
|
4
|
+
|
|
5
|
+
// src/answer.ts
|
|
6
|
+
var denyReasonFrom = (value, note) => {
|
|
7
|
+
const trimmed = note?.trim();
|
|
8
|
+
if (trimmed) return `Denied from your phone: ${trimmed}`;
|
|
9
|
+
return value && value !== "no" && value !== "yes" ? `Denied from your phone: ${value}` : "Denied via push notification";
|
|
10
|
+
};
|
|
11
|
+
var isDeferAnswer = (value) => value === "defer";
|
|
12
|
+
|
|
13
|
+
// src/session-notice.ts
|
|
14
|
+
import { existsSync, writeFileSync } from "fs";
|
|
15
|
+
import { tmpdir } from "os";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
var SUBSCRIPTION_REQUIRED_NOTICE_KIND = "subscription-required";
|
|
18
|
+
var oncePerSession = (kind, sessionId) => {
|
|
19
|
+
try {
|
|
20
|
+
const key = (sessionId || DEFAULT_SESSION).replace(/[^a-zA-Z0-9_-]/g, "");
|
|
21
|
+
const marker = join(tmpdir(), `pushary-${kind}-${key}`);
|
|
22
|
+
if (existsSync(marker)) return false;
|
|
23
|
+
writeFileSync(marker, "1", "utf-8");
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var subscriptionRequiredNotice = (upgradeUrl) => `[pushary] Your Pushary workspace has no active subscription, so approvals stay with your agent. Subscribe: ${upgradeUrl}`;
|
|
30
|
+
var announceSubscriptionRequired = (upgradeUrl, sessionId) => {
|
|
31
|
+
try {
|
|
32
|
+
if (!oncePerSession(SUBSCRIPTION_REQUIRED_NOTICE_KIND, sessionId)) return;
|
|
33
|
+
process.stderr.write(`${subscriptionRequiredNotice(upgradeUrl)}
|
|
34
|
+
`);
|
|
35
|
+
} catch {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
denyReasonFrom,
|
|
42
|
+
isDeferAnswer,
|
|
43
|
+
oncePerSession,
|
|
44
|
+
announceSubscriptionRequired
|
|
45
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLAUDE_HOOK_EVENTS,
|
|
3
|
+
GEMINI_HOOK_EVENTS,
|
|
4
|
+
hasGeminiHooks
|
|
5
|
+
} from "./chunk-3EVPNIBE.js";
|
|
6
|
+
import {
|
|
7
|
+
CODEX_HOOK_EVENTS,
|
|
8
|
+
hasCodexHooks,
|
|
9
|
+
holdBudgetOf
|
|
10
|
+
} from "./chunk-A2A2YTMG.js";
|
|
11
|
+
import {
|
|
12
|
+
CURSOR_GATE_EVENTS,
|
|
13
|
+
HOOK_LOCATOR_BINARY,
|
|
14
|
+
hookCommandHoldBudget
|
|
15
|
+
} from "./chunk-NQTHFF4W.js";
|
|
16
|
+
|
|
17
|
+
// src/diagnostics/wiring.ts
|
|
18
|
+
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
19
|
+
var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
|
|
20
|
+
var EXACT_HOOK_BINARY = /^pushary-[a-z-]*(hook|gate)$/;
|
|
21
|
+
var pusharyHookExecutable = (command) => {
|
|
22
|
+
const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
23
|
+
for (const raw of tokens.reverse()) {
|
|
24
|
+
const token = raw[0] === '"' && raw.at(-1) === '"' || raw[0] === "'" && raw.at(-1) === "'" ? raw.slice(1, -1) : raw;
|
|
25
|
+
const name = token.split(/[/\\]/).pop()?.replace(/\.(cmd|exe|mjs)$/i, "") ?? "";
|
|
26
|
+
if (name === HOOK_LOCATOR_BINARY || EXACT_HOOK_BINARY.test(name)) return token;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
var claudeWired = ({ claudeJson, settings, skillExists }) => {
|
|
31
|
+
if (record(record(claudeJson)?.mcpServers)?.pushary) return true;
|
|
32
|
+
const s = record(settings);
|
|
33
|
+
if (s) {
|
|
34
|
+
const hooks = JSON.stringify(s.hooks ?? {});
|
|
35
|
+
if (CLAUDE_HOOK_BINARY.test(hooks) || hooks.includes(HOOK_LOCATOR_BINARY)) return true;
|
|
36
|
+
const allow = record(s.permissions)?.allow;
|
|
37
|
+
if (Array.isArray(allow) && allow.some((rule) => String(rule).includes("mcp__pushary__"))) return true;
|
|
38
|
+
}
|
|
39
|
+
return skillExists;
|
|
40
|
+
};
|
|
41
|
+
var codexWired = ({ config, hooks, skillExists }) => {
|
|
42
|
+
if (record(record(config)?.mcp_servers)?.pushary) return true;
|
|
43
|
+
const h = record(hooks);
|
|
44
|
+
if (h && hasCodexHooks(h)) return true;
|
|
45
|
+
return skillExists;
|
|
46
|
+
};
|
|
47
|
+
var geminiWired = ({ settings }) => {
|
|
48
|
+
const s = record(settings);
|
|
49
|
+
if (!s) return false;
|
|
50
|
+
if (record(s.mcpServers)?.pushary) return true;
|
|
51
|
+
return hasGeminiHooks(s);
|
|
52
|
+
};
|
|
53
|
+
var cursorWired = ({ userHooks, mcp, pluginDirExists }) => {
|
|
54
|
+
if (pluginDirExists) return true;
|
|
55
|
+
if (record(record(mcp)?.mcpServers)?.pushary) return true;
|
|
56
|
+
const hooks = record(record(userHooks)?.hooks);
|
|
57
|
+
if (!hooks) return false;
|
|
58
|
+
return Object.values(hooks).some((entries) => Array.isArray(entries) && entries.some((entry) => pusharyHookExecutable(String(record(entry)?.command ?? "")) !== null));
|
|
59
|
+
};
|
|
60
|
+
var cursorMcpAuthorization = (pluginMcp, userMcp) => {
|
|
61
|
+
const pluginServers = record(record(pluginMcp)?.mcpServers);
|
|
62
|
+
const usePlugin = pluginServers !== null && Object.hasOwn(pluginServers, "pushary");
|
|
63
|
+
const servers = usePlugin ? pluginServers : record(record(userMcp)?.mcpServers);
|
|
64
|
+
const headers = record(record(servers?.pushary)?.headers);
|
|
65
|
+
return {
|
|
66
|
+
authorization: typeof headers?.Authorization === "string" ? headers.Authorization : null,
|
|
67
|
+
source: usePlugin ? "plugin mcp.json" : "~/.cursor/mcp.json"
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
var LONG_HOLD_SECONDS = 600;
|
|
71
|
+
var toSeconds = (timeout, unit) => {
|
|
72
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout)) return null;
|
|
73
|
+
return unit === "milliseconds" ? timeout / 1e3 : timeout;
|
|
74
|
+
};
|
|
75
|
+
var registeredHooks = (entries, shape, unit) => {
|
|
76
|
+
if (!Array.isArray(entries)) return [];
|
|
77
|
+
return entries.flatMap((entry) => {
|
|
78
|
+
const container = record(entry);
|
|
79
|
+
if (!container) return [];
|
|
80
|
+
const hooks = shape === "nested" ? Array.isArray(container.hooks) ? container.hooks : [] : [container];
|
|
81
|
+
return hooks.flatMap((hook) => {
|
|
82
|
+
const command = record(hook)?.command;
|
|
83
|
+
if (typeof command !== "string" || command.trim() === "") return [];
|
|
84
|
+
return [{ command, timeoutSeconds: toSeconds(record(hook)?.timeout, unit) }];
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
var holdersOnEvent = (event, hooks) => {
|
|
89
|
+
if (!hooks.some((hook) => pusharyHookExecutable(hook.command) !== null)) return [];
|
|
90
|
+
return hooks.filter((hook) => pusharyHookExecutable(hook.command) === null).map((hook) => ({ event, command: hook.command, timeoutSeconds: hook.timeoutSeconds }));
|
|
91
|
+
};
|
|
92
|
+
var otherHolders = (hooks, events, shape, unit) => {
|
|
93
|
+
const table = record(hooks);
|
|
94
|
+
if (!table) return [];
|
|
95
|
+
return events.flatMap((event) => holdersOnEvent(event, registeredHooks(table[event], shape, unit)));
|
|
96
|
+
};
|
|
97
|
+
var approvalEvents = (table) => table.filter((hook) => hook.delivery === "approval").map((hook) => hook.event);
|
|
98
|
+
var CLAUDE_GATE_EVENTS = approvalEvents(CLAUDE_HOOK_EVENTS);
|
|
99
|
+
var CODEX_GATE_EVENTS = approvalEvents(CODEX_HOOK_EVENTS);
|
|
100
|
+
var GEMINI_GATE_EVENTS = approvalEvents(GEMINI_HOOK_EVENTS);
|
|
101
|
+
var claudeOtherHolders = (settings) => otherHolders(record(settings)?.hooks, CLAUDE_GATE_EVENTS, "nested", "seconds");
|
|
102
|
+
var codexOtherHolders = (hooks) => otherHolders(record(hooks)?.hooks, CODEX_GATE_EVENTS, "nested", "seconds");
|
|
103
|
+
var geminiOtherHolders = (settings) => otherHolders(record(settings)?.hooks, GEMINI_GATE_EVENTS, "nested", "milliseconds");
|
|
104
|
+
var cursorOtherHolders = (userHooks) => otherHolders(record(userHooks)?.hooks, CURSOR_GATE_EVENTS, "flat", "seconds");
|
|
105
|
+
var holdEvents = (table) => table.flatMap((hook) => {
|
|
106
|
+
const seconds = holdBudgetOf(hook);
|
|
107
|
+
return seconds === void 0 ? [] : [{ event: hook.event, seconds }];
|
|
108
|
+
});
|
|
109
|
+
var unprovenHolds = (hooks, events) => {
|
|
110
|
+
const table = record(hooks);
|
|
111
|
+
if (!table) return [];
|
|
112
|
+
return events.filter(({ event, seconds }) => registeredHooks(table[event], "nested", "seconds").some((hook) => pusharyHookExecutable(hook.command) !== null && hookCommandHoldBudget(hook.command) !== seconds)).map(({ event }) => event);
|
|
113
|
+
};
|
|
114
|
+
var claudeUnprovenHolds = (settings) => unprovenHolds(record(settings)?.hooks, holdEvents(CLAUDE_HOOK_EVENTS));
|
|
115
|
+
var codexUnprovenHolds = (hooks) => unprovenHolds(record(hooks)?.hooks, holdEvents(CODEX_HOOK_EVENTS));
|
|
116
|
+
var holdsLong = (holder) => holder.timeoutSeconds !== null && holder.timeoutSeconds > LONG_HOLD_SECONDS;
|
|
117
|
+
var SHELLS = /* @__PURE__ */ new Set(["/bin/sh", "/bin/bash", "/bin/zsh", "/usr/bin/env"]);
|
|
118
|
+
var holderExecutable = (command) => {
|
|
119
|
+
const pathLike = command.match(/[^\s"']*[/\\][^\s"']*/g) ?? [];
|
|
120
|
+
const target = pathLike.find((token) => !SHELLS.has(token));
|
|
121
|
+
return target ?? command.trim().split(/\s+/)[0] ?? command;
|
|
122
|
+
};
|
|
123
|
+
var describeHoldTimeout = (holder) => {
|
|
124
|
+
const seconds = holder.timeoutSeconds;
|
|
125
|
+
if (seconds === null) return "no timeout set, so the agent default applies";
|
|
126
|
+
if (seconds >= 3600 && seconds % 3600 === 0) return `${seconds / 3600}h timeout`;
|
|
127
|
+
if (seconds >= 60 && seconds % 60 === 0) return `${seconds / 60}m timeout`;
|
|
128
|
+
return `${seconds}s timeout`;
|
|
129
|
+
};
|
|
130
|
+
var describeOtherHolder = (holder) => `another hook also registered on ${holder.event}: ${holderExecutable(holder.command)} (${describeHoldTimeout(holder)})`;
|
|
131
|
+
var describeHoldSentence = (agentLabel) => `${agentLabel} waits for every hook on an event, so the terminal cannot move until that one returns, however fast your phone answered.`;
|
|
132
|
+
|
|
133
|
+
export {
|
|
134
|
+
pusharyHookExecutable,
|
|
135
|
+
claudeWired,
|
|
136
|
+
codexWired,
|
|
137
|
+
geminiWired,
|
|
138
|
+
cursorWired,
|
|
139
|
+
cursorMcpAuthorization,
|
|
140
|
+
claudeOtherHolders,
|
|
141
|
+
codexOtherHolders,
|
|
142
|
+
geminiOtherHolders,
|
|
143
|
+
cursorOtherHolders,
|
|
144
|
+
claudeUnprovenHolds,
|
|
145
|
+
codexUnprovenHolds,
|
|
146
|
+
holdsLong,
|
|
147
|
+
describeOtherHolder,
|
|
148
|
+
describeHoldSentence
|
|
149
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/cli/errors.ts
|
|
2
|
+
var UsageError = class extends Error {
|
|
3
|
+
constructor(hint) {
|
|
4
|
+
super(hint);
|
|
5
|
+
this.hint = hint;
|
|
6
|
+
this.name = "UsageError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
var NetworkError = class extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "NetworkError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
UsageError,
|
|
18
|
+
NetworkError
|
|
19
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SETUP_FLAGS
|
|
3
|
+
} from "./chunk-ZVSKMPBZ.js";
|
|
4
|
+
import {
|
|
5
|
+
parseFlags
|
|
6
|
+
} from "./chunk-GMXKITVA.js";
|
|
7
|
+
import {
|
|
8
|
+
UsageError
|
|
9
|
+
} from "./chunk-7QLSKOSU.js";
|
|
10
|
+
import {
|
|
11
|
+
CLAUDE_CONNECTOR_GUIDE,
|
|
12
|
+
CLAUDE_CONNECTOR_STEPS,
|
|
13
|
+
CLAUDE_CONNECTOR_TEST,
|
|
14
|
+
CLAUDE_CONNECTOR_URL,
|
|
15
|
+
COWORK_CONNECTOR_LIMIT,
|
|
16
|
+
COWORK_STANDING_INSTRUCTIONS
|
|
17
|
+
} from "./chunk-NQTHFF4W.js";
|
|
18
|
+
|
|
19
|
+
// src/cli/cowork.ts
|
|
20
|
+
import { readFileSync } from "fs";
|
|
21
|
+
var coworkSetup = {
|
|
22
|
+
status: "manual_setup_required",
|
|
23
|
+
connectorUrl: CLAUDE_CONNECTOR_URL,
|
|
24
|
+
guideUrl: CLAUDE_CONNECTOR_GUIDE,
|
|
25
|
+
steps: CLAUDE_CONNECTOR_STEPS,
|
|
26
|
+
testPrompt: CLAUDE_CONNECTOR_TEST,
|
|
27
|
+
instructions: COWORK_STANDING_INSTRUCTIONS,
|
|
28
|
+
capability: COWORK_CONNECTOR_LIMIT
|
|
29
|
+
};
|
|
30
|
+
var renderCoworkSetup = () => [
|
|
31
|
+
"Claude Desktop / Cowork setup",
|
|
32
|
+
"",
|
|
33
|
+
CLAUDE_CONNECTOR_URL,
|
|
34
|
+
"",
|
|
35
|
+
...CLAUDE_CONNECTOR_STEPS.map((step, i) => `${i + 1}. ${step}`),
|
|
36
|
+
"",
|
|
37
|
+
`Test prompt: ${CLAUDE_CONNECTOR_TEST}`,
|
|
38
|
+
"",
|
|
39
|
+
COWORK_CONNECTOR_LIMIT,
|
|
40
|
+
"",
|
|
41
|
+
"Cowork supports plugin hooks on supported versions. This connector setup does not install or verify native permission hooks.",
|
|
42
|
+
"Run pushary cowork --instructions for the standing instructions, or --skill for the uploadable SKILL.md.",
|
|
43
|
+
`Full guide: ${CLAUDE_CONNECTOR_GUIDE}`,
|
|
44
|
+
""
|
|
45
|
+
].join("\n");
|
|
46
|
+
var parseCoworkOptions = (argv) => {
|
|
47
|
+
const flags = parseFlags(argv, { bools: ["json", "open", "instructions", "skill", "help", "h"], values: [] });
|
|
48
|
+
if (flags.command && flags.command !== "cowork" || flags.rest.length > 0) {
|
|
49
|
+
throw new UsageError("cowork takes no positional arguments. Run pushary cowork --help.");
|
|
50
|
+
}
|
|
51
|
+
const formats = ["json", "instructions", "skill"].filter((flag) => flags.bools.has(flag));
|
|
52
|
+
if (formats.length > 1) throw new UsageError("Choose only one of --json, --instructions, or --skill.");
|
|
53
|
+
return { format: formats[0] ?? "guide", open: flags.bools.has("open") };
|
|
54
|
+
};
|
|
55
|
+
var coworkOutput = (format) => {
|
|
56
|
+
if (format === "json") return JSON.stringify(coworkSetup, null, 2) + "\n";
|
|
57
|
+
if (format === "instructions") return COWORK_STANDING_INSTRUCTIONS + "\n";
|
|
58
|
+
if (format === "skill") {
|
|
59
|
+
for (const relative of ["../../data/cowork/SKILL.md", "../data/cowork/SKILL.md"]) {
|
|
60
|
+
try {
|
|
61
|
+
return readFileSync(new URL(relative, import.meta.url), "utf8");
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error.code !== "ENOENT") throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
throw new Error("Packaged Cowork skill is missing. Reinstall @pushary/agent-hooks.");
|
|
67
|
+
}
|
|
68
|
+
return renderCoworkSetup();
|
|
69
|
+
};
|
|
70
|
+
var coworkSelection = (argv) => {
|
|
71
|
+
const flags = parseFlags(argv, SETUP_FLAGS);
|
|
72
|
+
const selected = flags.values.get("agents")?.toLowerCase().split(",").map((value) => value.trim()) ?? [];
|
|
73
|
+
const aliases = /* @__PURE__ */ new Set(["cowork", "claude_cowork", "claude_desktop", "claude_chat"]);
|
|
74
|
+
if (!selected.some((value) => aliases.has(value))) return null;
|
|
75
|
+
if (selected.some((value) => !aliases.has(value)) || flags.rest.length > 0 || flags.command && flags.command !== "setup") {
|
|
76
|
+
throw new UsageError("Set up Cowork with pushary cowork, then run setup separately for your CLI agents.");
|
|
77
|
+
}
|
|
78
|
+
if ([...flags.values.keys()].some((key) => key !== "agents") || flags.bools.has("key-stdin") || flags.bools.has("take-over-hooks")) {
|
|
79
|
+
throw new UsageError("Cowork uses connector OAuth. Use pushary cowork; CLI key, control, and hook options do not apply.");
|
|
80
|
+
}
|
|
81
|
+
return { json: flags.bools.has("json") };
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
parseCoworkOptions,
|
|
86
|
+
coworkOutput,
|
|
87
|
+
coworkSelection
|
|
88
|
+
};
|