@pushary/agent-hooks 0.69.0 → 0.71.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +3 -2
  3. package/data/cursor-plugin/scripts/pushary-gate.mjs +34 -5
  4. package/data/vscode-plugin/scripts/pushary-gate.mjs +27 -2
  5. package/dist/bin/pushary-claude.js +2 -2
  6. package/dist/bin/pushary-clean.js +2 -2
  7. package/dist/bin/pushary-codex-hook.js +2 -2
  8. package/dist/bin/pushary-codex.js +1 -1
  9. package/dist/bin/pushary-connect.js +6 -4
  10. package/dist/bin/pushary-daemon.js +3 -3
  11. package/dist/bin/pushary-doctor.js +8 -29
  12. package/dist/bin/pushary-gemini-hook.js +2 -2
  13. package/dist/bin/pushary-hook.js +5 -5
  14. package/dist/bin/pushary-login.js +2 -2
  15. package/dist/bin/pushary-logout.js +2 -2
  16. package/dist/bin/pushary-mode.js +2 -2
  17. package/dist/bin/pushary-notification-hook.js +1 -1
  18. package/dist/bin/pushary-permission-denied-hook.js +3 -3
  19. package/dist/bin/pushary-permission-hook.js +3 -3
  20. package/dist/bin/pushary-post-hook.js +1 -1
  21. package/dist/bin/pushary-prompt-hook.js +1 -1
  22. package/dist/bin/pushary-session-end-hook.js +1 -1
  23. package/dist/bin/pushary-session-start-hook.js +1 -1
  24. package/dist/bin/pushary-setup.js +175 -34
  25. package/dist/bin/pushary-stats.js +2 -2
  26. package/dist/bin/pushary-status.js +4 -4
  27. package/dist/bin/pushary-stop-hook.js +1 -1
  28. package/dist/bin/pushary-stopfailure-hook.js +1 -1
  29. package/dist/bin/pushary-suggestions.js +2 -2
  30. package/dist/bin/pushary-upgrade.js +2 -2
  31. package/dist/bin/pushary-wait.js +2 -2
  32. package/dist/bin/pushary.js +1 -1
  33. package/dist/{chunk-JGH37QRB.js → chunk-7OYGFJYZ.js} +1 -1
  34. package/dist/chunk-B7R4YSAB.js +38 -0
  35. package/dist/{chunk-ATP6UQPH.js → chunk-BBK3TTU2.js} +1 -1
  36. package/dist/{chunk-H46LSQRT.js → chunk-FZIQSDTQ.js} +27 -12
  37. package/dist/{chunk-3USMXNVB.js → chunk-GD275GEX.js} +5 -3
  38. package/dist/{chunk-C5I2RAMT.js → chunk-HU43EZ5Q.js} +2 -2
  39. package/dist/{chunk-P7VBAYQO.js → chunk-MGFZXUUR.js} +32 -4
  40. package/dist/{chunk-YZT2CBC3.js → chunk-NQTRA7H5.js} +1 -1
  41. package/dist/src/index.js +3 -3
  42. package/package.json +1 -1
@@ -7,6 +7,11 @@ import {
7
7
  addPusharyHooks,
8
8
  addPusharyToolPermissions
9
9
  } from "../chunk-CAJZAFVS.js";
10
+ import {
11
+ claudeWired,
12
+ codexWired,
13
+ geminiWired
14
+ } from "../chunk-B7R4YSAB.js";
10
15
  import {
11
16
  describeDetection,
12
17
  detectAllAgents,
@@ -48,7 +53,7 @@ import {
48
53
  connectViaAppPairing,
49
54
  printConnectInstructions,
50
55
  setHumanStream
51
- } from "../chunk-P7VBAYQO.js";
56
+ } from "../chunk-MGFZXUUR.js";
52
57
  import "../chunk-3EGEA4KH.js";
53
58
  import {
54
59
  KEY_FILE_MODE,
@@ -86,10 +91,10 @@ import {
86
91
  getPackageVersion,
87
92
  isNewerVersion,
88
93
  renderCommandHelp
89
- } from "../chunk-JGH37QRB.js";
94
+ } from "../chunk-7OYGFJYZ.js";
90
95
  import {
91
96
  reportEvent
92
- } from "../chunk-H46LSQRT.js";
97
+ } from "../chunk-FZIQSDTQ.js";
93
98
  import "../chunk-BSZYIAZL.js";
94
99
  import "../chunk-SAF6HGAA.js";
95
100
  import {
@@ -105,7 +110,7 @@ import {
105
110
  } from "../chunk-KZERVKTD.js";
106
111
 
107
112
  // bin/pushary-setup.ts
108
- import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, rmSync, renameSync } from "fs";
113
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, rmSync, renameSync, realpathSync } from "fs";
109
114
  import { join, dirname, basename } from "path";
110
115
  import { homedir, tmpdir } from "os";
111
116
  import { execSync as execSync2 } from "child_process";
@@ -193,6 +198,53 @@ var describeClosing = (facts) => {
193
198
  };
194
199
  };
195
200
 
201
+ // src/setup/codex-version.ts
202
+ var parseCodexVersion = (raw) => {
203
+ const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
204
+ if (!match) return null;
205
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
206
+ };
207
+ var compareCodexVersion = (a, b) => {
208
+ for (let i = 0; i < 3; i++) {
209
+ if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
210
+ }
211
+ return 0;
212
+ };
213
+ var versionFromPackageManifest = (probe, binPath) => {
214
+ let dir = probe.dirname(binPath);
215
+ for (let depth = 0; depth < 4; depth++) {
216
+ const raw = probe.readFile(probe.join(dir, "package.json"));
217
+ if (raw) {
218
+ try {
219
+ const manifest = JSON.parse(raw);
220
+ if (typeof manifest.version === "string") {
221
+ const parsed = parseCodexVersion(manifest.version);
222
+ if (parsed) return parsed;
223
+ }
224
+ } catch {
225
+ }
226
+ }
227
+ const parent = probe.dirname(dir);
228
+ if (parent === dir) break;
229
+ dir = parent;
230
+ }
231
+ return null;
232
+ };
233
+ var readCodexVersion = (probe) => {
234
+ const onPath = probe.resolveOnPath();
235
+ if (onPath) {
236
+ const real = probe.realpath(onPath) ?? onPath;
237
+ const fromManifest = versionFromPackageManifest(probe, real);
238
+ if (fromManifest) return { kind: "known", version: fromManifest, source: "package manifest" };
239
+ }
240
+ const brew = probe.brewVersions();
241
+ if (brew) {
242
+ const parsed = parseCodexVersion(brew);
243
+ if (parsed) return { kind: "known", version: parsed, source: "homebrew" };
244
+ }
245
+ return { kind: "unknown" };
246
+ };
247
+
196
248
  // src/skills-cli.ts
197
249
  import { execSync } from "child_process";
198
250
  var installSkillViaSkillsCli = (agent) => {
@@ -318,6 +370,9 @@ var parseSetupOptions = (input2) => {
318
370
  if (key && keyStdin) {
319
371
  throw new UsageError("Pass either --key or --key-stdin, not both");
320
372
  }
373
+ if (key && !isValidApiKey(key)) {
374
+ throw new UsageError("--key is not a valid API key. Expected the pk_xxx.xxx shape from pushary.com/dashboard/agent/settings");
375
+ }
321
376
  return {
322
377
  connectMode,
323
378
  connectExplicit: connectRaw !== void 0,
@@ -328,7 +383,14 @@ var parseSetupOptions = (input2) => {
328
383
  // Anything that means "there is nobody to answer a prompt". A non-TTY stdin
329
384
  // is included because that is the case that used to hang: it is better to
330
385
  // take the defaults and say so than to wait forever for a keypress.
331
- nonInteractive: yes || agentsFlag !== void 0 || !input2.stdinIsTty,
386
+ //
387
+ // --json counts. Under it stdout carries exactly one object and every human
388
+ // line is redirected to stderr, so a prompt has nowhere to render: the
389
+ // question goes to stderr while the run blocks on a keypress the caller
390
+ // cannot see it is waiting for, and whatever is reading stdout waits forever
391
+ // for an object that never arrives. Asking for machine-readable output is
392
+ // itself a statement that no human is watching.
393
+ nonInteractive: yes || agentsFlag !== void 0 || flags.bools.has("json") || !input2.stdinIsTty,
332
394
  json: flags.bools.has("json"),
333
395
  dryRun: flags.bools.has("dry-run"),
334
396
  help,
@@ -359,6 +421,7 @@ var humanStream = process.stdout;
359
421
  var writeHuman = (text) => {
360
422
  humanStream.write(text);
361
423
  };
424
+ var jsonMode = false;
362
425
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
363
426
  var bold = (s) => `\x1B[1m${s}\x1B[0m`;
364
427
  var green = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -367,6 +430,10 @@ var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
367
430
  var check = green("\u2713");
368
431
  var abortWith = async (reason, code, keyCheck) => {
369
432
  await reportSetupAbort(reason, code, keyCheck);
433
+ if (jsonMode) {
434
+ process.stdout.write(`${JSON.stringify({ ok: false, reason, exitCode: code, ...keyCheck ? { keyCheck } : {} }, null, 2)}
435
+ `);
436
+ }
370
437
  return code;
371
438
  };
372
439
  var readKeyFromStdin = async (options) => {
@@ -621,26 +688,46 @@ var setupHermes = async (_apiKey) => {
621
688
  var CODEX_HOOKS_JSON = join(CODEX_HOME, "hooks.json");
622
689
  var CODEX_HOOKS_MIN_VERSION = [0, 122, 0];
623
690
  var CODEX_TRUST_VERIFIED_MAX = [0, 142, 2];
624
- var parseCodexVersion = (raw) => {
625
- const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
626
- if (!match) return null;
627
- return [Number(match[1]), Number(match[2]), Number(match[3])];
628
- };
629
- var compareCodexVersion = (a, b) => {
630
- for (let i = 0; i < 3; i++) {
631
- if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
632
- }
633
- return 0;
634
- };
635
- var readCodexVersion = () => {
636
- try {
637
- return parseCodexVersion(execSync2("codex --version", { encoding: "utf-8", stdio: "pipe", timeout: 1e4 }));
638
- } catch {
639
- return null;
640
- }
641
- };
642
- var codexSupportsHooks = (version) => version !== null && compareCodexVersion(version, CODEX_HOOKS_MIN_VERSION) >= 0;
643
- var codexTrustAutoSupported = (version) => version !== null && codexSupportsHooks(version) && compareCodexVersion(version, CODEX_TRUST_VERIFIED_MAX) <= 0;
691
+ var detectCodexVersion = () => readCodexVersion({
692
+ resolveOnPath: () => {
693
+ const whichCmd = IS_WINDOWS ? "where" : "which";
694
+ try {
695
+ const found = execSync2(`${whichCmd} codex`, { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).split("\n")[0].trim();
696
+ return found || null;
697
+ } catch {
698
+ return null;
699
+ }
700
+ },
701
+ realpath: (path) => {
702
+ try {
703
+ return realpathSync(path);
704
+ } catch {
705
+ return null;
706
+ }
707
+ },
708
+ readFile: (path) => {
709
+ try {
710
+ return readFileSync(path, "utf-8");
711
+ } catch {
712
+ return null;
713
+ }
714
+ },
715
+ brewVersions: () => {
716
+ try {
717
+ return execSync2("brew list --versions codex", {
718
+ encoding: "utf-8",
719
+ stdio: "pipe",
720
+ timeout: 1e4
721
+ }).trim() || null;
722
+ } catch {
723
+ return null;
724
+ }
725
+ },
726
+ join,
727
+ dirname
728
+ });
729
+ var codexSupportsHooks = (result) => result.kind === "known" && compareCodexVersion(result.version, CODEX_HOOKS_MIN_VERSION) >= 0;
730
+ var codexTrustAutoSupported = (result) => result.kind === "known" && codexSupportsHooks(result) && compareCodexVersion(result.version, CODEX_TRUST_VERIFIED_MAX) <= 0;
644
731
  var removeCodexNotifyEntry = (codexConfig) => {
645
732
  let raw = "";
646
733
  try {
@@ -691,7 +778,7 @@ var setupCodex = async (apiKey) => {
691
778
  addCodexMcpServer(config, apiKey);
692
779
  writeFileAtomic(codexConfig, stringifyTOML(config), KEY_FILE_MODE);
693
780
  });
694
- const codexVersion = readCodexVersion();
781
+ const codexVersion = detectCodexVersion();
695
782
  const hooksSupported = codexSupportsHooks(codexVersion);
696
783
  const trustAuto = codexTrustAutoSupported(codexVersion);
697
784
  let trusted = false;
@@ -719,9 +806,16 @@ var setupCodex = async (apiKey) => {
719
806
  removeCodexNotifyEntry(codexConfig);
720
807
  });
721
808
  } else {
722
- console.log(` ${yellow("!")} This Codex version predates native hooks (needs ${CODEX_HOOKS_MIN_VERSION.join(".")}+).`);
723
- console.log(` ${dim("Installing the deprecated notify handler instead. Upgrade Codex and re-run setup")}`);
724
- console.log(` ${dim("to get policy enforcement, phone approvals, and session tracking.")}`);
809
+ if (codexVersion.kind === "known") {
810
+ console.log(` ${yellow("!")} This Codex version predates native hooks (needs ${CODEX_HOOKS_MIN_VERSION.join(".")}+).`);
811
+ console.log(` ${dim("Installing the deprecated notify handler instead. Upgrade Codex and re-run setup")}`);
812
+ console.log(` ${dim("to get policy enforcement, phone approvals, and session tracking.")}`);
813
+ } else {
814
+ console.log(` ${yellow("!")} Could not read your Codex version from its install, so hooks were skipped.`);
815
+ console.log(` ${dim("Deliberately not run: asking the binary its version is what macOS quarantines.")}`);
816
+ console.log(` ${dim(`Installing the notify handler, which works on every version. If Codex is ${CODEX_HOOKS_MIN_VERSION.join(".")}+,`)}`);
817
+ console.log(` ${dim("re-run setup after installing it through npm or Homebrew to get native hooks.")}`);
818
+ }
725
819
  await spinner("Adding notify handler for Codex events (deprecated)", async () => {
726
820
  addCodexNotifyEntry(codexConfig);
727
821
  });
@@ -1053,6 +1147,44 @@ var setupGemini = async (apiKey) => {
1053
1147
  console.log(` ${dim("Restart Gemini CLI to load the new config.")}`);
1054
1148
  noteManual("Restart Gemini CLI to load the new config.");
1055
1149
  };
1150
+ var agentIsWired = (agent) => {
1151
+ const readJson2 = (path) => {
1152
+ try {
1153
+ return JSON.parse(readFileSync(path, "utf-8"));
1154
+ } catch {
1155
+ return null;
1156
+ }
1157
+ };
1158
+ switch (agent) {
1159
+ case "claude_code":
1160
+ return claudeWired({
1161
+ claudeJson: readJson2(CLAUDE_JSON),
1162
+ settings: readJson2(CLAUDE_SETTINGS),
1163
+ skillExists: existsSync(join(CLAUDE_SKILL_DIR, "SKILL.md"))
1164
+ });
1165
+ case "codex": {
1166
+ let config = null;
1167
+ try {
1168
+ config = parseTOML(readFileSync(join(CODEX_HOME, "config.toml"), "utf-8"));
1169
+ } catch {
1170
+ config = null;
1171
+ }
1172
+ return codexWired({
1173
+ config,
1174
+ hooks: readJson2(CODEX_HOOKS_JSON),
1175
+ skillExists: existsSync(join(CODEX_SKILL_DIR, "SKILL.md"))
1176
+ });
1177
+ }
1178
+ case "gemini_cli":
1179
+ return geminiWired({ settings: readJson2(GEMINI_SETTINGS) });
1180
+ case "cursor":
1181
+ return existsSync(join(CURSOR_PLUGIN_DIR, "mcp.json"));
1182
+ case "vscode":
1183
+ return existsSync(join(VSCODE_PLUGIN_DIR, ".mcp.json"));
1184
+ default:
1185
+ return true;
1186
+ }
1187
+ };
1056
1188
  var AGENT_SETUP = {
1057
1189
  claude_code: setupClaudeCode,
1058
1190
  codex: setupCodex,
@@ -1201,7 +1333,8 @@ var resolveAgents = async (options) => {
1201
1333
  return options.agents.agents;
1202
1334
  }
1203
1335
  const detected = detectAgents();
1204
- if (options.agents.kind === "auto") {
1336
+ const wantsAuto = options.agents.kind === "auto" || options.nonInteractive;
1337
+ if (wantsAuto) {
1205
1338
  const picked = Object.keys(detected).filter((agent) => detected[agent].found);
1206
1339
  if (picked.length === 0) {
1207
1340
  console.log(` ${yellow("!")} No agents detected, so nothing was configured. Your key is saved.`);
@@ -1283,6 +1416,7 @@ var main = async () => {
1283
1416
  }
1284
1417
  recordSetupOptions(options);
1285
1418
  if (options.json) {
1419
+ jsonMode = true;
1286
1420
  humanStream = process.stderr;
1287
1421
  setHumanStream(process.stderr);
1288
1422
  console.log = (...args) => {
@@ -1304,12 +1438,12 @@ var main = async () => {
1304
1438
  let keyFromPairing = false;
1305
1439
  const storedKey = readConfigFileKey();
1306
1440
  const pairingRequested = connectMode === "app" && options.connectExplicit && !options.skipPhone;
1307
- const ambientEligible = !(flagKey && isValidApiKey(flagKey)) && !pairingRequested;
1441
+ const ambientEligible = flagKey === void 0 && !pairingRequested;
1308
1442
  const envCheck = ambientEligible && envKey && isValidApiKey(envKey) ? await usableStoredKey(envKey) : null;
1309
1443
  const storedCheck = ambientEligible && envCheck === null && storedKey !== null && isValidApiKey(storedKey) && // login writes both, so without this the same key is probed twice.
1310
1444
  storedKey !== envKey ? await usableStoredKey(storedKey) : null;
1311
1445
  let storedKeyCheck = null;
1312
- if (flagKey && isValidApiKey(flagKey)) {
1446
+ if (flagKey) {
1313
1447
  const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
1314
1448
  console.log(` ${check} Using API key: ${dim(masked)}`);
1315
1449
  console.log();
@@ -1445,8 +1579,15 @@ var main = async () => {
1445
1579
  markStep(`configure:${agent}`);
1446
1580
  try {
1447
1581
  const outcome = await AGENT_SETUP[agent](trimmedKey, context);
1448
- if (outcome === "skipped") skipped.push(agent);
1449
- else completed.push(agent);
1582
+ if (outcome === "skipped") {
1583
+ skipped.push(agent);
1584
+ } else if (agentIsWired(agent)) {
1585
+ completed.push(agent);
1586
+ } else {
1587
+ failed.push(agent.replace("_", " "));
1588
+ console.log(` ${yellow("!")} ${agent.replace("_", " ")} reported success but its config is not there.`);
1589
+ console.log(` ${dim("Re-run setup, then")} ${cyan("npx @pushary/agent-hooks@latest doctor")} ${dim("to see what is missing.")}`);
1590
+ }
1450
1591
  } catch (err) {
1451
1592
  failed.push(agent.replace("_", " "));
1452
1593
  console.log(` ${yellow("!")} ${agent.replace("_", " ")} setup failed: ${formatError(err)}`);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  exitOnHelpFlag
4
- } from "../chunk-ATP6UQPH.js";
5
- import "../chunk-JGH37QRB.js";
4
+ } from "../chunk-BBK3TTU2.js";
5
+ import "../chunk-7OYGFJYZ.js";
6
6
  import {
7
7
  readLedgerSummary
8
8
  } from "../chunk-WLGEY4UX.js";
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  describeLastSeen,
4
4
  listMachines
5
- } from "../chunk-YZT2CBC3.js";
5
+ } from "../chunk-NQTRA7H5.js";
6
6
  import {
7
7
  describeReach,
8
8
  fetchChannels,
9
9
  reachVerdict
10
- } from "../chunk-P7VBAYQO.js";
10
+ } from "../chunk-MGFZXUUR.js";
11
11
  import "../chunk-3EGEA4KH.js";
12
12
  import {
13
13
  readKeySource
@@ -26,8 +26,8 @@ import {
26
26
  } from "../chunk-GMXKITVA.js";
27
27
  import {
28
28
  exitOnHelpFlag
29
- } from "../chunk-ATP6UQPH.js";
30
- import "../chunk-JGH37QRB.js";
29
+ } from "../chunk-BBK3TTU2.js";
30
+ import "../chunk-7OYGFJYZ.js";
31
31
  import "../chunk-BSZYIAZL.js";
32
32
  import "../chunk-SAF6HGAA.js";
33
33
  import {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStop
4
- } from "../chunk-H46LSQRT.js";
4
+ } from "../chunk-FZIQSDTQ.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStopFailure
4
- } from "../chunk-H46LSQRT.js";
4
+ } from "../chunk-FZIQSDTQ.js";
5
5
  import "../chunk-BSZYIAZL.js";
6
6
  import "../chunk-SAF6HGAA.js";
7
7
  import "../chunk-7QLSKOSU.js";
@@ -7,8 +7,8 @@ import {
7
7
  } from "../chunk-GMXKITVA.js";
8
8
  import {
9
9
  exitOnHelpFlag
10
- } from "../chunk-ATP6UQPH.js";
11
- import "../chunk-JGH37QRB.js";
10
+ } from "../chunk-BBK3TTU2.js";
11
+ import "../chunk-7OYGFJYZ.js";
12
12
  import {
13
13
  UsageError
14
14
  } from "../chunk-7QLSKOSU.js";
@@ -36,10 +36,10 @@ import {
36
36
  } from "../chunk-MUEW424A.js";
37
37
  import {
38
38
  exitOnHelpFlag
39
- } from "../chunk-ATP6UQPH.js";
39
+ } from "../chunk-BBK3TTU2.js";
40
40
  import {
41
41
  isNewerVersion
42
- } from "../chunk-JGH37QRB.js";
42
+ } from "../chunk-7OYGFJYZ.js";
43
43
  import "../chunk-DQAN3JQP.js";
44
44
  import {
45
45
  getApiKey
@@ -14,8 +14,8 @@ import {
14
14
  } from "../chunk-GMXKITVA.js";
15
15
  import {
16
16
  exitOnHelpFlag
17
- } from "../chunk-ATP6UQPH.js";
18
- import "../chunk-JGH37QRB.js";
17
+ } from "../chunk-BBK3TTU2.js";
18
+ import "../chunk-7OYGFJYZ.js";
19
19
  import "../chunk-SAF6HGAA.js";
20
20
  import {
21
21
  UsageError
@@ -4,7 +4,7 @@ import {
4
4
  getPackageVersion,
5
5
  isCommandName,
6
6
  renderHelp
7
- } from "../chunk-JGH37QRB.js";
7
+ } from "../chunk-7OYGFJYZ.js";
8
8
  import {
9
9
  installProxyDispatcher
10
10
  } from "../chunk-SAF6HGAA.js";
@@ -3,7 +3,7 @@ var COMMAND_LIST = [
3
3
  {
4
4
  name: "setup",
5
5
  module: "pushary-setup",
6
- summary: "Configure Claude Code, Codex, Gemini CLI, Hermes, or Cursor with Pushary"
6
+ summary: "Configure Claude Code, Codex, Gemini CLI, Hermes, Cursor, or VS Code with Pushary"
7
7
  },
8
8
  {
9
9
  name: "claude",
@@ -0,0 +1,38 @@
1
+ import {
2
+ hasGeminiHooks
3
+ } from "./chunk-E2U35RLD.js";
4
+ import {
5
+ hasCodexHooks
6
+ } from "./chunk-7HG4WUIE.js";
7
+
8
+ // src/diagnostics/wiring.ts
9
+ var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
10
+ var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
11
+ var claudeWired = ({ claudeJson, settings, skillExists }) => {
12
+ if (record(record(claudeJson)?.mcpServers)?.pushary) return true;
13
+ const s = record(settings);
14
+ if (s) {
15
+ if (CLAUDE_HOOK_BINARY.test(JSON.stringify(s.hooks ?? {}))) return true;
16
+ const allow = record(s.permissions)?.allow;
17
+ if (Array.isArray(allow) && allow.some((rule) => String(rule).includes("mcp__pushary__"))) return true;
18
+ }
19
+ return skillExists;
20
+ };
21
+ var codexWired = ({ config, hooks, skillExists }) => {
22
+ if (record(record(config)?.mcp_servers)?.pushary) return true;
23
+ const h = record(hooks);
24
+ if (h && hasCodexHooks(h)) return true;
25
+ return skillExists;
26
+ };
27
+ var geminiWired = ({ settings }) => {
28
+ const s = record(settings);
29
+ if (!s) return false;
30
+ if (record(s.mcpServers)?.pushary) return true;
31
+ return hasGeminiHooks(s);
32
+ };
33
+
34
+ export {
35
+ claudeWired,
36
+ codexWired,
37
+ geminiWired
38
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getPackageVersion,
3
3
  renderCommandHelp
4
- } from "./chunk-JGH37QRB.js";
4
+ } from "./chunk-7OYGFJYZ.js";
5
5
  import {
6
6
  EXIT
7
7
  } from "./chunk-KZERVKTD.js";
@@ -55,6 +55,10 @@ var isWaitForAnswerResponse = (data) => {
55
55
  // src/policy.ts
56
56
  var CACHE_TTL_MS = 60 * 1e3;
57
57
  var STALE_CACHE_MAX_MS = 12 * 60 * 60 * 1e3;
58
+ var policyRequestFor = (agentType) => ({
59
+ agent: agentType,
60
+ repoAware: agentType === "claude_code"
61
+ });
58
62
  var policyCacheFile = (apiKey, agent, repoAware = false) => {
59
63
  const parts = [apiKey, agent, repoAware ? "repoAware" : void 0].filter(Boolean);
60
64
  const hash = createHash("sha256").update(parts.join(":")).digest("hex").slice(0, 12);
@@ -132,6 +136,8 @@ var findBestMatch = (policies, toolName, args, repoKey) => {
132
136
  }
133
137
  return best;
134
138
  };
139
+ var isAutoApprove = (policy) => policy.timeoutSeconds === 0 && policy.timeoutAction === "approve";
140
+ var isAutoDeny = (policy) => policy.timeoutSeconds === 0 && policy.timeoutAction === "deny";
135
141
  var autoApprove = (tool) => ({
136
142
  tool,
137
143
  timeoutSeconds: 0,
@@ -164,7 +170,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey) =>
164
170
  if (!governedBySpecificRule && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg)) {
165
171
  base = autoApprove(base.tool);
166
172
  }
167
- const wouldAutoApprove = base.timeoutSeconds === 0 && base.timeoutAction === "approve";
173
+ const wouldAutoApprove = isAutoApprove(base);
168
174
  if (!governedBySpecificRule && wouldAutoApprove && toolName === "Bash" && typeof arg === "string" && classifyDecisionRisk(toolName, arg) === "destructive") {
169
175
  base = {
170
176
  tool: base.tool,
@@ -181,8 +187,8 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey) =>
181
187
  return base;
182
188
  };
183
189
  var restraintRank = (policy) => {
184
- if (policy.timeoutSeconds === 0 && policy.timeoutAction === "deny") return 4;
185
- if (policy.timeoutSeconds === 0 && policy.timeoutAction === "approve") return 0;
190
+ if (isAutoDeny(policy)) return 4;
191
+ if (isAutoApprove(policy)) return 0;
186
192
  if (policy.mode === "push_only") return 3;
187
193
  return policy.mode === "terminal_only" || policy.mode === "notify_only" ? 1 : 2;
188
194
  };
@@ -945,31 +951,38 @@ var notifyLateAnswers = async (apiKey, late, agentName, sessionId) => {
945
951
  };
946
952
  var CLAUDE_CODE_AGENT = { type: "claude_code", label: "Claude Code" };
947
953
  var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
948
- var readFreshCachedPolicy = (apiKey) => {
949
- const path = policyCacheFile(apiKey);
954
+ var readFreshCachedPolicy = (apiKey, agentType) => {
955
+ const request = policyRequestFor(agentType);
956
+ const path = policyCacheFile(apiKey, request.agent, request.repoAware);
950
957
  if (!existsSync5(path)) return null;
951
958
  const cached = JSON.parse(readFileSync4(path, "utf-8"));
952
959
  if (!isPolicyConfig(cached)) return null;
953
960
  if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
954
961
  return cached;
955
962
  };
956
- var deriveDecisionSource = (toolName, toolInput, liveMode, cwd) => {
963
+ var autoDecisionEnabled = () => {
964
+ const flag = process.env.PUSHARY_AUTO_DECISION_BEACON;
965
+ return flag === "1" || flag === "true";
966
+ };
967
+ var deriveDecisionSource = (toolName, toolInput, liveMode, agentType, cwd) => {
957
968
  try {
958
969
  if (liveMode.kill) return "terminal";
959
- const policy = readFreshCachedPolicy(getApiKey());
970
+ if (!autoDecisionEnabled()) return void 0;
971
+ const policy = readFreshCachedPolicy(getApiKey(), agentType);
960
972
  if (!policy) return void 0;
961
973
  const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
962
- if (resolved.timeoutSeconds === 0 && resolved.timeoutAction === "approve") return "policy_auto";
974
+ if (isAutoApprove(resolved)) return "policy_auto";
963
975
  if (resolved.mode === "push_only" || resolved.mode === "push_first") return "human";
964
976
  return "terminal";
965
977
  } catch {
966
978
  return void 0;
967
979
  }
968
980
  };
969
- var deriveAutoDecisionBeacon = (toolName, toolInput, liveMode, cwd) => {
981
+ var deriveAutoDecisionBeacon = (toolName, toolInput, liveMode, agentType, cwd) => {
970
982
  try {
971
983
  if (liveMode.kill) return void 0;
972
- const policy = readFreshCachedPolicy(getApiKey());
984
+ if (!autoDecisionEnabled()) return void 0;
985
+ const policy = readFreshCachedPolicy(getApiKey(), agentType);
973
986
  if (!policy) return void 0;
974
987
  const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
975
988
  if (resolved.timeoutSeconds !== 0 || resolved.timeoutAction !== "approve") return void 0;
@@ -1055,8 +1068,8 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1055
1068
  for (const l of late) removePendingQuestion(sessionKey, l.correlationId);
1056
1069
  }
1057
1070
  const isMirrorOnly = NON_GATED_MIRROR_TOOLS.has(input.tool_name);
1058
- const decisionSource = isMirrorOnly ? void 0 : deriveDecisionSource(lookup.tool, lookup.input, liveMode, input.cwd);
1059
- const beacon = !isMirrorOnly && decisionSource === "policy_auto" && throttlePass(`autodecision:${sessionKey}:${lookup.tool}`, AUTO_DECISION_WINDOW_MS) ? deriveAutoDecisionBeacon(lookup.tool, lookup.input, liveMode, input.cwd) : void 0;
1071
+ const decisionSource = isMirrorOnly ? void 0 : deriveDecisionSource(lookup.tool, lookup.input, liveMode, agent.type, input.cwd);
1072
+ const beacon = !isMirrorOnly && decisionSource === "policy_auto" && throttlePass(`autodecision:${sessionKey}:${lookup.tool}`, AUTO_DECISION_WINDOW_MS) ? deriveAutoDecisionBeacon(lookup.tool, lookup.input, liveMode, agent.type, input.cwd) : void 0;
1060
1073
  const toolReport = reportEvent({
1061
1074
  event: isError ? "tool_error" : "tool_complete",
1062
1075
  agentType: agent.type,
@@ -1310,6 +1323,8 @@ var handleStopFailure = async (input, agent = CLAUDE_CODE_AGENT) => {
1310
1323
 
1311
1324
  export {
1312
1325
  getPolicy,
1326
+ isAutoApprove,
1327
+ isAutoDeny,
1313
1328
  resolveAutoResolveOrigin,
1314
1329
  resolvePolicy,
1315
1330
  resolvePolicyAcross,
@@ -1,8 +1,10 @@
1
1
  import {
2
+ isAutoApprove,
3
+ isAutoDeny,
2
4
  isModeStateDegraded,
3
5
  resolveAutoResolveOrigin,
4
6
  resolvePolicyAcross
5
- } from "./chunk-H46LSQRT.js";
7
+ } from "./chunk-FZIQSDTQ.js";
6
8
  import {
7
9
  evaluateScope,
8
10
  scopeChangeReason,
@@ -34,8 +36,8 @@ var resolveGate = (input) => {
34
36
  const { modeState, config, toolName, toolInputs, cwd, repoKey } = input;
35
37
  if (modeState.kill) return { kind: "kill", reason: KILL_REASON };
36
38
  const policy = resolvePolicyAcross(config, toolName, modeState.mode, toolInputs, cwd, repoKey);
37
- const autoApproves = policy.timeoutSeconds === 0 && policy.timeoutAction === "approve";
38
- const autoDenies = policy.timeoutSeconds === 0 && policy.timeoutAction === "deny";
39
+ const autoApproves = isAutoApprove(policy);
40
+ const autoDenies = isAutoDeny(policy);
39
41
  if (autoDenies) return { kind: "deny", policy, reason: `Denied by policy for ${policy.tool}` };
40
42
  if (isModeStateDegraded(modeState)) {
41
43
  if (autoApproves) {
@@ -5,7 +5,7 @@ import {
5
5
  denyReasonFrom,
6
6
  isDeferAnswer,
7
7
  resolveGate
8
- } from "./chunk-3USMXNVB.js";
8
+ } from "./chunk-GD275GEX.js";
9
9
  import {
10
10
  DEFAULT_SESSION,
11
11
  askUser,
@@ -25,7 +25,7 @@ import {
25
25
  sendNotification,
26
26
  throttlePass,
27
27
  waitForAnswer
28
- } from "./chunk-H46LSQRT.js";
28
+ } from "./chunk-FZIQSDTQ.js";
29
29
  import {
30
30
  isGatingMoment,
31
31
  recordKeylessMoment