pushary 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 (186) hide show
  1. package/CHANGELOG.md +1943 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/data/SKILL.md +591 -0
  5. package/data/cowork/SKILL.md +77 -0
  6. package/data/cursor-plugin/.cursor-plugin/plugin.json +25 -0
  7. package/data/cursor-plugin/CHANGELOG.md +70 -0
  8. package/data/cursor-plugin/CONTRIBUTING.md +40 -0
  9. package/data/cursor-plugin/LICENSE +21 -0
  10. package/data/cursor-plugin/README.md +113 -0
  11. package/data/cursor-plugin/SECURITY.md +33 -0
  12. package/data/cursor-plugin/assets/logo.png +0 -0
  13. package/data/cursor-plugin/commands/notify-when-done.md +14 -0
  14. package/data/cursor-plugin/commands/pushary-test.md +13 -0
  15. package/data/cursor-plugin/hooks/hooks.json +75 -0
  16. package/data/cursor-plugin/mcp.json +11 -0
  17. package/data/cursor-plugin/rules/pushary.mdc +40 -0
  18. package/data/cursor-plugin/scripts/pushary-gate.mjs +724 -0
  19. package/data/cursor-plugin/scripts/pushary-gate.test.mjs +137 -0
  20. package/data/cursor-plugin/scripts/redaction.mjs +51 -0
  21. package/data/cursor-plugin/skills/pushary/SKILL.md +584 -0
  22. package/data/vscode-plugin/.claude-plugin/plugin.json +28 -0
  23. package/data/vscode-plugin/.mcp.json +11 -0
  24. package/data/vscode-plugin/CHANGELOG.md +49 -0
  25. package/data/vscode-plugin/CONTRIBUTING.md +40 -0
  26. package/data/vscode-plugin/LICENSE +21 -0
  27. package/data/vscode-plugin/README.md +128 -0
  28. package/data/vscode-plugin/SECURITY.md +56 -0
  29. package/data/vscode-plugin/assets/logo.png +0 -0
  30. package/data/vscode-plugin/commands/notify-when-done.md +14 -0
  31. package/data/vscode-plugin/commands/pushary-test.md +13 -0
  32. package/data/vscode-plugin/hooks/hooks.json +60 -0
  33. package/data/vscode-plugin/scripts/pushary-gate.mjs +762 -0
  34. package/data/vscode-plugin/scripts/redaction.mjs +51 -0
  35. package/data/vscode-plugin/skills/pushary/SKILL.md +584 -0
  36. package/dist/bin/pushary-bell-hook.d.ts +1 -0
  37. package/dist/bin/pushary-bell-hook.js +46 -0
  38. package/dist/bin/pushary-bell.d.ts +1 -0
  39. package/dist/bin/pushary-bell.js +146 -0
  40. package/dist/bin/pushary-claude.d.ts +1 -0
  41. package/dist/bin/pushary-claude.js +1916 -0
  42. package/dist/bin/pushary-clean.d.ts +1 -0
  43. package/dist/bin/pushary-clean.js +1300 -0
  44. package/dist/bin/pushary-codex-bridge.d.ts +1 -0
  45. package/dist/bin/pushary-codex-bridge.js +599 -0
  46. package/dist/bin/pushary-codex-hook.d.ts +1 -0
  47. package/dist/bin/pushary-codex-hook.js +886 -0
  48. package/dist/bin/pushary-codex.d.ts +1 -0
  49. package/dist/bin/pushary-codex.js +125 -0
  50. package/dist/bin/pushary-connect.d.ts +1 -0
  51. package/dist/bin/pushary-connect.js +100 -0
  52. package/dist/bin/pushary-cowork.d.ts +1 -0
  53. package/dist/bin/pushary-cowork.js +36 -0
  54. package/dist/bin/pushary-daemon-supervisor.d.ts +1 -0
  55. package/dist/bin/pushary-daemon-supervisor.js +27 -0
  56. package/dist/bin/pushary-daemon.d.ts +1 -0
  57. package/dist/bin/pushary-daemon.js +878 -0
  58. package/dist/bin/pushary-disconnect.d.ts +1 -0
  59. package/dist/bin/pushary-disconnect.js +264 -0
  60. package/dist/bin/pushary-doctor.d.ts +1 -0
  61. package/dist/bin/pushary-doctor.js +1433 -0
  62. package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
  63. package/dist/bin/pushary-elicitation-hook.js +149 -0
  64. package/dist/bin/pushary-gemini-bridge.d.ts +1 -0
  65. package/dist/bin/pushary-gemini-bridge.js +339 -0
  66. package/dist/bin/pushary-gemini-hook.d.ts +1 -0
  67. package/dist/bin/pushary-gemini-hook.js +381 -0
  68. package/dist/bin/pushary-hook.d.ts +1 -0
  69. package/dist/bin/pushary-hook.js +95 -0
  70. package/dist/bin/pushary-login.d.ts +1 -0
  71. package/dist/bin/pushary-login.js +198 -0
  72. package/dist/bin/pushary-logout.d.ts +1 -0
  73. package/dist/bin/pushary-logout.js +147 -0
  74. package/dist/bin/pushary-mcp.d.ts +1 -0
  75. package/dist/bin/pushary-mcp.js +114 -0
  76. package/dist/bin/pushary-mode.d.ts +1 -0
  77. package/dist/bin/pushary-mode.js +130 -0
  78. package/dist/bin/pushary-notification-hook.d.ts +1 -0
  79. package/dist/bin/pushary-notification-hook.js +56 -0
  80. package/dist/bin/pushary-opencode-hook.d.ts +1 -0
  81. package/dist/bin/pushary-opencode-hook.js +338 -0
  82. package/dist/bin/pushary-permission-denied-hook.d.ts +1 -0
  83. package/dist/bin/pushary-permission-denied-hook.js +60 -0
  84. package/dist/bin/pushary-permission-hook.d.ts +1 -0
  85. package/dist/bin/pushary-permission-hook.js +65 -0
  86. package/dist/bin/pushary-post-hook.d.ts +1 -0
  87. package/dist/bin/pushary-post-hook.js +70 -0
  88. package/dist/bin/pushary-prompt-hook.d.ts +1 -0
  89. package/dist/bin/pushary-prompt-hook.js +61 -0
  90. package/dist/bin/pushary-session-end-hook.d.ts +1 -0
  91. package/dist/bin/pushary-session-end-hook.js +59 -0
  92. package/dist/bin/pushary-session-start-hook.d.ts +1 -0
  93. package/dist/bin/pushary-session-start-hook.js +72 -0
  94. package/dist/bin/pushary-setup.d.ts +1 -0
  95. package/dist/bin/pushary-setup.js +2660 -0
  96. package/dist/bin/pushary-stats.d.ts +1 -0
  97. package/dist/bin/pushary-stats.js +44 -0
  98. package/dist/bin/pushary-status.d.ts +1 -0
  99. package/dist/bin/pushary-status.js +262 -0
  100. package/dist/bin/pushary-stop-hook.d.ts +1 -0
  101. package/dist/bin/pushary-stop-hook.js +63 -0
  102. package/dist/bin/pushary-stopfailure-hook.d.ts +1 -0
  103. package/dist/bin/pushary-stopfailure-hook.js +56 -0
  104. package/dist/bin/pushary-suggestions.d.ts +1 -0
  105. package/dist/bin/pushary-suggestions.js +100 -0
  106. package/dist/bin/pushary-transcript-register.d.ts +1 -0
  107. package/dist/bin/pushary-transcript-register.js +40 -0
  108. package/dist/bin/pushary-transcripts.d.ts +1 -0
  109. package/dist/bin/pushary-transcripts.js +77 -0
  110. package/dist/bin/pushary-upgrade.d.ts +1 -0
  111. package/dist/bin/pushary-upgrade.js +599 -0
  112. package/dist/bin/pushary-wait.d.ts +1 -0
  113. package/dist/bin/pushary-wait.js +122 -0
  114. package/dist/bin/pushary.d.ts +1 -0
  115. package/dist/bin/pushary.js +74 -0
  116. package/dist/chunk-2ABTSGFT.js +173 -0
  117. package/dist/chunk-2OY3ZZ5N.js +124 -0
  118. package/dist/chunk-3EGEA4KH.js +44 -0
  119. package/dist/chunk-3EVPNIBE.js +185 -0
  120. package/dist/chunk-5OP5MQI7.js +45 -0
  121. package/dist/chunk-6J2JC2RT.js +149 -0
  122. package/dist/chunk-7QLSKOSU.js +19 -0
  123. package/dist/chunk-7Z7Q4GGS.js +88 -0
  124. package/dist/chunk-A2A2YTMG.js +835 -0
  125. package/dist/chunk-AHV4LNB5.js +377 -0
  126. package/dist/chunk-AXPCESYO.js +197 -0
  127. package/dist/chunk-C2WCPBF7.js +86 -0
  128. package/dist/chunk-CJVVKOSR.js +73 -0
  129. package/dist/chunk-CULUZTWQ.js +2117 -0
  130. package/dist/chunk-CVQYGMSR.js +142 -0
  131. package/dist/chunk-CVWG2N3Y.js +16 -0
  132. package/dist/chunk-D2FX3EPW.js +15 -0
  133. package/dist/chunk-DB3ONZB4.js +44 -0
  134. package/dist/chunk-DE4Y6TCC.js +160 -0
  135. package/dist/chunk-E4FJTUH4.js +249 -0
  136. package/dist/chunk-EPCXUP3P.js +41 -0
  137. package/dist/chunk-EX3GAPA4.js +44 -0
  138. package/dist/chunk-EYK3GSRH.js +143 -0
  139. package/dist/chunk-GMXKITVA.js +70 -0
  140. package/dist/chunk-GPEGOGRJ.js +49 -0
  141. package/dist/chunk-GU3EJCVV.js +43 -0
  142. package/dist/chunk-HQ6G3B5R.js +59 -0
  143. package/dist/chunk-HZVVNGEW.js +22 -0
  144. package/dist/chunk-IPVKH2ST.js +231 -0
  145. package/dist/chunk-J5TIV3J6.js +238 -0
  146. package/dist/chunk-JU6W3XAU.js +64 -0
  147. package/dist/chunk-JYG4B33G.js +227 -0
  148. package/dist/chunk-JZTHGAPA.js +503 -0
  149. package/dist/chunk-KGDWDSNL.js +102 -0
  150. package/dist/chunk-KVQBWI5T.js +99 -0
  151. package/dist/chunk-KZERVKTD.js +16 -0
  152. package/dist/chunk-LXQ5FBBD.js +47 -0
  153. package/dist/chunk-M5ICYGHT.js +138 -0
  154. package/dist/chunk-M5JDZ45W.js +943 -0
  155. package/dist/chunk-MDEPU45E.js +1226 -0
  156. package/dist/chunk-MPWXGONV.js +157 -0
  157. package/dist/chunk-N6IOSZOE.js +185 -0
  158. package/dist/chunk-NGDD6TXY.js +192 -0
  159. package/dist/chunk-NQTHFF4W.js +2830 -0
  160. package/dist/chunk-OKXS7WDZ.js +68 -0
  161. package/dist/chunk-OLQRRFBX.js +21 -0
  162. package/dist/chunk-P7WQEVWA.js +307 -0
  163. package/dist/chunk-POFBE6ZY.js +12 -0
  164. package/dist/chunk-PQ4K74WL.js +30 -0
  165. package/dist/chunk-PUZZELE6.js +37 -0
  166. package/dist/chunk-Q7E4I6UA.js +767 -0
  167. package/dist/chunk-QCGNGEGE.js +15 -0
  168. package/dist/chunk-R7437YI3.js +151 -0
  169. package/dist/chunk-RHR6NARM.js +305 -0
  170. package/dist/chunk-SAXBC4AZ.js +60 -0
  171. package/dist/chunk-SGZJ5LYH.js +76 -0
  172. package/dist/chunk-SIFVFP6T.js +70 -0
  173. package/dist/chunk-SLFABHW3.js +150 -0
  174. package/dist/chunk-T7E75EKA.js +67 -0
  175. package/dist/chunk-TY4JT7W3.js +38 -0
  176. package/dist/chunk-U4TMAANR.js +111 -0
  177. package/dist/chunk-UI36QSBI.js +177 -0
  178. package/dist/chunk-VQKRG3AC.js +339 -0
  179. package/dist/chunk-WE2J62NC.js +1131 -0
  180. package/dist/chunk-XHKBHWLX.js +14 -0
  181. package/dist/chunk-ZSZSSJCV.js +275 -0
  182. package/dist/chunk-ZVSKMPBZ.js +135 -0
  183. package/dist/reapply-CW3MA66V.js +34 -0
  184. package/dist/src/index.d.ts +204 -0
  185. package/dist/src/index.js +64 -0
  186. package/package.json +108 -0
@@ -0,0 +1,150 @@
1
+ import {
2
+ parseSseMessages
3
+ } from "./chunk-M5ICYGHT.js";
4
+ import {
5
+ NPX_LATEST_COMMAND
6
+ } from "./chunk-E4FJTUH4.js";
7
+
8
+ // src/mcp-stdio-bridge.ts
9
+ var MCP_BRIDGE_BINARY = "pushary-mcp";
10
+ var MCP_BRIDGE_PATH = "/api/mcp/mcp";
11
+ var MCP_BRIDGE_REQUEST_TIMEOUT_MS = 9e4;
12
+ var JSON_RPC_PARSE_ERROR = -32700;
13
+ var MCP_BRIDGE_ERROR = -32e3;
14
+ var MCP_MAX_RESPONSE_BYTES = 1024 * 1024;
15
+ var MCP_SESSION_HEADER = "mcp-session-id";
16
+ var MCP_PROTOCOL_HEADER = "mcp-protocol-version";
17
+ var INITIALIZE_METHOD = "initialize";
18
+ var MISSING_KEY_MESSAGE = `Pushary has no API key for this machine. Run \`${NPX_LATEST_COMMAND} setup\`.`;
19
+ var REJECTED_KEY_MESSAGE = `Pushary rejected this machine's API key. Run \`${NPX_LATEST_COMMAND} setup\` to sign in again.`;
20
+ var PARSE_ERROR_MESSAGE = "Parse error";
21
+ var NO_RESPONSE_MESSAGE = "Pushary sent no response.";
22
+ var unreachableMessage = (err) => `Pushary is unreachable: ${err instanceof Error ? err.message : String(err)}`;
23
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
24
+ var isJsonRpcId = (value) => typeof value === "string" || typeof value === "number";
25
+ var messagesOf = (payload) => Array.isArray(payload) ? payload : [payload];
26
+ var requestOf = (message) => {
27
+ const record = asRecord(message);
28
+ return record && typeof record.method === "string" && isJsonRpcId(record.id) ? [{ id: record.id, method: record.method }] : [];
29
+ };
30
+ var requestsIn = (payload) => messagesOf(payload).flatMap(requestOf);
31
+ var isJsonRpcMessage = (message) => asRecord(message)?.jsonrpc === "2.0";
32
+ var responseIdOf = (message) => {
33
+ const record = asRecord(message);
34
+ return record && !("method" in record) && isJsonRpcId(record.id) ? record.id : void 0;
35
+ };
36
+ var bridgeError = (id, message, code = MCP_BRIDGE_ERROR) => ({
37
+ jsonrpc: "2.0",
38
+ id,
39
+ error: { code, message }
40
+ });
41
+ var parseBridgeLine = (line) => {
42
+ try {
43
+ return { ok: true, payload: JSON.parse(line) };
44
+ } catch {
45
+ return { ok: false };
46
+ }
47
+ };
48
+ var parseMcpMessages = (body, contentType) => {
49
+ if (!body.trim()) return [];
50
+ if (contentType?.includes("text/event-stream")) return parseSseMessages(body);
51
+ const parsed = parseBridgeLine(body);
52
+ return parsed.ok ? messagesOf(parsed.payload) : [];
53
+ };
54
+ var httpFailureMessage = (status, hasKey, body) => {
55
+ if (status === 401) return hasKey ? REJECTED_KEY_MESSAGE : MISSING_KEY_MESSAGE;
56
+ const detail = body.trim().slice(0, 300);
57
+ return detail ? `Pushary returned ${status}: ${detail}` : `Pushary returned ${status}.`;
58
+ };
59
+ var negotiatedProtocolOf = (messages, initializeIds) => {
60
+ for (const message of messages) {
61
+ const id = responseIdOf(message);
62
+ const version = asRecord(asRecord(message)?.result)?.protocolVersion;
63
+ if (id !== void 0 && initializeIds.has(id) && typeof version === "string") return version;
64
+ }
65
+ return void 0;
66
+ };
67
+ var readBoundedResponse = async (response) => {
68
+ if (!response.body) return "";
69
+ const reader = response.body.getReader();
70
+ const chunks = [];
71
+ let size = 0;
72
+ try {
73
+ for (; ; ) {
74
+ const { done, value } = await reader.read();
75
+ if (done) break;
76
+ size += value.byteLength;
77
+ if (size > MCP_MAX_RESPONSE_BYTES) throw new Error("MCP response exceeds the 1 MiB limit.");
78
+ chunks.push(value);
79
+ }
80
+ return Buffer.concat(chunks).toString("utf8");
81
+ } finally {
82
+ await reader.cancel().catch(() => void 0);
83
+ reader.releaseLock();
84
+ }
85
+ };
86
+ var createMcpStdioBridge = (deps) => {
87
+ let sessionId = null;
88
+ let protocolVersion = null;
89
+ const headersFor = (apiKey) => ({
90
+ "Content-Type": "application/json",
91
+ "Accept": "application/json, text/event-stream",
92
+ ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
93
+ ...sessionId ? { [MCP_SESSION_HEADER]: sessionId } : {},
94
+ ...protocolVersion ? { [MCP_PROTOCOL_HEADER]: protocolVersion } : {}
95
+ });
96
+ const failRequests = async (requests, message, answered = /* @__PURE__ */ new Set()) => {
97
+ for (const request of requests) {
98
+ if (!answered.has(request.id)) await deps.emit(bridgeError(request.id, message));
99
+ }
100
+ };
101
+ const post = async (line, apiKey) => deps.fetchImpl(deps.endpoint, {
102
+ method: "POST",
103
+ headers: headersFor(apiKey),
104
+ body: line,
105
+ signal: AbortSignal.timeout(deps.timeoutMs ?? MCP_BRIDGE_REQUEST_TIMEOUT_MS)
106
+ });
107
+ const forward = async (line) => {
108
+ const parsed = parseBridgeLine(line);
109
+ if (!parsed.ok) {
110
+ await deps.emit(bridgeError(null, PARSE_ERROR_MESSAGE, JSON_RPC_PARSE_ERROR));
111
+ return;
112
+ }
113
+ const requests = requestsIn(parsed.payload);
114
+ const apiKey = deps.apiKey();
115
+ let response;
116
+ let body;
117
+ try {
118
+ response = await post(line, apiKey);
119
+ body = await readBoundedResponse(response);
120
+ } catch (err) {
121
+ await failRequests(requests, unreachableMessage(err));
122
+ return;
123
+ }
124
+ sessionId = response.headers.get(MCP_SESSION_HEADER) ?? sessionId;
125
+ const messages = parseMcpMessages(body, response.headers.get("content-type")).filter(isJsonRpcMessage);
126
+ const initializeIds = new Set(requests.filter((request) => request.method === INITIALIZE_METHOD).map((request) => request.id));
127
+ protocolVersion = negotiatedProtocolOf(messages, initializeIds) ?? protocolVersion;
128
+ for (const message of messages) await deps.emit(message);
129
+ const answered = new Set(messages.map(responseIdOf).filter(isJsonRpcId));
130
+ const failure = response.ok ? NO_RESPONSE_MESSAGE : httpFailureMessage(response.status, apiKey !== null, body);
131
+ await failRequests(requests, failure, answered);
132
+ };
133
+ const reject = async (line, message) => {
134
+ const parsed = parseBridgeLine(line);
135
+ if (!parsed.ok) {
136
+ await deps.emit(bridgeError(null, PARSE_ERROR_MESSAGE, JSON_RPC_PARSE_ERROR));
137
+ return;
138
+ }
139
+ await failRequests(requestsIn(parsed.payload), message);
140
+ };
141
+ return { forward, reject };
142
+ };
143
+
144
+ export {
145
+ MCP_BRIDGE_BINARY,
146
+ MCP_BRIDGE_PATH,
147
+ bridgeError,
148
+ parseBridgeLine,
149
+ createMcpStdioBridge
150
+ };
@@ -0,0 +1,67 @@
1
+ // src/cli/fsx.ts
2
+ import { copyFileSync, lstatSync, realpathSync, existsSync, fsyncSync, mkdirSync, openSync, closeSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
3
+ import { dirname, join } from "path";
4
+ var readJsonSafe = (filePath) => {
5
+ let raw;
6
+ try {
7
+ raw = readFileSync(filePath, "utf-8");
8
+ } catch (err) {
9
+ const error = err;
10
+ if (error.code === "ENOENT") return { kind: "missing" };
11
+ return { kind: "unreadable", error };
12
+ }
13
+ if (raw.trim() === "") return { kind: "ok", value: {} };
14
+ try {
15
+ return { kind: "ok", value: JSON.parse(raw) };
16
+ } catch (err) {
17
+ return { kind: "unparseable", error: err, raw };
18
+ }
19
+ };
20
+ var stamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
21
+ var backupFile = (filePath) => {
22
+ const target = `${filePath}.pushary-backup-${stamp()}`;
23
+ copyFileSync(filePath, target);
24
+ return target;
25
+ };
26
+ var writeFileAtomic = (filePath, contents, mode, encoding = "utf-8") => {
27
+ const target = lstatSync(filePath, { throwIfNoEntry: false })?.isSymbolicLink() ? realpathSync(filePath) : filePath;
28
+ const dir = dirname(target);
29
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
30
+ const temp = join(dir, `.${process.pid}-${stamp()}.pushary-tmp`);
31
+ try {
32
+ writeFileSync(temp, contents, mode === void 0 ? encoding : { encoding, mode });
33
+ const handle = openSync(temp, "r+");
34
+ try {
35
+ fsyncSync(handle);
36
+ } finally {
37
+ closeSync(handle);
38
+ }
39
+ renameSync(temp, target);
40
+ } catch (err) {
41
+ try {
42
+ if (existsSync(temp)) unlinkSync(temp);
43
+ } catch {
44
+ }
45
+ throw err;
46
+ }
47
+ };
48
+ var writeJsonAtomic = (filePath, data, mode) => writeFileAtomic(filePath, `${JSON.stringify(data, null, 2)}
49
+ `, mode);
50
+ var writeJsonAtomicDurable = (filePath, data, mode) => {
51
+ writeJsonAtomic(filePath, data, mode);
52
+ if (process.platform === "win32") return;
53
+ const directory = openSync(dirname(filePath), "r");
54
+ try {
55
+ fsyncSync(directory);
56
+ } finally {
57
+ closeSync(directory);
58
+ }
59
+ };
60
+
61
+ export {
62
+ readJsonSafe,
63
+ backupFile,
64
+ writeFileAtomic,
65
+ writeJsonAtomic,
66
+ writeJsonAtomicDurable
67
+ };
@@ -0,0 +1,38 @@
1
+ import {
2
+ NPX_LATEST_COMMAND
3
+ } from "./chunk-E4FJTUH4.js";
4
+
5
+ // src/setup/codex-trust.ts
6
+ var CODEX_TRUST_TELEMETRY = {
7
+ verified: "verified",
8
+ refused: "manual",
9
+ unlisted: "manual",
10
+ skipped: "unverified",
11
+ unavailable: "unverified"
12
+ };
13
+ var isUnanswered = (confirmation) => confirmation.kind === "skipped" || confirmation.kind === "unavailable";
14
+ var codexTrustOutcome = (confirmation, portedTrustWritten) => isUnanswered(confirmation) && !portedTrustWritten ? "manual" : CODEX_TRUST_TELEMETRY[confirmation.kind];
15
+ var CODEX_TRUST_REFUSED = "Codex did not accept the trust Pushary asked for, so its hooks are not trusted yet. Open Codex, run /hooks, and choose Trust for the Pushary hooks.";
16
+ var describeUnlistedCodexHooks = (warnings) => {
17
+ const said = warnings[0] ? ` Codex said: ${warnings[0]}` : "";
18
+ return `Codex does not list the Pushary hooks, so it will not run them.${said} Run ${NPX_LATEST_COMMAND} doctor for details.`;
19
+ };
20
+ var describeCodexTrustConfirmation = (confirmation) => {
21
+ switch (confirmation.kind) {
22
+ case "verified":
23
+ return confirmation.wrote ? "Codex trusted the Pushary hooks when asked, and confirmed it." : "Codex confirms the Pushary hooks are trusted.";
24
+ case "refused":
25
+ return confirmation.writeError ? `${CODEX_TRUST_REFUSED} Codex said: ${confirmation.writeError}` : CODEX_TRUST_REFUSED;
26
+ case "unlisted":
27
+ return describeUnlistedCodexHooks(confirmation.warnings);
28
+ case "skipped":
29
+ return `Pushary did not ask Codex to confirm the trust: ${confirmation.reason}.`;
30
+ case "unavailable":
31
+ return `Codex could not be asked to confirm the Pushary hooks are trusted (${confirmation.reason}).`;
32
+ }
33
+ };
34
+
35
+ export {
36
+ codexTrustOutcome,
37
+ describeCodexTrustConfirmation
38
+ };
@@ -0,0 +1,111 @@
1
+ // src/cli/terminal-row.ts
2
+ var visibleLength = (text) => text.replace(/\x1b\[[0-9;]*m/g, "").length;
3
+ var clipToRow = (text, columns) => {
4
+ const budget = columns - 4;
5
+ if (budget <= 1 || visibleLength(text) <= budget) return text;
6
+ let visible = 0;
7
+ let out = "";
8
+ for (let i = 0; i < text.length; i += 1) {
9
+ const escape = /^\x1b\[[0-9;]*m/.exec(text.slice(i));
10
+ if (escape) {
11
+ out += escape[0];
12
+ i += escape[0].length - 1;
13
+ continue;
14
+ }
15
+ if (visible >= budget - 1) break;
16
+ out += text[i];
17
+ visible += 1;
18
+ }
19
+ return `${out}\u2026`;
20
+ };
21
+ var terminalColumns = (stream = process.stdout) => {
22
+ const columns = stream.columns;
23
+ return typeof columns === "number" && columns > 0 ? columns : 80;
24
+ };
25
+
26
+ // src/cli/io.ts
27
+ var truthyLevel = (value) => {
28
+ if (value === void 0) return void 0;
29
+ if (value === "" || value === "false") return 0;
30
+ const parsed = Number(value);
31
+ return Number.isFinite(parsed) ? parsed : 1;
32
+ };
33
+ var ANSI_RESET = "\x1B[0m";
34
+ var ansiPainter = (enabled) => (open) => (text) => enabled() ? `${open}${text}${ANSI_RESET}` : text;
35
+ var useColor = (context = {}, stream = process.stdout) => {
36
+ const forced = truthyLevel(process.env.FORCE_COLOR);
37
+ if (forced !== void 0) return forced > 0;
38
+ if (context.color === false) return false;
39
+ if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") return false;
40
+ if (process.env.TERM === "dumb") return false;
41
+ if (context.json) return false;
42
+ return stream.isTTY === true;
43
+ };
44
+ var SPINNER_FRAMES = [" ", ". ", ".. ", "..."];
45
+ var SPINNER_FRAME_WIDTH = SPINNER_FRAMES[0].length + 1;
46
+ var SPINNER_INTERVAL_MS = 200;
47
+ var describeError = (err) => {
48
+ if (err instanceof Error) {
49
+ const signalled = err;
50
+ if (signalled.killed && signalled.signal === "SIGTERM") {
51
+ return "timed out, check your network connection";
52
+ }
53
+ return err.message;
54
+ }
55
+ return String(err);
56
+ };
57
+ var createIo = (stream = process.stdout, context = {}) => {
58
+ const color = useColor(context, stream);
59
+ const wrap = (open) => (s) => color ? `${open}${s}\x1B[0m` : s;
60
+ const bold = wrap("\x1B[1m");
61
+ const dim = wrap("\x1B[2m");
62
+ const green = wrap("\x1B[32m");
63
+ const yellow = wrap("\x1B[33m");
64
+ const red = wrap("\x1B[31m");
65
+ const cyan = wrap("\x1B[36m");
66
+ const check = () => green("\u2713");
67
+ const line = (s = "") => {
68
+ stream.write(`${s}
69
+ `);
70
+ };
71
+ const warn = (s) => {
72
+ process.stderr.write(`${yellow("!")} ${s}
73
+ `);
74
+ };
75
+ const spinner = async (label, fn, options = {}) => {
76
+ if (stream.isTTY !== true) {
77
+ try {
78
+ await fn();
79
+ line(` ${check()} ${label}`);
80
+ } catch (err) {
81
+ line(` ${yellow("!")} ${label} ${dim(`(${describeError(err)})`)}`);
82
+ if (!options.optional) throw err;
83
+ }
84
+ return;
85
+ }
86
+ let frame = 0;
87
+ const timer = setInterval(() => {
88
+ stream.write(`\r ${dim(SPINNER_FRAMES[frame++ % SPINNER_FRAMES.length])} ${clipToRow(label, terminalColumns(stream) - SPINNER_FRAME_WIDTH)}\x1B[K`);
89
+ }, SPINNER_INTERVAL_MS);
90
+ try {
91
+ await fn();
92
+ clearInterval(timer);
93
+ stream.write(`\r ${check()} ${label}\x1B[K
94
+ `);
95
+ } catch (err) {
96
+ clearInterval(timer);
97
+ stream.write(`\r ${yellow("!")} ${label} ${dim(`(${describeError(err)})`)}\x1B[K
98
+ `);
99
+ if (!options.optional) throw err;
100
+ }
101
+ };
102
+ return { color, bold, dim, green, yellow, red, cyan, check, line, warn, spinner };
103
+ };
104
+
105
+ export {
106
+ clipToRow,
107
+ terminalColumns,
108
+ ansiPainter,
109
+ useColor,
110
+ createIo
111
+ };
@@ -0,0 +1,177 @@
1
+ import {
2
+ isDisabledByEnv
3
+ } from "./chunk-POFBE6ZY.js";
4
+ import {
5
+ autoUpdateState
6
+ } from "./chunk-A2A2YTMG.js";
7
+ import {
8
+ readJsonSafe,
9
+ writeJsonAtomic
10
+ } from "./chunk-T7E75EKA.js";
11
+ import {
12
+ CLI_PACKAGE_NAMES,
13
+ POSIX_GLOBAL_ROOT_SEGMENTS,
14
+ cliPackagePathSegments,
15
+ globalUpgradeEntry,
16
+ ownPackageRoot
17
+ } from "./chunk-NQTHFF4W.js";
18
+
19
+ // src/auto-update.ts
20
+ import { spawn } from "child_process";
21
+ import { dirname } from "path";
22
+ var AUTO_UPDATE_DISABLE_ENV = "PUSHARY_DISABLE_AUTOUPDATE";
23
+ var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
24
+ var AUTO_UPDATE_FLAG = "--auto";
25
+ var NON_GLOBAL_INSTALL_SEGMENTS = ["/_npx/", "/.volta/"];
26
+ var AUTO_UPDATE_RESULTS = ["updated", "current", "needs_manual", "failed", "finish_pending"];
27
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28
+ var isAutoUpdateResult = (value) => typeof value === "string" && AUTO_UPDATE_RESULTS.some((result) => result === value);
29
+ var stringField = (record, key) => {
30
+ const value = record[key];
31
+ return typeof value === "string" ? value : void 0;
32
+ };
33
+ var booleanField = (record, key) => {
34
+ const value = record[key];
35
+ return typeof value === "boolean" ? value : void 0;
36
+ };
37
+ var parseAutoUpdateState = (value) => {
38
+ if (!isRecord(value)) return {};
39
+ return {
40
+ disabled: booleanField(value, "disabled"),
41
+ lastCheckAt: stringField(value, "lastCheckAt"),
42
+ lastResult: isAutoUpdateResult(value.lastResult) ? value.lastResult : void 0,
43
+ from: stringField(value, "from"),
44
+ to: stringField(value, "to"),
45
+ at: stringField(value, "at"),
46
+ detail: stringField(value, "detail"),
47
+ announced: booleanField(value, "announced")
48
+ };
49
+ };
50
+ var isAutoUpdateDisabledByEnv = (env) => isDisabledByEnv(env, AUTO_UPDATE_DISABLE_ENV);
51
+ var isContinuousIntegration = (env) => Boolean(env.CI);
52
+ var autoUpdateOffReason = (input) => {
53
+ if (isAutoUpdateDisabledByEnv(input.env)) return "disabled_by_env";
54
+ if (input.state.disabled === true) return "disabled_by_user";
55
+ if (isContinuousIntegration(input.env)) return "ci";
56
+ if (input.platform === "win32") return "windows";
57
+ if (input.platform === "linux") return "linux";
58
+ return null;
59
+ };
60
+ var globalPackageSubpath = (name) => `/${[...POSIX_GLOBAL_ROOT_SEGMENTS, ...cliPackagePathSegments(name)].join("/")}`;
61
+ var prefixOfGlobalPackage = (root, name) => {
62
+ const subpath = globalPackageSubpath(name);
63
+ if (!root.endsWith(subpath)) return null;
64
+ const prefix = root.slice(0, -subpath.length);
65
+ return prefix.startsWith("/") ? prefix : null;
66
+ };
67
+ var isNonGlobalInstall = (root) => NON_GLOBAL_INSTALL_SEGMENTS.some((segment) => root.includes(segment));
68
+ var globalInstallLocation = (packageRoot) => {
69
+ const root = packageRoot.replace(/\/+$/, "");
70
+ if (isNonGlobalInstall(root)) return null;
71
+ for (const packageName of CLI_PACKAGE_NAMES) {
72
+ const prefix = prefixOfGlobalPackage(root, packageName);
73
+ if (prefix !== null) return { prefix, packageName };
74
+ }
75
+ return null;
76
+ };
77
+ var upgradeEntryFor = (location, platform = process.platform) => globalUpgradeEntry(location.prefix, location.packageName, platform);
78
+ var checkedWithinInterval = (lastCheckAt, now) => {
79
+ const last = lastCheckAt === void 0 ? Number.NaN : Date.parse(lastCheckAt);
80
+ if (Number.isNaN(last)) return false;
81
+ const elapsed = now - last;
82
+ return elapsed >= 0 && elapsed < AUTO_UPDATE_INTERVAL_MS;
83
+ };
84
+ var autoUpdateDecision = (input) => {
85
+ if (input.isSubagentStart) return { kind: "skip", reason: "subagent_start" };
86
+ const offReason = autoUpdateOffReason(input);
87
+ if (offReason !== null) return { kind: "skip", reason: offReason };
88
+ const location = input.packageRoot === void 0 ? null : globalInstallLocation(input.packageRoot);
89
+ if (location === null) return { kind: "skip", reason: "not_global_install" };
90
+ if (checkedWithinInterval(input.state.lastCheckAt, input.now)) return { kind: "skip", reason: "checked_recently" };
91
+ return { kind: "check", ...location };
92
+ };
93
+ var parseAutoUpdateFlag = (args) => {
94
+ const token = args.find((arg) => arg === AUTO_UPDATE_FLAG || arg.startsWith(`${AUTO_UPDATE_FLAG}=`));
95
+ if (token === void 0) return { kind: "absent" };
96
+ if (token === AUTO_UPDATE_FLAG) return { kind: "run" };
97
+ const value = token.slice(AUTO_UPDATE_FLAG.length + 1);
98
+ if (value === "on") return { kind: "set", enabled: true };
99
+ if (value === "off") return { kind: "set", enabled: false };
100
+ return { kind: "invalid", value };
101
+ };
102
+ var AUTO_UPDATE_RESULT_LABELS = {
103
+ updated: "updated",
104
+ finish_pending: "installed; configuration refresh pending",
105
+ current: "up to date",
106
+ needs_manual: "needs a manual upgrade",
107
+ failed: "failed"
108
+ };
109
+ var versionsOf = (state) => state.from && state.to ? `${state.from} to ${state.to}` : state.from;
110
+ var describeAutoUpdateCheck = (state) => {
111
+ const label = state.lastResult ? AUTO_UPDATE_RESULT_LABELS[state.lastResult] : "no result recorded";
112
+ return [label, versionsOf(state), state.detail].filter(Boolean).join(", ");
113
+ };
114
+ var hasPendingUpgradeFinish = (state, installed) => state.to === installed && (state.lastResult === "finish_pending" || state.lastResult === "updated" && Boolean(state.detail));
115
+ var pendingAutoUpdateAnnouncement = (state) => state.lastResult === "updated" && state.announced !== true && state.from && state.to ? { from: state.from, to: state.to } : null;
116
+ var describeAutoUpdateAnnouncement = (announcement) => `Pushary updated itself from ${announcement.from} to ${announcement.to}.`;
117
+ var takeAutoUpdateAnnouncement = (statePath, markAnnounced) => {
118
+ try {
119
+ const announcement = pendingAutoUpdateAnnouncement(readAutoUpdateState(statePath));
120
+ if (announcement && markAnnounced) updateAutoUpdateState(statePath, { announced: true });
121
+ return announcement;
122
+ } catch {
123
+ return null;
124
+ }
125
+ };
126
+ var readAutoUpdateState = (statePath) => {
127
+ const read = readJsonSafe(statePath);
128
+ return read.kind === "ok" ? parseAutoUpdateState(read.value) : {};
129
+ };
130
+ var updateAutoUpdateState = (statePath, patch) => {
131
+ const next = { ...readAutoUpdateState(statePath), ...patch };
132
+ writeJsonAtomic(statePath, next);
133
+ return next;
134
+ };
135
+ var ignoreSpawnFailure = () => void 0;
136
+ var spawnDetached = (command, args, cwd) => {
137
+ const child = spawn(command, [...args], { cwd, detached: true, stdio: "ignore" });
138
+ child.on("error", ignoreSpawnFailure);
139
+ child.unref();
140
+ };
141
+ var triggerAutoUpdate = (trigger) => {
142
+ try {
143
+ const statePath = trigger.statePath ?? autoUpdateState();
144
+ const now = trigger.now ?? Date.now();
145
+ const decision = autoUpdateDecision({
146
+ env: trigger.env ?? process.env,
147
+ state: readAutoUpdateState(statePath),
148
+ now,
149
+ platform: trigger.platform ?? process.platform,
150
+ packageRoot: trigger.packageRoot ?? ownPackageRoot(),
151
+ isSubagentStart: trigger.isSubagentStart
152
+ });
153
+ if (decision.kind === "skip") return decision;
154
+ updateAutoUpdateState(statePath, { lastCheckAt: new Date(now).toISOString() });
155
+ const spawnUpgrade = trigger.spawn ?? spawnDetached;
156
+ spawnUpgrade(trigger.execPath ?? process.execPath, [upgradeEntryFor(decision), AUTO_UPDATE_FLAG], dirname(statePath));
157
+ return decision;
158
+ } catch {
159
+ return null;
160
+ }
161
+ };
162
+
163
+ export {
164
+ AUTO_UPDATE_DISABLE_ENV,
165
+ isAutoUpdateDisabledByEnv,
166
+ autoUpdateOffReason,
167
+ globalInstallLocation,
168
+ upgradeEntryFor,
169
+ parseAutoUpdateFlag,
170
+ describeAutoUpdateCheck,
171
+ hasPendingUpgradeFinish,
172
+ describeAutoUpdateAnnouncement,
173
+ takeAutoUpdateAnnouncement,
174
+ readAutoUpdateState,
175
+ updateAutoUpdateState,
176
+ triggerAutoUpdate
177
+ };