@pushary/agent-hooks 1.9.62 → 1.9.64

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 (130) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +2 -2
  3. package/dist/{api-7HLZRIUV.js → api-HDLTZTVR.js} +10 -11
  4. package/dist/bin/pushary-bell-hook.js +27 -30
  5. package/dist/bin/pushary-bell.js +10 -11
  6. package/dist/bin/pushary-claude.js +29 -32
  7. package/dist/bin/pushary-clean.js +63 -42
  8. package/dist/bin/pushary-codex-bridge.js +22 -24
  9. package/dist/bin/pushary-codex-hook.js +33 -36
  10. package/dist/bin/pushary-codex-session.js +8 -8
  11. package/dist/bin/pushary-codex.js +13 -15
  12. package/dist/bin/pushary-connect.js +26 -29
  13. package/dist/bin/pushary-cowork.js +5 -5
  14. package/dist/bin/pushary-daemon.js +39 -41
  15. package/dist/bin/pushary-disconnect.js +60 -40
  16. package/dist/bin/pushary-doctor.js +89 -46
  17. package/dist/bin/pushary-elicitation-hook.js +28 -31
  18. package/dist/bin/pushary-gemini-bridge.js +16 -18
  19. package/dist/bin/pushary-gemini-hook.js +30 -33
  20. package/dist/bin/pushary-hook.js +31 -34
  21. package/dist/bin/pushary-login.js +27 -16
  22. package/dist/bin/pushary-logout.js +26 -29
  23. package/dist/bin/pushary-mcp.js +11 -12
  24. package/dist/bin/pushary-mode.js +14 -15
  25. package/dist/bin/pushary-notification-hook.js +26 -29
  26. package/dist/bin/pushary-opencode-hook.js +29 -32
  27. package/dist/bin/pushary-permission-denied-hook.js +31 -34
  28. package/dist/bin/pushary-permission-hook.js +30 -33
  29. package/dist/bin/pushary-post-hook.js +26 -29
  30. package/dist/bin/pushary-prompt-hook.js +26 -29
  31. package/dist/bin/pushary-session-end-hook.js +28 -31
  32. package/dist/bin/pushary-session-start-hook.js +35 -37
  33. package/dist/bin/pushary-setup.js +361 -4412
  34. package/dist/bin/pushary-stats.js +5 -5
  35. package/dist/bin/pushary-status.js +32 -35
  36. package/dist/bin/pushary-stop-hook.js +26 -29
  37. package/dist/bin/pushary-stopfailure-hook.js +26 -29
  38. package/dist/bin/pushary-suggestions.js +14 -15
  39. package/dist/bin/pushary-transcript-register.js +18 -21
  40. package/dist/bin/pushary-transcripts.js +63 -11
  41. package/dist/bin/pushary-upgrade.js +41 -43
  42. package/dist/bin/pushary-wait.js +14 -15
  43. package/dist/bin/pushary.js +7 -8
  44. package/dist/{chunk-SJCPIYWJ.js → chunk-2CEVEMJE.js} +3 -3
  45. package/dist/{chunk-3YGUWPNZ.js → chunk-2EY4HGLG.js} +3 -3
  46. package/dist/{chunk-ESXGCZDA.js → chunk-2KID4FK4.js} +4 -4
  47. package/dist/{chunk-EO6CL4PD.js → chunk-2PKHGHMA.js} +2 -2
  48. package/dist/{chunk-PSCXEH42.js → chunk-2VADJ6JH.js} +2 -2
  49. package/dist/{chunk-GV43RXR2.js → chunk-2YXBCZ3Z.js} +1 -1
  50. package/dist/{chunk-7GU4PXE5.js → chunk-2Z7I7DHT.js} +1 -1
  51. package/dist/{chunk-4K6YZPZ3.js → chunk-3KXBRSL3.js} +1 -1
  52. package/dist/{chunk-YQK72JGV.js → chunk-42PD2MQH.js} +5 -5
  53. package/dist/{chunk-EUNSJKJJ.js → chunk-57PU5OBL.js} +21 -14
  54. package/dist/{chunk-5R23J52J.js → chunk-5NP3OJ5P.js} +7 -9
  55. package/dist/{chunk-UWCPY4RY.js → chunk-5TNGBDIE.js} +2 -2
  56. package/dist/{chunk-ZEDGKCCM.js → chunk-5TUDGSDC.js} +1 -1
  57. package/dist/{chunk-M7MEVILL.js → chunk-66FRMK7V.js} +4 -4
  58. package/dist/{chunk-3LBB7GVF.js → chunk-6HPLEGUP.js} +4 -4
  59. package/dist/{chunk-LCNN7UOE.js → chunk-6QPDBD7Q.js} +3 -57
  60. package/dist/{chunk-AFV3FQNG.js → chunk-7UBDBJTG.js} +4 -4
  61. package/dist/{chunk-RFCEVROB.js → chunk-A5KQXZR7.js} +3 -3
  62. package/dist/{chunk-G3ZDCYMU.js → chunk-ADRPW7KU.js} +2 -2
  63. package/dist/{chunk-OZRT64BB.js → chunk-AN7C3CHH.js} +1 -1
  64. package/dist/{chunk-6JAWNRDR.js → chunk-AQAPPSDV.js} +3 -3
  65. package/dist/{chunk-FG5HMJ7C.js → chunk-BAGBCSXA.js} +4 -2
  66. package/dist/{chunk-W3CBFLIX.js → chunk-BLAIETMP.js} +18 -4
  67. package/dist/{chunk-CFIQ3252.js → chunk-BSZIPIFA.js} +128 -26
  68. package/dist/{chunk-7EHZXZB2.js → chunk-CCNH4PJ2.js} +12 -12
  69. package/dist/{chunk-NZLQ6GMX.js → chunk-CJADXAJU.js} +9 -9
  70. package/dist/{chunk-TMHE5CVX.js → chunk-DCMRNIDM.js} +4 -4
  71. package/dist/{chunk-QW3FRUFB.js → chunk-DVYVGBPB.js} +1 -1
  72. package/dist/{chunk-RSNN2TPE.js → chunk-E23RKH3T.js} +9 -9
  73. package/dist/{chunk-ZJFJUAHS.js → chunk-E2LB6JNV.js} +6 -6
  74. package/dist/{chunk-HSRNWCXJ.js → chunk-EEM65CGJ.js} +3 -3
  75. package/dist/chunk-H4BKC2GN.js +1201 -0
  76. package/dist/{chunk-7NXB7OVX.js → chunk-HVOQUPSO.js} +1 -1
  77. package/dist/{chunk-R3CLBNST.js → chunk-IZTDX2MX.js} +3 -3
  78. package/dist/{chunk-RU37NSZH.js → chunk-JTOBQUIL.js} +3 -3
  79. package/dist/{chunk-W6MYFAOK.js → chunk-JX3DHGZX.js} +44 -32
  80. package/dist/{chunk-7NJ4TDUH.js → chunk-KA7JIMTG.js} +6 -6
  81. package/dist/chunk-KEPESIIR.js +4117 -0
  82. package/dist/{chunk-EA6574W2.js → chunk-KX3RMF6T.js} +12 -6
  83. package/dist/{chunk-BIUSODLS.js → chunk-LA7CKRZN.js} +2 -2
  84. package/dist/{chunk-A4CZPUQ6.js → chunk-LMC5UPL7.js} +1 -1
  85. package/dist/{chunk-EVZDYG7L.js → chunk-LV6DX4P6.js} +2 -2
  86. package/dist/{chunk-LWR5VKNC.js → chunk-MIPJ2VXP.js} +12 -14
  87. package/dist/{chunk-557ZE5EX.js → chunk-ML2Q6WWE.js} +37 -214
  88. package/dist/{chunk-FXECTHAG.js → chunk-MNKIR2ZB.js} +37 -4
  89. package/dist/{chunk-LILS32LF.js → chunk-MPPTIZ2G.js} +212 -254
  90. package/dist/{chunk-VUFHYMSP.js → chunk-MXPOLQZH.js} +1 -1
  91. package/dist/{chunk-MYEALNW6.js → chunk-NOH3EJFH.js} +1 -1
  92. package/dist/{chunk-GFAG72LR.js → chunk-NUEPJ5W6.js} +7 -3
  93. package/dist/chunk-NYV45Q25.js +111 -0
  94. package/dist/{chunk-NAZRRXZM.js → chunk-O7PCNYEY.js} +2 -2
  95. package/dist/{chunk-7E2ZN7XA.js → chunk-OQKSF5ST.js} +3 -5
  96. package/dist/{chunk-VF3IL56Q.js → chunk-PLMX3IHG.js} +3 -3
  97. package/dist/{chunk-F35YILEN.js → chunk-PMLQRB3D.js} +1 -1
  98. package/dist/{chunk-SJJYXMJP.js → chunk-Q6ID23SU.js} +6 -6
  99. package/dist/{chunk-SDOWT67G.js → chunk-QECRQWLV.js} +5 -5
  100. package/dist/{chunk-Q4WTIKM6.js → chunk-QNATBOUH.js} +3 -3
  101. package/dist/{chunk-QIPW7OVB.js → chunk-QPPBH5NY.js} +5 -5
  102. package/dist/{chunk-7ETELRQX.js → chunk-QSW3HAKI.js} +16 -5
  103. package/dist/{chunk-FVERRWDP.js → chunk-RY4EPMZX.js} +2 -2
  104. package/dist/{chunk-MURJ4WAL.js → chunk-SROWUG5W.js} +1 -1
  105. package/dist/{chunk-UUAVYIWJ.js → chunk-TJUJAK5L.js} +4 -2
  106. package/dist/{chunk-3IFCXMC6.js → chunk-TR3GP5MH.js} +1 -1
  107. package/dist/{chunk-FX7KDF47.js → chunk-VHKK3US3.js} +3 -3
  108. package/dist/{chunk-6HIYOY6I.js → chunk-VNRWQCGU.js} +6 -6
  109. package/dist/{chunk-67Q3Q5IO.js → chunk-XF3AWNMO.js} +6 -6
  110. package/dist/{chunk-44FNEAXK.js → chunk-XJIIU36L.js} +1 -1
  111. package/dist/{chunk-DFKEJARZ.js → chunk-Y64JGYTB.js} +1 -1
  112. package/dist/{chunk-FYS27SUS.js → chunk-YUDZCJYC.js} +10 -10
  113. package/dist/{codex-app-cards-switch-IIL7IPP6.js → codex-app-cards-switch-JQ6356VX.js} +11 -12
  114. package/dist/{codex-desktop-launch-XCLMYVBD.js → codex-desktop-launch-YUB2LOAG.js} +1 -1
  115. package/dist/{codex-session-launch-UMVALJ3A.js → codex-session-launch-UKMAORXS.js} +19 -21
  116. package/dist/{config-5N3GUJD5.js → config-YY54G3CC.js} +3 -3
  117. package/dist/{global-upgrade-5F4CSXED.js → global-upgrade-Y4USGPS2.js} +10 -12
  118. package/dist/{identity-7ULYW4RL.js → identity-W5CYL5RC.js} +4 -5
  119. package/dist/{live-capabilities-QTSFXA6T.js → live-capabilities-JRNNA5EP.js} +5 -7
  120. package/dist/{paths-HEMK2ALW.js → paths-4MEBINV3.js} +7 -3
  121. package/dist/reapply-ZN7C3CB3.js +63 -0
  122. package/dist/src/index.js +20 -23
  123. package/dist/{upgrade-lock-IAZC5644.js → upgrade-lock-QAWUV2DO.js} +4 -5
  124. package/dist/{usage-NEFDYSRT.js → usage-43C2DHDU.js} +3 -3
  125. package/package.json +1 -1
  126. package/dist/chunk-6ICPEJY5.js +0 -20
  127. package/dist/chunk-6YSEOXP5.js +0 -615
  128. package/dist/chunk-LL2KHEJH.js +0 -41
  129. package/dist/chunk-QENTGE75.js +0 -86
  130. package/dist/reapply-AMFKVYZV.js +0 -63
@@ -1,154 +1,41 @@
1
+ import {
2
+ AgentConfigError,
3
+ AgentConfigWriteError,
4
+ configFailureKind,
5
+ retryAgentCommand,
6
+ writeSetupConfigFile
7
+ } from "./chunk-NYV45Q25.js";
1
8
  import {
2
9
  MCP_BRIDGE_BINARY
3
- } from "./chunk-EO6CL4PD.js";
10
+ } from "./chunk-2PKHGHMA.js";
4
11
  import {
5
12
  KEY_FILE_MODE
6
- } from "./chunk-W3CBFLIX.js";
13
+ } from "./chunk-BLAIETMP.js";
7
14
  import {
8
- NpmInstallError,
9
- NpmMissingError,
10
- NpmPermissionError
11
- } from "./chunk-CFIQ3252.js";
15
+ CLINE_SERVER_NAME_TAKEN,
16
+ CLINE_SETTINGS_REFUSALS,
17
+ clineEntryAuthorization,
18
+ clineEntryHoldsLiteralKey,
19
+ clineEntryRunsCommand,
20
+ clineEntrySignedIn,
21
+ clineEntrySwitchedOff,
22
+ clineEntryUsesKey,
23
+ clinePusharyEntryOf,
24
+ clineServerNameTakenElsewhere,
25
+ clineSettingsText,
26
+ isClinePusharyEntry,
27
+ otherPusharyServerNames,
28
+ readClineSettings,
29
+ renderAgentInstructions,
30
+ withClinePusharyServer,
31
+ withRefreshedClineKey,
32
+ withoutClinePusharyServer
33
+ } from "./chunk-H4BKC2GN.js";
12
34
  import {
13
35
  readJsonSafe,
14
36
  writeFileAtomic,
15
37
  writeJsonAtomic
16
- } from "./chunk-QENTGE75.js";
17
- import {
18
- NPX_LATEST_COMMAND
19
- } from "./chunk-FG5HMJ7C.js";
20
- import {
21
- readSetupAgentFailures
22
- } from "./chunk-FXECTHAG.js";
23
-
24
- // src/instruction-file.ts
25
- import { accessSync, constants, existsSync, readFileSync, statSync, writeFileSync, mkdirSync, rmSync } from "fs";
26
- import { dirname } from "path";
27
- var PUSHARY_BEGIN = "<!-- BEGIN PUSHARY (managed by @pushary/agent-hooks; do not edit this block) -->";
28
- var PUSHARY_END = "<!-- END PUSHARY -->";
29
- var instructionBody = (label) => `## Pushary notifications and approvals
30
-
31
- You have Pushary MCP tools that reach the user on their phone. Use them proactively. Do not wait for the user to ask.
32
-
33
- - 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.
34
- - 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.
35
- - 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.
36
- - 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.
37
- - 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.
38
- - 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.
39
- - 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.
40
- - 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.
41
- - Use \`cancel_question\` to retract a pending question once it is no longer needed.
42
- - When the user asks to be reminded later, call \`schedule_reminder\` with \`body\` and either \`inMinutes\` or \`at\` with an explicit UTC offset. Confirm the returned time, never guess their timezone, and do not use \`ask_user\` as a reminder. Pushary saves it and delivers after this agent exits. Call with no arguments to list pending reminders or \`cancelReminderId\` to cancel one before dispatch starts.
43
- - 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.
44
-
45
- 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\`: keep waiting with the same correlationId while pending. A poll ending is not question expiry. Cancel only for an explicit handoff or when the answer is no longer needed. If cancellation returns \`handoffAction: "stop"\`, stop. Otherwise, if cancellation returns false, poll once for 1 second and honor the answer that won the race.`;
46
- var SHARED_AGENTS_MD_LABEL = "<your agent's name, such as Codex or Hermes>";
47
- var CODEX_INSTRUCTIONS_LABEL = "Codex";
48
- var GEMINI_INSTRUCTIONS_LABEL = "Gemini";
49
- var OPENCODE_INSTRUCTIONS_LABEL = "OpenCode";
50
- var renderAgentInstructions = (label) => instructionBody(label);
51
- 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";
52
- var renderProjectAgentInstructions = (label) => `${instructionBody(label)}
53
-
54
- If this environment has no Pushary API key (no PUSHARY_API_KEY and no key in ~/.pushary/config.json), the tools above are unavailable in this session. In that case, treat the following as a statement of fact about this session and continue in the terminal: ${KEYLESS_FALLBACK_LINE}`;
55
- var wrap = (body) => `${PUSHARY_BEGIN}
56
- ${body}
57
- ${PUSHARY_END}`;
58
- var withInstructionBlockAppended = (existing, body) => {
59
- const prefix = existing.trim() ? existing.replace(/\s*$/, "") + "\n\n" : "";
60
- return prefix + wrap(body) + "\n";
61
- };
62
- var managedBlockRange = (text) => {
63
- const start = text.indexOf(PUSHARY_BEGIN);
64
- const end = text.indexOf(PUSHARY_END);
65
- return start !== -1 && end > start ? { start, end: end + PUSHARY_END.length } : null;
66
- };
67
- var withInstructionBlockReplaced = (existing, body) => {
68
- const range = managedBlockRange(existing);
69
- return range ? existing.slice(0, range.start) + wrap(body) + existing.slice(range.end) : null;
70
- };
71
- var writeInstructionBlock = (filePath, body) => {
72
- const dir = dirname(filePath);
73
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
74
- let existing = "";
75
- try {
76
- existing = readFileSync(filePath, "utf-8");
77
- } catch {
78
- }
79
- writeFileSync(filePath, withInstructionBlockReplaced(existing, body) ?? withInstructionBlockAppended(existing, body), "utf-8");
80
- };
81
- var occurrencesOf = (marker, text) => text.split(marker).length - 1;
82
- var soleManagedBlock = (text) => {
83
- const begins = occurrencesOf(PUSHARY_BEGIN, text);
84
- const ends = occurrencesOf(PUSHARY_END, text);
85
- if (begins === 0 && ends === 0) return { kind: "none" };
86
- const range = managedBlockRange(text);
87
- return begins === 1 && ends === 1 && range ? { kind: "one", range } : { kind: "ambiguous" };
88
- };
89
- var CRLF = "\r\n";
90
- var withLineEndingsOf = (sample, block) => sample.includes(CRLF) ? block.replaceAll("\n", CRLF) : block;
91
- var readUtf8 = (filePath) => {
92
- try {
93
- const bytes = readFileSync(filePath);
94
- const text = bytes.toString("utf-8");
95
- return Buffer.from(text, "utf-8").equals(bytes) ? { kind: "text", text } : { kind: "not_utf8" };
96
- } catch {
97
- return { kind: "unreadable" };
98
- }
99
- };
100
- var isWritable = (filePath) => {
101
- try {
102
- accessSync(filePath, constants.W_OK);
103
- return true;
104
- } catch {
105
- return false;
106
- }
107
- };
108
- var refreshInstructionBlock = (filePath, body) => {
109
- if (!existsSync(filePath)) return "absent";
110
- const read = readUtf8(filePath);
111
- if (read.kind !== "text") return read.kind;
112
- const block = soleManagedBlock(read.text);
113
- if (block.kind === "none") return "absent";
114
- if (block.kind === "ambiguous") return "ambiguous_markers";
115
- const existing = read.text.slice(block.range.start, block.range.end);
116
- const fresh = withLineEndingsOf(existing, wrap(body));
117
- if (existing === fresh) return "current";
118
- if (!isWritable(filePath)) return "read_only";
119
- try {
120
- const refreshed = read.text.slice(0, block.range.start) + fresh + read.text.slice(block.range.end);
121
- writeFileAtomic(filePath, refreshed, statSync(filePath).mode & 511);
122
- return "refreshed";
123
- } catch {
124
- return "write_failed";
125
- }
126
- };
127
- var removeInstructionBlock = (filePath) => {
128
- let existing = "";
129
- try {
130
- existing = readFileSync(filePath, "utf-8");
131
- } catch {
132
- return false;
133
- }
134
- const start = existing.indexOf(PUSHARY_BEGIN);
135
- const end = existing.indexOf(PUSHARY_END);
136
- if (start === -1 || end === -1 || end < start) return false;
137
- const remaining = (existing.slice(0, start) + existing.slice(end + PUSHARY_END.length)).trim();
138
- if (remaining === "") {
139
- rmSync(filePath, { force: true });
140
- } else {
141
- writeFileSync(filePath, remaining + "\n", "utf-8");
142
- }
143
- return true;
144
- };
145
- var hasInstructionBlock = (filePath) => {
146
- try {
147
- return readFileSync(filePath, "utf-8").includes(PUSHARY_BEGIN);
148
- } catch {
149
- return false;
150
- }
151
- };
38
+ } from "./chunk-BSZIPIFA.js";
152
39
 
153
40
  // src/fx-config.ts
154
41
  var FX_SERVER_NAME = "pushary";
@@ -491,13 +378,13 @@ var workspaceShowsQuestionTool = (workspace) => {
491
378
  var workspacesShowingQuestionTool = (settings) => Object.entries(asRecord(settings[WORKSPACES_KEY]) ?? {}).filter(([, workspace]) => workspaceShowsQuestionTool(workspace)).map(([path]) => path);
492
379
 
493
380
  // src/fx-files.ts
494
- import { existsSync as existsSync2, lstatSync, readFileSync as readFileSync2, realpathSync } from "fs";
381
+ import { existsSync, lstatSync, readFileSync, realpathSync } from "fs";
495
382
  import { relative, isAbsolute } from "path";
496
383
  var readFxFile = (path, read) => {
497
- if (!existsSync2(path)) return { kind: "missing" };
384
+ if (!existsSync(path)) return { kind: "missing" };
498
385
  let text;
499
386
  try {
500
- text = readFileSync2(path, "utf-8");
387
+ text = readFileSync(path, "utf-8");
501
388
  } catch {
502
389
  return { kind: "unreadable" };
503
390
  }
@@ -517,91 +404,8 @@ var fxIgnoresInstructionsFile = (path, fxDir) => {
517
404
  }
518
405
  };
519
406
 
520
- // src/setup/config-write.ts
521
- var AgentConfigWriteError = class extends Error {
522
- constructor(error) {
523
- super(error instanceof Error ? error.message : String(error), { cause: error });
524
- this.name = "AgentConfigWriteError";
525
- }
526
- };
527
- var writeSetupConfigFile = (path, content, mode = 384) => {
528
- try {
529
- writeFileAtomic(path, content, mode);
530
- } catch (error) {
531
- throw new AgentConfigWriteError(error);
532
- }
533
- };
534
- var writeSetupConfigJson = (path, value, mode) => writeSetupConfigFile(path, `${JSON.stringify(value, null, 2)}
535
- `, mode);
536
-
537
- // src/setup/agent-failure.ts
538
- var AgentConfigError = class extends Error {
539
- kind;
540
- filePath;
541
- constructor(kind, filePath, message) {
542
- super(message);
543
- this.name = "AgentConfigError";
544
- this.kind = kind;
545
- this.filePath = filePath;
546
- }
547
- };
548
- var configFailureKind = (readFailure) => readFailure === "unparseable" ? "config-unparseable" : "config-unreadable";
549
- var AGENT_FAILURE_DETAILS = {
550
- "not-wired": "reported success but its config is not there.",
551
- "app-account-mismatch": "Pushary for Mac set it up with an account that does not match this setup, so it was left as it is."
552
- };
553
- var DESCRIBED_FAILURE_REMEDIES = {
554
- "not-wired": "retry-agent",
555
- "app-account-mismatch": "match-mac-account"
556
- };
557
- var describedAgentFailure = (agent, kind) => ({
558
- agent,
559
- kind,
560
- detail: AGENT_FAILURE_DETAILS[kind],
561
- remedy: DESCRIBED_FAILURE_REMEDIES[kind],
562
- stage: kind === "not-wired" ? "hook-validation" : "account-check"
563
- });
564
- var NPM_PERMISSION_REMEDIES = {
565
- "global-prefix": "fix-npm-prefix",
566
- "file-in-use": "close-node-programs"
567
- };
568
- var remedyForError = (error) => {
569
- if (error instanceof NpmMissingError) return "install-node";
570
- if (error instanceof NpmPermissionError) return NPM_PERMISSION_REMEDIES[error.kind];
571
- return "retry-agent";
572
- };
573
- var setupFailureDiagnostic = (error) => {
574
- if (error instanceof AgentConfigError) return { stage: "config-read" };
575
- if (error instanceof AgentConfigWriteError) return { stage: "config-write", diagnosticCode: "config-write-failed" };
576
- if (error instanceof NpmMissingError) return { stage: "runtime-install", diagnosticCode: "npm-missing" };
577
- if (error instanceof NpmPermissionError) {
578
- return { stage: "runtime-install", diagnosticCode: error.kind === "global-prefix" ? "npm-global-prefix-denied" : "npm-file-in-use" };
579
- }
580
- if (error instanceof NpmInstallError) return { stage: "runtime-install", diagnosticCode: "npm-install-failed" };
581
- return { stage: "unknown" };
582
- };
583
- var agentFailureFromError = (agent, error, describeError) => ({
584
- agent,
585
- kind: error instanceof AgentConfigError ? error.kind : "installer-error",
586
- detail: describeError(error),
587
- remedy: remedyForError(error),
588
- ...setupFailureDiagnostic(error)
589
- });
590
- var retryAgentCommand = (agent) => `${NPX_LATEST_COMMAND} setup --agents ${agent}`;
591
- var takeOverHooksCommand = (agent) => `${retryAgentCommand(agent)} --take-over-hooks`;
592
- var matchMacAccountStep = (agent, setupAccount) => `Sign Pushary for Mac into ${setupAccount ?? "this setup's account"}, or run ${takeOverHooksCommand(agent)}`;
593
- var UNCHECKED_ACCOUNT = "an account that could not be checked";
594
- var describeMacAccountMismatch = (mismatch) => [
595
- `${mismatch.agentName}: Pushary for Mac uses ${mismatch.macAccount ?? UNCHECKED_ACCOUNT}. This setup uses ${mismatch.setupAccount ?? UNCHECKED_ACCOUNT}.`,
596
- matchMacAccountStep(mismatch.agent, mismatch.setupAccount)
597
- ];
598
- var REMAINING_AGENTS_CONTINUE = "Other agents will continue. Re-run setup to retry.";
599
- var afterAgentFailureNote = (agentsStillToSetUp) => agentsStillToSetUp > 0 ? REMAINING_AGENTS_CONTINUE : null;
600
- var failedAgentsContext = (configuredAgentNames) => configuredAgentNames.length > 0 ? `(configured: ${configuredAgentNames.join(", ")})` : null;
601
- var agentFailureWire = (failures) => readSetupAgentFailures(failures);
602
-
603
407
  // src/fx-lifecycle.ts
604
- import { existsSync as existsSync3 } from "fs";
408
+ import { existsSync as existsSync2 } from "fs";
605
409
  import { join } from "path";
606
410
  var fxRetryHint = () => `Fix it, then run ${retryAgentCommand("fx")}.`;
607
411
  var readFxProfileForSetup = (filePath) => {
@@ -638,7 +442,7 @@ var fxSettingsReadRefusal = (filePath, read) => {
638
442
  };
639
443
  var recordFxQuestionRule = (receiptPath) => {
640
444
  try {
641
- if (!existsSync3(receiptPath)) writeJsonAtomic(receiptPath, FX_QUESTION_RULE_RECEIPT, 384);
445
+ if (!existsSync2(receiptPath)) writeJsonAtomic(receiptPath, FX_QUESTION_RULE_RECEIPT, 384);
642
446
  return true;
643
447
  } catch {
644
448
  return false;
@@ -719,21 +523,182 @@ var planFxRemoval = (profilePath, settingsPath, receiptPath) => {
719
523
  actions.push({ kind: "kept", label: "fx question rule", detail: FX_QUESTION_RULE_KEPT[question.outcome] });
720
524
  }
721
525
  }
722
- if (existsSync3(receiptPath)) actions.push({ kind: "remove-receipt", path: receiptPath });
526
+ if (existsSync2(receiptPath)) actions.push({ kind: "remove-receipt", path: receiptPath });
723
527
  return actions;
724
528
  };
725
529
 
530
+ // src/cline-lifecycle.ts
531
+ import { randomUUID } from "crypto";
532
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, renameSync, rmdirSync, rmSync, statSync, writeFileSync } from "fs";
533
+ import { dirname, join as join2 } from "path";
534
+ var CLINE_SETTINGS_LOCK_STALE_MS = 1e4;
535
+ var CLINE_SETTINGS_LOCK_WAIT_MS = 1e4;
536
+ var CLINE_SETTINGS_LOCK_POLL_MS = 25;
537
+ var CLINE_SETTINGS_BUSY = "stayed locked by Cline";
538
+ var ClineSettingsBusyError = class extends AgentConfigWriteError {
539
+ constructor(settingsPath) {
540
+ super(`${settingsPath} ${CLINE_SETTINGS_BUSY}, so it was left untouched. Close Cline, then run ${retryAgentCommand("cline")}.`);
541
+ this.name = "ClineSettingsBusyError";
542
+ }
543
+ };
544
+ var readClineSettingsFile = (settingsPath) => {
545
+ if (!existsSync3(settingsPath)) return { kind: "missing" };
546
+ try {
547
+ return readClineSettings(readFileSync2(settingsPath, "utf-8"));
548
+ } catch {
549
+ return { kind: "unreadable" };
550
+ }
551
+ };
552
+ var pause = (milliseconds) => {
553
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
554
+ };
555
+ var lockDirectoryOf = (settingsPath) => `${settingsPath}.lock`;
556
+ var ownerFileOf = (directory, token) => join2(directory, `owner.${token}`);
557
+ var tryTakeLock = (lockDirectory, token) => {
558
+ const staging = `${lockDirectory}.tmp.${token}`;
559
+ mkdirSync(staging, { recursive: true });
560
+ writeFileSync(ownerFileOf(staging, token), token, { encoding: "utf-8", flag: "wx" });
561
+ try {
562
+ renameSync(staging, lockDirectory);
563
+ return true;
564
+ } catch (error) {
565
+ rmSync(staging, { recursive: true, force: true });
566
+ if (existsSync3(lockDirectory)) return false;
567
+ throw error;
568
+ }
569
+ };
570
+ var lockIsStale = (lockDirectory, now) => {
571
+ try {
572
+ return now - statSync(lockDirectory).mtimeMs > CLINE_SETTINGS_LOCK_STALE_MS;
573
+ } catch {
574
+ return false;
575
+ }
576
+ };
577
+ var reclaimStaleLock = (lockDirectory, token) => {
578
+ const setAside = `${lockDirectory}.stale.${token}`;
579
+ try {
580
+ renameSync(lockDirectory, setAside);
581
+ } catch {
582
+ return false;
583
+ }
584
+ rmSync(setAside, { recursive: true, force: true });
585
+ return true;
586
+ };
587
+ var releaseLock = (lockDirectory, token) => {
588
+ try {
589
+ rmSync(ownerFileOf(lockDirectory, token), { force: true });
590
+ rmdirSync(lockDirectory);
591
+ } catch {
592
+ return;
593
+ }
594
+ };
595
+ var withClineSettingsLock = (settingsPath, run) => {
596
+ const lockDirectory = lockDirectoryOf(settingsPath);
597
+ const token = `${process.pid}.${Date.now()}.${randomUUID()}`;
598
+ mkdirSync(dirname(lockDirectory), { recursive: true });
599
+ const deadline = Date.now() + CLINE_SETTINGS_LOCK_WAIT_MS;
600
+ while (!tryTakeLock(lockDirectory, token)) {
601
+ if (lockIsStale(lockDirectory, Date.now()) && reclaimStaleLock(lockDirectory, token)) continue;
602
+ if (Date.now() >= deadline) throw new ClineSettingsBusyError(settingsPath);
603
+ pause(CLINE_SETTINGS_LOCK_POLL_MS);
604
+ }
605
+ try {
606
+ return run();
607
+ } finally {
608
+ releaseLock(lockDirectory, token);
609
+ }
610
+ };
611
+ var writeClineSettingsText = (settingsPath, text) => {
612
+ writeSetupConfigFile(settingsPath, text, KEY_FILE_MODE);
613
+ };
614
+ var writeClineSettings = (settingsPath, settings) => {
615
+ writeClineSettingsText(settingsPath, clineSettingsText(settings));
616
+ };
617
+ var retryHint = () => `Fix it, then run ${retryAgentCommand("cline")}.`;
618
+ var settingsForSetup = (settingsPath, read) => {
619
+ if (read.kind === "settings") return read.settings;
620
+ if (read.kind === "missing") return {};
621
+ if (read.kind === "unreadable") {
622
+ throw new AgentConfigError(configFailureKind("unreadable"), settingsPath, `${settingsPath} could not be read, so it was left untouched. ${retryHint()}`);
623
+ }
624
+ throw new AgentConfigError(
625
+ configFailureKind("unparseable"),
626
+ settingsPath,
627
+ `${settingsPath} ${CLINE_SETTINGS_REFUSALS[read.reason]}, so it was left untouched. ${retryHint()}`
628
+ );
629
+ };
630
+ var ClineServerNameTakenError = class extends AgentConfigError {
631
+ constructor(settingsPath) {
632
+ super(
633
+ "config-conflict",
634
+ settingsPath,
635
+ `${settingsPath} ${CLINE_SERVER_NAME_TAKEN}, so it was left untouched. Rename that server in Cline, then run ${retryAgentCommand("cline")}.`
636
+ );
637
+ this.name = "ClineServerNameTakenError";
638
+ }
639
+ };
640
+ var unclaimedSettings = (settingsPath, settings) => {
641
+ if (clineServerNameTakenElsewhere(settings)) throw new ClineServerNameTakenError(settingsPath);
642
+ return settings;
643
+ };
644
+ var configureClineServer = (settingsPath, apiKey) => withClineSettingsLock(settingsPath, () => {
645
+ const current = unclaimedSettings(settingsPath, settingsForSetup(settingsPath, readClineSettingsFile(settingsPath)));
646
+ const settings = withClinePusharyServer(current, apiKey);
647
+ writeClineSettings(settingsPath, settings);
648
+ return { otherServers: otherPusharyServerNames(settings) };
649
+ });
650
+ var carriesPusharyKey = (entry) => clineEntryAuthorization(entry) !== void 0;
651
+ var refreshClineKey = (settingsPath, apiKey) => !existsSync3(settingsPath) ? "not-configured" : withClineSettingsLock(settingsPath, () => {
652
+ const read = readClineSettingsFile(settingsPath);
653
+ if (read.kind !== "settings") return "not-configured";
654
+ const entry = clinePusharyEntryOf(read.settings);
655
+ if (!isClinePusharyEntry(entry)) return "not-configured";
656
+ if (!apiKey || clineEntryRunsCommand(entry) || !clineEntryHoldsLiteralKey(entry) || clineEntryUsesKey(entry, apiKey)) return "unchanged";
657
+ writeClineSettings(settingsPath, withRefreshedClineKey(read.settings, apiKey));
658
+ return "refreshed";
659
+ });
660
+ var inspectClineInstallation = (settingsPath, apiKey) => {
661
+ const read = readClineSettingsFile(settingsPath);
662
+ const settings = read.kind === "settings" ? read.settings : {};
663
+ const entry = clinePusharyEntryOf(settings);
664
+ return {
665
+ read,
666
+ configured: isClinePusharyEntry(entry),
667
+ hasKey: carriesPusharyKey(entry),
668
+ holdsPusharyKey: clineEntryHoldsLiteralKey(entry),
669
+ runsCommand: clineEntryRunsCommand(entry),
670
+ signedIn: clineEntrySignedIn(entry),
671
+ switchedOff: clineEntrySwitchedOff(entry),
672
+ usesCurrentKey: apiKey !== void 0 && clineEntryUsesKey(entry, apiKey),
673
+ nameTakenElsewhere: clineServerNameTakenElsewhere(settings),
674
+ otherServers: otherPusharyServerNames(settings)
675
+ };
676
+ };
677
+ var planClineRemoval = (settingsPath) => {
678
+ const read = readClineSettingsFile(settingsPath);
679
+ if (read.kind === "missing") return { kind: "absent" };
680
+ if (read.kind === "unreadable") return { kind: "kept", detail: `${settingsPath} could not be read; left untouched` };
681
+ if (read.kind === "refused" && !read.mentionsPushary) return { kind: "absent" };
682
+ if (read.kind === "refused") return { kind: "kept", detail: `${settingsPath} ${CLINE_SETTINGS_REFUSALS[read.reason]}; left untouched` };
683
+ const result = withoutClinePusharyServer(read.settings);
684
+ return result.removed ? { kind: "write", path: settingsPath, text: clineSettingsText(result.settings) } : { kind: "absent" };
685
+ };
686
+ var failedRemovalDetail = (settingsPath, error) => error instanceof ClineSettingsBusyError ? `${settingsPath} ${CLINE_SETTINGS_BUSY}; close Cline and try again` : `${settingsPath} could not be updated: ${error instanceof Error ? error.message : String(error)}`;
687
+ var removeFromLockedSettings = (settingsPath) => withClineSettingsLock(settingsPath, () => {
688
+ const plan = planClineRemoval(settingsPath);
689
+ if (plan.kind === "write") writeClineSettingsText(plan.path, plan.text);
690
+ return plan;
691
+ });
692
+ var removeClineServer = (settingsPath) => {
693
+ if (!existsSync3(settingsPath)) return { kind: "absent" };
694
+ try {
695
+ return removeFromLockedSettings(settingsPath);
696
+ } catch (error) {
697
+ return { kind: "failed", detail: failedRemovalDetail(settingsPath, error) };
698
+ }
699
+ };
700
+
726
701
  export {
727
- SHARED_AGENTS_MD_LABEL,
728
- CODEX_INSTRUCTIONS_LABEL,
729
- GEMINI_INSTRUCTIONS_LABEL,
730
- OPENCODE_INSTRUCTIONS_LABEL,
731
- renderAgentInstructions,
732
- renderProjectAgentInstructions,
733
- writeInstructionBlock,
734
- refreshInstructionBlock,
735
- removeInstructionBlock,
736
- hasInstructionBlock,
737
702
  FX_PUSHARY_TOOLS_RULE,
738
703
  fxInstructionsBody,
739
704
  FX_PROFILE_REFUSALS,
@@ -741,23 +706,16 @@ export {
741
706
  withFxPusharyServer,
742
707
  FX_QUESTION_TOOL,
743
708
  fxIgnoresInstructionsFile,
744
- writeSetupConfigFile,
745
- writeSetupConfigJson,
746
- AgentConfigError,
747
- configFailureKind,
748
- describedAgentFailure,
749
- agentFailureFromError,
750
- retryAgentCommand,
751
- matchMacAccountStep,
752
- describeMacAccountMismatch,
753
- afterAgentFailureNote,
754
- failedAgentsContext,
755
- agentFailureWire,
756
709
  readFxProfileForSetup,
757
710
  writeFxProfile,
758
711
  macAppFxBridge,
759
712
  fxServerHolder,
760
713
  configureFxSettings,
761
714
  inspectFxInstallation,
762
- planFxRemoval
715
+ planFxRemoval,
716
+ configureClineServer,
717
+ refreshClineKey,
718
+ inspectClineInstallation,
719
+ planClineRemoval,
720
+ removeClineServer
763
721
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isFile
3
- } from "./chunk-QENTGE75.js";
3
+ } from "./chunk-BSZIPIFA.js";
4
4
 
5
5
  // src/claude-native-install.ts
6
6
  import { accessSync, constants } from "fs";
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-3EGEA4KH.js";
10
10
  import {
11
11
  getBaseUrl
12
- } from "./chunk-DFKEJARZ.js";
12
+ } from "./chunk-Y64JGYTB.js";
13
13
 
14
14
  // src/cli/login-ui.ts
15
15
  import { hostname } from "os";
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  buildTranscriptRecords,
3
3
  decodePublicKey,
4
+ encryptRecord,
4
5
  generateSessionKey,
6
+ recordSealerFor,
5
7
  wrapSessionKey
6
- } from "./chunk-7ETELRQX.js";
8
+ } from "./chunk-QSW3HAKI.js";
7
9
  import {
8
10
  fetchTranscriptRecipients,
9
11
  uploadTranscriptRecords
10
- } from "./chunk-UUAVYIWJ.js";
12
+ } from "./chunk-TJUJAK5L.js";
11
13
 
12
14
  // src/live-session.ts
13
15
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -68,6 +70,7 @@ var LiveTranscriptRelay = class {
68
70
  wrappedKey = null;
69
71
  providerWrappedKey = null;
70
72
  providerKeyId = null;
73
+ sealRecord = encryptRecord;
71
74
  keyUploaded = false;
72
75
  keyRejected = false;
73
76
  flushing = null;
@@ -98,13 +101,14 @@ var LiveTranscriptRelay = class {
98
101
  if (!recipients) return false;
99
102
  this.sessionKey = generateSessionKey();
100
103
  this.wrappedKey = wrapSessionKey(this.sessionKey, decodePublicKey(recipients.phonePublicKey));
104
+ this.sealRecord = recordSealerFor(recipients.phoneReadsCompressedRecords);
101
105
  if (recipients.provider) {
102
106
  this.providerWrappedKey = wrapSessionKey(this.sessionKey, decodePublicKey(recipients.provider.publicKey));
103
107
  this.providerKeyId = recipients.provider.keyId;
104
108
  }
105
109
  }
106
110
  const count = Math.min(this.queue.length, 50);
107
- const records = buildTranscriptRecords(this.queue.slice(0, count), this.sessionKey).records;
111
+ const records = buildTranscriptRecords(this.queue.slice(0, count), this.sessionKey, /* @__PURE__ */ new Set(), this.sealRecord).records;
108
112
  if (records.length === 0) {
109
113
  this.queue.splice(0, count);
110
114
  return true;