@pushary/agent-hooks 0.99.0 → 1.1.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 +14 -0
- package/data/SKILL.md +250 -49
- package/data/cursor-plugin/scripts/pushary-gate.mjs +24 -0
- package/data/cursor-plugin/skills/pushary/SKILL.md +250 -49
- package/data/vscode-plugin/scripts/pushary-gate.mjs +24 -0
- package/data/vscode-plugin/skills/pushary/SKILL.md +250 -49
- package/dist/bin/pushary-bell-hook.js +18 -18
- package/dist/bin/pushary-bell.js +6 -6
- package/dist/bin/pushary-claude.js +22 -24
- package/dist/bin/pushary-clean.js +60 -18
- package/dist/bin/pushary-codex-bridge.js +10 -10
- package/dist/bin/pushary-codex-hook.js +33 -35
- package/dist/bin/pushary-codex.js +6 -7
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-cowork.js +3 -3
- package/dist/bin/pushary-daemon.js +22 -22
- package/dist/bin/pushary-disconnect.js +48 -7
- package/dist/bin/pushary-doctor.js +88 -19
- package/dist/bin/pushary-elicitation-hook.js +17 -17
- package/dist/bin/pushary-gemini-bridge.js +9 -9
- package/dist/bin/pushary-gemini-hook.js +25 -27
- package/dist/bin/pushary-hook.js +61 -33
- package/dist/bin/pushary-login.js +7 -7
- package/dist/bin/pushary-logout.js +4 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +26 -21
- package/dist/bin/pushary-opencode-hook.js +27 -29
- package/dist/bin/pushary-permission-denied-hook.js +24 -23
- package/dist/bin/pushary-permission-hook.js +32 -24
- package/dist/bin/pushary-post-hook.js +30 -22
- package/dist/bin/pushary-prompt-hook.js +26 -21
- package/dist/bin/pushary-session-end-hook.js +26 -21
- package/dist/bin/pushary-session-start-hook.js +30 -25
- package/dist/bin/pushary-setup.js +145 -37
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +10 -10
- package/dist/bin/pushary-stop-hook.js +27 -22
- package/dist/bin/pushary-stopfailure-hook.js +26 -21
- package/dist/bin/pushary-transcript-register.js +16 -16
- package/dist/bin/pushary-transcripts.js +4 -4
- package/dist/bin/pushary-upgrade.js +14 -14
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-ID5FDKDZ.js → chunk-2MOQUNFL.js} +1 -1
- package/dist/{chunk-CRFLU455.js → chunk-6JUCFMSF.js} +3 -3
- package/dist/{chunk-QS55LDX4.js → chunk-6T4OINZB.js} +16 -16
- package/dist/{chunk-6C6MS57P.js → chunk-76ZCJZBK.js} +1 -1
- package/dist/{chunk-JXELRCU5.js → chunk-AH5OO2YZ.js} +3 -3
- package/dist/{chunk-XPPXOKIG.js → chunk-BBUZD7MU.js} +1 -1
- package/dist/{chunk-T3JZZUAR.js → chunk-DKTFZCLQ.js} +30 -29
- package/dist/{chunk-RALVI7OF.js → chunk-DZYIAMKR.js} +9 -6
- package/dist/{chunk-GCD3SQSV.js → chunk-E3WQMDCE.js} +1 -1
- package/dist/{chunk-JBJBVERL.js → chunk-EWQZISYV.js} +1 -1
- package/dist/{chunk-LWKPMSBB.js → chunk-EZ3GX3NS.js} +5 -5
- package/dist/{chunk-5KLAETSU.js → chunk-FO4HHPMO.js} +1 -1
- package/dist/{chunk-6KLCP7VF.js → chunk-IH7YUPFN.js} +3 -3
- package/dist/{chunk-OPR2BPD4.js → chunk-INBTKFT6.js} +1 -1
- package/dist/{chunk-K4XFCEVI.js → chunk-IPDBFJ2M.js} +1 -1
- package/dist/{chunk-6IFUXNFX.js → chunk-ISELLVBP.js} +1 -1
- package/dist/{chunk-562UJ2DA.js → chunk-J7PJALS2.js} +1 -1
- package/dist/{chunk-O3LZQEQP.js → chunk-LQ3BHPPZ.js} +1 -1
- package/dist/{chunk-3RVCQDC4.js → chunk-LUYYB6V2.js} +1 -1
- package/dist/{chunk-V262ATS5.js → chunk-MR2PWE62.js} +17 -3
- package/dist/{chunk-WGV3VY7O.js → chunk-MZUXTN7X.js} +2 -2
- package/dist/{chunk-LBNAGGLV.js → chunk-OYYQHJAO.js} +3 -2
- package/dist/{chunk-4C4XFAGJ.js → chunk-PHJOSAXW.js} +2 -2
- package/dist/{chunk-SI7GYBQP.js → chunk-PQG6NTPT.js} +1 -1
- package/dist/{chunk-3ZAEUPM4.js → chunk-PUDQJ772.js} +1 -1
- package/dist/{chunk-NSFNEDCT.js → chunk-PWIMJP4U.js} +6 -6
- package/dist/{chunk-JL33EDN7.js → chunk-QACXZYDA.js} +4 -4
- package/dist/{chunk-CP34FI4B.js → chunk-RZTH4MIY.js} +2 -2
- package/dist/{chunk-HB2KB7QL.js → chunk-S2BNTXP6.js} +1 -1
- package/dist/{chunk-EXHXR2X5.js → chunk-SA6UB3GD.js} +1 -1
- package/dist/{chunk-IRLTW6KF.js → chunk-SLEWDZOA.js} +1 -1
- package/dist/chunk-SM54HHVY.js +224 -0
- package/dist/{chunk-4PBCNVI3.js → chunk-UP7NZXJU.js} +1 -1
- package/dist/{chunk-XOOWC62T.js → chunk-VACFOGF3.js} +1 -1
- package/dist/{chunk-OUAE3GKO.js → chunk-VQYARC2K.js} +279 -1
- package/dist/{chunk-64WARXGV.js → chunk-WTT24AUA.js} +698 -43
- package/dist/{chunk-K6BASBQJ.js → chunk-XIHKSZHL.js} +1 -1
- package/dist/{chunk-PD6JY33P.js → chunk-YM4G6CFN.js} +4 -3
- package/dist/{reapply-HQ264ZTR.js → reapply-STSGYPF2.js} +8 -8
- package/dist/src/index.js +12 -10
- package/package.json +1 -1
- package/dist/chunk-A76GNRIK.js +0 -608
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
openCodeConfigDirFrom
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-S2BNTXP6.js";
|
|
4
4
|
import {
|
|
5
5
|
codexHomeFrom
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-EWQZISYV.js";
|
|
7
|
+
import {
|
|
8
|
+
HOOK_COMPATIBLE_PROFILES,
|
|
9
|
+
compatibleConfigDir
|
|
10
|
+
} from "./chunk-VQYARC2K.js";
|
|
7
11
|
|
|
8
12
|
// src/vscode-config.ts
|
|
9
13
|
import { homedir } from "os";
|
|
@@ -234,10 +238,19 @@ var agentProbes = (home = homedir2()) => ({
|
|
|
234
238
|
// never used.
|
|
235
239
|
configMarkers: [openCodeConfigDirFrom(home)]
|
|
236
240
|
},
|
|
241
|
+
...compatibleProbes(home),
|
|
237
242
|
// "Other" is a set of printed instructions for any MCP or HTTP client. There is
|
|
238
243
|
// nothing to detect, and it must never be auto-selected.
|
|
239
244
|
custom: { binary: null, paths: [], configMarkers: [] }
|
|
240
245
|
});
|
|
246
|
+
var compatibleProbes = (home) => HOOK_COMPATIBLE_PROFILES.reduce((probes, profile) => ({
|
|
247
|
+
...probes,
|
|
248
|
+
[profile.agentType]: {
|
|
249
|
+
binary: profile.binary,
|
|
250
|
+
paths: [],
|
|
251
|
+
configMarkers: [join2(home, ...compatibleConfigDir(profile))]
|
|
252
|
+
}
|
|
253
|
+
}), {});
|
|
241
254
|
var detectAllAgents = (deps = defaultDeps, home) => {
|
|
242
255
|
const probes = agentProbes(home);
|
|
243
256
|
return Object.keys(probes).map((agent) => ({
|
|
@@ -265,7 +278,8 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
|
|
|
265
278
|
- When you need the user to make a decision or answer a clarifying question, call \`ask_user\` instead of guessing or stalling. Use type "confirm" for yes/no, "select" for a fixed set of options, and "input" for free text. Read \`answered\`, \`status\` and the returned handoff instead of assuming the tool blocked. Silence is not consent; choice/text answers do not authorize a separate action.
|
|
266
279
|
- Ask once, on one channel. If you also have a built-in way to prompt the user, use that or \`ask_user\` for a given question, never both. \`ask_user\` is the one that reaches the user when they are away from this machine, so prefer it. Raising both strands the user in whichever prompt you are not waiting on.
|
|
267
280
|
- Honor authorization already granted in this session. Ask before a risky or irreversible action outside that authorization, or when an enforced host policy requires it. Do not ask again for an already authorized step, and never bypass an enforced gate. When a Pushary hook result says the user approved a specific command after a denial, rerunning that exact command is the approved path and not a bypass of that gate.
|
|
268
|
-
- Call \`propose_scope\` only for an unresolved or requested file boundary. A ratified scope narrows what may proceed; it does not bypass other approval rules. Do not add a plan approval merely because work spans several files, and do not propose a new scope mid-run just to widen it. Let the installed gate request any scope expansion before the edit executes.
|
|
281
|
+
- Call \`propose_scope\` only for an unresolved or requested file boundary. A ratified scope narrows what may proceed; it does not bypass other approval rules. Do not add a plan approval merely because work spans several files, and do not propose a new scope mid-run just to widen it. Let the installed gate request any scope expansion before the edit executes. Only file paths are checked, and only on tool calls that carry one, so put a boundary that is not a path (recipients, channels, spend, systems you will not open) in \`promises\` and never in \`allowedPaths\`. Read the \`enforces\` field that comes back: an empty array means nothing in that contract is checked automatically, and you must say so rather than report that a scope is in force. \`hookSeen: false\` means no hook has reported that sessionId, so the gate will never find the contract: fix the session id rather than proceeding as if a scope were in force.
|
|
282
|
+
- Before a task of more than two steps, break it into steps and find the forks. Settle by yourself every fork that a file, a command or a tool call can answer, then ask the rest as one numbered round with your recommended answer against each. A question in the terminal is free; a push is not. When the user is at the terminal, ask the whole round there. When the user is away, send only the fork that stops the run, and decide the rest on your recommendation.
|
|
269
283
|
- When you are blocked, stuck, or hit an error you cannot resolve on your own, call \`send_notification\` so the user knows, and call \`ask_user\` if you need a decision to continue.
|
|
270
284
|
- When meaningful work finishes while the user is away, or they requested an alert, call \`send_notification\` so the user knows it is done. Skip trivial completions and duplicate reports to someone already reading. Always pass \`context\` with a \`type\` of "task_complete", "error" or "info": it is what tells Pushary this is a task update, so the user's own setting for where updates land can route it.
|
|
271
285
|
- Confirm notifications can offer lock-screen actions; arbitrary choices and text open the app. The Mac notch handles personal account questions, not Partner customer enrollment.
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
resolveBinary,
|
|
3
3
|
resolveGlobalPackageVersion,
|
|
4
4
|
resolvePackageVersionFromBinary
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-EWQZISYV.js";
|
|
6
6
|
import {
|
|
7
7
|
MANAGED_PROVIDERS
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VQYARC2K.js";
|
|
9
9
|
|
|
10
10
|
// src/live-capabilities.ts
|
|
11
11
|
var CLAUDE_BRIDGE_CAPABILITY = "claude-bridge";
|
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
UsageError
|
|
6
6
|
} from "./chunk-7QLSKOSU.js";
|
|
7
7
|
import {
|
|
8
|
+
COMPATIBLE_AGENT_TYPES,
|
|
8
9
|
isValidApiKey
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VQYARC2K.js";
|
|
10
11
|
|
|
11
12
|
// src/setup/options.ts
|
|
12
|
-
var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "opencode", "custom"];
|
|
13
|
+
var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "opencode", ...COMPATIBLE_AGENT_TYPES, "custom"];
|
|
13
14
|
var CONNECT_MODES = ["app", "browser", "web", "none"];
|
|
14
15
|
var CONNECT_ALIASES = { auto: "app", pwa: "web" };
|
|
15
16
|
var parseConnectMode = (raw) => {
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
} from "./chunk-EYRGBYZG.js";
|
|
16
16
|
import {
|
|
17
17
|
reportedInstallSource
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-E3WQMDCE.js";
|
|
19
19
|
import {
|
|
20
20
|
isAnswerSource
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-VQYARC2K.js";
|
|
22
22
|
|
|
23
23
|
// src/reach.ts
|
|
24
24
|
var reachVerdict = (channels) => {
|
|
@@ -6,16 +6,16 @@ import {
|
|
|
6
6
|
} from "./chunk-YKLCWWEI.js";
|
|
7
7
|
import {
|
|
8
8
|
hookOwnerOf
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DZYIAMKR.js";
|
|
10
10
|
import {
|
|
11
11
|
ensureDaemonRunning
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6JUCFMSF.js";
|
|
13
13
|
import {
|
|
14
14
|
buildTranscriptRecords,
|
|
15
15
|
decodePublicKey,
|
|
16
16
|
generateSessionKey,
|
|
17
17
|
wrapSessionKey
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-BBUZD7MU.js";
|
|
19
19
|
import {
|
|
20
20
|
fetchTranscriptRecipients,
|
|
21
21
|
uploadTranscriptRecords
|
|
@@ -23,14 +23,14 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
transcriptEnvironmentPreference,
|
|
25
25
|
transcriptsEnabled
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-2MOQUNFL.js";
|
|
27
27
|
import {
|
|
28
28
|
codexHome,
|
|
29
29
|
pusharyDir
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-EWQZISYV.js";
|
|
31
31
|
import {
|
|
32
32
|
readTranscriptPreference
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-UP7NZXJU.js";
|
|
34
34
|
import {
|
|
35
35
|
writeJsonAtomic
|
|
36
36
|
} from "./chunk-4LL6LG5X.js";
|
|
@@ -2,24 +2,24 @@ import {
|
|
|
2
2
|
detectProviderLiveCapabilities,
|
|
3
3
|
hasSpawnProvider,
|
|
4
4
|
resolveControlPolicy
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MZUXTN7X.js";
|
|
6
6
|
import {
|
|
7
7
|
daemonHasRemoteProof,
|
|
8
8
|
daemonServiceManagerAvailable,
|
|
9
9
|
hasDaemonServiceDefinition,
|
|
10
10
|
readDaemonNeedsLogin,
|
|
11
11
|
readDaemonStatus
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6JUCFMSF.js";
|
|
13
13
|
import {
|
|
14
14
|
getMachineId
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-J7PJALS2.js";
|
|
16
16
|
import {
|
|
17
17
|
getPackageVersion
|
|
18
18
|
} from "./chunk-DINDL2CF.js";
|
|
19
19
|
import {
|
|
20
20
|
readControlMode,
|
|
21
21
|
readKeySource
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-UP7NZXJU.js";
|
|
23
23
|
import {
|
|
24
24
|
getBaseUrl
|
|
25
25
|
} from "./chunk-2UMNXADU.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SETUP_FLAGS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OYYQHJAO.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-VQYARC2K.js";
|
|
18
18
|
|
|
19
19
|
// src/cli/cowork.ts
|
|
20
20
|
import { readFileSync } from "fs";
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addClaudeMcpServer,
|
|
3
|
+
addPusharyToolPermissions,
|
|
4
|
+
isPusharyHook,
|
|
5
|
+
removeClaudeMcpServers
|
|
6
|
+
} from "./chunk-6T4OINZB.js";
|
|
7
|
+
import {
|
|
8
|
+
CLAUDE_HOOK_EVENTS,
|
|
9
|
+
addGeminiMcpServer,
|
|
10
|
+
removeGeminiMcpServer
|
|
11
|
+
} from "./chunk-YM4G6CFN.js";
|
|
12
|
+
import {
|
|
13
|
+
CLAUDE_CODE_AGENT,
|
|
14
|
+
parseApplyPatchFiles
|
|
15
|
+
} from "./chunk-WTT24AUA.js";
|
|
16
|
+
import {
|
|
17
|
+
guardBinary,
|
|
18
|
+
holdBudgetOf,
|
|
19
|
+
registerHookEntry
|
|
20
|
+
} from "./chunk-EWQZISYV.js";
|
|
21
|
+
import {
|
|
22
|
+
PHONE_ANSWERED_TOOLS,
|
|
23
|
+
hookCompatibleProfile,
|
|
24
|
+
toToolLookup,
|
|
25
|
+
withHookHoldBudget
|
|
26
|
+
} from "./chunk-VQYARC2K.js";
|
|
27
|
+
|
|
28
|
+
// src/compatible-config.ts
|
|
29
|
+
import { homedir } from "os";
|
|
30
|
+
import { isAbsolute, join, resolve } from "path";
|
|
31
|
+
var AGENT_FLAG = "--agent";
|
|
32
|
+
var agentArgument = (profile) => `${AGENT_FLAG}=${profile.agentType}`;
|
|
33
|
+
var profileFromArgv = (argv) => {
|
|
34
|
+
const prefix = `${AGENT_FLAG}=`;
|
|
35
|
+
const named = argv.find((argument) => argument.startsWith(prefix));
|
|
36
|
+
return named ? hookCompatibleProfile(named.slice(prefix.length)) : void 0;
|
|
37
|
+
};
|
|
38
|
+
var activeIdentity = (argv) => {
|
|
39
|
+
const profile = profileFromArgv(argv);
|
|
40
|
+
return profile ? { type: profile.agentType, label: profile.label } : CLAUDE_CODE_AGENT;
|
|
41
|
+
};
|
|
42
|
+
var activeHookSource = (argv) => profileFromArgv(argv)?.source ?? "claude";
|
|
43
|
+
var PATCH_INPUT_KEYS = ["command", "patch", "input"];
|
|
44
|
+
var patchedFiles = (toolInput) => [...new Set(PATCH_INPUT_KEYS.flatMap((key) => parseApplyPatchFiles(toolInput[key])))];
|
|
45
|
+
var lookupFor = (profile, input) => {
|
|
46
|
+
const absolute = (path) => input.cwd && !isAbsolute(path) ? resolve(input.cwd, path) : path;
|
|
47
|
+
const toolInput = input.tool_input ?? {};
|
|
48
|
+
const lookup = toToolLookup(profile, input.tool_name ?? "", toolInput, absolute);
|
|
49
|
+
if (!lookup.pathless) return lookup;
|
|
50
|
+
const files = patchedFiles(toolInput);
|
|
51
|
+
return files.length === 1 ? { ...lookup, input: { ...lookup.input, file_path: absolute(files[0]) }, pathless: false } : lookup;
|
|
52
|
+
};
|
|
53
|
+
var normalizeToolCall = (profile, input) => {
|
|
54
|
+
if (!profile || !input.tool_name) return input;
|
|
55
|
+
const lookup = lookupFor(profile, input);
|
|
56
|
+
return { ...input, tool_name: lookup.tool, tool_input: lookup.input };
|
|
57
|
+
};
|
|
58
|
+
var unreadablePathTool = (profile, input) => {
|
|
59
|
+
if (!profile || !input.tool_name) return void 0;
|
|
60
|
+
return lookupFor(profile, input).pathless ? input.tool_name : void 0;
|
|
61
|
+
};
|
|
62
|
+
var underHome = (segments, home = homedir()) => join(home, ...segments);
|
|
63
|
+
var compatibleConfigFile = (profile, home) => underHome(profile.configFile, home);
|
|
64
|
+
var compatibleMcpFile = (profile, home) => profile.mcp ? underHome(profile.mcp.file, home) : null;
|
|
65
|
+
var compatibleInstructionsFile = (profile, home) => profile.instructions ? underHome(profile.instructions.file, home) : null;
|
|
66
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
67
|
+
var ensureRecord = (target, key) => {
|
|
68
|
+
const existing = asRecord(target[key]);
|
|
69
|
+
if (existing) return existing;
|
|
70
|
+
const created = {};
|
|
71
|
+
target[key] = created;
|
|
72
|
+
return created;
|
|
73
|
+
};
|
|
74
|
+
var descend = (config, keyPath) => keyPath.reduce(ensureRecord, config);
|
|
75
|
+
var findMap = (config, keyPath) => keyPath.reduce((node, key) => node && asRecord(node[key]), config);
|
|
76
|
+
var pruneEmptyMap = (config, keyPath) => {
|
|
77
|
+
if (keyPath.length === 0) return false;
|
|
78
|
+
const parent = findMap(config, keyPath.slice(0, -1));
|
|
79
|
+
const key = keyPath[keyPath.length - 1];
|
|
80
|
+
const map = asRecord(parent?.[key]);
|
|
81
|
+
if (!parent || !map || Object.keys(map).length > 0) return false;
|
|
82
|
+
delete parent[key];
|
|
83
|
+
return true;
|
|
84
|
+
};
|
|
85
|
+
var aliasesFor = (profile, canonical) => Object.entries(profile.toolAliases).flatMap(([agentName, target]) => target === canonical ? [agentName] : []);
|
|
86
|
+
var carriesPhoneAnswer = (profile, canonical) => profile.wire === "legacy_decision" && PHONE_ANSWERED_TOOLS.has(canonical);
|
|
87
|
+
var translateMatcher = (profile, matcher) => {
|
|
88
|
+
const spellings = matcher.split("|").filter((canonical) => !carriesPhoneAnswer(profile, canonical)).flatMap((canonical) => [canonical, ...aliasesFor(profile, canonical)]);
|
|
89
|
+
return [.../* @__PURE__ */ new Set([...spellings, ...profile.extraMatchers])].join("|");
|
|
90
|
+
};
|
|
91
|
+
var TOOL_MATCHED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
|
|
92
|
+
var withoutMatcher = ({ matcher, ...hook }) => hook;
|
|
93
|
+
var compatibleHookEvents = (profile) => CLAUDE_HOOK_EVENTS.filter((hook) => profile.events.includes(hook.event)).map((hook) => {
|
|
94
|
+
if (!hook.matcher) return hook;
|
|
95
|
+
if (!TOOL_MATCHED_EVENTS.has(hook.event)) return withoutMatcher(hook);
|
|
96
|
+
return { ...hook, matcher: translateMatcher(profile, hook.matcher) };
|
|
97
|
+
});
|
|
98
|
+
var WRITTEN = { ok: true };
|
|
99
|
+
var ROOT_KEY = "(root)";
|
|
100
|
+
var dotted = (segments) => segments.join(".") || ROOT_KEY;
|
|
101
|
+
var hooksShapeConflict = (config, profile) => {
|
|
102
|
+
if (!asRecord(config)) return ROOT_KEY;
|
|
103
|
+
const walked = [];
|
|
104
|
+
let node = config;
|
|
105
|
+
for (const key of profile.hooksKeyPath) {
|
|
106
|
+
if (node[key] === void 0) return void 0;
|
|
107
|
+
const nested = asRecord(node[key]);
|
|
108
|
+
if (!nested) return dotted([...walked, key]);
|
|
109
|
+
walked.push(key);
|
|
110
|
+
node = nested;
|
|
111
|
+
}
|
|
112
|
+
const clash = CLAUDE_HOOK_EVENTS.find(({ event }) => node[event] !== void 0 && !Array.isArray(node[event]));
|
|
113
|
+
return clash ? dotted([...walked, clash.event]) : void 0;
|
|
114
|
+
};
|
|
115
|
+
var permissionsShapeConflict = (config) => {
|
|
116
|
+
if (!asRecord(config)) return ROOT_KEY;
|
|
117
|
+
if (config.permissions === void 0) return void 0;
|
|
118
|
+
const permissions = asRecord(config.permissions);
|
|
119
|
+
if (!permissions) return "permissions";
|
|
120
|
+
return permissions.allow !== void 0 && !Array.isArray(permissions.allow) ? "permissions.allow" : void 0;
|
|
121
|
+
};
|
|
122
|
+
var addCompatibleToolPermissions = (config) => {
|
|
123
|
+
const conflictAt = permissionsShapeConflict(config);
|
|
124
|
+
if (conflictAt) return { ok: false, conflictAt };
|
|
125
|
+
addPusharyToolPermissions(config);
|
|
126
|
+
return WRITTEN;
|
|
127
|
+
};
|
|
128
|
+
var entryFor = (profile, definition, resolve2) => ({
|
|
129
|
+
...definition.matcher ? { matcher: definition.matcher } : {},
|
|
130
|
+
hooks: [{
|
|
131
|
+
type: "command",
|
|
132
|
+
command: withHookHoldBudget(
|
|
133
|
+
`${resolve2(definition.binary)} ${agentArgument(profile)}`,
|
|
134
|
+
holdBudgetOf(definition)
|
|
135
|
+
),
|
|
136
|
+
timeout: definition.timeout
|
|
137
|
+
}]
|
|
138
|
+
});
|
|
139
|
+
var addCompatibleHooks = (config, profile, binDir) => {
|
|
140
|
+
const conflictAt = hooksShapeConflict(config, profile);
|
|
141
|
+
if (conflictAt) return { ok: false, conflictAt };
|
|
142
|
+
const resolve2 = (name) => guardBinary(binDir ? join(binDir, name) : name);
|
|
143
|
+
const hooks = descend(config, profile.hooksKeyPath);
|
|
144
|
+
const supported = new Map(compatibleHookEvents(profile).map((hook) => [hook.event, hook]));
|
|
145
|
+
for (const known of CLAUDE_HOOK_EVENTS) {
|
|
146
|
+
const current = Array.isArray(hooks[known.event]) ? hooks[known.event] : [];
|
|
147
|
+
const definition = supported.get(known.event);
|
|
148
|
+
if (definition) {
|
|
149
|
+
hooks[known.event] = registerHookEntry(current, isPusharyHook, entryFor(profile, definition, resolve2));
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const others = current.filter((item) => !isPusharyHook(item));
|
|
153
|
+
if (others.length > 0) hooks[known.event] = others;
|
|
154
|
+
else delete hooks[known.event];
|
|
155
|
+
}
|
|
156
|
+
return WRITTEN;
|
|
157
|
+
};
|
|
158
|
+
var removeCompatibleHooks = (config, profile, scope) => {
|
|
159
|
+
const hooks = findMap(config, profile.hooksKeyPath);
|
|
160
|
+
if (!hooks) return false;
|
|
161
|
+
let changed = false;
|
|
162
|
+
for (const { event } of CLAUDE_HOOK_EVENTS) {
|
|
163
|
+
const entries = hooks[event];
|
|
164
|
+
if (!Array.isArray(entries)) continue;
|
|
165
|
+
const kept = entries.filter((entry) => !isPusharyHook(entry, scope));
|
|
166
|
+
if (kept.length === entries.length) continue;
|
|
167
|
+
if (kept.length === 0) delete hooks[event];
|
|
168
|
+
else hooks[event] = kept;
|
|
169
|
+
changed = true;
|
|
170
|
+
}
|
|
171
|
+
const pruned = pruneEmptyMap(config, profile.hooksKeyPath);
|
|
172
|
+
return changed || pruned;
|
|
173
|
+
};
|
|
174
|
+
var hasCompatibleHooks = (config, profile) => {
|
|
175
|
+
const hooks = findMap(config, profile.hooksKeyPath);
|
|
176
|
+
if (!hooks) return false;
|
|
177
|
+
return compatibleHookEvents(profile).some(({ event }) => {
|
|
178
|
+
const entries = hooks[event];
|
|
179
|
+
return Array.isArray(entries) && entries.some((entry) => isPusharyHook(entry));
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
var missingCompatibleHookEvents = (config, profile) => {
|
|
183
|
+
const hooks = findMap(config, profile.hooksKeyPath);
|
|
184
|
+
return compatibleHookEvents(profile).filter(({ event }) => {
|
|
185
|
+
const entries = hooks?.[event];
|
|
186
|
+
return !Array.isArray(entries) || !entries.some((entry) => isPusharyHook(entry));
|
|
187
|
+
}).map(({ event }) => event);
|
|
188
|
+
};
|
|
189
|
+
var compatiblePermissionRuleFile = (profile, home) => profile.permissionRuleFile ? underHome(profile.permissionRuleFile, home) : null;
|
|
190
|
+
var addCompatibleMcpServer = (config, profile, apiKey) => {
|
|
191
|
+
if (!profile.mcp) return;
|
|
192
|
+
if (profile.mcp.shape === "gemini_http") {
|
|
193
|
+
addGeminiMcpServer(config, apiKey);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
addClaudeMcpServer(config, apiKey);
|
|
197
|
+
if (!profile.mcp.trusted) return;
|
|
198
|
+
const server = asRecord(asRecord(config.mcpServers)?.pushary);
|
|
199
|
+
if (server) server.trust = true;
|
|
200
|
+
};
|
|
201
|
+
var removeCompatibleMcpServer = (config, profile) => {
|
|
202
|
+
if (!profile.mcp) return false;
|
|
203
|
+
return profile.mcp.shape === "gemini_http" ? removeGeminiMcpServer(config) : removeClaudeMcpServers(config);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export {
|
|
207
|
+
profileFromArgv,
|
|
208
|
+
activeIdentity,
|
|
209
|
+
activeHookSource,
|
|
210
|
+
normalizeToolCall,
|
|
211
|
+
unreadablePathTool,
|
|
212
|
+
compatibleConfigFile,
|
|
213
|
+
compatibleMcpFile,
|
|
214
|
+
compatibleInstructionsFile,
|
|
215
|
+
compatibleHookEvents,
|
|
216
|
+
addCompatibleToolPermissions,
|
|
217
|
+
addCompatibleHooks,
|
|
218
|
+
removeCompatibleHooks,
|
|
219
|
+
hasCompatibleHooks,
|
|
220
|
+
missingCompatibleHookEvents,
|
|
221
|
+
compatiblePermissionRuleFile,
|
|
222
|
+
addCompatibleMcpServer,
|
|
223
|
+
removeCompatibleMcpServer
|
|
224
|
+
};
|