@pushary/agent-hooks 1.7.3 → 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.
Files changed (102) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +13 -1
  3. package/data/cursor-plugin/scripts/pushary-gate.mjs +2 -21
  4. package/data/cursor-plugin/scripts/redaction.mjs +51 -0
  5. package/data/vscode-plugin/scripts/pushary-gate.mjs +1 -21
  6. package/data/vscode-plugin/scripts/redaction.mjs +51 -0
  7. package/dist/bin/pushary-bell-hook.js +23 -23
  8. package/dist/bin/pushary-bell.js +9 -9
  9. package/dist/bin/pushary-claude.js +26 -26
  10. package/dist/bin/pushary-clean.js +91 -107
  11. package/dist/bin/pushary-codex-bridge.js +18 -18
  12. package/dist/bin/pushary-codex-hook.js +42 -43
  13. package/dist/bin/pushary-codex.js +13 -13
  14. package/dist/bin/pushary-connect.js +13 -13
  15. package/dist/bin/pushary-cowork.js +5 -5
  16. package/dist/bin/pushary-daemon.js +27 -27
  17. package/dist/bin/pushary-disconnect.js +18 -18
  18. package/dist/bin/pushary-doctor.js +85 -79
  19. package/dist/bin/pushary-elicitation-hook.js +25 -25
  20. package/dist/bin/pushary-gemini-bridge.js +12 -12
  21. package/dist/bin/pushary-gemini-hook.js +29 -30
  22. package/dist/bin/pushary-hook.js +32 -32
  23. package/dist/bin/pushary-login.js +12 -12
  24. package/dist/bin/pushary-logout.js +7 -7
  25. package/dist/bin/pushary-mcp.js +85 -28
  26. package/dist/bin/pushary-mode.js +9 -9
  27. package/dist/bin/pushary-notification-hook.js +28 -28
  28. package/dist/bin/pushary-opencode-hook.js +28 -29
  29. package/dist/bin/pushary-permission-denied-hook.js +32 -32
  30. package/dist/bin/pushary-permission-hook.js +31 -31
  31. package/dist/bin/pushary-post-hook.js +28 -28
  32. package/dist/bin/pushary-prompt-hook.js +28 -28
  33. package/dist/bin/pushary-session-end-hook.js +28 -28
  34. package/dist/bin/pushary-session-start-hook.js +32 -32
  35. package/dist/bin/pushary-setup.js +60 -127
  36. package/dist/bin/pushary-stats.js +5 -5
  37. package/dist/bin/pushary-status.js +15 -15
  38. package/dist/bin/pushary-stop-hook.js +28 -28
  39. package/dist/bin/pushary-stopfailure-hook.js +28 -28
  40. package/dist/bin/pushary-suggestions.js +4 -4
  41. package/dist/bin/pushary-transcript-register.js +19 -19
  42. package/dist/bin/pushary-transcripts.js +7 -7
  43. package/dist/bin/pushary-upgrade.js +231 -105
  44. package/dist/bin/pushary-wait.js +9 -9
  45. package/dist/bin/pushary.js +6 -6
  46. package/dist/{chunk-EEEJ2JIL.js → chunk-2ABTSGFT.js} +3 -3
  47. package/dist/{chunk-DUQO4C4I.js → chunk-2OY3ZZ5N.js} +3 -3
  48. package/dist/{chunk-MIXWMA73.js → chunk-3EVPNIBE.js} +3 -3
  49. package/dist/{chunk-WTJM7PSL.js → chunk-6J2JC2RT.js} +3 -3
  50. package/dist/{chunk-EADXTSHE.js → chunk-7Z7Q4GGS.js} +2 -2
  51. package/dist/{chunk-MZBRL5H5.js → chunk-A2A2YTMG.js} +108 -25
  52. package/dist/{chunk-KEL5MXRL.js → chunk-AHV4LNB5.js} +1 -1
  53. package/dist/{chunk-G3OMOTJ4.js → chunk-AXPCESYO.js} +3 -3
  54. package/dist/{chunk-3EUBQARL.js → chunk-C2WCPBF7.js} +1 -1
  55. package/dist/chunk-CJVVKOSR.js +73 -0
  56. package/dist/{chunk-U6RVDUJ5.js → chunk-CULUZTWQ.js} +283 -41
  57. package/dist/{chunk-N5MZH5A6.js → chunk-CVQYGMSR.js} +4 -4
  58. package/dist/{chunk-VKVSRCBO.js → chunk-D2FX3EPW.js} +1 -1
  59. package/dist/{chunk-62CVUUNU.js → chunk-DB3ONZB4.js} +2 -2
  60. package/dist/{chunk-WGMW7OXW.js → chunk-DE4Y6TCC.js} +6 -6
  61. package/dist/{chunk-YYCTF4Q5.js → chunk-E4FJTUH4.js} +1 -1
  62. package/dist/{chunk-EUGNVLMG.js → chunk-EPCXUP3P.js} +3 -1
  63. package/dist/{chunk-PRNFSORD.js → chunk-EX3GAPA4.js} +1 -1
  64. package/dist/{chunk-TCVMK6GI.js → chunk-EYK3GSRH.js} +4 -4
  65. package/dist/{chunk-R6OYCUNK.js → chunk-GPEGOGRJ.js} +1 -1
  66. package/dist/chunk-GU3EJCVV.js +43 -0
  67. package/dist/{chunk-Y4ZH64UK.js → chunk-HQ6G3B5R.js} +2 -2
  68. package/dist/{chunk-5S2VBSJI.js → chunk-HZVVNGEW.js} +2 -2
  69. package/dist/{chunk-IWJKSIB4.js → chunk-IPVKH2ST.js} +1 -1
  70. package/dist/{chunk-UOQRM7JQ.js → chunk-J5TIV3J6.js} +5 -5
  71. package/dist/{chunk-IRZG3HEU.js → chunk-JU6W3XAU.js} +1 -1
  72. package/dist/{chunk-G62JL2NT.js → chunk-JYG4B33G.js} +1 -1
  73. package/dist/{chunk-OTNOANMX.js → chunk-JZTHGAPA.js} +8 -8
  74. package/dist/{chunk-7RUJKCR4.js → chunk-KGDWDSNL.js} +2 -2
  75. package/dist/{chunk-6JQFIHZC.js → chunk-KVQBWI5T.js} +3 -3
  76. package/dist/{chunk-OVC5QXN5.js → chunk-LXQ5FBBD.js} +1 -1
  77. package/dist/{chunk-VE7TXLR4.js → chunk-M5ICYGHT.js} +3 -3
  78. package/dist/{chunk-YPUBA2FL.js → chunk-M5JDZ45W.js} +4 -4
  79. package/dist/{chunk-O3JDWNLU.js → chunk-MDEPU45E.js} +183 -28
  80. package/dist/{chunk-P2VIBGXA.js → chunk-MPWXGONV.js} +1 -1
  81. package/dist/{chunk-YC27PROF.js → chunk-NGDD6TXY.js} +20 -5
  82. package/dist/{chunk-G4GLCLKK.js → chunk-NQTHFF4W.js} +138 -17
  83. package/dist/{chunk-NOI3XTCY.js → chunk-OKXS7WDZ.js} +1 -1
  84. package/dist/{chunk-LPNJLKBQ.js → chunk-OLQRRFBX.js} +1 -1
  85. package/dist/{chunk-D6OODZCM.js → chunk-P7WQEVWA.js} +2 -2
  86. package/dist/{chunk-62VT3BPH.js → chunk-Q7E4I6UA.js} +28 -16
  87. package/dist/{chunk-BIH2Y2HP.js → chunk-R7437YI3.js} +2 -2
  88. package/dist/{chunk-RLW3JWDY.js → chunk-RHR6NARM.js} +2 -2
  89. package/dist/{chunk-WPX67AKT.js → chunk-SAXBC4AZ.js} +1 -1
  90. package/dist/{chunk-IPSRMLCJ.js → chunk-SIFVFP6T.js} +1 -1
  91. package/dist/{chunk-7OA34PNW.js → chunk-SLFABHW3.js} +40 -10
  92. package/dist/{chunk-AWVNAYGD.js → chunk-TY4JT7W3.js} +1 -1
  93. package/dist/{chunk-KJXUIPS4.js → chunk-UI36QSBI.js} +28 -16
  94. package/dist/{chunk-G6RRVJOG.js → chunk-VQKRG3AC.js} +6 -6
  95. package/dist/{chunk-ZKJ34T2F.js → chunk-WE2J62NC.js} +31 -17
  96. package/dist/{chunk-LHC45YUC.js → chunk-ZSZSSJCV.js} +1 -1
  97. package/dist/{chunk-BGBLLHZ4.js → chunk-ZVSKMPBZ.js} +1 -1
  98. package/dist/{reapply-PH5T4BGR.js → reapply-CW3MA66V.js} +9 -9
  99. package/dist/src/index.js +20 -20
  100. package/package.json +1 -1
  101. package/dist/chunk-5GB22QYL.js +0 -255
  102. package/dist/chunk-VYPJHGG6.js +0 -92
@@ -2,10 +2,10 @@ import {
2
2
  resolveBinary,
3
3
  resolveGlobalPackageVersion,
4
4
  resolvePackageVersionFromBinary
5
- } from "./chunk-MZBRL5H5.js";
5
+ } from "./chunk-A2A2YTMG.js";
6
6
  import {
7
7
  MANAGED_PROVIDERS
8
- } from "./chunk-G4GLCLKK.js";
8
+ } from "./chunk-NQTHFF4W.js";
9
9
 
10
10
  // src/live-capabilities.ts
11
11
  var CLAUDE_BRIDGE_CAPABILITY = "claude-bridge";
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-KZERVKTD.js";
4
4
  import {
5
5
  renderCommandHelp
6
- } from "./chunk-YYCTF4Q5.js";
6
+ } from "./chunk-E4FJTUH4.js";
7
7
  import {
8
8
  getPackageVersion
9
- } from "./chunk-G4GLCLKK.js";
9
+ } from "./chunk-NQTHFF4W.js";
10
10
 
11
11
  // src/cli/usage.ts
12
12
  var exitOnHelpFlag = (command) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readConfigFileKey
3
- } from "./chunk-D6OODZCM.js";
3
+ } from "./chunk-P7WQEVWA.js";
4
4
 
5
5
  // src/bell/fleet.ts
6
6
  import { closeSync, constants, futimesSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";
@@ -3,28 +3,28 @@ import {
3
3
  addPusharyToolPermissions,
4
4
  isPusharyHook,
5
5
  removeClaudeMcpServers
6
- } from "./chunk-G3OMOTJ4.js";
6
+ } from "./chunk-AXPCESYO.js";
7
7
  import {
8
8
  CLAUDE_HOOK_EVENTS,
9
9
  addGeminiMcpServer,
10
10
  removeGeminiMcpServer
11
- } from "./chunk-MIXWMA73.js";
11
+ } from "./chunk-3EVPNIBE.js";
12
12
  import {
13
13
  CLAUDE_CODE_AGENT,
14
14
  parseApplyPatchFiles
15
- } from "./chunk-U6RVDUJ5.js";
15
+ } from "./chunk-CULUZTWQ.js";
16
16
  import {
17
17
  guardBinary,
18
18
  holdBudgetOf,
19
19
  registerHookEntry
20
- } from "./chunk-MZBRL5H5.js";
20
+ } from "./chunk-A2A2YTMG.js";
21
21
  import {
22
22
  PHONE_ANSWERED_TOOLS,
23
23
  hookCompatibleProfile,
24
24
  hookCompatibleProfileBySource,
25
25
  toToolLookup,
26
26
  withHookHoldBudget
27
- } from "./chunk-G4GLCLKK.js";
27
+ } from "./chunk-NQTHFF4W.js";
28
28
 
29
29
  // src/compatible-config.ts
30
30
  import { homedir } from "os";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-G4GLCLKK.js";
3
+ } from "./chunk-NQTHFF4W.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-3EGEA4KH.js";
10
10
  import {
11
11
  getBaseUrl
12
- } from "./chunk-R6OYCUNK.js";
12
+ } from "./chunk-GPEGOGRJ.js";
13
13
 
14
14
  // src/cli/login-ui.ts
15
15
  import { hostname } from "os";
@@ -6,38 +6,38 @@ import {
6
6
  } from "./chunk-SGZJ5LYH.js";
7
7
  import {
8
8
  hookOwnerOf
9
- } from "./chunk-TCVMK6GI.js";
9
+ } from "./chunk-EYK3GSRH.js";
10
10
  import {
11
11
  ensureDaemonRunning
12
- } from "./chunk-ZKJ34T2F.js";
12
+ } from "./chunk-WE2J62NC.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
16
  generateSessionKey,
17
17
  wrapSessionKey
18
- } from "./chunk-P2VIBGXA.js";
18
+ } from "./chunk-MPWXGONV.js";
19
19
  import {
20
20
  fetchTranscriptRecipients,
21
21
  uploadTranscriptRecords
22
- } from "./chunk-WPX67AKT.js";
22
+ } from "./chunk-SAXBC4AZ.js";
23
23
  import {
24
24
  transcriptEnvironmentPreference,
25
25
  transcriptsEnabled
26
- } from "./chunk-LPNJLKBQ.js";
26
+ } from "./chunk-OLQRRFBX.js";
27
27
  import {
28
28
  readTranscriptPreference
29
- } from "./chunk-D6OODZCM.js";
29
+ } from "./chunk-P7WQEVWA.js";
30
30
  import {
31
31
  codexHome,
32
32
  pusharyDir
33
- } from "./chunk-MZBRL5H5.js";
33
+ } from "./chunk-A2A2YTMG.js";
34
34
  import {
35
35
  writeJsonAtomic
36
36
  } from "./chunk-T7E75EKA.js";
37
37
  import {
38
38
  getApiKey,
39
39
  getBaseUrl
40
- } from "./chunk-R6OYCUNK.js";
40
+ } from "./chunk-GPEGOGRJ.js";
41
41
 
42
42
  // src/transcript-registration.ts
43
43
  import { createHash } from "crypto";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  registerTranscript
3
- } from "./chunk-OTNOANMX.js";
3
+ } from "./chunk-JZTHGAPA.js";
4
4
  import {
5
5
  writeDegraded
6
- } from "./chunk-N5MZH5A6.js";
6
+ } from "./chunk-CVQYGMSR.js";
7
7
 
8
8
  // src/tool-hook-input.ts
9
9
  var OPTIONAL_TEXT_FIELDS = ["hook_event_name", "session_id", "cwd", "transcript_path", "tool_use_id", "permission_mode"];
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  pusharyHookExecutable
3
- } from "./chunk-WTJM7PSL.js";
3
+ } from "./chunk-6J2JC2RT.js";
4
4
  import {
5
5
  isProtectedByScope,
6
6
  registerHookEntry
7
- } from "./chunk-MZBRL5H5.js";
7
+ } from "./chunk-A2A2YTMG.js";
8
8
 
9
9
  // src/skills-cli.ts
10
10
  import { execSync } from "child_process";
@@ -79,7 +79,7 @@ var describeService = (effects) => {
79
79
  return `write ${where} and register it with the OS${condition}`;
80
80
  };
81
81
  var describeSetupSideEffects = (effects) => [
82
- `run npm install -g @pushary/agent-hooks@${effects.version} (the hooks run from the global copy; up to two minutes)`,
82
+ `run npm install -g ${effects.globalPackage}@${effects.version} (the hooks run from the global copy; up to two minutes)`,
83
83
  `write ${effects.configFile} (the API key, mode 600)`,
84
84
  effects.rcPath ? `edit ${effects.rcPath} (export PUSHARY_API_KEY)` : "print the export line for your shell, since no rc file was found",
85
85
  describeService(effects),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-MZBRL5H5.js";
3
+ } from "./chunk-A2A2YTMG.js";
4
4
  import {
5
5
  writeFileAtomic
6
6
  } from "./chunk-T7E75EKA.js";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  keyCheckFromResponse
3
- } from "./chunk-EEEJ2JIL.js";
3
+ } from "./chunk-2ABTSGFT.js";
4
4
  import {
5
5
  installNetworkDefaults
6
- } from "./chunk-DUQO4C4I.js";
6
+ } from "./chunk-2OY3ZZ5N.js";
7
7
  import {
8
8
  getBaseUrl
9
- } from "./chunk-R6OYCUNK.js";
9
+ } from "./chunk-GPEGOGRJ.js";
10
10
 
11
11
  // src/retry.ts
12
12
  var isRetryable = (err) => {
@@ -14,17 +14,17 @@ import {
14
14
  } from "./chunk-U4TMAANR.js";
15
15
  import {
16
16
  callMcpTool
17
- } from "./chunk-VE7TXLR4.js";
17
+ } from "./chunk-M5ICYGHT.js";
18
18
  import {
19
19
  reportedInstallSource
20
- } from "./chunk-62CVUUNU.js";
20
+ } from "./chunk-DB3ONZB4.js";
21
21
  import {
22
22
  NPX_LATEST_COMMAND
23
- } from "./chunk-YYCTF4Q5.js";
23
+ } from "./chunk-E4FJTUH4.js";
24
24
  import {
25
25
  PAIR_TTL_SECONDS,
26
26
  isAnswerSource
27
- } from "./chunk-G4GLCLKK.js";
27
+ } from "./chunk-NQTHFF4W.js";
28
28
 
29
29
  // src/reach.ts
30
30
  var reachVerdict = (channels) => {
@@ -1,16 +1,28 @@
1
1
  import {
2
2
  MCP_BRIDGE_BINARY
3
- } from "./chunk-7OA34PNW.js";
3
+ } from "./chunk-SLFABHW3.js";
4
4
  import {
5
5
  openCodeConfigDirFrom
6
- } from "./chunk-KEL5MXRL.js";
6
+ } from "./chunk-AHV4LNB5.js";
7
7
  import {
8
+ KEY_FILE_MODE
9
+ } from "./chunk-P7WQEVWA.js";
10
+ import {
11
+ NpmMissingError,
8
12
  codexHomeFrom,
9
13
  resolvePathBinary
10
- } from "./chunk-MZBRL5H5.js";
14
+ } from "./chunk-A2A2YTMG.js";
15
+ import {
16
+ readJsonSafe,
17
+ writeFileAtomic,
18
+ writeJsonAtomic
19
+ } from "./chunk-T7E75EKA.js";
20
+ import {
21
+ NPX_LATEST_COMMAND
22
+ } from "./chunk-E4FJTUH4.js";
11
23
  import {
12
24
  HOOK_COMPATIBLE_PROFILES
13
- } from "./chunk-G4GLCLKK.js";
25
+ } from "./chunk-NQTHFF4W.js";
14
26
 
15
27
  // src/hermes-config.ts
16
28
  import { execFileSync } from "child_process";
@@ -680,6 +692,154 @@ var fxIgnoresInstructionsFile = (path, fxDir) => {
680
692
  }
681
693
  };
682
694
 
695
+ // src/setup/agent-failure.ts
696
+ var AgentConfigError = class extends Error {
697
+ kind;
698
+ filePath;
699
+ constructor(kind, filePath, message) {
700
+ super(message);
701
+ this.name = "AgentConfigError";
702
+ this.kind = kind;
703
+ this.filePath = filePath;
704
+ }
705
+ };
706
+ var configFailureKind = (readFailure) => readFailure === "unparseable" ? "config-unparseable" : "config-unreadable";
707
+ var AGENT_FAILURE_DETAILS = {
708
+ "not-wired": "reported success but its config is not there.",
709
+ "app-account-mismatch": "Mac app hooks kept, but their Pushary account could not be matched to this setup."
710
+ };
711
+ var describedAgentFailure = (agent, kind) => ({
712
+ agent,
713
+ kind,
714
+ detail: AGENT_FAILURE_DETAILS[kind],
715
+ remedy: "retry-agent"
716
+ });
717
+ var agentFailureFromError = (agent, error, describeError) => ({
718
+ agent,
719
+ kind: error instanceof AgentConfigError ? error.kind : "installer-error",
720
+ detail: describeError(error),
721
+ remedy: error instanceof NpmMissingError ? "install-node" : "retry-agent"
722
+ });
723
+ var retryAgentCommand = (agent) => `${NPX_LATEST_COMMAND} setup --agents ${agent}`;
724
+ var REMAINING_AGENTS_CONTINUE = "Other agents will continue. Re-run setup to retry.";
725
+ var afterAgentFailureNote = (agentsStillToSetUp) => agentsStillToSetUp > 0 ? REMAINING_AGENTS_CONTINUE : null;
726
+ var failedAgentsContext = (configuredAgentNames) => configuredAgentNames.length > 0 ? `(configured: ${configuredAgentNames.join(", ")})` : null;
727
+ var agentFailureWire = (failures) => failures.map(({ agent, kind }) => ({ agent, kind }));
728
+
729
+ // src/fx-lifecycle.ts
730
+ import { existsSync as existsSync3 } from "fs";
731
+ var fxRetryHint = () => `Fix it, then run ${retryAgentCommand("fx")}.`;
732
+ var readFxProfileForSetup = (filePath) => {
733
+ const read = readFxFile(filePath, readFxProfile);
734
+ if (read.kind === "profile") return read.profile;
735
+ if (read.kind === "missing") return {};
736
+ if (read.kind === "unreadable") {
737
+ throw new AgentConfigError(configFailureKind("unreadable"), filePath, `${filePath} could not be read, so it was left untouched. ${fxRetryHint()}`);
738
+ }
739
+ throw new AgentConfigError(
740
+ configFailureKind("unparseable"),
741
+ filePath,
742
+ `${filePath} ${FX_PROFILE_REFUSALS[read.reason]}, so it was left untouched. ${fxRetryHint()}`
743
+ );
744
+ };
745
+ var writeFxProfile = (filePath, profile) => {
746
+ const rendered = fxProfileText(profile);
747
+ if (rendered.kind === "refused") {
748
+ throw new Error(`${filePath} would grow past the size fx reads, so it was left untouched. ${fxRetryHint()}`);
749
+ }
750
+ writeFileAtomic(filePath, rendered.text, KEY_FILE_MODE);
751
+ };
752
+ var fxSettingsReadRefusal = (filePath, read) => {
753
+ if (read.kind === "unreadable") return `${filePath} could not be read`;
754
+ if (read.kind === "refused") return `${filePath} ${FX_SETTINGS_REFUSALS[read.reason]}`;
755
+ return null;
756
+ };
757
+ var recordFxQuestionRule = (receiptPath) => {
758
+ try {
759
+ if (!existsSync3(receiptPath)) writeJsonAtomic(receiptPath, FX_QUESTION_RULE_RECEIPT, 384);
760
+ return true;
761
+ } catch {
762
+ return false;
763
+ }
764
+ };
765
+ var configureFxSettings = (filePath, receiptPath, environment) => {
766
+ const read = readFxFile(filePath, readFxSettings);
767
+ const refusal = fxSettingsReadRefusal(filePath, read);
768
+ if (refusal) return { kind: "refused", detail: refusal };
769
+ const allowed = withPusharyToolsAllowed(read.kind === "settings" ? read.settings : {});
770
+ const routed = withQuestionsRoutedToPushary(allowed.settings);
771
+ const written = {
772
+ kind: "written",
773
+ toolsChange: allowed.change,
774
+ questionChange: routed.change,
775
+ questionRouting: fxQuestionRouting(routed.settings, environment),
776
+ workspacesWithOwnRules: [.../* @__PURE__ */ new Set([...workspacesOverridingPusharyRule(routed.settings), ...workspacesShowingQuestionTool(routed.settings)])],
777
+ ruleRecorded: true
778
+ };
779
+ if (allowed.change !== "added" && routed.change !== "added") return written;
780
+ const rendered = fxSettingsText(routed.settings);
781
+ if (rendered.kind === "refused") return { kind: "refused", detail: `${filePath} would grow past the 64 KiB fx reads` };
782
+ try {
783
+ writeFileAtomic(filePath, rendered.text, KEY_FILE_MODE);
784
+ } catch (err) {
785
+ return { kind: "refused", detail: `${filePath} could not be written (${err instanceof Error ? err.message : String(err)})` };
786
+ }
787
+ return { ...written, ruleRecorded: routed.change !== "added" || recordFxQuestionRule(receiptPath) };
788
+ };
789
+ var inspectFxInstallation = (profilePath, settingsPath, environment) => {
790
+ const profile = readFxFile(profilePath, readFxProfile);
791
+ const entry = profile.kind === "profile" ? fxPusharyEntryOf(profile.profile) : void 0;
792
+ const settingsRead = readFxFile(settingsPath, readFxSettings);
793
+ const settings = settingsRead.kind === "settings" ? settingsRead.settings : {};
794
+ const settingsProblem = settingsRead.kind === "missing" ? `${settingsPath} is missing, so fx runs with no Pushary rules; re-run ${retryAgentCommand("fx")}` : fxSettingsReadRefusal(settingsPath, settingsRead);
795
+ return {
796
+ profile,
797
+ configured: isFxPusharyEntry(entry),
798
+ bridge: fxBridgeTargetOf(entry),
799
+ settingsProblem,
800
+ toolsAllowed: pusharyToolsAllowed(settings),
801
+ questionRouting: fxQuestionRouting(settings, environment),
802
+ workspacesShowingQuestions: workspacesShowingQuestionTool(settings)
803
+ };
804
+ };
805
+ var removalWrite = (path, rendered, label) => rendered.kind === "refused" ? { kind: "kept", label, detail: `${path} would grow past the size fx reads; left untouched` } : { kind: "write", path, text: rendered.text, label };
806
+ var FX_QUESTION_RULE_KEPT = {
807
+ "not-ours": "no setup record says Pushary added it, so it may be yours; left as it is",
808
+ "changed-after-setup": "changed after setup; left as it is"
809
+ };
810
+ var planFxRemoval = (profilePath, settingsPath, receiptPath) => {
811
+ const actions = [];
812
+ const profile = readFxFile(profilePath, readFxProfile);
813
+ if (profile.kind === "profile") {
814
+ const result = withoutFxPusharyServer(profile.profile);
815
+ actions.push(result.removed ? removalWrite(profilePath, fxProfileText(result.profile), "fx MCP") : { kind: "kept", label: "fx MCP", detail: "no pushary entry" });
816
+ } else {
817
+ let detail = "not found";
818
+ if (profile.kind === "unreadable") detail = `${profilePath} could not be read; left untouched`;
819
+ if (profile.kind === "refused") detail = `${profilePath} ${FX_PROFILE_REFUSALS[profile.reason]}; left untouched`;
820
+ actions.push({ kind: "kept", label: "fx MCP", detail });
821
+ }
822
+ const read = readFxFile(settingsPath, readFxSettings);
823
+ const problem = fxSettingsReadRefusal(settingsPath, read);
824
+ if (problem) return [...actions, { kind: "kept", label: "fx settings", detail: `${problem}; left untouched` }];
825
+ if (read.kind === "settings") {
826
+ const rawReceipt = readJsonSafe(receiptPath);
827
+ const receipt = rawReceipt.kind === "ok" && isFxQuestionRuleReceipt(rawReceipt.value) ? rawReceipt.value : null;
828
+ const tools = withoutPusharyToolsAllowed(read.settings);
829
+ const question = withoutQuestionsRoutedToPushary(tools.settings, receipt);
830
+ if (tools.removed || question.outcome === "removed") {
831
+ const write = removalWrite(settingsPath, fxSettingsText(question.settings), "fx permission rules");
832
+ actions.push(write);
833
+ if (write.kind === "kept") return actions;
834
+ }
835
+ if (question.outcome === "not-ours" || question.outcome === "changed-after-setup") {
836
+ actions.push({ kind: "kept", label: "fx question rule", detail: FX_QUESTION_RULE_KEPT[question.outcome] });
837
+ }
838
+ }
839
+ if (existsSync3(receiptPath)) actions.push({ kind: "remove-receipt", path: receiptPath });
840
+ return actions;
841
+ };
842
+
683
843
  // src/vscode-config.ts
684
844
  import { homedir as homedir2 } from "os";
685
845
  import { join as join2 } from "path";
@@ -828,7 +988,7 @@ var vscodeSettingsTargets = (exists, home, platform) => {
828
988
 
829
989
  // src/setup/detect.ts
830
990
  import { execSync } from "child_process";
831
- import { existsSync as existsSync3 } from "fs";
991
+ import { existsSync as existsSync4 } from "fs";
832
992
  import { homedir as homedir3 } from "os";
833
993
  import { dirname, join as join3 } from "path";
834
994
  var whichCommand = () => process.platform === "win32" ? "where" : "which";
@@ -840,7 +1000,7 @@ var binaryOnPath = (binary) => {
840
1000
  return false;
841
1001
  }
842
1002
  };
843
- var defaultDeps = { onPath: binaryOnPath, exists: existsSync3 };
1003
+ var defaultDeps = { onPath: binaryOnPath, exists: existsSync4 };
844
1004
  var detectAgent = (probe, deps = defaultDeps) => {
845
1005
  const checked = [];
846
1006
  if (probe.binary) {
@@ -943,7 +1103,7 @@ var describeDetection = (result, home) => {
943
1103
  };
944
1104
 
945
1105
  // src/instruction-file.ts
946
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, rmSync as rmSync2 } from "fs";
1106
+ import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, rmSync as rmSync2 } from "fs";
947
1107
  import { dirname as dirname2 } from "path";
948
1108
  var PUSHARY_BEGIN = "<!-- BEGIN PUSHARY (managed by @pushary/agent-hooks; do not edit this block) -->";
949
1109
  var PUSHARY_END = "<!-- END PUSHARY -->";
@@ -963,6 +1123,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
963
1123
  - Respect the user's Pushary delivery mode: it decides where an approval is surfaced (phone, terminal, or awareness only), not whether it is required. In Terminal mode do not expect a phone answer; the approval waits in the terminal. If the Pushary hook already gated an action, a single approval is enough, do not ask again.
964
1124
 
965
1125
  Pass \`agentName\` as "${label} - <project folder name>" so the user knows which session is asking. If a question times out, follow \`handoffAction\` when present, otherwise \`nextAction\`: poll once, then cancel a live phone question before asking in the current client. If cancellation returns \`handoffAction: "stop"\`, stop. Otherwise, if cancellation returns false, poll once for 1 second and honor the answer that won the race.`;
1126
+ var SHARED_AGENTS_MD_LABEL = "<your agent's name, such as Codex or Hermes>";
966
1127
  var renderAgentInstructions = (label) => instructionBody(label);
967
1128
  var KEYLESS_FALLBACK_LINE = "This repo uses Pushary phone approvals. Your teammates get agent questions on their lock screens; you do not have a key yet, so questions will fall back to the terminal. Get your own key (3-day trial): https://pushary.com/sign-up?utm_source=agents-md&utm_medium=teammate";
968
1129
  var renderProjectAgentInstructions = (label) => `${instructionBody(label)}
@@ -973,7 +1134,7 @@ ${body}
973
1134
  ${PUSHARY_END}`;
974
1135
  var writeInstructionBlock = (filePath, body) => {
975
1136
  const dir = dirname2(filePath);
976
- if (!existsSync4(dir)) mkdirSync(dir, { recursive: true });
1137
+ if (!existsSync5(dir)) mkdirSync(dir, { recursive: true });
977
1138
  let existing = "";
978
1139
  try {
979
1140
  existing = readFileSync3(filePath, "utf-8");
@@ -1028,30 +1189,23 @@ export {
1028
1189
  FX_PUSHARY_TOOLS_RULE,
1029
1190
  FX_TOOL_DISCOVERY_NOTE,
1030
1191
  FX_PROFILE_REFUSALS,
1031
- FX_SETTINGS_REFUSALS,
1032
- readFxProfile,
1033
1192
  fxPusharyEntry,
1034
1193
  withFxPusharyServer,
1035
- withoutFxPusharyServer,
1036
- isFxPusharyEntry,
1037
- fxPusharyEntryOf,
1038
- fxBridgeTargetOf,
1039
- fxProfileText,
1040
- fxSettingsText,
1041
- readFxSettings,
1042
- withPusharyToolsAllowed,
1043
- withoutPusharyToolsAllowed,
1044
- pusharyToolsAllowed,
1045
- workspacesOverridingPusharyRule,
1046
1194
  FX_QUESTION_TOOL,
1047
- FX_QUESTION_RULE_RECEIPT,
1048
- isFxQuestionRuleReceipt,
1049
- fxQuestionRouting,
1050
- withQuestionsRoutedToPushary,
1051
- withoutQuestionsRoutedToPushary,
1052
- workspacesShowingQuestionTool,
1053
- readFxFile,
1054
1195
  fxIgnoresInstructionsFile,
1196
+ AgentConfigError,
1197
+ configFailureKind,
1198
+ describedAgentFailure,
1199
+ agentFailureFromError,
1200
+ retryAgentCommand,
1201
+ afterAgentFailureNote,
1202
+ failedAgentsContext,
1203
+ agentFailureWire,
1204
+ readFxProfileForSetup,
1205
+ writeFxProfile,
1206
+ configureFxSettings,
1207
+ inspectFxInstallation,
1208
+ planFxRemoval,
1055
1209
  registerPluginLocation,
1056
1210
  unregisterPluginLocation,
1057
1211
  isPluginRegistered,
@@ -1063,6 +1217,7 @@ export {
1063
1217
  isDetected,
1064
1218
  shortenHome,
1065
1219
  describeDetection,
1220
+ SHARED_AGENTS_MD_LABEL,
1066
1221
  renderAgentInstructions,
1067
1222
  renderProjectAgentInstructions,
1068
1223
  writeInstructionBlock,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  redactSecretsFromContent
3
- } from "./chunk-G4GLCLKK.js";
3
+ } from "./chunk-NQTHFF4W.js";
4
4
 
5
5
  // src/crypto.ts
6
6
  import nacl from "tweetnacl";
@@ -1,17 +1,23 @@
1
1
  import {
2
2
  clientErrorKindOf,
3
3
  reportClientError
4
- } from "./chunk-N5MZH5A6.js";
4
+ } from "./chunk-CVQYGMSR.js";
5
5
  import {
6
6
  SubscriptionRequiredError,
7
7
  callMcpTool
8
- } from "./chunk-VE7TXLR4.js";
8
+ } from "./chunk-M5ICYGHT.js";
9
9
  import {
10
10
  installNetworkDefaults
11
- } from "./chunk-DUQO4C4I.js";
11
+ } from "./chunk-2OY3ZZ5N.js";
12
12
  import {
13
13
  getBaseUrl
14
- } from "./chunk-R6OYCUNK.js";
14
+ } from "./chunk-GPEGOGRJ.js";
15
+ import {
16
+ ASK_AGENT_NAME_MAX,
17
+ ASK_QUESTION_MAX,
18
+ DECISION_LINE_MAX,
19
+ truncateLine
20
+ } from "./chunk-NQTHFF4W.js";
15
21
 
16
22
  // src/api.ts
17
23
  import { randomUUID } from "crypto";
@@ -59,9 +65,18 @@ var unavailableQuestionUnlessSubscriptionRequired = (error) => {
59
65
  if (error instanceof SubscriptionRequiredError) throw error;
60
66
  return { correlationId: "", status: "unavailable" };
61
67
  };
68
+ var truncateOptional = (text, max) => text === void 0 ? void 0 : truncateLine(text, max);
69
+ var withinAskUserLimits = (params) => ({
70
+ ...params,
71
+ question: truncateLine(params.question, ASK_QUESTION_MAX),
72
+ agentName: truncateOptional(params.agentName, ASK_AGENT_NAME_MAX),
73
+ intent: truncateOptional(params.intent, DECISION_LINE_MAX),
74
+ action: truncateOptional(params.action, DECISION_LINE_MAX),
75
+ blocker: truncateOptional(params.blocker, DECISION_LINE_MAX)
76
+ });
62
77
  var askUser = async (apiKey, params, timeoutMs = 15e3) => {
63
78
  const identified = params.toolUseId || params.requestId ? params : { ...params, requestId: randomUUID() };
64
- const result = await callMcpTool(apiKey, "ask_user", { ...identified, wait: false }, { maxRetries: 3, timeoutMs });
79
+ const result = await callMcpTool(apiKey, "ask_user", { ...withinAskUserLimits(identified), wait: false }, { maxRetries: 3, timeoutMs });
65
80
  if (!isAskUserResponse(result)) throw new Error("Invalid ask_user response");
66
81
  return result;
67
82
  };