@pushary/agent-hooks 0.95.2 → 0.95.5
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 +19 -0
- package/README.md +1 -1
- package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
- package/data/cursor-plugin/CHANGELOG.md +5 -0
- package/data/cursor-plugin/README.md +6 -8
- package/data/cursor-plugin/hooks/hooks.json +56 -1
- package/data/cursor-plugin/scripts/pushary-gate.mjs +120 -71
- package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
- package/data/vscode-plugin/CHANGELOG.md +5 -0
- package/data/vscode-plugin/README.md +7 -15
- package/data/vscode-plugin/hooks/hooks.json +49 -0
- package/data/vscode-plugin/scripts/pushary-gate.mjs +208 -88
- package/dist/bin/pushary-bell-hook.js +20 -22
- package/dist/bin/pushary-bell.js +9 -11
- package/dist/bin/pushary-claude.js +58 -46
- package/dist/bin/pushary-clean.js +21 -20
- package/dist/bin/pushary-codex-bridge.js +120 -25
- package/dist/bin/pushary-codex-hook.js +43 -63
- package/dist/bin/pushary-codex.js +11 -10
- package/dist/bin/pushary-connect.js +8 -10
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +26 -28
- package/dist/bin/pushary-disconnect.js +11 -10
- package/dist/bin/pushary-doctor.js +25 -25
- package/dist/bin/pushary-elicitation-hook.js +30 -31
- package/dist/bin/pushary-gemini-bridge.js +9 -11
- package/dist/bin/pushary-gemini-hook.js +45 -79
- package/dist/bin/pushary-hook.js +27 -29
- package/dist/bin/pushary-login.js +57 -31
- package/dist/bin/pushary-logout.js +6 -8
- package/dist/bin/pushary-mode.js +5 -5
- package/dist/bin/pushary-notification-hook.js +22 -24
- package/dist/bin/pushary-opencode-hook.js +38 -74
- package/dist/bin/pushary-permission-denied-hook.js +25 -27
- package/dist/bin/pushary-permission-hook.js +25 -27
- package/dist/bin/pushary-post-hook.js +22 -24
- package/dist/bin/pushary-prompt-hook.js +22 -24
- package/dist/bin/pushary-session-end-hook.js +22 -24
- package/dist/bin/pushary-session-start-hook.js +25 -27
- package/dist/bin/pushary-setup.js +78 -55
- package/dist/bin/pushary-stats.js +4 -4
- package/dist/bin/pushary-status.js +10 -12
- package/dist/bin/pushary-stop-hook.js +22 -24
- package/dist/bin/pushary-stopfailure-hook.js +22 -24
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-transcript-register.js +18 -20
- package/dist/bin/pushary-transcripts.js +7 -9
- package/dist/bin/pushary-upgrade.js +17 -19
- package/dist/bin/pushary-wait.js +5 -5
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-7LUFYRH4.js → chunk-2D7BAOMY.js} +7 -7
- package/dist/{chunk-3ZUTNTZA.js → chunk-2ZTL3PGN.js} +1 -1
- package/dist/{chunk-EPFDN2ZT.js → chunk-32RMQO5Z.js} +1 -1
- package/dist/{chunk-EZIEXHYM.js → chunk-3AJIK5AO.js} +5 -2
- package/dist/{chunk-IPHOUBTU.js → chunk-4PX4QK2Z.js} +1 -1
- package/dist/{chunk-UW4LWAWY.js → chunk-4RF52LSU.js} +8 -8
- package/dist/{chunk-UOTSWS5N.js → chunk-67YNQ3QA.js} +43 -98
- package/dist/{chunk-G4TL4YWH.js → chunk-6AEM5RCS.js} +1 -1
- package/dist/{chunk-G24SEVCI.js → chunk-6VANQN5Q.js} +3 -7
- package/dist/{chunk-CKOLDI47.js → chunk-BDI2MXTT.js} +2 -2
- package/dist/{chunk-YKLCWWEI.js → chunk-BMANRGJQ.js} +1 -1
- package/dist/{chunk-2XUPP4WM.js → chunk-CUEZMPLJ.js} +1 -1
- package/dist/{chunk-A4R6BZCK.js → chunk-D62EONG3.js} +1 -1
- package/dist/{chunk-ZDAT5KMU.js → chunk-D6PRC3YQ.js} +1 -1
- package/dist/{chunk-MOY5X6HJ.js → chunk-DLLAWCQB.js} +1 -1
- package/dist/{chunk-DSTLA2TC.js → chunk-GOPIMNVC.js} +1 -1
- package/dist/{chunk-CH6DTGQR.js → chunk-HBHEX3JQ.js} +3 -3
- package/dist/{chunk-DNVDKHQD.js → chunk-HCMM3BOW.js} +226 -58
- package/dist/{chunk-76XICHYT.js → chunk-HGUZOUB2.js} +4 -4
- package/dist/{chunk-E3NF5CL2.js → chunk-HQIVR7MM.js} +6 -3
- package/dist/{chunk-JDFB3YKG.js → chunk-I6GZX3FT.js} +3 -3
- package/dist/{chunk-3OXHYPZG.js → chunk-IM2A5H33.js} +4 -1
- package/dist/{chunk-V27AHID3.js → chunk-IVQMNLSA.js} +3 -14
- package/dist/{chunk-4LL6LG5X.js → chunk-KAVDAZ7A.js} +43 -1
- package/dist/chunk-KBITFQST.js +6 -0
- package/dist/{chunk-AMZJ3CKW.js → chunk-KI4PA7MQ.js} +1 -1
- package/dist/{chunk-IAEXPTXA.js → chunk-N4VNLISN.js} +2 -2
- package/dist/{chunk-VX3BWMC5.js → chunk-OEKFD5BC.js} +1 -1
- package/dist/{chunk-QF4RI5CD.js → chunk-OI3DJA7N.js} +1 -1
- package/dist/{chunk-WB2C7VPF.js → chunk-OW6AZDY2.js} +3 -3
- package/dist/{chunk-4XXUHWIK.js → chunk-QHWXUNMN.js} +1 -1
- package/dist/{chunk-M7WWEFG6.js → chunk-QQGNFHIK.js} +2 -2
- package/dist/{chunk-YJAQRKH5.js → chunk-RLXVW4AM.js} +1 -1
- package/dist/{chunk-GUVCYYDK.js → chunk-S5CMLR35.js} +94 -15
- package/dist/{chunk-EUAQ4YAN.js → chunk-UCS65VVR.js} +1 -1
- package/dist/{chunk-WM5XMCID.js → chunk-VOSGZ6PH.js} +4 -4
- package/dist/{chunk-QUE76J4H.js → chunk-VTO3TNIB.js} +1 -1
- package/dist/{chunk-6FAHPGCZ.js → chunk-WDKHZRAS.js} +183 -25
- package/dist/{chunk-5GLPYGKS.js → chunk-WRDLHR2F.js} +2 -2
- package/dist/{chunk-RVSEGFPD.js → chunk-XLOELLAS.js} +9 -4
- package/dist/chunk-XS33QBJA.js +112 -0
- package/dist/{chunk-FQUV5KTM.js → chunk-Z2UWFEJW.js} +8 -8
- package/dist/src/index.d.ts +8 -1
- package/dist/src/index.js +11 -12
- package/package.json +1 -1
- package/dist/chunk-BC3VCZ3E.js +0 -86
- package/dist/chunk-RER3FRTD.js +0 -265
- package/dist/chunk-X7YWD6IS.js +0 -44
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HOOK_BUDGETS,
|
|
3
3
|
HOOK_LOCATOR_BINARY
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WDKHZRAS.js";
|
|
5
5
|
|
|
6
6
|
// src/removal-scope.ts
|
|
7
7
|
var namesAppLocator = (command) => command.includes(HOOK_LOCATOR_BINARY);
|
|
@@ -30,8 +30,8 @@ var codexAgentsMd = () => join(codexHome(), "AGENTS.md");
|
|
|
30
30
|
var codexSkillDir = () => join(codexHome(), "skills", "pushary");
|
|
31
31
|
var CODEX_HOOK_EVENTS = [
|
|
32
32
|
{ event: "PermissionRequest", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Waiting for your phone" },
|
|
33
|
-
{ event: "PreToolUse", matcher: "Bash|apply_patch", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Checking Pushary policy" },
|
|
34
|
-
{ event: "PostToolUse", matcher: "Bash|apply_patch", delivery: "telemetry", timeout: 10 },
|
|
33
|
+
{ event: "PreToolUse", matcher: "Bash|apply_patch|mcp__.*", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Checking Pushary policy" },
|
|
34
|
+
{ event: "PostToolUse", matcher: "Bash|apply_patch|mcp__.*", delivery: "telemetry", timeout: 10 },
|
|
35
35
|
{ event: "UserPromptSubmit", delivery: "telemetry", timeout: 10 },
|
|
36
36
|
{ event: "Stop", delivery: "telemetry", timeout: 10 },
|
|
37
37
|
// `clear` alongside startup and resume, matching what we already do on Claude
|
|
@@ -1,23 +1,99 @@
|
|
|
1
|
-
import {
|
|
2
|
-
resolveShellRc
|
|
3
|
-
} from "./chunk-BC3VCZ3E.js";
|
|
4
|
-
import {
|
|
5
|
-
resolveInstallSourceFor
|
|
6
|
-
} from "./chunk-X7YWD6IS.js";
|
|
7
1
|
import {
|
|
8
2
|
backupFile,
|
|
9
3
|
readJsonSafe,
|
|
4
|
+
resolveInstallSourceFor,
|
|
10
5
|
writeFileAtomic,
|
|
11
6
|
writeJsonAtomic
|
|
12
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KAVDAZ7A.js";
|
|
13
8
|
import {
|
|
14
9
|
configFilePath
|
|
15
10
|
} from "./chunk-2UMNXADU.js";
|
|
16
11
|
|
|
17
|
-
// src/
|
|
18
|
-
import { chmodSync, existsSync, readFileSync, realpathSync, writeFileSync } from "fs";
|
|
12
|
+
// src/shell-alias.ts
|
|
19
13
|
import { homedir } from "os";
|
|
14
|
+
import { existsSync, readFileSync, appendFileSync, writeFileSync } from "fs";
|
|
20
15
|
import { join } from "path";
|
|
16
|
+
var MARKER_START = "# >>> pushary >>>";
|
|
17
|
+
var MARKER_END = "# <<< pushary <<<";
|
|
18
|
+
var CANDIDATE_RCS = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"];
|
|
19
|
+
var aliasBlock = () => `${MARKER_START}
|
|
20
|
+
# Make \`claude\` reachable from your phone when idle (runs it through Pushary).
|
|
21
|
+
# Remove this block, or run \`pushary clean\`, to undo.
|
|
22
|
+
alias claude='pushary claude'
|
|
23
|
+
${MARKER_END}
|
|
24
|
+
`;
|
|
25
|
+
var hasClaudeAlias = (content) => content.includes(MARKER_START) || /alias\s+claude=['"]?pushary claude/.test(content);
|
|
26
|
+
var stripPusharyBlock = (content) => {
|
|
27
|
+
const lines = content.split("\n");
|
|
28
|
+
const out = [];
|
|
29
|
+
let inBlock = false;
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
if (trimmed === MARKER_START) {
|
|
33
|
+
inBlock = true;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (trimmed === MARKER_END) {
|
|
37
|
+
inBlock = false;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!inBlock) out.push(line);
|
|
41
|
+
}
|
|
42
|
+
return out.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
43
|
+
};
|
|
44
|
+
var resolveShellRc = () => {
|
|
45
|
+
if (process.platform === "win32") return null;
|
|
46
|
+
const shell = (process.env.SHELL ?? "").split("/").pop() ?? "";
|
|
47
|
+
const home = homedir();
|
|
48
|
+
if (shell === "zsh") return { path: join(home, ".zshrc"), shell };
|
|
49
|
+
if (shell === "bash") {
|
|
50
|
+
const bashrc = join(home, ".bashrc");
|
|
51
|
+
const profile = join(home, ".bash_profile");
|
|
52
|
+
if (process.platform === "darwin") return { path: profile, shell };
|
|
53
|
+
return { path: existsSync(bashrc) ? bashrc : profile, shell };
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
var installClaudeAlias = () => {
|
|
58
|
+
const rc = resolveShellRc();
|
|
59
|
+
if (!rc) return { installed: false, reason: "unsupported-shell" };
|
|
60
|
+
let content = "";
|
|
61
|
+
try {
|
|
62
|
+
content = readFileSync(rc.path, "utf-8");
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
if (hasClaudeAlias(content)) return { installed: false, path: rc.path, reason: "exists" };
|
|
66
|
+
const gap = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
67
|
+
appendFileSync(rc.path, `${gap}${aliasBlock()}`, "utf-8");
|
|
68
|
+
return { installed: true, path: rc.path };
|
|
69
|
+
};
|
|
70
|
+
var removeClaudeAlias = () => {
|
|
71
|
+
const removed = [];
|
|
72
|
+
for (const name of CANDIDATE_RCS) {
|
|
73
|
+
const path = join(homedir(), name);
|
|
74
|
+
if (!existsSync(path)) continue;
|
|
75
|
+
let content;
|
|
76
|
+
try {
|
|
77
|
+
content = readFileSync(path, "utf-8");
|
|
78
|
+
} catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const stripped = stripPusharyBlock(content);
|
|
82
|
+
if (stripped !== content) {
|
|
83
|
+
try {
|
|
84
|
+
writeFileSync(path, stripped, "utf-8");
|
|
85
|
+
removed.push(path);
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return removed;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/keystore.ts
|
|
94
|
+
import { chmodSync, existsSync as existsSync2, readFileSync as readFileSync2, realpathSync, writeFileSync as writeFileSync2 } from "fs";
|
|
95
|
+
import { homedir as homedir2 } from "os";
|
|
96
|
+
import { join as join2 } from "path";
|
|
21
97
|
var KEY_FILE_MODE = 384;
|
|
22
98
|
var FALLBACK_RCS = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"];
|
|
23
99
|
var OUR_EXPORT = /^[ \t]*export[ \t]+PUSHARY_API_KEY=(['"]?)(pk_[a-f0-9]+\.[a-f0-9]+)\1[ \t]*$/;
|
|
@@ -46,7 +122,7 @@ var rewriteRcExport = (rcPath, apiKey, shell = null) => {
|
|
|
46
122
|
const target = realPathOf(rcPath);
|
|
47
123
|
let content = "";
|
|
48
124
|
try {
|
|
49
|
-
content =
|
|
125
|
+
content = readFileSync2(target, "utf-8");
|
|
50
126
|
} catch (err) {
|
|
51
127
|
const error = err;
|
|
52
128
|
if (error.code !== "ENOENT") {
|
|
@@ -101,7 +177,7 @@ var rewriteRcExport = (rcPath, apiKey, shell = null) => {
|
|
|
101
177
|
var resolveRcTarget = () => {
|
|
102
178
|
const resolved = resolveShellRc();
|
|
103
179
|
if (resolved) return { path: resolved.path, shell: resolved.shell };
|
|
104
|
-
const fallback = FALLBACK_RCS.map((name) =>
|
|
180
|
+
const fallback = FALLBACK_RCS.map((name) => join2(homedir2(), name)).find(existsSync2);
|
|
105
181
|
return fallback ? { path: fallback, shell: null } : null;
|
|
106
182
|
};
|
|
107
183
|
var writeKey = (apiKey, options = {}) => {
|
|
@@ -151,13 +227,13 @@ var clearKey = (options = {}) => {
|
|
|
151
227
|
if (target) {
|
|
152
228
|
try {
|
|
153
229
|
const real = realpathSync(target.path);
|
|
154
|
-
const lines =
|
|
230
|
+
const lines = readFileSync2(real, "utf8").split("\n");
|
|
155
231
|
const next = lines.map((line) => {
|
|
156
232
|
if (!OUR_EXPORT.test(line)) return line;
|
|
157
233
|
rcLinesDisabled += 1;
|
|
158
234
|
return `# ${line.trim()} # removed by pushary logout`;
|
|
159
235
|
});
|
|
160
|
-
if (rcLinesDisabled > 0)
|
|
236
|
+
if (rcLinesDisabled > 0) writeFileSync2(real, next.join("\n"));
|
|
161
237
|
} catch {
|
|
162
238
|
rcLinesDisabled = 0;
|
|
163
239
|
}
|
|
@@ -183,8 +259,8 @@ var readConfigKey = (configPath) => {
|
|
|
183
259
|
return key || null;
|
|
184
260
|
};
|
|
185
261
|
var readConfigFileKey = () => readConfigKey(configFilePath());
|
|
186
|
-
var readControlMode = () => {
|
|
187
|
-
const result = readJsonSafe(
|
|
262
|
+
var readControlMode = (configPath = configFilePath()) => {
|
|
263
|
+
const result = readJsonSafe(configPath);
|
|
188
264
|
if (result.kind !== "ok") return null;
|
|
189
265
|
const value = result.value.controlMode;
|
|
190
266
|
return value === "auto" || value === "on" || value === "off" ? value : null;
|
|
@@ -205,6 +281,9 @@ var readKeySource = () => {
|
|
|
205
281
|
};
|
|
206
282
|
|
|
207
283
|
export {
|
|
284
|
+
resolveShellRc,
|
|
285
|
+
installClaudeAlias,
|
|
286
|
+
removeClaudeAlias,
|
|
208
287
|
KEY_FILE_MODE,
|
|
209
288
|
exportLine,
|
|
210
289
|
keyPrefix,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGENT_CHOICES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2ZTL3PGN.js";
|
|
4
4
|
import {
|
|
5
5
|
isBridgeLocator
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CUEZMPLJ.js";
|
|
7
7
|
import {
|
|
8
8
|
pusharyHookExecutable
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-HBHEX3JQ.js";
|
|
10
10
|
import {
|
|
11
11
|
BELL_HOOK_BINARY
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GOPIMNVC.js";
|
|
13
13
|
|
|
14
14
|
// src/setup/hook-ownership.ts
|
|
15
15
|
var commandsIn = (value) => {
|
|
@@ -21,6 +21,166 @@ var COWORK_STANDING_INSTRUCTIONS = `Pushary is connected as a custom connector.
|
|
|
21
21
|
- Cancel a question when it is no longer needed. Once this turn ends, reopen Cowork for follow-up work; this connector cannot restart it.
|
|
22
22
|
- If Pushary tools are missing or disabled, report that once and continue within my authorization. Do not claim a notification or approval was delivered.`;
|
|
23
23
|
|
|
24
|
+
// ../contracts/src/agent-manifest.ts
|
|
25
|
+
var defaults = {
|
|
26
|
+
hasStoredIdentity: true,
|
|
27
|
+
hookSource: null,
|
|
28
|
+
policyAgent: null,
|
|
29
|
+
repoAware: false,
|
|
30
|
+
disconnectable: false,
|
|
31
|
+
managedProvider: false,
|
|
32
|
+
enforcement: "cooperative",
|
|
33
|
+
connectVia: "Plain MCP",
|
|
34
|
+
setupCommand: null,
|
|
35
|
+
setupChoice: null,
|
|
36
|
+
setupGuide: "",
|
|
37
|
+
enforcementDetail: "Cooperative. The agent chooses when to ask; no native permission interception.",
|
|
38
|
+
capabilityNote: "",
|
|
39
|
+
documented: true
|
|
40
|
+
};
|
|
41
|
+
var AGENTS = {
|
|
42
|
+
claude_code: {
|
|
43
|
+
...defaults,
|
|
44
|
+
label: "Claude Code",
|
|
45
|
+
hookSource: "claude",
|
|
46
|
+
policyAgent: "claude_code",
|
|
47
|
+
repoAware: true,
|
|
48
|
+
disconnectable: true,
|
|
49
|
+
managedProvider: true,
|
|
50
|
+
enforcement: "enforced",
|
|
51
|
+
connectVia: "Native hooks + MCP",
|
|
52
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
53
|
+
enforcementDetail: "Shell, writes and edits, subject to Claude Code permission mode and explicit policy."
|
|
54
|
+
},
|
|
55
|
+
claude_chat: {
|
|
56
|
+
...defaults,
|
|
57
|
+
label: "Claude Chat",
|
|
58
|
+
connectVia: "Custom MCP connector",
|
|
59
|
+
setupGuide: "[Claude.ai and Claude Desktop setup](/docs/agents/guides/claude-desktop)"
|
|
60
|
+
},
|
|
61
|
+
claude_cowork: {
|
|
62
|
+
...defaults,
|
|
63
|
+
label: "Claude Cowork",
|
|
64
|
+
connectVia: "Custom MCP connector + optional Cowork skill",
|
|
65
|
+
setupGuide: "[Cowork setup](/docs/agents/guides/claude-desktop#use-it-in-cowork)",
|
|
66
|
+
enforcementDetail: COWORK_CONNECTOR_LIMIT
|
|
67
|
+
},
|
|
68
|
+
codex: {
|
|
69
|
+
...defaults,
|
|
70
|
+
label: "Codex",
|
|
71
|
+
hookSource: "codex",
|
|
72
|
+
policyAgent: "codex",
|
|
73
|
+
repoAware: true,
|
|
74
|
+
disconnectable: true,
|
|
75
|
+
managedProvider: true,
|
|
76
|
+
enforcement: "enforced",
|
|
77
|
+
connectVia: "Native hooks + MCP",
|
|
78
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
79
|
+
enforcementDetail: "Shell and apply_patch edits through native permission hooks; requires a compatible Codex runtime."
|
|
80
|
+
},
|
|
81
|
+
chatgpt: {
|
|
82
|
+
...defaults,
|
|
83
|
+
label: "ChatGPT",
|
|
84
|
+
connectVia: "Custom MCP connector",
|
|
85
|
+
setupGuide: "[ChatGPT setup](/docs/agents/guides/chatgpt)"
|
|
86
|
+
},
|
|
87
|
+
gemini_cli: {
|
|
88
|
+
...defaults,
|
|
89
|
+
label: "Gemini CLI",
|
|
90
|
+
hookSource: "gemini",
|
|
91
|
+
policyAgent: "gemini_cli",
|
|
92
|
+
repoAware: true,
|
|
93
|
+
disconnectable: true,
|
|
94
|
+
managedProvider: true,
|
|
95
|
+
enforcement: "enforced",
|
|
96
|
+
connectVia: "Native hooks + MCP",
|
|
97
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
98
|
+
enforcementDetail: "Shell, writes and edits through native BeforeTool hooks."
|
|
99
|
+
},
|
|
100
|
+
hermes: {
|
|
101
|
+
...defaults,
|
|
102
|
+
label: "Hermes",
|
|
103
|
+
policyAgent: "hermes",
|
|
104
|
+
enforcement: "enforced",
|
|
105
|
+
connectVia: "Native Python plugin + approval transport",
|
|
106
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
107
|
+
enforcementDetail: "Hermes dangerous-command approvals when the Pushary transport is selected; optional PUSHARY_GATE_TOOLS gates named tools.",
|
|
108
|
+
capabilityNote: "Uses the Python plugin, not a macOS bridge HookSource. CLI disconnect and managed-provider controls are not implemented for Hermes."
|
|
109
|
+
},
|
|
110
|
+
cursor: {
|
|
111
|
+
...defaults,
|
|
112
|
+
label: "Cursor",
|
|
113
|
+
hookSource: "cursor",
|
|
114
|
+
policyAgent: "cursor",
|
|
115
|
+
disconnectable: true,
|
|
116
|
+
enforcement: "enforced",
|
|
117
|
+
connectVia: "Editor plugin hooks + MCP",
|
|
118
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
119
|
+
enforcementDetail: "With installed hooks: shell, MCP execution and Write/Delete/Edit tool calls. Plain MCP remains cooperative."
|
|
120
|
+
},
|
|
121
|
+
vscode: {
|
|
122
|
+
...defaults,
|
|
123
|
+
label: "VS Code",
|
|
124
|
+
hookSource: "vscode",
|
|
125
|
+
policyAgent: "vscode",
|
|
126
|
+
enforcement: "enforced",
|
|
127
|
+
connectVia: "Agent plugin hooks + MCP",
|
|
128
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
129
|
+
enforcementDetail: "PreToolUse approval hooks from the installed agent plugin; the host decides which events it exposes.",
|
|
130
|
+
capabilityNote: "Has both a bridge HookSource and a per-agent policy profile. CLI disconnect and managed-provider controls are not implemented for VS Code."
|
|
131
|
+
},
|
|
132
|
+
opencode: {
|
|
133
|
+
...defaults,
|
|
134
|
+
label: "OpenCode",
|
|
135
|
+
hookSource: "opencode",
|
|
136
|
+
policyAgent: "opencode",
|
|
137
|
+
repoAware: true,
|
|
138
|
+
disconnectable: true,
|
|
139
|
+
enforcement: "enforced",
|
|
140
|
+
connectVia: "Generated plugin + MCP",
|
|
141
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
142
|
+
enforcementDetail: "Shell, writes, edits and web fetches when OpenCode itself asks for permission."
|
|
143
|
+
},
|
|
144
|
+
windsurf: {
|
|
145
|
+
...defaults,
|
|
146
|
+
label: "Windsurf",
|
|
147
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
148
|
+
setupChoice: "Other"
|
|
149
|
+
},
|
|
150
|
+
unknown: {
|
|
151
|
+
...defaults,
|
|
152
|
+
label: "Agent",
|
|
153
|
+
documented: false
|
|
154
|
+
},
|
|
155
|
+
lovable: {
|
|
156
|
+
...defaults,
|
|
157
|
+
label: "Lovable",
|
|
158
|
+
setupGuide: "[Lovable setup](/docs/agents/guides/lovable)"
|
|
159
|
+
},
|
|
160
|
+
mcp: {
|
|
161
|
+
...defaults,
|
|
162
|
+
hasStoredIdentity: false,
|
|
163
|
+
label: "Any MCP client",
|
|
164
|
+
setupCommand: "npx @pushary/agent-hooks setup",
|
|
165
|
+
setupChoice: "Other"
|
|
166
|
+
},
|
|
167
|
+
http: {
|
|
168
|
+
...defaults,
|
|
169
|
+
hasStoredIdentity: false,
|
|
170
|
+
label: "No-code and HTTP",
|
|
171
|
+
connectVia: "REST with a Bearer token",
|
|
172
|
+
setupGuide: "[Connect any agent](/docs/agents/connect-any-agent)"
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
var HOOK_SOURCES = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.hookSource === null ? [] : [agent.hookSource]));
|
|
176
|
+
var POLICY_AGENTS = [...Object.values(AGENTS).flatMap((agent) => agent.policyAgent === null ? [] : [agent.policyAgent]), "*"];
|
|
177
|
+
var AGENT_LABELS_BY_TYPE = Object.fromEntries(
|
|
178
|
+
Object.entries(AGENTS).flatMap(([id, agent]) => agent.hasStoredIdentity ? [[id, agent.label]] : [])
|
|
179
|
+
);
|
|
180
|
+
var DISCONNECTABLE_AGENTS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.disconnectable ? [agent.hookSource] : []));
|
|
181
|
+
var REPO_AWARE_AGENTS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.repoAware ? [agent.policyAgent] : []));
|
|
182
|
+
var MANAGED_PROVIDERS = Object.freeze(Object.values(AGENTS).flatMap((agent) => agent.managedProvider ? [agent.hookSource] : []));
|
|
183
|
+
|
|
24
184
|
// ../contracts/src/index.ts
|
|
25
185
|
var KEY_SCOPES = ["full_access", "agent", "connector", "bound"];
|
|
26
186
|
var normalizeKeyScope = (value) => typeof value === "string" && KEY_SCOPES.includes(value) ? value : null;
|
|
@@ -140,23 +300,8 @@ var hookWaitDeadline = (startMs, policyWaitSeconds, agent, nowMs) => {
|
|
|
140
300
|
};
|
|
141
301
|
var hookWaitClamped = (startMs, policyWaitSeconds, agent, nowMs) => startMs + hookMaxWaitSeconds(agent) * 1e3 < nowMs + Math.max(policyWaitSeconds, 0) * 1e3;
|
|
142
302
|
var effectiveWaitSeconds = (timeoutAction, policySeconds, agent) => timeoutAction === "wait" ? hookMaxWaitSeconds(agent) : policySeconds;
|
|
143
|
-
var HOOK_SOURCES = ["claude", "codex", "gemini", "cursor", "vscode", "opencode"];
|
|
144
|
-
var AGENT_LABELS_BY_TYPE = {
|
|
145
|
-
claude_code: "Claude Code",
|
|
146
|
-
claude_chat: "Claude Chat",
|
|
147
|
-
claude_cowork: "Claude Cowork",
|
|
148
|
-
codex: "Codex",
|
|
149
|
-
chatgpt: "ChatGPT",
|
|
150
|
-
gemini_cli: "Gemini CLI",
|
|
151
|
-
hermes: "Hermes",
|
|
152
|
-
cursor: "Cursor",
|
|
153
|
-
vscode: "VS Code",
|
|
154
|
-
opencode: "OpenCode",
|
|
155
|
-
windsurf: "Windsurf",
|
|
156
|
-
unknown: "Agent"
|
|
157
|
-
};
|
|
158
303
|
var OPENCODE_APPROVAL_EVENT = "permission.ask";
|
|
159
|
-
var CURSOR_GATE_EVENTS = ["beforeShellExecution", "beforeMCPExecution"];
|
|
304
|
+
var CURSOR_GATE_EVENTS = ["beforeShellExecution", "beforeMCPExecution", "preToolUse"];
|
|
160
305
|
var HOOK_LOCATOR_BINARY = "pushary-bridge";
|
|
161
306
|
var HOOK_BATCH_MAX_BYTES = 4 * 1024 * 1024;
|
|
162
307
|
var REPO_KEY_MAX_LENGTH = 200;
|
|
@@ -1111,6 +1256,17 @@ var shouldDeferToNativeMode = (permissionMode, toolName, options = {}) => {
|
|
|
1111
1256
|
var ASKED_IN_EVERY_MODE_TOOLS = ["AskUserQuestion", "ExitPlanMode"];
|
|
1112
1257
|
var ASKED_IN_EVERY_MODE = new Set(ASKED_IN_EVERY_MODE_TOOLS);
|
|
1113
1258
|
var isAskedInEveryMode = (toolName) => ASKED_IN_EVERY_MODE.has(toolName);
|
|
1259
|
+
var PRETOOLUSE_GATED_TOOLS = [
|
|
1260
|
+
"Bash",
|
|
1261
|
+
"Write",
|
|
1262
|
+
"Edit",
|
|
1263
|
+
"MultiEdit",
|
|
1264
|
+
"NotebookEdit",
|
|
1265
|
+
"PowerShell",
|
|
1266
|
+
"Monitor"
|
|
1267
|
+
];
|
|
1268
|
+
var PRETOOLUSE_HANDLED_TOOLS = /* @__PURE__ */ new Set([...PRETOOLUSE_GATED_TOOLS, ...ASKED_IN_EVERY_MODE_TOOLS]);
|
|
1269
|
+
var isPermissionRequestOwnedByPreToolUse = (toolName, permissionMode, defers = shouldDeferToNativeMode) => PRETOOLUSE_HANDLED_TOOLS.has(toolName) && (isAskedInEveryMode(toolName) || !defers(permissionMode, toolName));
|
|
1114
1270
|
var resolveGate = (input) => {
|
|
1115
1271
|
const { modeState, config, toolName, toolInputs, cwd, repoKey, sessionId } = input;
|
|
1116
1272
|
if (isPusharyOwnTool(toolName)) {
|
|
@@ -1189,14 +1345,10 @@ var pendingCommandEvents = (source) => {
|
|
|
1189
1345
|
return ["PostToolUse", "PreToolUse", "UserPromptSubmit"];
|
|
1190
1346
|
case "gemini":
|
|
1191
1347
|
return ["AfterTool", "BeforeTool", "BeforeAgent"];
|
|
1192
|
-
// Registered events only. Cursor's carriers are `postToolUse` and `stop`,
|
|
1193
|
-
// which this build does not write into the config; the ones it does write
|
|
1194
|
-
// (`afterShellExecution`, `afterFileEdit`, `afterAgentResponse`) are
|
|
1195
|
-
// observational and discard whatever they print.
|
|
1196
1348
|
case "cursor":
|
|
1197
|
-
return [];
|
|
1349
|
+
return ["postToolUse"];
|
|
1198
1350
|
case "vscode":
|
|
1199
|
-
return [];
|
|
1351
|
+
return ["PostToolUse", "UserPromptSubmit"];
|
|
1200
1352
|
// The prompt is appended through the TUI rather than through a hook return,
|
|
1201
1353
|
// so there is nothing for this to print.
|
|
1202
1354
|
case "opencode":
|
|
@@ -1239,7 +1391,7 @@ var gateOutputFor = (source, event, decision, reason) => {
|
|
|
1239
1391
|
} else if (source === "opencode" && event === OPENCODE_APPROVAL_EVENT) {
|
|
1240
1392
|
output = decision === "deny" ? { status: "deny", reason: reason ?? "Denied from Pushary" } : { status: decision };
|
|
1241
1393
|
} else if (source === "cursor" && CURSOR_GATE_EVENTS.includes(event)) {
|
|
1242
|
-
output = decision === "allow" ? { permission: "allow" } : decision === "ask" ? { permission: "ask" } : {
|
|
1394
|
+
output = decision === "allow" ? { permission: "allow" } : decision === "ask" && event !== "preToolUse" ? { permission: "ask" } : {
|
|
1243
1395
|
permission: "deny",
|
|
1244
1396
|
user_message: "Command denied via Pushary.",
|
|
1245
1397
|
agent_message: reason ?? "Denied from Pushary"
|
|
@@ -1379,7 +1531,13 @@ export {
|
|
|
1379
1531
|
CLAUDE_CONNECTOR_STEPS,
|
|
1380
1532
|
CLAUDE_CONNECTOR_TEST,
|
|
1381
1533
|
COWORK_STANDING_INSTRUCTIONS,
|
|
1534
|
+
HOOK_SOURCES,
|
|
1535
|
+
AGENT_LABELS_BY_TYPE,
|
|
1536
|
+
DISCONNECTABLE_AGENTS,
|
|
1537
|
+
REPO_AWARE_AGENTS,
|
|
1538
|
+
MANAGED_PROVIDERS,
|
|
1382
1539
|
normalizeKeyScope,
|
|
1540
|
+
parseAgentQuestion,
|
|
1383
1541
|
parseAgentQuestions,
|
|
1384
1542
|
parseAgentQuestionAnswers,
|
|
1385
1543
|
normalizeInstallSource,
|
|
@@ -1387,8 +1545,6 @@ export {
|
|
|
1387
1545
|
hookWaitDeadline,
|
|
1388
1546
|
hookWaitClamped,
|
|
1389
1547
|
effectiveWaitSeconds,
|
|
1390
|
-
HOOK_SOURCES,
|
|
1391
|
-
AGENT_LABELS_BY_TYPE,
|
|
1392
1548
|
OPENCODE_APPROVAL_EVENT,
|
|
1393
1549
|
CURSOR_GATE_EVENTS,
|
|
1394
1550
|
HOOK_LOCATOR_BINARY,
|
|
@@ -1409,6 +1565,8 @@ export {
|
|
|
1409
1565
|
isPusharyOwnTool,
|
|
1410
1566
|
shouldDeferToNativeMode,
|
|
1411
1567
|
ASKED_IN_EVERY_MODE_TOOLS,
|
|
1568
|
+
PRETOOLUSE_GATED_TOOLS,
|
|
1569
|
+
isPermissionRequestOwnedByPreToolUse,
|
|
1412
1570
|
resolveGate,
|
|
1413
1571
|
modeStateFromResponse,
|
|
1414
1572
|
stoppedOutput,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SETUP_FLAGS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2ZTL3PGN.js";
|
|
4
4
|
import {
|
|
5
5
|
parseFlags
|
|
6
6
|
} from "./chunk-GMXKITVA.js";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
CLAUDE_CONNECTOR_URL,
|
|
15
15
|
COWORK_CONNECTOR_LIMIT,
|
|
16
16
|
COWORK_STANDING_INSTRUCTIONS
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WDKHZRAS.js";
|
|
18
18
|
|
|
19
19
|
// src/cli/cowork.ts
|
|
20
20
|
import { readFileSync } from "fs";
|
|
@@ -7,19 +7,22 @@ import {
|
|
|
7
7
|
} from "./chunk-L4ZCFMWW.js";
|
|
8
8
|
import {
|
|
9
9
|
getMachineId
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-N4VNLISN.js";
|
|
11
11
|
import {
|
|
12
12
|
pusharyDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-OW6AZDY2.js";
|
|
14
14
|
import {
|
|
15
15
|
getPackageVersion
|
|
16
16
|
} from "./chunk-DINDL2CF.js";
|
|
17
|
+
import {
|
|
18
|
+
readControlMode
|
|
19
|
+
} from "./chunk-S5CMLR35.js";
|
|
17
20
|
import {
|
|
18
21
|
readJsonSafe,
|
|
19
22
|
writeFileAtomic,
|
|
20
23
|
writeJsonAtomic,
|
|
21
24
|
writeJsonAtomicDurable
|
|
22
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-KAVDAZ7A.js";
|
|
23
26
|
import {
|
|
24
27
|
getApiKey
|
|
25
28
|
} from "./chunk-2UMNXADU.js";
|
|
@@ -737,7 +740,9 @@ var clearDaemonNeedsLogin = (options = {}) => {
|
|
|
737
740
|
var describeDaemonLoginBlock = (record) => record.reason === "key_rejected" ? `this machine's key was rejected at ${record.at}; run \`pushary login\` to sign in again` : `this workspace had no active plan at ${record.at}; pick one at pushary.com/dashboard/settings?tab=billing, then run \`pushary login\``;
|
|
738
741
|
var recoverDaemonAfterLogin = (options = {}) => {
|
|
739
742
|
const cleared = clearDaemonNeedsLogin(options);
|
|
740
|
-
if (!cleared ||
|
|
743
|
+
if (!cleared || readControlMode(join(options.dir ?? pusharyDir(), "config.json")) === "off" || !hasDaemonServiceDefinition(options)) {
|
|
744
|
+
return { cleared, restarted: false, detail: null };
|
|
745
|
+
}
|
|
741
746
|
const started = manageDaemonService("start", options);
|
|
742
747
|
return started.kind === "ok" ? { cleared, restarted: true, detail: null } : { cleared, restarted: false, detail: started.detail };
|
|
743
748
|
};
|