@paradigma-inc/flywheel 0.1.53 → 0.1.60

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 (121) hide show
  1. package/README.md +121 -10
  2. package/package.json +1 -1
  3. package/skills/flywheel/references/flywheel-cli-tool-map.md +8 -0
  4. package/skills/flywheel/references/flywheel-mcp-tool-map.md +8 -0
  5. package/src/agents.mjs +114 -48
  6. package/src/cli.mjs +267 -35
  7. package/src/mcp-writer.mjs +190 -17
  8. package/src/runtime/required-runtime-commands.json +17 -4
  9. package/src/runtime/vendor/flywheel-cli-dist/auth/login.d.ts +1 -0
  10. package/src/runtime/vendor/flywheel-cli-dist/auth/login.js +3 -6
  11. package/src/runtime/vendor/flywheel-cli-dist/auth/login.js.map +1 -1
  12. package/src/runtime/vendor/flywheel-cli-dist/auth/profiles.d.ts +1 -0
  13. package/src/runtime/vendor/flywheel-cli-dist/auth/profiles.js +1 -1
  14. package/src/runtime/vendor/flywheel-cli-dist/auth/profiles.js.map +1 -1
  15. package/src/runtime/vendor/flywheel-cli-dist/commands/_admin-auth.d.ts +25 -0
  16. package/src/runtime/vendor/flywheel-cli-dist/commands/_admin-auth.js +35 -0
  17. package/src/runtime/vendor/flywheel-cli-dist/commands/_admin-auth.js.map +1 -0
  18. package/src/runtime/vendor/flywheel-cli-dist/commands/_stream-file-out.d.ts +39 -0
  19. package/src/runtime/vendor/flywheel-cli-dist/commands/_stream-file-out.js +72 -0
  20. package/src/runtime/vendor/flywheel-cli-dist/commands/_stream-file-out.js.map +1 -0
  21. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-compute-policy-get.d.ts +2 -0
  22. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-compute-policy-get.js +10 -0
  23. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-compute-policy-get.js.map +1 -0
  24. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-compute-policy-set.d.ts +2 -0
  25. package/src/runtime/vendor/flywheel-cli-dist/commands/{compute-policy-set.js → admin-compute-policy-set.js} +8 -3
  26. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-compute-policy-set.js.map +1 -0
  27. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-export-stream.d.ts +2 -0
  28. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-export-stream.js +114 -0
  29. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-export-stream.js.map +1 -0
  30. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-export.d.ts +2 -0
  31. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-export.js +11 -0
  32. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-export.js.map +1 -0
  33. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-list.d.ts +4 -0
  34. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-list.js +38 -0
  35. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-graphs-list.js.map +1 -0
  36. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-nodes-audit-list.d.ts +2 -0
  37. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-nodes-audit-list.js +14 -0
  38. package/src/runtime/vendor/flywheel-cli-dist/commands/admin-nodes-audit-list.js.map +1 -0
  39. package/src/runtime/vendor/flywheel-cli-dist/commands/enums.d.ts +6 -0
  40. package/src/runtime/vendor/flywheel-cli-dist/commands/enums.js +3 -0
  41. package/src/runtime/vendor/flywheel-cli-dist/commands/enums.js.map +1 -1
  42. package/src/runtime/vendor/flywheel-cli-dist/commands/env.d.ts +0 -4
  43. package/src/runtime/vendor/flywheel-cli-dist/commands/env.js +7 -12
  44. package/src/runtime/vendor/flywheel-cli-dist/commands/env.js.map +1 -1
  45. package/src/runtime/vendor/flywheel-cli-dist/commands/export-history.js +40 -30
  46. package/src/runtime/vendor/flywheel-cli-dist/commands/export-history.js.map +1 -1
  47. package/src/runtime/vendor/flywheel-cli-dist/commands/help.d.ts +2 -0
  48. package/src/runtime/vendor/flywheel-cli-dist/commands/help.js +60 -10
  49. package/src/runtime/vendor/flywheel-cli-dist/commands/help.js.map +1 -1
  50. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-create.d.ts +2 -0
  51. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-create.js +25 -0
  52. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-create.js.map +1 -0
  53. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-delete.d.ts +1 -0
  54. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-delete.js +6 -0
  55. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-delete.js.map +1 -0
  56. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-list.d.ts +2 -0
  57. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-list.js +10 -0
  58. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-list.js.map +1 -0
  59. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-runs-list.d.ts +2 -0
  60. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-runs-list.js +15 -0
  61. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-runs-list.js.map +1 -0
  62. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-create.d.ts +2 -0
  63. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-create.js +8 -0
  64. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-create.js.map +1 -0
  65. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-delete.d.ts +1 -0
  66. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-delete.js +6 -0
  67. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-delete.js.map +1 -0
  68. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-list.d.ts +1 -0
  69. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-list.js +6 -0
  70. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-list.js.map +1 -0
  71. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-update.d.ts +2 -0
  72. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-update.js +8 -0
  73. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-secrets-update.js.map +1 -0
  74. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-set-enabled.d.ts +2 -0
  75. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-set-enabled.js +8 -0
  76. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-set-enabled.js.map +1 -0
  77. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-update.d.ts +2 -0
  78. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-update.js +29 -0
  79. package/src/runtime/vendor/flywheel-cli-dist/commands/hooks-update.js.map +1 -0
  80. package/src/runtime/vendor/flywheel-cli-dist/commands/index.js +32 -6
  81. package/src/runtime/vendor/flywheel-cli-dist/commands/index.js.map +1 -1
  82. package/src/runtime/vendor/flywheel-cli-dist/commands/profile-set.js +4 -2
  83. package/src/runtime/vendor/flywheel-cli-dist/commands/profile-set.js.map +1 -1
  84. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/admin.d.ts +8 -0
  85. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/admin.js +118 -0
  86. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/admin.js.map +1 -0
  87. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/auth.js +2 -2
  88. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/auth.js.map +1 -1
  89. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/env.js +7 -5
  90. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/env.js.map +1 -1
  91. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/hooks.d.ts +2 -0
  92. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/hooks.js +244 -0
  93. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/hooks.js.map +1 -0
  94. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/lifecycle.js +1 -16
  95. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/lifecycle.js.map +1 -1
  96. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/profile.js +2 -2
  97. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/profile.js.map +1 -1
  98. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/resources.js +1 -31
  99. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/resources.js.map +1 -1
  100. package/src/runtime/vendor/flywheel-cli-dist/commands/registry.d.ts +2 -1
  101. package/src/runtime/vendor/flywheel-cli-dist/commands/registry.js +4 -0
  102. package/src/runtime/vendor/flywheel-cli-dist/commands/registry.js.map +1 -1
  103. package/src/runtime/vendor/flywheel-cli-dist/main.js +1 -1
  104. package/src/runtime/vendor/flywheel-cli-dist/main.js.map +1 -1
  105. package/src/runtime/vendor/manifest.json +61 -10
  106. package/src/setup/io/patchers/json.mjs +2 -0
  107. package/src/setup/io/patchers/toml.mjs +7 -1
  108. package/src/setup/io/patchers/yaml.mjs +2 -0
  109. package/src/setup/modes/cli.mjs +41 -16
  110. package/src/setup/modes/mcp-command.mjs +98 -19
  111. package/src/setup/modes/mcp.mjs +26 -1
  112. package/src/setup/shared/prior-mode-detect.mjs +133 -14
  113. package/src/skill-installer.mjs +179 -1
  114. package/src/runtime/vendor/flywheel-cli-dist/commands/compute-policy-get.d.ts +0 -1
  115. package/src/runtime/vendor/flywheel-cli-dist/commands/compute-policy-get.js +0 -6
  116. package/src/runtime/vendor/flywheel-cli-dist/commands/compute-policy-get.js.map +0 -1
  117. package/src/runtime/vendor/flywheel-cli-dist/commands/compute-policy-set.d.ts +0 -2
  118. package/src/runtime/vendor/flywheel-cli-dist/commands/compute-policy-set.js.map +0 -1
  119. package/src/runtime/vendor/flywheel-cli-dist/commands/nodes-audit-list.d.ts +0 -2
  120. package/src/runtime/vendor/flywheel-cli-dist/commands/nodes-audit-list.js +0 -7
  121. package/src/runtime/vendor/flywheel-cli-dist/commands/nodes-audit-list.js.map +0 -1
package/README.md CHANGED
@@ -38,6 +38,31 @@ Configure your AI host (Claude Code, Codex, etc.) to call Flywheel over MCP:
38
38
  npx --yes @paradigma-inc/flywheel@latest setup --mode mcp --install-skill
39
39
  ```
40
40
 
41
+ ## Uninstall
42
+
43
+ One-shot uninstaller (macOS and Linux):
44
+
45
+ ```bash
46
+ curl -fsSL https://flywheel.paradigma.inc/uninstall | sh
47
+ ```
48
+
49
+ The script removes Flywheel MCP entries, bundled skills, the global
50
+ `@paradigma-inc/flywheel` package, the `flywheel` binary, default CLI
51
+ config/profiles under `~/.config/flywheel-cli`, and the local runtime cache
52
+ under `~/.flywheel`. It does not delete remote Flywheel account data, nodes,
53
+ artifacts, billing data, GitHub connections, or other remote resources.
54
+
55
+ Manual fallback:
56
+
57
+ ```bash
58
+ npx --yes @paradigma-inc/flywheel@latest uninstall --yes --scope all
59
+ npm uninstall -g @paradigma-inc/flywheel
60
+ rm -rf "$HOME/.config/flywheel-cli" "$HOME/.flywheel"
61
+ ```
62
+
63
+ With `--yes --scope all` and no host selectors, `flywheel uninstall` targets
64
+ every supported host.
65
+
41
66
  ## Setup Modes
42
67
 
43
68
  ```bash
@@ -156,15 +181,18 @@ npx --yes @paradigma-inc/flywheel setup --mode mcp --install-skill \
156
181
 
157
182
  ## Host Matrix
158
183
 
159
- | Host | MCP wiring (`--mode mcp`) | CLI-mode bundled skill install (`--mode cli`) |
160
- | ------------ | ------------------------- | --------------------------------------------- |
161
- | Claude Code | yes | yes |
162
- | OpenCode | yes | yes |
163
- | Codex | yes | yes |
164
- | Cursor | yes | yes |
165
- | Pi (pi-mono) | yes | yes |
166
- | OpenClaw | yes | yes |
167
- | Hermes Agent | yes | no (MCP-only host) |
184
+ | Host | MCP wiring (`--mode mcp`) | CLI-mode bundled skill install (`--mode cli`) |
185
+ | ----------------------------- | ------------------------- | --------------------------------------------- |
186
+ | Claude Code | yes | yes |
187
+ | Cursor | yes | yes |
188
+ | Universal (`.agents/skills`) | no (skill-only) | yes |
189
+ | Antigravity (`.agent/skills`) | no (skill-only) | yes |
190
+ | OpenCode | yes | yes |
191
+ | Codex | yes | yes |
192
+ | Gemini CLI | yes | yes |
193
+ | Pi (pi-mono) | yes | yes |
194
+ | OpenClaw | yes | yes |
195
+ | Hermes Agent | yes | no (MCP-only host) |
168
196
 
169
197
  CLI mode handling for Hermes Agent:
170
198
 
@@ -172,6 +200,88 @@ CLI mode handling for Hermes Agent:
172
200
  - mixed supported + unsupported selection: command continues for supported
173
201
  hosts and reports skipped unsupported hosts
174
202
 
203
+ ### New flags (ctx7 alignment)
204
+
205
+ These flags align the `flywheel setup` surface with the `npx ctx7 setup`
206
+ convention while keeping every Flywheel-specific flag intact. They are
207
+ additive: existing invocations continue to behave as before.
208
+
209
+ #### `--oauth`
210
+
211
+ Writes an MCP config that omits the `Authorization` header and delegates
212
+ authentication to the IDE's MCP 2.0 OAuth discovery flow. No API key is
213
+ minted, stored, or transmitted by `flywheel setup` itself.
214
+
215
+ ```bash
216
+ npx --yes @paradigma-inc/flywheel setup --mode mcp --claude --oauth --project
217
+ ```
218
+
219
+ The resulting `.mcp.json`:
220
+
221
+ ```json
222
+ {
223
+ "mcpServers": {
224
+ "flywheel": {
225
+ "type": "http",
226
+ "url": "https://flywheel.paradigma.inc/mcp-server"
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ The IDE must support MCP 2.0 OAuth discovery (Claude Code, Cursor, and
233
+ Gemini CLI currently do). On the first MCP tool call the IDE hits
234
+ `/mcp-server`, follows the `WWW-Authenticate` hint to
235
+ `/.well-known/oauth-protected-resource`, registers itself via
236
+ `/oauth/register`, completes the browser flow, and from then on manages
237
+ its own refresh tokens. No API key ever touches the user's filesystem.
238
+
239
+ `--api-key` and `--oauth` are mutually exclusive; passing both produces a
240
+ usage error (`exit 2`).
241
+
242
+ If a previous `flywheel setup` run wrote an MCP entry with an
243
+ `Authorization` header against the same URL, re-running with `--oauth`
244
+ will classify that entry as matching and leave the header in place. To
245
+ rewrite the config to drop the header, first run
246
+ `flywheel uninstall --<host> --project` and then re-run setup with
247
+ `--oauth`.
248
+
249
+ #### `--gemini`
250
+
251
+ Registers Flywheel as an MCP server for Gemini CLI at
252
+ `.gemini/settings.json` (project) or `~/.gemini/settings.json` (global).
253
+ Gemini CLI uses the `mcpServers.flywheel.httpUrl` key (not `url`); when
254
+ `--api-key <key>` is supplied, the setup writes a `Bearer <key>`
255
+ `Authorization` header. With `--oauth` the header is omitted.
256
+
257
+ ```bash
258
+ npx --yes @paradigma-inc/flywheel setup --mode mcp --gemini --project --api-key <token>
259
+ ```
260
+
261
+ #### `--universal`
262
+
263
+ Skill-only target that writes Flywheel's bundled skills into
264
+ `./.agents/skills/<skill-name>/` (project) or `~/.agents/skills/<skill-name>/`
265
+ (global). Writes no MCP server config and no agent-specific rules. Useful
266
+ for toolchains that read the conventional `.agents/skills/` layout without
267
+ needing a Flywheel MCP binding. Requires `--mode cli` explicitly and is
268
+ never auto-detected; opt-in only.
269
+
270
+ ```bash
271
+ npx --yes @paradigma-inc/flywheel setup --mode cli --universal --project
272
+ ```
273
+
274
+ #### `--antigravity`
275
+
276
+ Same skill-only shape as `--universal` but targets
277
+ `./.agent/skills/<skill-name>/` (project) or `~/.agent/skills/<skill-name>/`
278
+ (global) — note the singular `.agent` directory. Writes no MCP server
279
+ config. Requires `--mode cli` explicitly and is never auto-detected.
280
+
281
+ ```bash
282
+ npx --yes @paradigma-inc/flywheel setup --mode cli --antigravity --project
283
+ ```
284
+
175
285
  ## Public Update Flow
176
286
 
177
287
  After a new release:
@@ -200,7 +310,8 @@ flags.
200
310
 
201
311
  - `setup`: setup flow with explicit mode routing.
202
312
  - `uninstall`: removes Flywheel MCP entries from host configs and removes
203
- bundled skills from supported hosts.
313
+ bundled skills from supported hosts. Use the one-shot uninstaller above for a
314
+ full local cleanup, including the global npm package and default local state.
204
315
 
205
316
  ```bash
206
317
  npx --yes @paradigma-inc/flywheel uninstall
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paradigma-inc/flywheel",
3
- "version": "0.1.53",
3
+ "version": "0.1.60",
4
4
  "description": "One-command setup for Flywheel MCP hosts",
5
5
  "type": "module",
6
6
  "files": [
@@ -237,6 +237,14 @@ These commands have no MCP counterpart. They cover auth/profile management, acco
237
237
  - `flywheel artifacts:list`, `flywheel artifacts:get` inspect node artifact metadata (`title` is the display label) and consume `storage_url` for raw artifact bytes only.
238
238
  - `flywheel nodes:commit-new`, `flywheel nodes:commit` publish the caller's full staged payload for an existing node once terminal and contract-complete; commit on an existing node requires an active stage lease and explicit `--base_committed_revision`.
239
239
 
240
+ ## Admin only (FLY-359)
241
+
242
+ These commands require `FLYWHEEL_ADMIN_KEY` in the environment (forwarded as the `X-Admin-Key` header) or an authenticated `@paradigma.inc` session. The server rejects all other callers with 401/403. The endpoints are omitted from the public OpenAPI schema and from the API-key allowlist.
243
+
244
+ - `flywheel admin:graphs:list` — list every root-node graph across all users. Filters: `--tag-id`, `--kind`, `--owner-user-id`, `--created-after`, `--created-before`, `--cursor`, `--page-size` (1..200).
245
+ - `flywheel admin:graphs:export --root-node-id=<id>` — export one graph as canonical JSON, bypassing per-caller visibility.
246
+ - `flywheel admin:graphs:export:stream` — stream canonical graph JSON for many graphs as NDJSON (one page per call, `--out=<file>` to write to disk). Filters match `admin:graphs:list`.
247
+
240
248
  ## See Also
241
249
 
242
250
  - `flywheel-mcp-tool-map.md` — the matching MCP routing reference.
@@ -194,3 +194,11 @@ Hook workflow-if contract (first version):
194
194
  - `flywheel_delete_artifact`: remove an accidental/obsolete node artifact.
195
195
  - `flywheel_list_artifacts`, `flywheel_get_artifact`: inspect node artifact metadata (`title` is the display label) and consume `storage_url` for raw artifact bytes only.
196
196
  - `flywheel_commit_new_node`, `flywheel_commit_node`: publish the caller's full staged payload for an existing node once terminal and contract-complete; requires an active stage lease and explicit `base_committed_revision`.
197
+
198
+ ### Admin only (FLY-359)
199
+
200
+ These tools require an `@paradigma.inc`-authenticated MCP session **or** a valid `X-Admin-Key` header sent on the MCP HTTP request. The MCP streamable HTTP mount reads `X-Admin-Key` from the inbound request and forwards it through to the in-process `/admin/graphs*` call; on admin paths the proxy also suppresses the session bearer to sidestep the API-key route allowlist check. `require_paradigma_admin` then grants access based on the admin key. The routes are omitted from the public OpenAPI schema and from the API-key allowlist.
201
+
202
+ - `flywheel_admin_list_graphs`: list every root-node graph across all users. Filters: `tag_id`, `kind`, `owner_user_id`, `created_after`, `created_before`, `cursor`, `page_size` (1..200). Returns `{graphs, has_more, next_cursor}`.
203
+ - `flywheel_admin_export_graph`: export one graph by `root_node_id` as canonical JSON (`{version, exported_at, node_count, nodes}`). Bypasses per-caller node-visibility checks by design.
204
+ - `flywheel_admin_export_graphs_stream`: batch-export one page of canonical graph JSON payloads matching the filter; iterate pages via `next_cursor`. The NDJSON HTTP endpoint (`GET /admin/graphs/export.jsonl`) is the CLI-first streaming surface; the MCP tool materializes a page to fit the RPC model.
package/src/agents.mjs CHANGED
@@ -5,23 +5,33 @@ import path from "node:path";
5
5
  export const SERVER_NAME = "flywheel";
6
6
  export const ALL_HOST_NAMES = [
7
7
  "claude",
8
+ "cursor",
9
+ "universal",
10
+ "antigravity",
8
11
  "opencode",
9
12
  "codex",
10
- "cursor",
13
+ "gemini",
11
14
  "pi-mono",
12
15
  "hermes-agent",
13
16
  "openclaw",
14
17
  ];
15
18
  export const SETUP_HOST_NAMES = {
16
19
  claude: "Claude Code",
20
+ cursor: "Cursor",
21
+ universal: "Universal (.agents/skills)",
22
+ antigravity: "Antigravity (.agent/skills)",
17
23
  opencode: "OpenCode",
18
24
  codex: "Codex",
19
- cursor: "Cursor",
25
+ gemini: "Gemini CLI",
20
26
  "pi-mono": "Pi (pi-mono)",
21
27
  "hermes-agent": "Hermes Agent",
22
28
  openclaw: "OpenClaw",
23
29
  };
24
30
 
31
+ export function isSkillOnlyHost(host) {
32
+ return host != null && host.mcp == null;
33
+ }
34
+
25
35
  const HOME = os.homedir();
26
36
 
27
37
  const hosts = {
@@ -33,13 +43,13 @@ const hosts = {
33
43
  projectPaths: [".mcp.json"],
34
44
  globalPaths: [path.join(HOME, ".claude.json")],
35
45
  configKey: "mcpServers",
36
- buildEntry: ({ serverUrl, apiKey }) => ({
37
- type: "http",
38
- url: serverUrl,
39
- headers: {
40
- Authorization: `Bearer ${apiKey}`,
41
- },
42
- }),
46
+ buildEntry: ({ serverUrl, apiKey }) => {
47
+ const entry = { type: "http", url: serverUrl };
48
+ if (apiKey) {
49
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
50
+ }
51
+ return entry;
52
+ },
43
53
  },
44
54
  detect: {
45
55
  projectPaths: [".mcp.json", ".claude"],
@@ -67,14 +77,13 @@ const hosts = {
67
77
  path.join(HOME, ".config", "opencode", ".opencode.jsonc"),
68
78
  ],
69
79
  configKey: "mcp",
70
- buildEntry: ({ serverUrl, apiKey }) => ({
71
- type: "remote",
72
- url: serverUrl,
73
- enabled: true,
74
- headers: {
75
- Authorization: `Bearer ${apiKey}`,
76
- },
77
- }),
80
+ buildEntry: ({ serverUrl, apiKey }) => {
81
+ const entry = { type: "remote", url: serverUrl, enabled: true };
82
+ if (apiKey) {
83
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
84
+ }
85
+ return entry;
86
+ },
78
87
  },
79
88
  detect: {
80
89
  projectPaths: [
@@ -94,13 +103,13 @@ const hosts = {
94
103
  projectPaths: [path.join(".codex", "config.toml")],
95
104
  globalPaths: [path.join(HOME, ".codex", "config.toml")],
96
105
  configKey: "mcp_servers",
97
- buildEntry: ({ serverUrl, apiKey }) => ({
98
- type: "http",
99
- url: serverUrl,
100
- headers: {
101
- Authorization: `Bearer ${apiKey}`,
102
- },
103
- }),
106
+ buildEntry: ({ serverUrl, apiKey }) => {
107
+ const entry = { type: "http", url: serverUrl };
108
+ if (apiKey) {
109
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
110
+ }
111
+ return entry;
112
+ },
104
113
  },
105
114
  detect: {
106
115
  projectPaths: [".codex", path.join(".codex", "config.toml")],
@@ -115,18 +124,43 @@ const hosts = {
115
124
  projectPaths: [path.join(".cursor", "mcp.json")],
116
125
  globalPaths: [path.join(HOME, ".cursor", "mcp.json")],
117
126
  configKey: "mcpServers",
118
- buildEntry: ({ serverUrl, apiKey }) => ({
119
- url: serverUrl,
120
- headers: {
121
- Authorization: `Bearer ${apiKey}`,
122
- },
123
- }),
127
+ buildEntry: ({ serverUrl, apiKey }) => {
128
+ const entry = { url: serverUrl };
129
+ if (apiKey) {
130
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
131
+ }
132
+ return entry;
133
+ },
124
134
  },
125
135
  detect: {
126
136
  projectPaths: [".cursor", path.join(".cursor", "mcp.json")],
127
137
  globalPaths: [path.join(HOME, ".cursor")],
128
138
  },
129
139
  },
140
+ gemini: {
141
+ name: "gemini",
142
+ displayName: "Gemini CLI",
143
+ mcp: {
144
+ configType: "json",
145
+ projectPaths: [path.join(".gemini", "settings.json")],
146
+ globalPaths: [path.join(HOME, ".gemini", "settings.json")],
147
+ configKey: "mcpServers",
148
+ buildEntry: ({ serverUrl, apiKey }) => {
149
+ const entry = { httpUrl: serverUrl };
150
+ if (apiKey) {
151
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
152
+ }
153
+ return entry;
154
+ },
155
+ },
156
+ detect: {
157
+ projectPaths: [".gemini", path.join(".gemini", "settings.json")],
158
+ globalPaths: [
159
+ path.join(HOME, ".gemini"),
160
+ path.join(HOME, ".gemini", "settings.json"),
161
+ ],
162
+ },
163
+ },
130
164
  "pi-mono": {
131
165
  name: "pi-mono",
132
166
  displayName: "Pi (pi-mono)",
@@ -135,12 +169,13 @@ const hosts = {
135
169
  projectPaths: [path.join(".pi", "settings.json")],
136
170
  globalPaths: [path.join(HOME, ".pi", "agent", "settings.json")],
137
171
  configKey: ["mcp", "servers"],
138
- buildEntry: ({ serverUrl, apiKey }) => ({
139
- url: serverUrl,
140
- headers: {
141
- Authorization: `Bearer ${apiKey}`,
142
- },
143
- }),
172
+ buildEntry: ({ serverUrl, apiKey }) => {
173
+ const entry = { url: serverUrl };
174
+ if (apiKey) {
175
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
176
+ }
177
+ return entry;
178
+ },
144
179
  },
145
180
  detect: {
146
181
  projectPaths: [".pi", path.join(".pi", "settings.json")],
@@ -155,18 +190,31 @@ const hosts = {
155
190
  projectPaths: [path.join(".hermes", "config.yaml")],
156
191
  globalPaths: [path.join(HOME, ".hermes", "config.yaml")],
157
192
  configKey: "mcp_servers",
158
- buildEntry: ({ serverUrl, apiKey }) => ({
159
- url: serverUrl,
160
- headers: {
161
- Authorization: `Bearer ${apiKey}`,
162
- },
163
- }),
193
+ buildEntry: ({ serverUrl, apiKey }) => {
194
+ const entry = { url: serverUrl };
195
+ if (apiKey) {
196
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
197
+ }
198
+ return entry;
199
+ },
164
200
  },
165
201
  detect: {
166
202
  projectPaths: [".hermes", path.join(".hermes", "config.yaml")],
167
203
  globalPaths: [path.join(HOME, ".hermes"), path.join(HOME, ".hermes", "config.yaml")],
168
204
  },
169
205
  },
206
+ universal: {
207
+ name: "universal",
208
+ displayName: "Universal (.agents/skills)",
209
+ mcp: null,
210
+ detect: { projectPaths: [], globalPaths: [] },
211
+ },
212
+ antigravity: {
213
+ name: "antigravity",
214
+ displayName: "Antigravity (.agent/skills)",
215
+ mcp: null,
216
+ detect: { projectPaths: [], globalPaths: [] },
217
+ },
170
218
  openclaw: {
171
219
  name: "openclaw",
172
220
  displayName: "OpenClaw",
@@ -175,12 +223,13 @@ const hosts = {
175
223
  projectPaths: [path.join(".openclaw", "openclaw.json")],
176
224
  globalPaths: [path.join(HOME, ".openclaw", "openclaw.json")],
177
225
  configKey: ["mcp", "servers"],
178
- buildEntry: ({ serverUrl, apiKey }) => ({
179
- url: serverUrl,
180
- headers: {
181
- Authorization: `Bearer ${apiKey}`,
182
- },
183
- }),
226
+ buildEntry: ({ serverUrl, apiKey }) => {
227
+ const entry = { url: serverUrl };
228
+ if (apiKey) {
229
+ entry.headers = { Authorization: `Bearer ${apiKey}` };
230
+ }
231
+ return entry;
232
+ },
184
233
  },
185
234
  detect: {
186
235
  projectPaths: [".openclaw", path.join(".openclaw", "openclaw.json")],
@@ -200,6 +249,23 @@ export function getHost(name) {
200
249
  return host;
201
250
  }
202
251
 
252
+ /**
253
+ * Names of hosts whose only role is installing skill directories — they have
254
+ * no MCP configuration surface and therefore no "mode" to conflict with.
255
+ *
256
+ * Code that reasons about cross-mode conflicts, ownership of shared skill
257
+ * paths, or skill-only retain/rollback rules must iterate THIS list rather
258
+ * than the wider set of hosts that happen to use the direct-copy skill
259
+ * install path. Specifically, Gemini CLI is MCP-capable (full `mcp` block,
260
+ * `httpUrl`-keyed config under `.gemini/settings.json`) but also uses the
261
+ * direct-copy skill path; treating it as skill-only would silently let a
262
+ * CLI-to-MCP transition proceed without the canonical `--force`
263
+ * reconciliation and leave both wirings active.
264
+ */
265
+ export const SKILL_ONLY_HOST_NAMES = Object.freeze(
266
+ ALL_HOST_NAMES.filter((name) => isSkillOnlyHost(hosts[name])),
267
+ );
268
+
203
269
  async function pathExists(filePath) {
204
270
  try {
205
271
  await access(filePath);