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,381 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ triggerAutoUpdate
4
+ } from "../chunk-UI36QSBI.js";
5
+ import {
6
+ isGatingMoment,
7
+ recordKeylessMoment
8
+ } from "../chunk-JU6W3XAU.js";
9
+ import {
10
+ readHookInput
11
+ } from "../chunk-KGDWDSNL.js";
12
+ import {
13
+ announceSubscriptionRequired,
14
+ denyReasonFrom,
15
+ isDeferAnswer
16
+ } from "../chunk-5OP5MQI7.js";
17
+ import "../chunk-JZTHGAPA.js";
18
+ import "../chunk-SGZJ5LYH.js";
19
+ import "../chunk-EYK3GSRH.js";
20
+ import "../chunk-ZVSKMPBZ.js";
21
+ import "../chunk-C2WCPBF7.js";
22
+ import "../chunk-6J2JC2RT.js";
23
+ import "../chunk-WE2J62NC.js";
24
+ import "../chunk-3EVPNIBE.js";
25
+ import "../chunk-OKXS7WDZ.js";
26
+ import "../chunk-MPWXGONV.js";
27
+ import "../chunk-SAXBC4AZ.js";
28
+ import "../chunk-OLQRRFBX.js";
29
+ import "../chunk-XHKBHWLX.js";
30
+ import {
31
+ UNNAMED_ACTION,
32
+ deriveActionBody,
33
+ deriveBlocker,
34
+ deriveToolTarget,
35
+ describeToolCall,
36
+ fetchModeState,
37
+ getPolicy,
38
+ handlePostToolUse,
39
+ handleSessionEnd,
40
+ handleStop,
41
+ handleUserPrompt,
42
+ policyRequestFor,
43
+ readLastPrompt,
44
+ readLastUserPrompt,
45
+ repoKeyFor,
46
+ reportEvent,
47
+ scopePathFor
48
+ } from "../chunk-CULUZTWQ.js";
49
+ import {
50
+ DEFAULT_SESSION
51
+ } from "../chunk-N6IOSZOE.js";
52
+ import "../chunk-EPCXUP3P.js";
53
+ import {
54
+ DECISION_STOP_REASON,
55
+ UNVERIFIABLE_APPROVAL_REASON,
56
+ askUser,
57
+ isDecisionStopped,
58
+ isLocalQuestionHandoff,
59
+ sendNotification,
60
+ waitForQuestion
61
+ } from "../chunk-NGDD6TXY.js";
62
+ import {
63
+ exitAfterClientReports
64
+ } from "../chunk-CVQYGMSR.js";
65
+ import "../chunk-POFBE6ZY.js";
66
+ import "../chunk-GMXKITVA.js";
67
+ import "../chunk-P7WQEVWA.js";
68
+ import {
69
+ SubscriptionRequiredError
70
+ } from "../chunk-M5ICYGHT.js";
71
+ import "../chunk-2ABTSGFT.js";
72
+ import "../chunk-2OY3ZZ5N.js";
73
+ import {
74
+ getMachineId
75
+ } from "../chunk-LXQ5FBBD.js";
76
+ import "../chunk-7QLSKOSU.js";
77
+ import {
78
+ EXIT
79
+ } from "../chunk-KZERVKTD.js";
80
+ import "../chunk-DB3ONZB4.js";
81
+ import "../chunk-A2A2YTMG.js";
82
+ import "../chunk-T7E75EKA.js";
83
+ import {
84
+ getApiKey
85
+ } from "../chunk-GPEGOGRJ.js";
86
+ import "../chunk-E4FJTUH4.js";
87
+ import {
88
+ KILL_REASON,
89
+ effectiveWaitSeconds,
90
+ gateOutputFor,
91
+ hookWaitClamped,
92
+ hookWaitDeadline,
93
+ resolveGate
94
+ } from "../chunk-NQTHFF4W.js";
95
+
96
+ // bin/pushary-gemini-hook.ts
97
+ import { basename } from "path";
98
+
99
+ // src/gemini-adapter.ts
100
+ import { isAbsolute, resolve } from "path";
101
+ var GEMINI_AGENT = { type: "gemini_cli", label: "Gemini CLI" };
102
+ var geminiOwnsLifecycle = (bridge) => bridge !== "1";
103
+ var GEMINI_TOOL_MAP = {
104
+ run_shell_command: "Bash",
105
+ write_file: "Write",
106
+ replace: "Edit",
107
+ read_file: "Read"
108
+ };
109
+ var firstString = (...values) => values.find((v) => typeof v === "string" && v.length > 0);
110
+ var toGeminiPolicyLookup = (toolName, toolInput, cwd, mcpContext) => {
111
+ if (mcpContext?.server_name && mcpContext.tool_name) {
112
+ return { tool: `mcp__${mcpContext.server_name}__${mcpContext.tool_name}`, input: toolInput };
113
+ }
114
+ const canonical = GEMINI_TOOL_MAP[toolName];
115
+ if (!canonical) return { tool: toolName, input: toolInput };
116
+ if (canonical === "Bash") {
117
+ const command = firstString(toolInput.command);
118
+ return { tool: "Bash", input: command ? { command } : {} };
119
+ }
120
+ const filePath = firstString(toolInput.file_path, toolInput.absolute_path, toolInput.path);
121
+ if (!filePath) return { tool: canonical, input: {} };
122
+ return {
123
+ tool: canonical,
124
+ input: { file_path: cwd && !isAbsolute(filePath) ? resolve(cwd, filePath) : filePath }
125
+ };
126
+ };
127
+ var geminiAllow = () => ({ kind: "allow" });
128
+ var geminiDeny = (reason) => ({ kind: "deny", reason });
129
+ var geminiPass = () => ({ kind: "pass" });
130
+ var NO_LOCAL_INPUT = "This unattended Gemini session has no local approval input. Use an interactive session or Every time.";
131
+ var NO_ANSWER_IN_TIME = "No answer from your phone in time, and this unattended Gemini session has no local approval input. Retry when you can answer on your phone.";
132
+ var HANDLED_ON_MACHINE = "You chose to handle this on your machine, but this unattended Gemini session has no local approval input. Use an interactive session.";
133
+ var NO_DEVICE = "No phone or other notification channel is connected to Pushary, and this unattended Gemini session has no local approval input. Connect one in Pushary, then retry.";
134
+ var SUBSCRIPTION_REQUIRED = "Your Pushary workspace has no active subscription, so approvals cannot reach your phone, and this unattended Gemini session has no local approval input. Renew Pushary, then retry.";
135
+ var geminiLocalHandoff = (bridge, reason = NO_LOCAL_INPUT) => geminiOwnsLifecycle(bridge) ? geminiPass() : geminiDeny(reason);
136
+ var geminiSubscriptionRequiredDecision = (bridge) => geminiLocalHandoff(bridge, SUBSCRIPTION_REQUIRED);
137
+ var geminiPushOnlyHandoff = (askStatus, bridge) => askStatus === "notified" || askStatus === "terminal" ? geminiLocalHandoff(bridge) : geminiPass();
138
+ var fromPhoneAnswer = (answer) => answer.value === "yes" ? geminiAllow() : geminiDeny(denyReasonFrom(answer.value, answer.note));
139
+ var geminiPushFirstDecision = (pushed, bridge) => {
140
+ if (!pushed) return geminiPass();
141
+ const { answer, localHandoff } = pushed;
142
+ if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
143
+ if (localHandoff) return geminiLocalHandoff(bridge, pushed.noDevices ? NO_DEVICE : NO_LOCAL_INPUT);
144
+ if (answer.answered) return isDeferAnswer(answer.value) ? geminiLocalHandoff(bridge, HANDLED_ON_MACHINE) : fromPhoneAnswer(answer);
145
+ return geminiLocalHandoff(bridge, NO_ANSWER_IN_TIME);
146
+ };
147
+ var geminiPushOnlyDecision = (pushed, timeoutAction, bridge) => {
148
+ if (!pushed) return geminiDeny(UNVERIFIABLE_APPROVAL_REASON);
149
+ const { answer, localHandoff, clampCut, askStatus } = pushed;
150
+ if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
151
+ if (localHandoff) return geminiPushOnlyHandoff(askStatus, bridge);
152
+ if (answer.answered) return isDeferAnswer(answer.value) ? geminiPass() : fromPhoneAnswer(answer);
153
+ if (clampCut) return geminiPass();
154
+ return geminiTimeoutDecision(timeoutAction);
155
+ };
156
+ var toGeminiWire = (decision) => {
157
+ if (decision.kind === "pass") return null;
158
+ return gateOutputFor(
159
+ "gemini",
160
+ "BeforeTool",
161
+ decision.kind,
162
+ decision.kind === "deny" ? decision.reason : void 0
163
+ ) ?? null;
164
+ };
165
+ var geminiTimeoutDecision = (timeoutAction, denyReason = "No response within timeout") => {
166
+ if (timeoutAction === "approve") return geminiAllow();
167
+ if (timeoutAction === "deny") return geminiDeny(denyReason);
168
+ return geminiPass();
169
+ };
170
+
171
+ // bin/pushary-gemini-hook.ts
172
+ var START_MS = Date.now();
173
+ var agentNameFor = (input) => `${GEMINI_AGENT.label} - ${basename(input.cwd ?? process.cwd())}`;
174
+ var pushQuestion = async (apiKey, input, lookup, waitSeconds, mode, pollInterval = 2e3) => {
175
+ const projectName = basename(input.cwd ?? process.cwd());
176
+ const description = describeToolCall(lookup.tool, lookup.input, "hook");
177
+ const result = await askUser(apiKey, {
178
+ question: `Allow ${description || UNNAMED_ACTION}?`,
179
+ type: "confirm",
180
+ context: `Agent wants to run this in ${projectName}`,
181
+ agentName: agentNameFor(input),
182
+ sessionId: input.session_id,
183
+ machineId: getMachineId(),
184
+ toolName: lookup.tool,
185
+ toolTarget: deriveToolTarget(lookup.tool, lookup.input),
186
+ intent: readLastPrompt(input.session_id || DEFAULT_SESSION) ?? (input.transcript_path ? readLastUserPrompt(input.transcript_path) : void 0),
187
+ action: description,
188
+ blocker: deriveBlocker(mode),
189
+ actionBody: deriveActionBody(input.tool_name ?? "", input.tool_input ?? {}),
190
+ repoKey: repoKeyFor(input.cwd)
191
+ });
192
+ const now = Date.now();
193
+ const deadline = hookWaitDeadline(START_MS, waitSeconds, "gemini", now);
194
+ const clampCut = hookWaitClamped(START_MS, waitSeconds, "gemini", now);
195
+ const answer = await waitForQuestion(apiKey, result, deadline, pollInterval);
196
+ return {
197
+ answer,
198
+ localHandoff: !answer.answered && isLocalQuestionHandoff(result),
199
+ clampCut,
200
+ askStatus: result.status,
201
+ noDevices: result.noDevices === true
202
+ };
203
+ };
204
+ var notifyApprovalNeeded = async (apiKey, input, lookup) => {
205
+ try {
206
+ await sendNotification(apiKey, {
207
+ title: "Agent needs approval",
208
+ body: describeToolCall(lookup.tool, lookup.input, "hook"),
209
+ agentName: agentNameFor(input),
210
+ sessionId: input.session_id,
211
+ machineId: getMachineId()
212
+ });
213
+ } catch {
214
+ }
215
+ };
216
+ var geminiStepsAside = (error, input) => {
217
+ announceSubscriptionRequired(error.upgradeUrl, input.session_id);
218
+ return geminiSubscriptionRequiredDecision(process.env.PUSHARY_GEMINI_BRIDGE);
219
+ };
220
+ var handlePushOnly = async (apiKey, input, lookup, timeoutSeconds, timeoutAction) => {
221
+ let pushed;
222
+ try {
223
+ pushed = await pushQuestion(apiKey, input, lookup, effectiveWaitSeconds(timeoutAction, timeoutSeconds, "gemini"), "push_only");
224
+ } catch (error) {
225
+ if (error instanceof SubscriptionRequiredError) return geminiStepsAside(error, input);
226
+ pushed = void 0;
227
+ }
228
+ return geminiPushOnlyDecision(pushed, timeoutAction, process.env.PUSHARY_GEMINI_BRIDGE);
229
+ };
230
+ var handlePushFirst = async (apiKey, input, lookup, pushFirstSeconds) => {
231
+ let pushed;
232
+ try {
233
+ pushed = await pushQuestion(apiKey, input, lookup, pushFirstSeconds, "push_first", 1500);
234
+ } catch (error) {
235
+ if (error instanceof SubscriptionRequiredError) return geminiStepsAside(error, input);
236
+ pushed = void 0;
237
+ }
238
+ return geminiPushFirstDecision(pushed, process.env.PUSHARY_GEMINI_BRIDGE);
239
+ };
240
+ var decideBeforeTool = async (input) => {
241
+ let apiKey;
242
+ try {
243
+ apiKey = getApiKey();
244
+ } catch {
245
+ try {
246
+ const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd, input.mcp_context);
247
+ if (isGatingMoment(lookup.tool, lookup.input)) {
248
+ recordKeylessMoment({
249
+ ts: Date.now(),
250
+ tool: lookup.tool,
251
+ project: basename(input.cwd ?? process.cwd()),
252
+ sessionId: input.session_id
253
+ });
254
+ }
255
+ } catch {
256
+ }
257
+ return geminiPass();
258
+ }
259
+ try {
260
+ const modeState = await fetchModeState(apiKey, input.session_id);
261
+ const policyRequest = policyRequestFor("gemini_cli");
262
+ const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd, input.mcp_context);
263
+ const config = await getPolicy(apiKey, modeState.policyVersion, policyRequest.agent, policyRequest.repoAware, input.session_id);
264
+ const scopePath = modeState.scope ? scopePathFor(lookup.tool, lookup.input, input.cwd) : void 0;
265
+ const verdict = resolveGate({
266
+ modeState,
267
+ config,
268
+ toolName: lookup.tool,
269
+ toolInputs: [lookup.input],
270
+ scopePaths: scopePath ? [scopePath] : [],
271
+ cwd: input.cwd,
272
+ repoKey: repoKeyFor(input.cwd),
273
+ sessionId: input.session_id
274
+ });
275
+ if (verdict.kind === "kill") return geminiDeny(KILL_REASON);
276
+ if (verdict.kind === "allow") return geminiAllow();
277
+ if (verdict.kind === "deny") return geminiDeny(verdict.reason);
278
+ if (verdict.kind === "defer") return geminiPass();
279
+ const toolPolicy = verdict.policy;
280
+ switch (toolPolicy.mode) {
281
+ case "push_only":
282
+ return handlePushOnly(apiKey, input, lookup, toolPolicy.timeoutSeconds, toolPolicy.timeoutAction);
283
+ case "terminal_only":
284
+ return geminiLocalHandoff(process.env.PUSHARY_GEMINI_BRIDGE);
285
+ case "push_first":
286
+ return handlePushFirst(apiKey, input, lookup, toolPolicy.pushFirstSeconds);
287
+ case "notify_only":
288
+ await notifyApprovalNeeded(apiKey, input, lookup);
289
+ return geminiLocalHandoff(process.env.PUSHARY_GEMINI_BRIDGE);
290
+ default:
291
+ return handlePushFirst(apiKey, input, lookup, toolPolicy.pushFirstSeconds);
292
+ }
293
+ } catch {
294
+ return geminiPass();
295
+ }
296
+ };
297
+ var reportSessionStart = async (input) => {
298
+ try {
299
+ await reportEvent({
300
+ event: "session_begin",
301
+ agentType: GEMINI_AGENT.type,
302
+ agentName: agentNameFor(input),
303
+ action: "Session started",
304
+ sessionId: input.session_id
305
+ }, { maxAttempts: 1, timeoutMs: 5e3 });
306
+ } catch {
307
+ }
308
+ };
309
+ var toCanonicalResult = (toolResponse) => {
310
+ if (!toolResponse || typeof toolResponse !== "object" || Array.isArray(toolResponse)) return void 0;
311
+ const error = toolResponse.error;
312
+ return error ? { error } : {};
313
+ };
314
+ var emitContext = (event, additionalContext) => {
315
+ if (!additionalContext) return;
316
+ process.stdout.write(JSON.stringify({
317
+ hookSpecificOutput: { hookEventName: event, additionalContext }
318
+ }));
319
+ };
320
+ var emit = (wire) => {
321
+ if (wire) process.stdout.write(JSON.stringify(wire));
322
+ };
323
+ var main = async () => {
324
+ const input = await readHookInput("gemini");
325
+ if (!input) {
326
+ return exitAfterClientReports(EXIT.OK);
327
+ }
328
+ try {
329
+ switch (input.hook_event_name) {
330
+ case "BeforeTool":
331
+ emit(toGeminiWire(await decideBeforeTool(input)));
332
+ break;
333
+ case "AfterTool": {
334
+ const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd, input.mcp_context);
335
+ emitContext("AfterTool", await handlePostToolUse({
336
+ tool_name: lookup.tool,
337
+ tool_input: lookup.input,
338
+ tool_result: toCanonicalResult(input.tool_response),
339
+ cwd: input.cwd,
340
+ session_id: input.session_id,
341
+ transcript_path: input.transcript_path
342
+ }, GEMINI_AGENT));
343
+ break;
344
+ }
345
+ case "BeforeAgent":
346
+ emitContext("BeforeAgent", await handleUserPrompt({
347
+ prompt: input.prompt,
348
+ cwd: input.cwd,
349
+ session_id: input.session_id
350
+ }, GEMINI_AGENT));
351
+ break;
352
+ case "AfterAgent":
353
+ if (geminiOwnsLifecycle(process.env.PUSHARY_GEMINI_BRIDGE)) {
354
+ await handleStop({
355
+ cwd: input.cwd,
356
+ session_id: input.session_id,
357
+ transcript_path: input.transcript_path,
358
+ last_assistant_message: input.prompt_response
359
+ }, GEMINI_AGENT);
360
+ }
361
+ break;
362
+ case "SessionEnd":
363
+ if (geminiOwnsLifecycle(process.env.PUSHARY_GEMINI_BRIDGE)) {
364
+ await handleSessionEnd({
365
+ cwd: input.cwd,
366
+ session_id: input.session_id,
367
+ reason: input.reason
368
+ }, GEMINI_AGENT);
369
+ }
370
+ break;
371
+ case "SessionStart":
372
+ triggerAutoUpdate({ isSubagentStart: false });
373
+ if (geminiOwnsLifecycle(process.env.PUSHARY_GEMINI_BRIDGE)) await reportSessionStart(input);
374
+ break;
375
+ default:
376
+ break;
377
+ }
378
+ } catch {
379
+ }
380
+ };
381
+ main();
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ handlePreToolUse
4
+ } from "../chunk-Q7E4I6UA.js";
5
+ import {
6
+ activeHookSource,
7
+ normalizeToolCall,
8
+ profileFromArgv
9
+ } from "../chunk-J5TIV3J6.js";
10
+ import "../chunk-AXPCESYO.js";
11
+ import "../chunk-CJVVKOSR.js";
12
+ import "../chunk-JU6W3XAU.js";
13
+ import {
14
+ readToolHookInput
15
+ } from "../chunk-KGDWDSNL.js";
16
+ import "../chunk-5OP5MQI7.js";
17
+ import "../chunk-JZTHGAPA.js";
18
+ import "../chunk-SGZJ5LYH.js";
19
+ import "../chunk-EYK3GSRH.js";
20
+ import "../chunk-ZVSKMPBZ.js";
21
+ import "../chunk-C2WCPBF7.js";
22
+ import "../chunk-6J2JC2RT.js";
23
+ import "../chunk-WE2J62NC.js";
24
+ import "../chunk-3EVPNIBE.js";
25
+ import "../chunk-OKXS7WDZ.js";
26
+ import "../chunk-MPWXGONV.js";
27
+ import "../chunk-SAXBC4AZ.js";
28
+ import "../chunk-OLQRRFBX.js";
29
+ import "../chunk-XHKBHWLX.js";
30
+ import {
31
+ canonicalGateInputs
32
+ } from "../chunk-CULUZTWQ.js";
33
+ import "../chunk-N6IOSZOE.js";
34
+ import "../chunk-EPCXUP3P.js";
35
+ import "../chunk-NGDD6TXY.js";
36
+ import {
37
+ exitAfterClientReports
38
+ } from "../chunk-CVQYGMSR.js";
39
+ import "../chunk-POFBE6ZY.js";
40
+ import {
41
+ exitOnHelpFlag
42
+ } from "../chunk-HZVVNGEW.js";
43
+ import "../chunk-GMXKITVA.js";
44
+ import "../chunk-P7WQEVWA.js";
45
+ import "../chunk-M5ICYGHT.js";
46
+ import "../chunk-2ABTSGFT.js";
47
+ import "../chunk-2OY3ZZ5N.js";
48
+ import "../chunk-LXQ5FBBD.js";
49
+ import "../chunk-7QLSKOSU.js";
50
+ import {
51
+ EXIT
52
+ } from "../chunk-KZERVKTD.js";
53
+ import "../chunk-DB3ONZB4.js";
54
+ import "../chunk-A2A2YTMG.js";
55
+ import "../chunk-T7E75EKA.js";
56
+ import "../chunk-GPEGOGRJ.js";
57
+ import "../chunk-E4FJTUH4.js";
58
+ import {
59
+ renderPreToolUseVerdict
60
+ } from "../chunk-NQTHFF4W.js";
61
+
62
+ // bin/pushary-hook.ts
63
+ exitOnHelpFlag("hook");
64
+ var profile = profileFromArgv(process.argv);
65
+ var FALLBACK_TO_TERMINAL = {
66
+ hookSpecificOutput: {
67
+ hookEventName: "PreToolUse",
68
+ permissionDecision: "ask",
69
+ permissionDecisionReason: "Pushary hook error, falling back to terminal"
70
+ }
71
+ };
72
+ var emit = (verdict) => {
73
+ const output = verdict && profile ? renderPreToolUseVerdict(profile, verdict) : verdict;
74
+ if (output !== void 0) process.stdout.write(JSON.stringify(output));
75
+ };
76
+ var main = async () => {
77
+ const raw = await readToolHookInput(
78
+ activeHookSource(process.argv),
79
+ "PreToolUse"
80
+ );
81
+ if (!raw?.tool_name) {
82
+ return exitAfterClientReports(EXIT.OK);
83
+ }
84
+ try {
85
+ const canonical = profile ? canonicalGateInputs(profile.source, raw.tool_name, [raw.tool_input ?? {}], raw.cwd) : void 0;
86
+ emit(await handlePreToolUse(normalizeToolCall(profile, raw), canonical));
87
+ } catch (err) {
88
+ process.stderr.write(
89
+ `[pushary-hook] Error: ${err instanceof Error ? err.message : String(err)}
90
+ `
91
+ );
92
+ emit(FALLBACK_TO_TERMINAL);
93
+ }
94
+ };
95
+ main();
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runBrowserLogin
4
+ } from "../chunk-JYG4B33G.js";
5
+ import "../chunk-PUZZELE6.js";
6
+ import {
7
+ recoverDaemonAfterLogin
8
+ } from "../chunk-WE2J62NC.js";
9
+ import "../chunk-XHKBHWLX.js";
10
+ import "../chunk-3EGEA4KH.js";
11
+ import {
12
+ createIo
13
+ } from "../chunk-U4TMAANR.js";
14
+ import {
15
+ exitOnHelpFlag
16
+ } from "../chunk-HZVVNGEW.js";
17
+ import {
18
+ parseFlags
19
+ } from "../chunk-GMXKITVA.js";
20
+ import {
21
+ readConfigFileKey,
22
+ readControlMode,
23
+ readKeySource,
24
+ writeKey
25
+ } from "../chunk-P7WQEVWA.js";
26
+ import {
27
+ checkApiKey,
28
+ describeKeyCheck
29
+ } from "../chunk-2ABTSGFT.js";
30
+ import "../chunk-2OY3ZZ5N.js";
31
+ import "../chunk-LXQ5FBBD.js";
32
+ import {
33
+ UsageError
34
+ } from "../chunk-7QLSKOSU.js";
35
+ import {
36
+ EXIT
37
+ } from "../chunk-KZERVKTD.js";
38
+ import "../chunk-DB3ONZB4.js";
39
+ import "../chunk-A2A2YTMG.js";
40
+ import "../chunk-T7E75EKA.js";
41
+ import "../chunk-GPEGOGRJ.js";
42
+ import "../chunk-E4FJTUH4.js";
43
+ import {
44
+ getPackageVersion,
45
+ isValidApiKey
46
+ } from "../chunk-NQTHFF4W.js";
47
+
48
+ // bin/pushary-login.ts
49
+ exitOnHelpFlag("login");
50
+ var parseLoginOptions = (argv) => {
51
+ const flags = parseFlags(argv, {
52
+ bools: ["with-token", "no-browser", "json", "force", "help", "h"],
53
+ values: []
54
+ });
55
+ if (flags.command !== void 0 && flags.command !== "login") {
56
+ throw new UsageError(`login takes no positional arguments (got ${flags.command})`);
57
+ }
58
+ return {
59
+ withToken: flags.bools.has("with-token"),
60
+ noBrowser: flags.bools.has("no-browser"),
61
+ json: flags.bools.has("json"),
62
+ force: flags.bools.has("force")
63
+ };
64
+ };
65
+ var options;
66
+ try {
67
+ options = parseLoginOptions(process.argv.slice(2));
68
+ } catch (err) {
69
+ process.stderr.write(` ! ${err instanceof Error ? err.message : String(err)}
70
+ `);
71
+ process.stderr.write(" Run `pushary login --help` for the full list.\n");
72
+ process.exit(EXIT.USAGE);
73
+ }
74
+ var { bold, dim, green, cyan, yellow } = createIo();
75
+ var check = green("\u2713");
76
+ var say = options.json ? (line) => process.stderr.write(`${line}
77
+ `) : (line) => process.stdout.write(`${line}
78
+ `);
79
+ var emit = (payload) => {
80
+ if (options.json) process.stdout.write(`${JSON.stringify(payload, null, 2)}
81
+ `);
82
+ };
83
+ var readStdin = async () => {
84
+ const chunks = [];
85
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
86
+ return Buffer.concat(chunks).toString("utf8").trim();
87
+ };
88
+ var recoverControl = () => {
89
+ const daemon = recoverDaemonAfterLogin();
90
+ if (daemon?.cleared) {
91
+ say(readControlMode() === "off" ? ` ${check} ${dim("Account verified. Phone-start remains off.")}` : daemon.restarted ? ` ${check} ${dim("Background daemon restarted with the verified key.")}` : ` ${yellow("!")} ${dim(`Background daemon may start again${daemon.detail ? ` (${daemon.detail})` : ""}`)}`);
92
+ }
93
+ return daemon;
94
+ };
95
+ var store = async (apiKey, siteSlug, keyName) => {
96
+ const verdict = await checkApiKey(apiKey);
97
+ const report = describeKeyCheck(verdict);
98
+ if (verdict.kind !== "ok") {
99
+ say(` ${yellow("!")} ${report.message}`);
100
+ for (const line of report.next) say(` ${dim(line)}`);
101
+ if (report.fatal) {
102
+ emit({ ok: false, reason: verdict.kind, keyCheck: verdict.kind, exitCode: report.exitCode });
103
+ return report.exitCode;
104
+ }
105
+ }
106
+ if (verdict.kind === "ok") siteSlug = verdict.identity.siteSlug;
107
+ const result = writeKey(apiKey);
108
+ say("");
109
+ say(verdict.kind === "ok" ? ` ${check} ${bold("Signed in.")}${siteSlug ? ` ${dim(`Site ${siteSlug}`)}` : ""}` : ` ${yellow("!")} ${verdict.kind === "locked" ? "Key saved, approvals inactive." : "Key saved, account readiness unverified."}`);
110
+ say(` ${dim(`Key saved to ${result.configPath}`)}`);
111
+ if (result.rc.kind === "written" || result.rc.kind === "updated") {
112
+ say(` ${dim(`Shell export ${result.rc.kind} in ${result.rc.path}`)}`);
113
+ } else if (result.rc.kind === "declined") {
114
+ say(` ${yellow("!")} ${dim(`Left ${result.rc.path} alone: line ${result.rc.declinedLine} already sets PUSHARY_API_KEY.`)}`);
115
+ }
116
+ const daemon = verdict.kind === "ok" ? recoverControl() : null;
117
+ emit({
118
+ ok: verdict.kind === "ok",
119
+ siteSlug,
120
+ keyName,
121
+ configPath: result.configPath,
122
+ rc: result.rc.kind,
123
+ daemon,
124
+ keyCheck: verdict.kind,
125
+ ...verdict.kind !== "ok" ? { reason: verdict.kind } : {},
126
+ exitCode: report.exitCode
127
+ });
128
+ return report.exitCode;
129
+ };
130
+ var loginWithToken = async () => {
131
+ const token = await readStdin();
132
+ if (!token) {
133
+ say(` ${yellow("!")} Nothing arrived on stdin. Pipe the key in: pushary login --with-token < key.txt`);
134
+ emit({ ok: false, reason: "no_input" });
135
+ process.exit(EXIT.INPUT_REQUIRED);
136
+ }
137
+ if (!isValidApiKey(token)) {
138
+ say(` ${yellow("!")} That does not look like a Pushary key. Expected pk_xxx.xxx`);
139
+ emit({ ok: false, reason: "malformed" });
140
+ process.exit(EXIT.USAGE);
141
+ }
142
+ process.exit(await store(token, "", "pasted key"));
143
+ };
144
+ var main = async () => {
145
+ if (options.withToken) await loginWithToken();
146
+ if (!options.force) {
147
+ const existing = readKeySource();
148
+ if (existing.key) {
149
+ const verdict = await checkApiKey(existing.key);
150
+ if (verdict.kind === "ok") {
151
+ say("");
152
+ say(` ${check} Already signed in ${dim(`(${verdict.identity.siteSlug})`)}`);
153
+ say(` ${dim("Run")} ${cyan("pushary login --force")} ${dim("to replace this key.")}`);
154
+ const daemon = existing.key === readConfigFileKey() ? recoverControl() : null;
155
+ if (daemon === null) say(" The saved service key differs from this shell. Run pushary setup with the intended key before starting managed control.");
156
+ emit({ ok: true, reused: true, siteSlug: verdict.identity.siteSlug, keyCheck: "ok", daemon, exitCode: EXIT.OK });
157
+ process.exit(EXIT.OK);
158
+ }
159
+ if (verdict.kind === "locked" || verdict.kind === "unreachable") {
160
+ const report = describeKeyCheck(verdict);
161
+ say(` ${yellow("!")} ${report.message}`);
162
+ for (const line of report.next) say(` ${dim(line)}`);
163
+ say(" Existing key kept. Re-run pushary login to recheck readiness without replacing it.");
164
+ emit({ ok: false, reused: true, reason: verdict.kind, keyCheck: verdict.kind, exitCode: report.exitCode });
165
+ process.exit(report.exitCode);
166
+ }
167
+ }
168
+ }
169
+ const outcome = await runBrowserLogin(
170
+ { say, bold, dim, cyan, yellow },
171
+ { cliVersion: getPackageVersion(), noBrowser: options.noBrowser }
172
+ );
173
+ if (outcome.kind === "ok") {
174
+ process.exit(await store(outcome.apiKey, outcome.siteSlug, outcome.keyName));
175
+ }
176
+ if (outcome.kind === "unavailable") {
177
+ say(` ${yellow("!")} Couldn't start a login ${dim(`(${outcome.detail})`)}`);
178
+ say(` ${dim("Check your connection, or paste a key with")} ${cyan("pushary login --with-token < key.txt")}`);
179
+ emit({ ok: false, reason: "unreachable", detail: outcome.detail });
180
+ process.exit(EXIT.UNREACHABLE);
181
+ }
182
+ if (outcome.kind === "rejected") {
183
+ say("");
184
+ say(` ${yellow("!")} That request was rejected in the browser. Nothing was written.`);
185
+ emit({ ok: false, reason: "rejected" });
186
+ process.exit(EXIT.NOT_CONFIGURED);
187
+ }
188
+ say("");
189
+ say(` ${yellow("!")} The code expired before it was approved.`);
190
+ say(` ${dim("Run")} ${cyan("pushary login")} ${dim("for a fresh one.")}`);
191
+ emit({ ok: false, reason: "expired" });
192
+ process.exit(EXIT.NOT_CONFIGURED);
193
+ };
194
+ main().catch((err) => {
195
+ process.stderr.write(` ! login failed: ${err instanceof Error ? err.message : String(err)}
196
+ `);
197
+ process.exit(EXIT.FAILED);
198
+ });
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node