@ken-jo/agent-connector 0.1.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 (203) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +348 -0
  4. package/dist/amp-QSFH2V7Y.js +217 -0
  5. package/dist/amp-QSFH2V7Y.js.map +1 -0
  6. package/dist/amp-TULUPXXL.js +237 -0
  7. package/dist/amp-TULUPXXL.js.map +1 -0
  8. package/dist/antigravity-CX2YLSY7.js +17 -0
  9. package/dist/antigravity-CX2YLSY7.js.map +1 -0
  10. package/dist/antigravity-STHZWMP3.js +171 -0
  11. package/dist/antigravity-STHZWMP3.js.map +1 -0
  12. package/dist/antigravity-cli-P6HADOAO.js +121 -0
  13. package/dist/antigravity-cli-P6HADOAO.js.map +1 -0
  14. package/dist/antigravity-cli-YQDFVV4U.js +86 -0
  15. package/dist/antigravity-cli-YQDFVV4U.js.map +1 -0
  16. package/dist/chunk-54EMKTTX.js +191 -0
  17. package/dist/chunk-54EMKTTX.js.map +1 -0
  18. package/dist/chunk-7L6ITC4T.js +54 -0
  19. package/dist/chunk-7L6ITC4T.js.map +1 -0
  20. package/dist/chunk-7MSDGHJW.js +316 -0
  21. package/dist/chunk-7MSDGHJW.js.map +1 -0
  22. package/dist/chunk-BCHWFTV4.js +196 -0
  23. package/dist/chunk-BCHWFTV4.js.map +1 -0
  24. package/dist/chunk-DBQMX75U.js +36 -0
  25. package/dist/chunk-DBQMX75U.js.map +1 -0
  26. package/dist/chunk-EXLS7BSI.js +29 -0
  27. package/dist/chunk-EXLS7BSI.js.map +1 -0
  28. package/dist/chunk-JNM3TPLX.js +593 -0
  29. package/dist/chunk-JNM3TPLX.js.map +1 -0
  30. package/dist/chunk-JWNT5KW7.js +197 -0
  31. package/dist/chunk-JWNT5KW7.js.map +1 -0
  32. package/dist/chunk-KHO5WNTP.js +30 -0
  33. package/dist/chunk-KHO5WNTP.js.map +1 -0
  34. package/dist/chunk-LTT56GI3.js +155 -0
  35. package/dist/chunk-LTT56GI3.js.map +1 -0
  36. package/dist/chunk-MEH7GC7R.js +111 -0
  37. package/dist/chunk-MEH7GC7R.js.map +1 -0
  38. package/dist/chunk-MQPNU2CY.js +64 -0
  39. package/dist/chunk-MQPNU2CY.js.map +1 -0
  40. package/dist/chunk-OPXAP3AC.js +12 -0
  41. package/dist/chunk-OPXAP3AC.js.map +1 -0
  42. package/dist/chunk-RK6VBLXP.js +50 -0
  43. package/dist/chunk-RK6VBLXP.js.map +1 -0
  44. package/dist/chunk-TIIP3RG2.js +420 -0
  45. package/dist/chunk-TIIP3RG2.js.map +1 -0
  46. package/dist/chunk-TRKWUNO4.js +702 -0
  47. package/dist/chunk-TRKWUNO4.js.map +1 -0
  48. package/dist/chunk-TRQKHP7D.js +93 -0
  49. package/dist/chunk-TRQKHP7D.js.map +1 -0
  50. package/dist/chunk-WCETQWWK.js +59 -0
  51. package/dist/chunk-WCETQWWK.js.map +1 -0
  52. package/dist/chunk-WV63X7IK.js +357 -0
  53. package/dist/chunk-WV63X7IK.js.map +1 -0
  54. package/dist/chunk-XD7MY6HB.js +343 -0
  55. package/dist/chunk-XD7MY6HB.js.map +1 -0
  56. package/dist/chunk-XNAM7F35.js +276 -0
  57. package/dist/chunk-XNAM7F35.js.map +1 -0
  58. package/dist/chunk-XUCWLJY5.js +238 -0
  59. package/dist/chunk-XUCWLJY5.js.map +1 -0
  60. package/dist/chunk-YNZU6PAV.js +800 -0
  61. package/dist/chunk-YNZU6PAV.js.map +1 -0
  62. package/dist/chunk-YT43MUSJ.js +146 -0
  63. package/dist/chunk-YT43MUSJ.js.map +1 -0
  64. package/dist/claude-code-HKDV7T32.js +637 -0
  65. package/dist/claude-code-HKDV7T32.js.map +1 -0
  66. package/dist/claude-code-W74MNTAC.js +652 -0
  67. package/dist/claude-code-W74MNTAC.js.map +1 -0
  68. package/dist/cli/sdk.d.ts +54 -0
  69. package/dist/cli/sdk.js +82 -0
  70. package/dist/cli/sdk.js.map +1 -0
  71. package/dist/cli.d.ts +2 -0
  72. package/dist/cli.js +15 -0
  73. package/dist/cli.js.map +1 -0
  74. package/dist/codebuff-3PMZJJHP.js +307 -0
  75. package/dist/codebuff-3PMZJJHP.js.map +1 -0
  76. package/dist/codebuff-QX57FJDN.js +220 -0
  77. package/dist/codebuff-QX57FJDN.js.map +1 -0
  78. package/dist/codex-JFQTS6LU.js +593 -0
  79. package/dist/codex-JFQTS6LU.js.map +1 -0
  80. package/dist/codex-Q3ATRRX3.js +364 -0
  81. package/dist/codex-Q3ATRRX3.js.map +1 -0
  82. package/dist/copilot-cli-MTCGHZ2T.js +622 -0
  83. package/dist/copilot-cli-MTCGHZ2T.js.map +1 -0
  84. package/dist/copilot-cli-SJGIXVSD.js +369 -0
  85. package/dist/copilot-cli-SJGIXVSD.js.map +1 -0
  86. package/dist/crush-I7PK3FGI.js +360 -0
  87. package/dist/crush-I7PK3FGI.js.map +1 -0
  88. package/dist/crush-LN5QFFR2.js +183 -0
  89. package/dist/crush-LN5QFFR2.js.map +1 -0
  90. package/dist/cursor-4TMXKJOZ.js +603 -0
  91. package/dist/cursor-4TMXKJOZ.js.map +1 -0
  92. package/dist/cursor-QZ6MTO74.js +194 -0
  93. package/dist/cursor-QZ6MTO74.js.map +1 -0
  94. package/dist/detect-WIAGVYS4.js +68 -0
  95. package/dist/detect-WIAGVYS4.js.map +1 -0
  96. package/dist/doctor-EZW2K4NI.js +372 -0
  97. package/dist/doctor-EZW2K4NI.js.map +1 -0
  98. package/dist/droid-KN6VRMMT.js +424 -0
  99. package/dist/droid-KN6VRMMT.js.map +1 -0
  100. package/dist/droid-W5WF5XEX.js +224 -0
  101. package/dist/droid-W5WF5XEX.js.map +1 -0
  102. package/dist/gemini-cli-CIRW37TU.js +369 -0
  103. package/dist/gemini-cli-CIRW37TU.js.map +1 -0
  104. package/dist/gemini-cli-Y35FFK4M.js +705 -0
  105. package/dist/gemini-cli-Y35FFK4M.js.map +1 -0
  106. package/dist/goose-A3M5GODH.js +212 -0
  107. package/dist/goose-A3M5GODH.js.map +1 -0
  108. package/dist/goose-ZTGTSUFJ.js +480 -0
  109. package/dist/goose-ZTGTSUFJ.js.map +1 -0
  110. package/dist/hermes-H2KV44GK.js +470 -0
  111. package/dist/hermes-H2KV44GK.js.map +1 -0
  112. package/dist/hermes-ZYLMOFLZ.js +196 -0
  113. package/dist/hermes-ZYLMOFLZ.js.map +1 -0
  114. package/dist/hook-YVDILAMZ.js +75 -0
  115. package/dist/hook-YVDILAMZ.js.map +1 -0
  116. package/dist/index.d.ts +22 -0
  117. package/dist/index.js +10 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/install-HQRYZOQ2.js +60 -0
  120. package/dist/install-HQRYZOQ2.js.map +1 -0
  121. package/dist/jetbrains-copilot-HWA3DMH7.js +633 -0
  122. package/dist/jetbrains-copilot-HWA3DMH7.js.map +1 -0
  123. package/dist/kilo-NSWAVP6P.js +235 -0
  124. package/dist/kilo-NSWAVP6P.js.map +1 -0
  125. package/dist/kilo-QXJRCSLT.js +384 -0
  126. package/dist/kilo-QXJRCSLT.js.map +1 -0
  127. package/dist/kilo-cli-EAQTHXUW.js +587 -0
  128. package/dist/kilo-cli-EAQTHXUW.js.map +1 -0
  129. package/dist/kilo-cli-EVBKUODA.js +160 -0
  130. package/dist/kilo-cli-EVBKUODA.js.map +1 -0
  131. package/dist/kimi-EI3UM3ZV.js +155 -0
  132. package/dist/kimi-EI3UM3ZV.js.map +1 -0
  133. package/dist/kimi-GQ7VXNYT.js +423 -0
  134. package/dist/kimi-GQ7VXNYT.js.map +1 -0
  135. package/dist/kiro-UCLSLBW2.js +217 -0
  136. package/dist/kiro-UCLSLBW2.js.map +1 -0
  137. package/dist/kiro-VQTT6LO2.js +451 -0
  138. package/dist/kiro-VQTT6LO2.js.map +1 -0
  139. package/dist/leaderboard-K6ON3SWW.js +137 -0
  140. package/dist/leaderboard-K6ON3SWW.js.map +1 -0
  141. package/dist/mux-T2TV4CIQ.js +211 -0
  142. package/dist/mux-T2TV4CIQ.js.map +1 -0
  143. package/dist/mux-ZMLVAXOO.js +96 -0
  144. package/dist/mux-ZMLVAXOO.js.map +1 -0
  145. package/dist/omp-PB4VDKS6.js +586 -0
  146. package/dist/omp-PB4VDKS6.js.map +1 -0
  147. package/dist/openclaw-ETN2M63O.js +751 -0
  148. package/dist/openclaw-ETN2M63O.js.map +1 -0
  149. package/dist/openclaw-IYA7ASL6.js +150 -0
  150. package/dist/openclaw-IYA7ASL6.js.map +1 -0
  151. package/dist/opencode-FZ33JKUZ.js +734 -0
  152. package/dist/opencode-FZ33JKUZ.js.map +1 -0
  153. package/dist/opencode-HWT3RSMN.js +216 -0
  154. package/dist/opencode-HWT3RSMN.js.map +1 -0
  155. package/dist/package-DLOVE2E4.js +1132 -0
  156. package/dist/package-DLOVE2E4.js.map +1 -0
  157. package/dist/pi-CIU2QHT4.js +208 -0
  158. package/dist/pi-CIU2QHT4.js.map +1 -0
  159. package/dist/pi-UFX2OYFV.js +139 -0
  160. package/dist/pi-UFX2OYFV.js.map +1 -0
  161. package/dist/qwen-code-5POCNU6P.js +573 -0
  162. package/dist/qwen-code-5POCNU6P.js.map +1 -0
  163. package/dist/qwen-code-W7NUCDEN.js +134 -0
  164. package/dist/qwen-code-W7NUCDEN.js.map +1 -0
  165. package/dist/roo-code-IOIUXRDE.js +242 -0
  166. package/dist/roo-code-IOIUXRDE.js.map +1 -0
  167. package/dist/roo-code-ZLKH4A7N.js +229 -0
  168. package/dist/roo-code-ZLKH4A7N.js.map +1 -0
  169. package/dist/runtime/index.d.ts +157 -0
  170. package/dist/runtime/index.js +20 -0
  171. package/dist/runtime/index.js.map +1 -0
  172. package/dist/serve-HVWWY4B6.js +65 -0
  173. package/dist/serve-HVWWY4B6.js.map +1 -0
  174. package/dist/status-FE7TECZD.js +128 -0
  175. package/dist/status-FE7TECZD.js.map +1 -0
  176. package/dist/synthetic-UKR6SMGS.js +178 -0
  177. package/dist/synthetic-UKR6SMGS.js.map +1 -0
  178. package/dist/telemetry-36GESKQQ.js +349 -0
  179. package/dist/telemetry-36GESKQQ.js.map +1 -0
  180. package/dist/trae-JSNDZFKL.js +204 -0
  181. package/dist/trae-JSNDZFKL.js.map +1 -0
  182. package/dist/trae-PA26DPBW.js +157 -0
  183. package/dist/trae-PA26DPBW.js.map +1 -0
  184. package/dist/types-CYUB1OTr.d.ts +427 -0
  185. package/dist/uninstall-IGCKNOSF.js +72 -0
  186. package/dist/uninstall-IGCKNOSF.js.map +1 -0
  187. package/dist/upgrade-JV2VK242.js +104 -0
  188. package/dist/upgrade-JV2VK242.js.map +1 -0
  189. package/dist/usage-6ER6EMEY.js +324 -0
  190. package/dist/usage-6ER6EMEY.js.map +1 -0
  191. package/dist/usage-event-JKBI6XCF.js +48 -0
  192. package/dist/usage-event-JKBI6XCF.js.map +1 -0
  193. package/dist/vscode-copilot-CK3NO54M.js +715 -0
  194. package/dist/vscode-copilot-CK3NO54M.js.map +1 -0
  195. package/dist/warp-JNQA7PBU.js +126 -0
  196. package/dist/warp-JNQA7PBU.js.map +1 -0
  197. package/dist/warp-QNVDXOG4.js +199 -0
  198. package/dist/warp-QNVDXOG4.js.map +1 -0
  199. package/dist/zed-NNNX5MNT.js +272 -0
  200. package/dist/zed-NNNX5MNT.js.map +1 -0
  201. package/dist/zed-VRJKWYUR.js +209 -0
  202. package/dist/zed-VRJKWYUR.js.map +1 -0
  203. package/package.json +79 -0
@@ -0,0 +1,705 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ writeTomlString
4
+ } from "./chunk-OPXAP3AC.js";
5
+ import {
6
+ resolveEnvRefsDeep
7
+ } from "./chunk-DBQMX75U.js";
8
+ import {
9
+ BaseAdapter
10
+ } from "./chunk-TIIP3RG2.js";
11
+ import "./chunk-7L6ITC4T.js";
12
+ import {
13
+ buildHomeBinHookCommand,
14
+ buildServeWrapperCommand,
15
+ buildUsageEventCommand,
16
+ isHomeBinHookCommand,
17
+ isHostNativeUsageEnabled,
18
+ isUsageEventCommand,
19
+ shouldWrapForTelemetry
20
+ } from "./chunk-MEH7GC7R.js";
21
+ import "./chunk-TRQKHP7D.js";
22
+
23
+ // src/adapters/gemini-cli/index.ts
24
+ import { existsSync } from "fs";
25
+ import { homedir } from "os";
26
+ import { join } from "path";
27
+ var HOST = "gemini-cli";
28
+ var MCP_ROOT_KEY = "mcpServers";
29
+ var GEMINI_EVENT = {
30
+ BeforeTool: "BeforeTool",
31
+ AfterTool: "AfterTool",
32
+ PreCompress: "PreCompress",
33
+ SessionStart: "SessionStart",
34
+ SessionEnd: "SessionEnd",
35
+ BeforeAgent: "BeforeAgent",
36
+ Notification: "Notification",
37
+ /** Fires AFTER each model turn; payload carries `usageMetadata` (opt-in 4a). */
38
+ AfterModel: "AfterModel"
39
+ };
40
+ var EVENT_MAP = {
41
+ PreToolUse: GEMINI_EVENT.BeforeTool,
42
+ PostToolUse: GEMINI_EVENT.AfterTool,
43
+ PreCompact: GEMINI_EVENT.PreCompress,
44
+ SessionStart: GEMINI_EVENT.SessionStart,
45
+ SessionEnd: GEMINI_EVENT.SessionEnd,
46
+ UserPromptSubmit: GEMINI_EVENT.BeforeAgent,
47
+ Notification: GEMINI_EVENT.Notification
48
+ };
49
+ var GeminiCliAdapter = class extends BaseAdapter {
50
+ id = HOST;
51
+ name = "Gemini CLI";
52
+ paradigm = "json-stdio";
53
+ capabilities = {
54
+ // Gemini fires Before/AfterTool, PreCompress, SessionStart/End, BeforeAgent
55
+ // (≈ UserPromptSubmit), and Notification. It has no `Stop` equivalent.
56
+ preToolUse: true,
57
+ postToolUse: true,
58
+ preCompact: true,
59
+ sessionStart: true,
60
+ sessionEnd: true,
61
+ userPromptSubmit: true,
62
+ stop: false,
63
+ notification: true,
64
+ // BeforeTool can rewrite tool input (hookSpecificOutput.tool_input); unlike
65
+ // the Claude-family hosts, AfterTool CAN replace already-emitted tool output
66
+ // (via a deny + reason), so output modification is supported here.
67
+ canModifyArgs: true,
68
+ canModifyOutput: true,
69
+ canInjectSessionContext: true,
70
+ // Transport is selected by key: command/args (stdio), url (sse), httpUrl (http).
71
+ transports: ["stdio", "sse", "http"],
72
+ // Content surfaces: Gemini CLI supports all three. Commands are authored as
73
+ // TOML (description + prompt with {{args}} placeholders); skills follow the
74
+ // uniform <geminiDir>/skills/<name>/SKILL.md layout; subagents are md+fm.
75
+ supportsCommands: true,
76
+ supportsSkills: true,
77
+ supportsSubagents: true
78
+ };
79
+ // ── Detection ────────────────────────────────────────────────────────────
80
+ detectInstalled(projectDir) {
81
+ const userDir = join(homedir(), ".gemini");
82
+ const userSettings = join(userDir, "settings.json");
83
+ const projectDirGemini = join(projectDir, ".gemini");
84
+ const projectSettings = join(projectDirGemini, "settings.json");
85
+ const userInstalled = existsSync(userDir) || existsSync(userSettings);
86
+ const projInstalled = existsSync(projectDirGemini) || existsSync(projectSettings);
87
+ const installed = userInstalled || projInstalled;
88
+ const scope = projInstalled && !userInstalled ? "project" : "user";
89
+ const configPath = scope === "project" ? projectSettings : userSettings;
90
+ return {
91
+ id: this.id,
92
+ name: this.name,
93
+ installed,
94
+ paradigm: this.paradigm,
95
+ capabilities: this.capabilities,
96
+ configPath,
97
+ scope,
98
+ reason: installed ? scope === "project" ? `found project Gemini CLI config at ${projectSettings}` : `found Gemini CLI config under ${userDir}` : `no Gemini CLI config at ${userDir}`,
99
+ confidence: installed ? "high" : "low"
100
+ };
101
+ }
102
+ // ── Native paths ─────────────────────────────────────────────────────────
103
+ getConfigDir(ctx) {
104
+ return ctx.scope === "project" ? join(ctx.projectDir, ".gemini") : join(homedir(), ".gemini");
105
+ }
106
+ /** MCP servers live in settings.json under `mcpServers`. */
107
+ getServerConfigPath(ctx) {
108
+ return join(this.getConfigDir(ctx), "settings.json");
109
+ }
110
+ /** Hooks live in the SAME settings.json under the top-level `hooks` key. */
111
+ getHookConfigPath(ctx) {
112
+ return join(this.getConfigDir(ctx), "settings.json");
113
+ }
114
+ // ── MCP server install / uninstall ───────────────────────────────────────
115
+ installServer(ctx) {
116
+ const { connector } = ctx;
117
+ const override = connector.platforms[HOST]?.server;
118
+ if (!connector.server || override === false) {
119
+ return [
120
+ {
121
+ platform: this.id,
122
+ action: "skip",
123
+ detail: connector.server ? "server registration disabled for gemini-cli" : "connector declares no MCP server"
124
+ }
125
+ ];
126
+ }
127
+ const server = override && typeof override === "object" ? { ...connector.server, ...override } : connector.server;
128
+ const serverPath = this.getServerConfigPath(ctx);
129
+ const entry = this.renderServerEntry(ctx, server);
130
+ return [
131
+ this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun)
132
+ ];
133
+ }
134
+ uninstallServer(ctx) {
135
+ const serverPath = this.getServerConfigPath(ctx);
136
+ return [
137
+ this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun)
138
+ ];
139
+ }
140
+ /**
141
+ * Render a normalized ServerDef into Gemini's native mcpServers entry. The
142
+ * transport is encoded by WHICH KEY is present, not a `type` field.
143
+ */
144
+ renderServerEntry(ctx, server) {
145
+ const transport = server.transport;
146
+ if (transport === "stdio") {
147
+ let command = server.command ?? "";
148
+ let args = [...server.args ?? []];
149
+ if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {
150
+ const wrapped = buildServeWrapperCommand(
151
+ ctx.homeBinPath,
152
+ ctx.connector.id,
153
+ command,
154
+ args,
155
+ ctx.scope,
156
+ this.id
157
+ );
158
+ command = wrapped.command;
159
+ args = wrapped.args;
160
+ }
161
+ const entry2 = { command: resolveEnvRefsDeep(command) };
162
+ if (args.length > 0) entry2.args = args.map((a) => resolveEnvRefsDeep(a));
163
+ const env = this.renderEnv(server.env);
164
+ if (env) entry2.env = env;
165
+ if (server.cwd) entry2.cwd = resolveEnvRefsDeep(server.cwd);
166
+ return entry2;
167
+ }
168
+ if (transport === "sse") {
169
+ const entry2 = { url: resolveEnvRefsDeep(server.url ?? "") };
170
+ const headers2 = this.renderEnv(server.headers);
171
+ if (headers2) entry2.headers = headers2;
172
+ return entry2;
173
+ }
174
+ const entry = { httpUrl: resolveEnvRefsDeep(server.url ?? "") };
175
+ const headers = this.renderEnv(server.headers);
176
+ if (headers) entry.headers = headers;
177
+ return entry;
178
+ }
179
+ /**
180
+ * Render env/header values. Gemini's native settings token is `${VAR}` (not
181
+ * the framework's `${env:VAR}`), so refs resolve to literals at install time —
182
+ * the safe default for a host without `${env:VAR}` interpolation.
183
+ */
184
+ renderEnv(env) {
185
+ if (!env || Object.keys(env).length === 0) return void 0;
186
+ return resolveEnvRefsDeep({ ...env });
187
+ }
188
+ // ── Hook install / uninstall ─────────────────────────────────────────────
189
+ installHooks(ctx) {
190
+ const { connector } = ctx;
191
+ if (connector.platforms[HOST]?.hooks === false) {
192
+ return [{ platform: this.id, action: "skip", detail: "hooks disabled for gemini-cli" }];
193
+ }
194
+ const hostNative = isHostNativeUsageEnabled(connector.telemetry);
195
+ if (connector.hookEvents.length === 0 && !hostNative) {
196
+ return [{ platform: this.id, action: "skip", detail: "connector declares no hooks" }];
197
+ }
198
+ const settingsPath = this.getHookConfigPath(ctx);
199
+ const settings = this.readJson(settingsPath) ?? {};
200
+ const hooks = settings.hooks ??= {};
201
+ const changes = [];
202
+ let mutated = false;
203
+ for (const event of connector.hookEvents) {
204
+ const geminiEvent = EVENT_MAP[event];
205
+ if (!geminiEvent) {
206
+ changes.push({
207
+ platform: this.id,
208
+ action: "warn",
209
+ path: settingsPath,
210
+ detail: `${event} has no Gemini CLI hook equivalent \u2014 skipped`
211
+ });
212
+ continue;
213
+ }
214
+ const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);
215
+ const matcher = connector.hooks[event]?.matcher ?? "";
216
+ const entry = {
217
+ matcher,
218
+ hooks: [{ type: "command", command }]
219
+ };
220
+ const bucket = hooks[geminiEvent] ??= [];
221
+ const existingIdx = bucket.findIndex((e) => this.entryHasOurCommand(e, ctx));
222
+ if (existingIdx >= 0) {
223
+ if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {
224
+ changes.push({
225
+ platform: this.id,
226
+ action: "skip",
227
+ path: settingsPath,
228
+ detail: `hooks.${geminiEvent} already registered`
229
+ });
230
+ continue;
231
+ }
232
+ bucket[existingIdx] = entry;
233
+ changes.push({
234
+ platform: this.id,
235
+ action: "update",
236
+ path: settingsPath,
237
+ detail: `hooks.${geminiEvent}`
238
+ });
239
+ } else {
240
+ bucket.push(entry);
241
+ changes.push({
242
+ platform: this.id,
243
+ action: "create",
244
+ path: settingsPath,
245
+ detail: `hooks.${geminiEvent}`
246
+ });
247
+ }
248
+ mutated = true;
249
+ }
250
+ if (hostNative) {
251
+ if (this.installUsageHook(hooks, ctx, settingsPath, changes)) mutated = true;
252
+ }
253
+ if (mutated) this.writeJson(settingsPath, settings, ctx.dryRun);
254
+ return changes;
255
+ }
256
+ /**
257
+ * Register the opt-in AfterModel host-native usage hook (4a) into `hooks`.
258
+ * Idempotent: a byte-identical entry skips, a drifted one updates. Returns true
259
+ * when the file was mutated. The command is the hidden `usage-event` entrypoint
260
+ * (NOT a connector hook), so it carries an empty matcher.
261
+ */
262
+ installUsageHook(hooks, ctx, settingsPath, changes) {
263
+ const command = buildUsageEventCommand(ctx.homeBinPath, HOST, ctx.connector.id);
264
+ const entry = {
265
+ matcher: "",
266
+ hooks: [{ type: "command", command }]
267
+ };
268
+ const bucket = hooks[GEMINI_EVENT.AfterModel] ??= [];
269
+ const existingIdx = bucket.findIndex(
270
+ (e) => (e.hooks ?? []).some((h) => isUsageEventCommand(h.command, ctx.homeBinPath, ctx.connector.id))
271
+ );
272
+ if (existingIdx >= 0) {
273
+ if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {
274
+ changes.push({
275
+ platform: this.id,
276
+ action: "skip",
277
+ path: settingsPath,
278
+ detail: `hooks.${GEMINI_EVENT.AfterModel} (host-native usage) already registered`
279
+ });
280
+ return false;
281
+ }
282
+ bucket[existingIdx] = entry;
283
+ changes.push({
284
+ platform: this.id,
285
+ action: "update",
286
+ path: settingsPath,
287
+ detail: `hooks.${GEMINI_EVENT.AfterModel} (host-native usage)`
288
+ });
289
+ } else {
290
+ bucket.push(entry);
291
+ changes.push({
292
+ platform: this.id,
293
+ action: "create",
294
+ path: settingsPath,
295
+ detail: `hooks.${GEMINI_EVENT.AfterModel} (host-native usage)`
296
+ });
297
+ }
298
+ return true;
299
+ }
300
+ uninstallHooks(ctx) {
301
+ const settingsPath = this.getHookConfigPath(ctx);
302
+ const settings = this.readJson(settingsPath);
303
+ const hooks = settings?.hooks;
304
+ if (!settings || !hooks) {
305
+ return [
306
+ {
307
+ platform: this.id,
308
+ action: "skip",
309
+ path: settingsPath,
310
+ detail: "no hooks section present"
311
+ }
312
+ ];
313
+ }
314
+ const changes = [];
315
+ let mutated = false;
316
+ for (const geminiEvent of Object.keys(hooks)) {
317
+ const bucket = hooks[geminiEvent];
318
+ if (!Array.isArray(bucket)) continue;
319
+ const next = [];
320
+ let removed = 0;
321
+ for (const e of bucket) {
322
+ const innerBefore = e.hooks?.length ?? 0;
323
+ const inner = (e.hooks ?? []).filter((h) => !this.isOurCommand(h.command, ctx));
324
+ removed += innerBefore - inner.length;
325
+ if (inner.length > 0) next.push({ matcher: e.matcher ?? "", hooks: inner });
326
+ }
327
+ if (removed > 0) {
328
+ if (next.length > 0) hooks[geminiEvent] = next;
329
+ else delete hooks[geminiEvent];
330
+ changes.push({
331
+ platform: this.id,
332
+ action: "remove",
333
+ path: settingsPath,
334
+ detail: `hooks.${geminiEvent} (${removed})`
335
+ });
336
+ mutated = true;
337
+ }
338
+ }
339
+ if (mutated) this.writeJson(settingsPath, settings, ctx.dryRun);
340
+ if (changes.length === 0) {
341
+ changes.push({
342
+ platform: this.id,
343
+ action: "skip",
344
+ path: settingsPath,
345
+ detail: "no matching hook entries"
346
+ });
347
+ }
348
+ return changes;
349
+ }
350
+ entryHasOurCommand(entry, ctx) {
351
+ return (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx));
352
+ }
353
+ /**
354
+ * True when a hook command is ONE OF OURS for this connector — either the
355
+ * universal `hook` dispatcher OR the opt-in `usage-event` sink. Both are
356
+ * anchored on the connector id (see isHomeBinHookCommand) so a shared-prefix id
357
+ * can't collide, and so uninstall reverses the AfterModel usage hook too.
358
+ */
359
+ isOurCommand(command, ctx) {
360
+ return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id) || isUsageEventCommand(command, ctx.homeBinPath, ctx.connector.id);
361
+ }
362
+ // ── Content surfaces: commands / skills / subagents ──────────────────────
363
+ // CONTENT-ONLY: pure native-file writers under <geminiDir>/{commands,skills,
364
+ // agents}. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each
365
+ // method is idempotent (byte-identical → skip) via BaseAdapter.writeContentFile
366
+ // and reversible via removeContentFile. Honors platforms["gemini-cli"]
367
+ // per-surface false to skip. Commands are TOML (Gemini's native format, with
368
+ // {{args}} placeholders preserved verbatim); skills/subagents mirror the
369
+ // Claude reference (uniform SKILL.md + md+frontmatter subagents).
370
+ commandsDir(ctx) {
371
+ return join(this.getConfigDir(ctx), "commands");
372
+ }
373
+ skillsDir(ctx) {
374
+ return join(this.getConfigDir(ctx), "skills");
375
+ }
376
+ agentsDir(ctx) {
377
+ return join(this.getConfigDir(ctx), "agents");
378
+ }
379
+ /** Native command file path: <geminiDir>/commands/<name>.toml. */
380
+ commandPath(ctx, name) {
381
+ return join(this.commandsDir(ctx), `${name}.toml`);
382
+ }
383
+ /** Native skill dir: <geminiDir>/skills/<name>. */
384
+ skillDir(ctx, name) {
385
+ return join(this.skillsDir(ctx), name);
386
+ }
387
+ /** Native subagent file path: <geminiDir>/agents/<name>.md. */
388
+ subagentPath(ctx, name) {
389
+ return join(this.agentsDir(ctx), `${name}.md`);
390
+ }
391
+ // ── Commands ──────────────────────────────────────────────────────────────
392
+ installCommands(ctx) {
393
+ const { connector } = ctx;
394
+ if (connector.platforms[HOST]?.commands === false) {
395
+ return [{ platform: this.id, action: "skip", detail: "commands disabled for gemini-cli" }];
396
+ }
397
+ if (connector.commands.length === 0) {
398
+ return [{ platform: this.id, action: "skip", detail: "connector declares no commands" }];
399
+ }
400
+ return connector.commands.map(
401
+ (cmd) => this.writeContentFile(this.commandPath(ctx, cmd.name), this.renderCommand(cmd), ctx.dryRun)
402
+ );
403
+ }
404
+ uninstallCommands(ctx) {
405
+ const { connector } = ctx;
406
+ if (connector.commands.length === 0) {
407
+ return [{ platform: this.id, action: "skip", detail: "connector declares no commands" }];
408
+ }
409
+ return connector.commands.map(
410
+ (cmd) => this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun)
411
+ );
412
+ }
413
+ /**
414
+ * Render a command to Gemini's native TOML: { description, prompt }. Gemini
415
+ * uses `{{args}}` placeholders in the prompt; the prompt body is passed
416
+ * through verbatim so any placeholders are preserved.
417
+ */
418
+ renderCommand(cmd) {
419
+ const obj = {};
420
+ if (cmd.description !== void 0) obj.description = cmd.description;
421
+ obj.prompt = cmd.prompt;
422
+ return writeTomlString(obj);
423
+ }
424
+ // ── Skills ────────────────────────────────────────────────────────────────
425
+ installSkills(ctx) {
426
+ const { connector } = ctx;
427
+ if (connector.platforms[HOST]?.skills === false) {
428
+ return [{ platform: this.id, action: "skip", detail: "skills disabled for gemini-cli" }];
429
+ }
430
+ if (connector.skills.length === 0) {
431
+ return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
432
+ }
433
+ const changes = [];
434
+ for (const skill of connector.skills) {
435
+ const dir = this.skillDir(ctx, skill.name);
436
+ changes.push(
437
+ this.writeContentFile(join(dir, "SKILL.md"), this.renderSkill(skill), ctx.dryRun)
438
+ );
439
+ for (const [rel, contents] of Object.entries(skill.resources ?? {})) {
440
+ const target = this.resolveWithin(dir, rel);
441
+ if (target === null) {
442
+ changes.push({
443
+ platform: this.id,
444
+ action: "warn",
445
+ detail: `skill resource "${rel}" escapes the skill dir; skipped`
446
+ });
447
+ continue;
448
+ }
449
+ changes.push(this.writeContentFile(target, contents, ctx.dryRun));
450
+ }
451
+ }
452
+ return changes;
453
+ }
454
+ uninstallSkills(ctx) {
455
+ const { connector } = ctx;
456
+ if (connector.skills.length === 0) {
457
+ return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
458
+ }
459
+ const changes = [];
460
+ for (const skill of connector.skills) {
461
+ const dir = this.skillDir(ctx, skill.name);
462
+ changes.push(this.removeContentFile(join(dir, "SKILL.md"), ctx.dryRun));
463
+ for (const rel of Object.keys(skill.resources ?? {})) {
464
+ const target = this.resolveWithin(dir, rel);
465
+ if (target === null) continue;
466
+ changes.push(this.removeContentFile(target, ctx.dryRun));
467
+ }
468
+ changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));
469
+ }
470
+ return changes;
471
+ }
472
+ /**
473
+ * Render a skill's SKILL.md: frontmatter (name, description + optional model,
474
+ * allowed-tools, disable-model-invocation) + body. Uniform across platforms.
475
+ */
476
+ renderSkill(skill) {
477
+ const frontmatter = {
478
+ name: skill.name,
479
+ description: skill.description
480
+ };
481
+ if (skill.model !== void 0) frontmatter.model = skill.model;
482
+ const allow = skill.tools?.allow;
483
+ if (allow && allow.length > 0) frontmatter["allowed-tools"] = allow.join(", ");
484
+ if (skill.disableModelInvocation !== void 0) {
485
+ frontmatter["disable-model-invocation"] = skill.disableModelInvocation;
486
+ }
487
+ if (skill.extra) Object.assign(frontmatter, skill.extra);
488
+ return this.renderFrontmatterMd(frontmatter, skill.body);
489
+ }
490
+ // ── Subagents ───────────────────────────────────────────────────────────────
491
+ installSubagents(ctx) {
492
+ const { connector } = ctx;
493
+ if (connector.platforms[HOST]?.subagents === false) {
494
+ return [{ platform: this.id, action: "skip", detail: "subagents disabled for gemini-cli" }];
495
+ }
496
+ if (connector.subagents.length === 0) {
497
+ return [{ platform: this.id, action: "skip", detail: "connector declares no subagents" }];
498
+ }
499
+ return connector.subagents.map(
500
+ (agent) => this.writeContentFile(this.subagentPath(ctx, agent.name), this.renderSubagent(agent), ctx.dryRun)
501
+ );
502
+ }
503
+ uninstallSubagents(ctx) {
504
+ const { connector } = ctx;
505
+ if (connector.subagents.length === 0) {
506
+ return [{ platform: this.id, action: "skip", detail: "connector declares no subagents" }];
507
+ }
508
+ return connector.subagents.map(
509
+ (agent) => this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun)
510
+ );
511
+ }
512
+ /** Render a subagent to md+frontmatter (name, description, tools, model) + prompt body. */
513
+ renderSubagent(agent) {
514
+ const frontmatter = {
515
+ name: agent.name,
516
+ description: agent.description
517
+ };
518
+ const allow = agent.tools?.allow;
519
+ if (allow && allow.length > 0) frontmatter.tools = allow.join(", ");
520
+ if (agent.model !== void 0) frontmatter.model = agent.model;
521
+ if (agent.extra) Object.assign(frontmatter, agent.extra);
522
+ return this.renderFrontmatterMd(frontmatter, agent.prompt);
523
+ }
524
+ // ── Diagnostics ──────────────────────────────────────────────────────────
525
+ getHealthChecks(ctx) {
526
+ const settingsPath = this.getServerConfigPath(ctx);
527
+ const connectorId = ctx.connector.id;
528
+ const homeBin = ctx.homeBinPath;
529
+ const hookEvents = ctx.connector.hookEvents;
530
+ const checks = [
531
+ {
532
+ name: `${this.name}: settings.json present`,
533
+ check: () => existsSync(settingsPath) ? { status: "OK", detail: settingsPath } : { status: "FAIL", detail: `not found: ${settingsPath}` }
534
+ },
535
+ {
536
+ name: `${this.name}: hook command registered`,
537
+ check: () => {
538
+ if (hookEvents.length === 0) {
539
+ return { status: "OK", detail: "no hooks declared" };
540
+ }
541
+ const settings = this.readJson(settingsPath);
542
+ if (!settings) return { status: "FAIL", detail: `cannot read ${settingsPath}` };
543
+ const hooks = settings.hooks ?? {};
544
+ const registered = Object.values(hooks).some(
545
+ (entries) => (entries ?? []).some(
546
+ (e) => (e.hooks ?? []).some(
547
+ (h) => isHomeBinHookCommand(h.command, homeBin, connectorId)
548
+ )
549
+ )
550
+ );
551
+ return registered ? { status: "OK", detail: "hook command present" } : { status: "FAIL", detail: `no hook for ${connectorId} in ${settingsPath}` };
552
+ }
553
+ }
554
+ ];
555
+ for (const cmd of ctx.connector.commands) {
556
+ const p = this.commandPath(ctx, cmd.name);
557
+ checks.push({
558
+ name: `${this.name}: command ${cmd.name} present`,
559
+ check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
560
+ });
561
+ }
562
+ for (const skill of ctx.connector.skills) {
563
+ const p = join(this.skillDir(ctx, skill.name), "SKILL.md");
564
+ checks.push({
565
+ name: `${this.name}: skill ${skill.name} present`,
566
+ check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
567
+ });
568
+ }
569
+ for (const agent of ctx.connector.subagents) {
570
+ const p = this.subagentPath(ctx, agent.name);
571
+ checks.push({
572
+ name: `${this.name}: subagent ${agent.name} present`,
573
+ check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
574
+ });
575
+ }
576
+ return checks;
577
+ }
578
+ // ── Runtime: parse Gemini stdin JSON → normalized event ──────────────────
579
+ parseEvent(event, raw) {
580
+ const input = raw ?? {};
581
+ const connectorId = typeof input.connector === "string" ? input.connector : "";
582
+ const sessionId = typeof input.session_id === "string" ? input.session_id : "";
583
+ const projectDir = typeof input.cwd === "string" ? input.cwd : void 0;
584
+ const base = {
585
+ hostPlatform: HOST,
586
+ connectorId,
587
+ sessionId,
588
+ raw,
589
+ ...projectDir !== void 0 ? { projectDir } : {}
590
+ };
591
+ switch (event) {
592
+ case "PreToolUse": {
593
+ const ev = {
594
+ ...base,
595
+ toolName: input.tool_name ?? "",
596
+ toolInput: input.tool_input ?? {}
597
+ };
598
+ return ev;
599
+ }
600
+ case "PostToolUse": {
601
+ const ev = {
602
+ ...base,
603
+ toolName: input.tool_name ?? "",
604
+ toolInput: input.tool_input ?? {},
605
+ ...input.tool_output !== void 0 ? { toolOutput: input.tool_output } : {},
606
+ ...input.is_error === true ? { isError: true } : {}
607
+ };
608
+ return ev;
609
+ }
610
+ case "SessionStart": {
611
+ const ev = {
612
+ ...base,
613
+ source: normalizeSessionSource(input.source)
614
+ };
615
+ return ev;
616
+ }
617
+ case "SessionEnd": {
618
+ const ev = {
619
+ ...base,
620
+ ...typeof input.reason === "string" ? { reason: input.reason } : {}
621
+ };
622
+ return ev;
623
+ }
624
+ case "UserPromptSubmit": {
625
+ const ev = {
626
+ ...base,
627
+ prompt: typeof input.prompt === "string" ? input.prompt : ""
628
+ };
629
+ return ev;
630
+ }
631
+ case "PreCompact": {
632
+ const ev = {
633
+ ...base,
634
+ ...input.trigger === "auto" || input.trigger === "manual" ? { trigger: input.trigger } : {}
635
+ };
636
+ return ev;
637
+ }
638
+ case "Notification": {
639
+ const ev = {
640
+ ...base,
641
+ message: typeof input.message === "string" ? input.message : ""
642
+ };
643
+ return ev;
644
+ }
645
+ default: {
646
+ throw new Error(`unsupported gemini-cli hook event: ${String(event)}`);
647
+ }
648
+ }
649
+ }
650
+ // ── Runtime: normalized response → Gemini native hook reply ──────────────
651
+ formatReply(event, response) {
652
+ const decision = response.decision ?? "allow";
653
+ if (decision === "deny") {
654
+ return this.stdout({
655
+ decision: "deny",
656
+ reason: response.reason ?? "Blocked by hook"
657
+ });
658
+ }
659
+ if (decision === "ask") {
660
+ return this.stdout({
661
+ decision: "deny",
662
+ reason: response.reason ?? "Action requires user confirmation (security policy)"
663
+ });
664
+ }
665
+ if (decision === "modify") {
666
+ if (event === "PreToolUse" && response.updatedInput) {
667
+ return this.stdout({
668
+ hookSpecificOutput: { tool_input: response.updatedInput }
669
+ });
670
+ }
671
+ if (event === "PostToolUse" && response.updatedOutput !== void 0) {
672
+ return this.stdout({ decision: "deny", reason: response.updatedOutput });
673
+ }
674
+ }
675
+ if (decision === "context" && response.additionalContext) {
676
+ return this.stdout({
677
+ hookSpecificOutput: { additionalContext: response.additionalContext }
678
+ });
679
+ }
680
+ return { exitCode: 0 };
681
+ }
682
+ stdout(payload) {
683
+ return { exitCode: 0, stdout: JSON.stringify(payload) };
684
+ }
685
+ };
686
+ function normalizeSessionSource(source) {
687
+ switch (source) {
688
+ case "compact":
689
+ return "compact";
690
+ case "resume":
691
+ return "resume";
692
+ case "clear":
693
+ return "clear";
694
+ default:
695
+ return "startup";
696
+ }
697
+ }
698
+ var adapter = new GeminiCliAdapter();
699
+ var gemini_cli_default = adapter;
700
+ export {
701
+ GeminiCliAdapter,
702
+ adapter,
703
+ gemini_cli_default as default
704
+ };
705
+ //# sourceMappingURL=gemini-cli-Y35FFK4M.js.map